For requesters, it must be a String of at most 64 characters from [0-9A-Za-z-_].
*
*/
-@Data
-@NoArgsConstructor
@Entity
-@Table(uniqueConstraints = { @UniqueConstraint(columnNames = {"onChainObjectAddress", "fixedSecretOwner", "key"}) })
+@Getter
+@NoArgsConstructor(access = AccessLevel.PROTECTED)
+@AllArgsConstructor(access = AccessLevel.PRIVATE)
public class TeeTaskComputeSecret implements Serializable {
- public static final int SECRET_KEY_MIN_LENGTH = 1;
- public static final int SECRET_KEY_MAX_LENGTH = 64;
-
- @Id
- @GeneratedValue(generator = "system-uuid")
- @GenericGenerator(name = "system-uuid", strategy = "uuid")
- private String id;
-
- /**
- * Represents the blockchain address of the deployed object
- * (0xapplication, 0xdataset, 0xworkerpool)
- *
- * In a future release, it should also handle ENS names.
- */
- @NotNull
- private String onChainObjectAddress; // Will be empty for a secret belonging to a requester
- @NotNull
- private OnChainObjectType onChainObjectType;
- @NotNull
- private SecretOwnerRole secretOwnerRole;
- @NotNull
- private String fixedSecretOwner; // Will be empty for a secret belonging to an application developer
@NotNull
- @Size(min = SECRET_KEY_MIN_LENGTH, max = SECRET_KEY_MAX_LENGTH)
- private String key;
+ @EmbeddedId
+ private TeeTaskComputeSecretHeader header;
+
@NotNull
/*
* Expected behavior of AES encryption is to not expand the data very much.
@@ -102,11 +80,31 @@ public TeeTaskComputeSecret(
String fixedSecretOwner,
String key,
String value) {
- this.onChainObjectType = onChainObjectType;
- this.onChainObjectAddress = onChainObjectAddress;
- this.secretOwnerRole = secretOwnerRole;
- this.fixedSecretOwner = fixedSecretOwner;
- this.key = key;
+ this.header = new TeeTaskComputeSecretHeader(
+ onChainObjectType,
+ onChainObjectAddress,
+ secretOwnerRole,
+ fixedSecretOwner,
+ key
+ );
this.value = value;
}
+
+ public TeeTaskComputeSecret withValue(String newValue) {
+ return new TeeTaskComputeSecret(header, newValue);
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ final TeeTaskComputeSecret that = (TeeTaskComputeSecret) o;
+ return Objects.equals(header, that.header)
+ && Objects.equals(value, that.value);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(header, value);
+ }
}
diff --git a/src/main/java/com/iexec/sms/secret/compute/TeeTaskComputeSecretHeader.java b/src/main/java/com/iexec/sms/secret/compute/TeeTaskComputeSecretHeader.java
new file mode 100644
index 00000000..3457eb26
--- /dev/null
+++ b/src/main/java/com/iexec/sms/secret/compute/TeeTaskComputeSecretHeader.java
@@ -0,0 +1,110 @@
+/*
+ * Copyright 2022 IEXEC BLOCKCHAIN TECH
+ *
+ * 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.
+ */
+
+package com.iexec.sms.secret.compute;
+
+import lombok.AccessLevel;
+import lombok.Getter;
+import lombok.NoArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.lang3.StringUtils;
+
+import javax.persistence.Embeddable;
+import javax.validation.*;
+import javax.validation.constraints.NotNull;
+import javax.validation.constraints.Size;
+import java.io.Serializable;
+import java.util.Objects;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+@Embeddable
+@Slf4j
+@Getter
+@NoArgsConstructor(access = AccessLevel.PROTECTED)
+public class TeeTaskComputeSecretHeader implements Serializable {
+ public static final int SECRET_KEY_MIN_LENGTH = 1;
+ public static final int SECRET_KEY_MAX_LENGTH = 64;
+
+ @NotNull
+ private OnChainObjectType onChainObjectType;
+ /**
+ * Represents the blockchain address of the deployed object
+ * (0xapplication, 0xdataset, 0xworkerpool)
+ * as a lower case string.
+ *
+ * In a future release, it should also handle ENS names.
+ */
+ @NotNull
+ private String onChainObjectAddress; // Will be empty for a secret belonging to a requester
+ @NotNull
+ private SecretOwnerRole secretOwnerRole;
+ @NotNull
+ private String fixedSecretOwner; // Will be empty for a secret belonging to an application developer
+ @NotNull
+ @Size(min = SECRET_KEY_MIN_LENGTH, max = SECRET_KEY_MAX_LENGTH)
+ private String key;
+
+ public TeeTaskComputeSecretHeader(OnChainObjectType onChainObjectType,
+ String onChainObjectAddress,
+ SecretOwnerRole secretOwnerRole,
+ String fixedSecretOwner,
+ String key) {
+ if (secretOwnerRole == SecretOwnerRole.REQUESTER && !StringUtils.isEmpty(onChainObjectAddress)) {
+ throw new ValidationException("On-chain object address should be empty for a requester secret.");
+ }
+
+ if (secretOwnerRole == SecretOwnerRole.APPLICATION_DEVELOPER && !StringUtils.isEmpty(fixedSecretOwner)) {
+ throw new ValidationException("Fixed secret owner should be empty for an application developer secret.");
+ }
+
+ this.onChainObjectAddress = onChainObjectAddress == null ? "" : onChainObjectAddress.toLowerCase();
+ this.onChainObjectType = onChainObjectType;
+ this.secretOwnerRole = secretOwnerRole;
+ this.fixedSecretOwner = fixedSecretOwner == null ? "" : fixedSecretOwner.toLowerCase();
+ this.key = key;
+
+ validateFields();
+ }
+
+ private void validateFields() {
+ try (ValidatorFactory factory = Validation.buildDefaultValidatorFactory()) {
+ final Validator validator = factory.getValidator();
+ final Set> issues = validator.validate(this);
+ if (!issues.isEmpty()) {
+ log.warn("{}", issues.stream().map(ConstraintViolation::getMessage).collect(Collectors.toList()));
+ throw new ValidationException("Can't create TeeTaskComputeSecretHeader.");
+ }
+ }
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ final TeeTaskComputeSecretHeader that = (TeeTaskComputeSecretHeader) o;
+ return onChainObjectType == that.onChainObjectType
+ && Objects.equals(onChainObjectAddress, that.onChainObjectAddress)
+ && secretOwnerRole == that.secretOwnerRole
+ && Objects.equals(fixedSecretOwner, that.fixedSecretOwner)
+ && Objects.equals(key, that.key);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(onChainObjectType, onChainObjectAddress, secretOwnerRole, fixedSecretOwner, key);
+ }
+}
diff --git a/src/main/java/com/iexec/sms/secret/compute/TeeTaskComputeSecretRepository.java b/src/main/java/com/iexec/sms/secret/compute/TeeTaskComputeSecretRepository.java
index 88180feb..2803b6b3 100644
--- a/src/main/java/com/iexec/sms/secret/compute/TeeTaskComputeSecretRepository.java
+++ b/src/main/java/com/iexec/sms/secret/compute/TeeTaskComputeSecretRepository.java
@@ -18,5 +18,5 @@
import org.springframework.data.jpa.repository.JpaRepository;
-public interface TeeTaskComputeSecretRepository extends JpaRepository {
+public interface TeeTaskComputeSecretRepository extends JpaRepository {
}
diff --git a/src/main/java/com/iexec/sms/secret/compute/TeeTaskComputeSecretService.java b/src/main/java/com/iexec/sms/secret/compute/TeeTaskComputeSecretService.java
index 0c321b04..3969b44d 100644
--- a/src/main/java/com/iexec/sms/secret/compute/TeeTaskComputeSecretService.java
+++ b/src/main/java/com/iexec/sms/secret/compute/TeeTaskComputeSecretService.java
@@ -18,9 +18,6 @@
import com.iexec.sms.encryption.EncryptionService;
import lombok.extern.slf4j.Slf4j;
-import org.apache.commons.lang3.SerializationUtils;
-import org.springframework.data.domain.Example;
-import org.springframework.data.domain.ExampleMatcher;
import org.springframework.stereotype.Service;
import java.util.Optional;
@@ -48,28 +45,21 @@ public Optional getSecret(
SecretOwnerRole secretOwnerRole,
String secretOwner,
String secretKey) {
- onChainObjectAddress = onChainObjectAddress.toLowerCase();
- final TeeTaskComputeSecret wantedSecret = TeeTaskComputeSecret
- .builder()
- .onChainObjectType(onChainObjectType)
- .onChainObjectAddress(onChainObjectAddress)
- .secretOwnerRole(secretOwnerRole)
- .fixedSecretOwner(secretOwner)
- .key(secretKey)
- .build();
- final ExampleMatcher exampleMatcher = ExampleMatcher.matching()
- .withIgnorePaths("value");
+ final TeeTaskComputeSecretHeader header = new TeeTaskComputeSecretHeader(
+ onChainObjectType,
+ onChainObjectAddress,
+ secretOwnerRole,
+ secretOwner,
+ secretKey
+ );
final Optional oSecret = teeTaskComputeSecretRepository
- .findOne(Example.of(wantedSecret, exampleMatcher));
+ .findById(header);
if (oSecret.isEmpty()) {
return Optional.empty();
}
final TeeTaskComputeSecret secret = oSecret.get();
final String decryptedValue = encryptionService.decrypt(secret.getValue());
- // deep copy to avoid altering original object
- //TODO: Improve this out-of-the box cloning to get better performances
- TeeTaskComputeSecret decryptedSecret = SerializationUtils.clone(secret);
- decryptedSecret.setValue(decryptedValue);
+ TeeTaskComputeSecret decryptedSecret = secret.withValue(decryptedValue);
return Optional.of(decryptedSecret);
}
@@ -116,7 +106,6 @@ public boolean encryptAndSaveSecret(OnChainObjectType onChainObjectType,
" [secret:{}]", secret);
return false;
}
- onChainObjectAddress = onChainObjectAddress.toLowerCase();
final TeeTaskComputeSecret secret = TeeTaskComputeSecret
.builder()
.onChainObjectType(onChainObjectType)
diff --git a/src/main/java/com/iexec/sms/secret/web2/NotAnExistingSecretException.java b/src/main/java/com/iexec/sms/secret/web2/NotAnExistingSecretException.java
new file mode 100644
index 00000000..a85749f4
--- /dev/null
+++ b/src/main/java/com/iexec/sms/secret/web2/NotAnExistingSecretException.java
@@ -0,0 +1,14 @@
+package com.iexec.sms.secret.web2;
+
+import lombok.Getter;
+
+@Getter
+public class NotAnExistingSecretException extends Exception {
+ private final String ownerAddress;
+ private final String secretAddress;
+
+ public NotAnExistingSecretException(String ownerAddress, String secretAddress) {
+ this.ownerAddress = ownerAddress;
+ this.secretAddress = secretAddress;
+ }
+}
diff --git a/src/main/java/com/iexec/sms/secret/web2/SameSecretException.java b/src/main/java/com/iexec/sms/secret/web2/SameSecretException.java
new file mode 100644
index 00000000..af06678b
--- /dev/null
+++ b/src/main/java/com/iexec/sms/secret/web2/SameSecretException.java
@@ -0,0 +1,14 @@
+package com.iexec.sms.secret.web2;
+
+import lombok.Getter;
+
+@Getter
+public class SameSecretException extends Exception {
+ private final String ownerAddress;
+ private final String secretAddress;
+
+ public SameSecretException(String ownerAddress, String secretAddress) {
+ this.ownerAddress = ownerAddress;
+ this.secretAddress = secretAddress;
+ }
+}
diff --git a/src/main/java/com/iexec/sms/secret/web2/SecretAlreadyExistsException.java b/src/main/java/com/iexec/sms/secret/web2/SecretAlreadyExistsException.java
new file mode 100644
index 00000000..eac3d0bb
--- /dev/null
+++ b/src/main/java/com/iexec/sms/secret/web2/SecretAlreadyExistsException.java
@@ -0,0 +1,14 @@
+package com.iexec.sms.secret.web2;
+
+import lombok.Getter;
+
+@Getter
+public class SecretAlreadyExistsException extends Exception {
+ private final String ownerAddress;
+ private final String secretAddress;
+
+ public SecretAlreadyExistsException(String ownerAddress, String secretAddress) {
+ this.ownerAddress = ownerAddress;
+ this.secretAddress = secretAddress;
+ }
+}
diff --git a/src/main/java/com/iexec/sms/secret/web2/Web2Secret.java b/src/main/java/com/iexec/sms/secret/web2/Web2Secret.java
new file mode 100644
index 00000000..95030797
--- /dev/null
+++ b/src/main/java/com/iexec/sms/secret/web2/Web2Secret.java
@@ -0,0 +1,55 @@
+/*
+ *
+ * * Copyright 2022 IEXEC BLOCKCHAIN TECH
+ * *
+ * * 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.
+ *
+ */
+
+package com.iexec.sms.secret.web2;
+
+import com.iexec.sms.secret.Secret;
+import lombok.AccessLevel;
+import lombok.Getter;
+import lombok.NoArgsConstructor;
+
+import javax.persistence.EmbeddedId;
+import javax.persistence.Entity;
+
+@Entity
+@Getter
+@NoArgsConstructor(access = AccessLevel.PROTECTED)
+public class Web2Secret extends Secret {
+ @EmbeddedId
+ private Web2SecretHeader header;
+
+ public Web2Secret(String ownerAddress, String address, String value) {
+ this(new Web2SecretHeader(ownerAddress, address), value);
+ }
+
+ private Web2Secret(Web2SecretHeader header, String value) {
+ super(value);
+ this.header = header;
+ }
+
+ /**
+ * Copies the current {@link Web2Secret} object,
+ * while replacing the old value with a new value.
+ *
+ * @param newValue Value to use for new object.
+ * @return A new {@link Web2Secret} object with new value.
+ */
+ public Web2Secret withValue(String newValue) {
+ return new Web2Secret(header, newValue);
+ }
+}
diff --git a/src/main/java/com/iexec/sms/secret/web2/Web2SecretHeader.java b/src/main/java/com/iexec/sms/secret/web2/Web2SecretHeader.java
new file mode 100644
index 00000000..4afebe95
--- /dev/null
+++ b/src/main/java/com/iexec/sms/secret/web2/Web2SecretHeader.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright 2022 IEXEC BLOCKCHAIN TECH
+ *
+ * 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.
+ */
+
+package com.iexec.sms.secret.web2;
+
+import lombok.AccessLevel;
+import lombok.Getter;
+import lombok.NoArgsConstructor;
+
+import javax.persistence.Embeddable;
+import java.io.Serializable;
+import java.util.Objects;
+
+@Embeddable
+@Getter
+@NoArgsConstructor(access = AccessLevel.PROTECTED)
+public class Web2SecretHeader implements Serializable {
+ private static final long serialVersionUID = -6126999592529129002L;
+ private String ownerAddress;
+ private String address; //0xdataset1, aws.amazon.com, beneficiary.key.iex.ec (Kb)
+
+ Web2SecretHeader(String ownerAddress, String address) {
+ Objects.requireNonNull(ownerAddress, "Web2 secret owner address can't be null.");
+ Objects.requireNonNull(address, "Web2 secret address can't be null.");
+
+ this.ownerAddress = ownerAddress.toLowerCase();
+ this.address = address.toLowerCase();
+ }
+}
diff --git a/src/main/java/com/iexec/sms/secret/web2/Web2SecretRepository.java b/src/main/java/com/iexec/sms/secret/web2/Web2SecretRepository.java
new file mode 100644
index 00000000..cbeacde1
--- /dev/null
+++ b/src/main/java/com/iexec/sms/secret/web2/Web2SecretRepository.java
@@ -0,0 +1,25 @@
+/*
+ *
+ * * Copyright 2022 IEXEC BLOCKCHAIN TECH
+ * *
+ * * 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.
+ *
+ */
+
+package com.iexec.sms.secret.web2;
+
+import org.springframework.data.repository.CrudRepository;
+
+public interface Web2SecretRepository extends CrudRepository {
+
+}
diff --git a/src/main/java/com/iexec/sms/secret/web2/Web2SecretService.java b/src/main/java/com/iexec/sms/secret/web2/Web2SecretService.java
new file mode 100644
index 00000000..112f5268
--- /dev/null
+++ b/src/main/java/com/iexec/sms/secret/web2/Web2SecretService.java
@@ -0,0 +1,115 @@
+/*
+ *
+ * * Copyright 2022 IEXEC BLOCKCHAIN TECH
+ * *
+ * * 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.
+ *
+ */
+
+package com.iexec.sms.secret.web2;
+
+import com.iexec.sms.encryption.EncryptionService;
+import com.iexec.sms.secret.AbstractSecretService;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.stereotype.Service;
+
+import java.util.Objects;
+import java.util.Optional;
+
+@Slf4j
+@Service
+public class Web2SecretService extends AbstractSecretService {
+ private final Web2SecretRepository web2SecretRepository;
+
+ protected Web2SecretService(EncryptionService encryptionService,
+ Web2SecretRepository web2SecretRepository) {
+ super(encryptionService);
+ this.web2SecretRepository = web2SecretRepository;
+ }
+
+ /**
+ * Get the secret as it was saved in DB.
+ * Its value should then be encrypted.
+ *
+ * @param ownerAddress Address of the secret owner.
+ * @param secretAddress Address of the secret.
+ * @return An empty {@link Optional} if no secret is found,
+ * an {@link Optional} containing the secret if it exists.
+ */
+ Optional getSecret(String ownerAddress, String secretAddress) {
+ return web2SecretRepository.findById(new Web2SecretHeader(ownerAddress, secretAddress));
+ }
+
+ public Optional getDecryptedValue(String ownerAddress, String secretAddress) {
+ return getSecret(ownerAddress, secretAddress)
+ .map(secret -> encryptionService.decrypt(secret.getValue()));
+ }
+
+ public boolean isSecretPresent(String ownerAddress, String secretAddress) {
+ return getSecret(ownerAddress, secretAddress).isPresent();
+ }
+
+ /**
+ * Creates and saves a new {@link Web2Secret}.
+ * If a secret with same {@code ownerAddress}/{@code secretAddress} couple already exists, then cancels the save.
+ *
+ * @param ownerAddress Address of the secret owner.
+ * @param secretAddress Address of the secret.
+ * @param secretValue Unencrypted value of the secret.
+ * @return The {@link Web2Secret} that has been saved.
+ * @throws SecretAlreadyExistsException throw when a secret
+ * with same {@code ownerAddress}/{@code secretAddress} couple already exists
+ */
+ public Web2Secret addSecret(String ownerAddress, String secretAddress, String secretValue) throws SecretAlreadyExistsException {
+ if (isSecretPresent(ownerAddress, secretAddress)) {
+ log.error("Secret already exists [ownerAddress:{}, secretAddress:{}]", ownerAddress, secretAddress);
+ throw new SecretAlreadyExistsException(ownerAddress, secretAddress);
+ }
+
+ final String encryptedValue = encryptionService.encrypt(secretValue);
+ final Web2Secret newSecret = new Web2Secret(ownerAddress, secretAddress, encryptedValue);
+ return web2SecretRepository.save(newSecret);
+ }
+
+ /**
+ * Updates an existing {@link Web2Secret}.
+ * If the secret does not already exist, then cancels the save.
+ * If the secret already exists with the same encrypted value, then cancels the save.
+ *
+ * @param ownerAddress Address of the secret owner.
+ * @param secretAddress Address of the secret.
+ * @param newSecretValue New, unencrypted value of the secret.
+ * @return The {@link Web2Secret} that has been saved.
+ * @throws NotAnExistingSecretException thrown when the requested secret does not exist.
+ * @throws SameSecretException thrown when the requested secret already contains the encrypted value.
+ */
+ public Web2Secret updateSecret(String ownerAddress, String secretAddress, String newSecretValue) throws NotAnExistingSecretException, SameSecretException {
+ final Optional oSecret = getSecret(ownerAddress, secretAddress);
+ if (oSecret.isEmpty()) {
+ log.error("Secret does not exist, can't update it [ownerAddress:{}, secretAddress:{}]",
+ ownerAddress, secretAddress);
+ throw new NotAnExistingSecretException(ownerAddress, secretAddress);
+ }
+
+ final Web2Secret secret = oSecret.get();
+ final String encryptedValue = encryptionService.encrypt(newSecretValue);
+ if (Objects.equals(secret.getValue(), encryptedValue)) {
+ log.info("No need to update secret [ownerAddress:{}, secretAddress:{}]",
+ ownerAddress, secretAddress);
+ throw new SameSecretException(ownerAddress, secretAddress);
+ }
+
+ final Web2Secret newSecret = secret.withValue(encryptedValue);
+ return web2SecretRepository.save(newSecret);
+ }
+}
diff --git a/src/main/java/com/iexec/sms/secret/web2/Web2Secrets.java b/src/main/java/com/iexec/sms/secret/web2/Web2Secrets.java
deleted file mode 100644
index dccdebd9..00000000
--- a/src/main/java/com/iexec/sms/secret/web2/Web2Secrets.java
+++ /dev/null
@@ -1,57 +0,0 @@
-/*
- * Copyright 2020 IEXEC BLOCKCHAIN TECH
- *
- * 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.
- */
-
-package com.iexec.sms.secret.web2;
-
-import com.iexec.sms.secret.Secret;
-import lombok.Data;
-import lombok.Getter;
-import lombok.NoArgsConstructor;
-import org.hibernate.annotations.GenericGenerator;
-
-import javax.persistence.*;
-import java.util.ArrayList;
-import java.util.List;
-
-@Data
-@Getter
-@Entity
-@NoArgsConstructor
-public class Web2Secrets {
-
- @Id
- @GeneratedValue(generator = "system-uuid")
- @GenericGenerator(name = "system-uuid", strategy = "uuid")
- private String id;
-
- private String ownerAddress;
- @OneToMany(cascade = {CascadeType.ALL})
- private List secrets;
-
- Web2Secrets(String ownerAddress) {
- this.ownerAddress = ownerAddress;
- this.secrets = new ArrayList<>();
- }
-
- public Secret getSecret(String secretAddress) {
- for (Secret secret : secrets) {
- if (secret.getAddress().equals(secretAddress)) {
- return secret;
- }
- }
- return null;
- }
-}
diff --git a/src/main/java/com/iexec/sms/secret/web2/Web2SecretsService.java b/src/main/java/com/iexec/sms/secret/web2/Web2SecretsService.java
deleted file mode 100644
index 4122451f..00000000
--- a/src/main/java/com/iexec/sms/secret/web2/Web2SecretsService.java
+++ /dev/null
@@ -1,98 +0,0 @@
-/*
- * Copyright 2020 IEXEC BLOCKCHAIN TECH
- *
- * 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.
- */
-
-package com.iexec.sms.secret.web2;
-
-
-import com.iexec.sms.encryption.EncryptionService;
-import com.iexec.sms.secret.AbstractSecretService;
-import com.iexec.sms.secret.Secret;
-import lombok.extern.slf4j.Slf4j;
-import org.springframework.stereotype.Service;
-
-import java.util.Optional;
-
-@Slf4j
-@Service
-public class Web2SecretsService extends AbstractSecretService {
-
- private final Web2SecretsRepository web2SecretsRepository;
-
- public Web2SecretsService(Web2SecretsRepository web2SecretsRepository,
- EncryptionService encryptionService) {
- super(encryptionService);
- this.web2SecretsRepository = web2SecretsRepository;
- }
-
- public Optional getWeb2Secrets(String ownerAddress) {
- ownerAddress = ownerAddress.toLowerCase();
- return web2SecretsRepository.findWeb2SecretsByOwnerAddress(ownerAddress);
- }
-
- public Optional getSecret(String ownerAddress, String secretAddress) {
- return getSecret(ownerAddress, secretAddress, false);
- }
-
- public Optional getSecret(String ownerAddress, String secretAddress, boolean shouldDecryptValue) {
- ownerAddress = ownerAddress.toLowerCase();
- Optional web2Secrets = getWeb2Secrets(ownerAddress);
- if (web2Secrets.isEmpty()) {
- return Optional.empty();
- }
- Secret secret = web2Secrets.get().getSecret(secretAddress);
- if (secret == null) {
- return Optional.empty();
- }
- if (shouldDecryptValue) {
- decryptSecret(secret);
- }
- return Optional.of(secret);
- }
-
- public void addSecret(String ownerAddress, String secretAddress, String secretValue) {
- ownerAddress = ownerAddress.toLowerCase();
- Web2Secrets web2Secrets = new Web2Secrets(ownerAddress);
- Optional existingWeb2Secrets = getWeb2Secrets(ownerAddress);
- if (existingWeb2Secrets.isPresent()) {
- web2Secrets = existingWeb2Secrets.get();
- }
-
- Secret secret = new Secret(secretAddress, secretValue);
- encryptSecret(secret);
- log.info("Adding new secret [ownerAddress:{}, secretAddress:{}, encryptedSecretValue:{}]",
- ownerAddress, secretAddress, secret.getValue());
- web2Secrets.getSecrets().add(secret);
- web2SecretsRepository.save(web2Secrets);
- }
-
- public void updateSecret(String ownerAddress, String secretAddress, String newSecretValue) {
- ownerAddress = ownerAddress.toLowerCase();
- Secret newSecret = new Secret(secretAddress, newSecretValue);
- encryptSecret(newSecret);
- Optional web2Secrets = getWeb2Secrets(ownerAddress);
- Secret existingSecret = web2Secrets.get().getSecret(secretAddress);
- if (existingSecret.getValue().equals(newSecret.getValue())) {
- log.info("No need to update secret [ownerAddress:{}, secretAddress:{}]",
- ownerAddress, secretAddress);
- return;
- }
-
- log.info("Updating secret [ownerAddress:{}, secretAddress:{}, oldEncryptedSecretValue:{}, newEncryptedSecretValue:{}]",
- ownerAddress, secretAddress, existingSecret.getValue(), newSecret.getValue());
- existingSecret.setValue(newSecret.getValue(), true);
- web2SecretsRepository.save(web2Secrets.get());
- }
-}
diff --git a/src/main/java/com/iexec/sms/secret/web3/Web3Secret.java b/src/main/java/com/iexec/sms/secret/web3/Web3Secret.java
index 570f2a8d..75f35292 100644
--- a/src/main/java/com/iexec/sms/secret/web3/Web3Secret.java
+++ b/src/main/java/com/iexec/sms/secret/web3/Web3Secret.java
@@ -17,29 +17,26 @@
package com.iexec.sms.secret.web3;
import com.iexec.sms.secret.Secret;
-import lombok.Data;
-import lombok.EqualsAndHashCode;
+import lombok.AccessLevel;
import lombok.Getter;
import lombok.NoArgsConstructor;
-import org.hibernate.annotations.GenericGenerator;
+import javax.persistence.EmbeddedId;
import javax.persistence.Entity;
-import javax.persistence.GeneratedValue;
-import javax.persistence.Id;
-@EqualsAndHashCode(callSuper = true)
-@Data
-@Getter
@Entity
-@NoArgsConstructor
+@Getter
+@NoArgsConstructor(access = AccessLevel.PROTECTED)
public class Web3Secret extends Secret {
-
- @Id
- @GeneratedValue(generator = "system-uuid")
- @GenericGenerator(name = "system-uuid", strategy = "uuid")
- private String id;
+ @EmbeddedId
+ private Web3SecretHeader header;
public Web3Secret(String address, String value) {
- super(address, value);
+ this(new Web3SecretHeader(address), value);
+ }
+
+ private Web3Secret(Web3SecretHeader header, String value) {
+ super(value);
+ this.header = header;
}
}
diff --git a/src/main/java/com/iexec/sms/secret/web3/Web3SecretHeader.java b/src/main/java/com/iexec/sms/secret/web3/Web3SecretHeader.java
new file mode 100644
index 00000000..be856cb2
--- /dev/null
+++ b/src/main/java/com/iexec/sms/secret/web3/Web3SecretHeader.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright 2022 IEXEC BLOCKCHAIN TECH
+ *
+ * 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.
+ */
+
+package com.iexec.sms.secret.web3;
+
+import lombok.AccessLevel;
+import lombok.Getter;
+import lombok.NoArgsConstructor;
+
+import javax.persistence.Embeddable;
+import java.io.Serializable;
+import java.util.Objects;
+
+@Embeddable
+@Getter
+@NoArgsConstructor(access = AccessLevel.PROTECTED)
+public class Web3SecretHeader implements Serializable {
+ private static final long serialVersionUID = -6181164795694317827L;
+ private String address;
+
+ Web3SecretHeader(String address) {
+ Objects.requireNonNull(address, "Web3 secret address can't be null.");
+
+ this.address = address.toLowerCase();
+ }
+}
diff --git a/src/main/java/com/iexec/sms/secret/web3/Web3SecretRepository.java b/src/main/java/com/iexec/sms/secret/web3/Web3SecretRepository.java
index 9b2d39c5..6a23e496 100644
--- a/src/main/java/com/iexec/sms/secret/web3/Web3SecretRepository.java
+++ b/src/main/java/com/iexec/sms/secret/web3/Web3SecretRepository.java
@@ -16,13 +16,8 @@
package com.iexec.sms.secret.web3;
-
import org.springframework.data.repository.CrudRepository;
-import java.util.Optional;
-
-public interface Web3SecretRepository extends CrudRepository {
-
- Optional findWeb3SecretByAddress(String secretAddress);
+public interface Web3SecretRepository extends CrudRepository {
}
diff --git a/src/main/java/com/iexec/sms/secret/web3/Web3SecretService.java b/src/main/java/com/iexec/sms/secret/web3/Web3SecretService.java
index ecf197b8..fbd3f5c1 100644
--- a/src/main/java/com/iexec/sms/secret/web3/Web3SecretService.java
+++ b/src/main/java/com/iexec/sms/secret/web3/Web3SecretService.java
@@ -36,33 +36,44 @@ public Web3SecretService(Web3SecretRepository web3SecretRepository,
this.web3SecretRepository = web3SecretRepository;
}
- public Optional getSecret(String secretAddress, boolean shouldDecryptValue) {
- secretAddress = secretAddress.toLowerCase();
- Optional secret = web3SecretRepository.findWeb3SecretByAddress(secretAddress);
- if (secret.isEmpty()) {
- return Optional.empty();
- }
- if (shouldDecryptValue) {
- decryptSecret(secret.get());
- }
- return secret;
+ /**
+ * Get the secret as it was saved in DB.
+ * Its value should then be encrypted.
+ *
+ * @param secretAddress Address of the secret.
+ * @return An empty {@link Optional} if no secret is found,
+ * an {@link Optional} containing the secret if it exists.
+ */
+ Optional getSecret(String secretAddress) {
+ return web3SecretRepository.findById(new Web3SecretHeader(secretAddress));
+ }
+
+ public Optional getDecryptedValue(String secretAddress) {
+ return getSecret(secretAddress)
+ .map(secret -> encryptionService.decrypt(secret.getValue()));
}
- public Optional getSecret(String secretAddress) {
- return getSecret(secretAddress, false);
+ public boolean isSecretPresent(String secretAddress) {
+ return getSecret(secretAddress).isPresent();
}
/*
*
* Stores encrypted secrets
* */
- public void addSecret(String secretAddress, String secretValue) {
- secretAddress = secretAddress.toLowerCase();
- Web3Secret web3Secret = new Web3Secret(secretAddress, secretValue);
- encryptSecret(web3Secret);
+ public boolean addSecret(String secretAddress, String secretValue) {
+ if (isSecretPresent(secretAddress)) {
+ log.error("Secret already exists [secretAddress:{}]", secretAddress);
+ return false;
+ }
+
+ final String encryptedValue = encryptionService.encrypt(secretValue);
log.info("Adding new web3 secret [secretAddress:{}, encryptedSecretValue:{}]",
- secretAddress, web3Secret.getValue());
+ secretAddress, encryptedValue);
+
+ final Web3Secret web3Secret = new Web3Secret(secretAddress, encryptedValue);
web3SecretRepository.save(web3Secret);
+ return true;
}
}
diff --git a/src/main/java/com/iexec/sms/ssl/SslConfig.java b/src/main/java/com/iexec/sms/ssl/SslConfig.java
index 78eb0c54..9f8a0ffd 100644
--- a/src/main/java/com/iexec/sms/ssl/SslConfig.java
+++ b/src/main/java/com/iexec/sms/ssl/SslConfig.java
@@ -16,6 +16,9 @@
package com.iexec.sms.ssl;
+import com.iexec.common.tee.TeeFramework;
+import com.iexec.sms.tee.ConditionalOnTeeFramework;
+import lombok.extern.slf4j.Slf4j;
import org.apache.http.ssl.SSLContexts;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Configuration;
@@ -29,19 +32,21 @@
import java.security.UnrecoverableKeyException;
import java.security.cert.CertificateException;
+@Slf4j
@Configuration
+@ConditionalOnTeeFramework(frameworks = TeeFramework.SCONE)
public class SslConfig {
- private String sslKeystore;
- private String sslKeystoreType;
- private String sslKeyAlias;
- private char[] sslKeystorePasswordChar;
+ private final String sslKeystore;
+ private final String sslKeystoreType;
+ private final String sslKeyAlias;
+ private final char[] sslKeystorePasswordChar;
public SslConfig(
- @Value("${server.ssl.key-store}") String sslKeystore,
- @Value("${server.ssl.key-store-type}") String sslKeystoreType,
- @Value("${server.ssl.key-alias}") String sslKeyAlias,
- @Value("${server.ssl.key-store-password}") String sslKeystorePassword) {
+ @Value("${tee.ssl.key-store}") String sslKeystore,
+ @Value("${tee.ssl.key-store-type}") String sslKeystoreType,
+ @Value("${tee.ssl.key-alias}") String sslKeyAlias,
+ @Value("${tee.ssl.key-store-password}") String sslKeystorePassword) {
this.sslKeystore = sslKeystore;
this.sslKeystoreType = sslKeystoreType;
this.sslKeyAlias = sslKeyAlias;
@@ -62,7 +67,7 @@ public SSLContext getFreshSslContext() {
.loadTrustMaterial(null, (chain, authType) -> true)////TODO: Add CAS certificate to truststore
.build();
} catch (IOException | NoSuchAlgorithmException | KeyStoreException | UnrecoverableKeyException | CertificateException | KeyManagementException e) {
- e.printStackTrace();
+ log.warn("Failed to create a fresh SSL context", e);
}
return null;
}
diff --git a/src/main/java/com/iexec/sms/ssl/TwoWaySslClient.java b/src/main/java/com/iexec/sms/ssl/TwoWaySslClient.java
index 9056488c..c6b0aad5 100644
--- a/src/main/java/com/iexec/sms/ssl/TwoWaySslClient.java
+++ b/src/main/java/com/iexec/sms/ssl/TwoWaySslClient.java
@@ -16,6 +16,8 @@
package com.iexec.sms.ssl;
+import com.iexec.common.tee.TeeFramework;
+import com.iexec.sms.tee.ConditionalOnTeeFramework;
import org.apache.http.conn.ssl.NoopHostnameVerifier;
import org.apache.http.impl.client.HttpClientBuilder;
import org.springframework.context.annotation.Configuration;
@@ -23,6 +25,7 @@
import org.springframework.web.client.RestTemplate;
@Configuration
+@ConditionalOnTeeFramework(frameworks = TeeFramework.SCONE)
public class TwoWaySslClient {
private final SslConfig sslConfig;
diff --git a/src/main/java/com/iexec/sms/tee/ConditionalOnTeeFramework.java b/src/main/java/com/iexec/sms/tee/ConditionalOnTeeFramework.java
new file mode 100644
index 00000000..8f83b789
--- /dev/null
+++ b/src/main/java/com/iexec/sms/tee/ConditionalOnTeeFramework.java
@@ -0,0 +1,27 @@
+package com.iexec.sms.tee;
+
+import com.iexec.common.tee.TeeFramework;
+import org.springframework.context.annotation.Conditional;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * Defines a way to include beans based on active profiles.
+ *
+ * E.g.:
+ *
+ * - a {@code SconeConfig} bean annotated with {@code ConditionalOnTeeFramework(frameworks = SCONE)}
+ * will be loaded only if a {@code scone} profile is active.
+ * - a {@code TeeConfig} bean annotated with {@code ConditionalOnTeeFramework(frameworks = {SCONE, GRAMINE})}
+ * will be loaded only if any of {@code scone} or {@code gramine} profile is active.
+ *
+ */
+@Target({ ElementType.TYPE, ElementType.METHOD })
+@Retention(RetentionPolicy.RUNTIME)
+@Conditional(OnTeeFrameworkCondition.class)
+public @interface ConditionalOnTeeFramework {
+ TeeFramework[] frameworks();
+}
diff --git a/src/main/java/com/iexec/sms/tee/OnTeeFrameworkCondition.java b/src/main/java/com/iexec/sms/tee/OnTeeFrameworkCondition.java
new file mode 100644
index 00000000..741215b6
--- /dev/null
+++ b/src/main/java/com/iexec/sms/tee/OnTeeFrameworkCondition.java
@@ -0,0 +1,72 @@
+package com.iexec.sms.tee;
+
+import com.iexec.common.tee.TeeFramework;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.boot.autoconfigure.condition.ConditionMessage;
+import org.springframework.boot.autoconfigure.condition.ConditionOutcome;
+import org.springframework.boot.autoconfigure.condition.SpringBootCondition;
+import org.springframework.context.annotation.Condition;
+import org.springframework.context.annotation.ConditionContext;
+import org.springframework.core.type.AnnotatedTypeMetadata;
+import org.springframework.core.type.AnnotationMetadata;
+import org.springframework.core.type.MethodMetadata;
+
+import java.util.Arrays;
+import java.util.Map;
+
+/**
+ * {@link Condition} that checks for a specific profile to be enabled.
+ * To be used with {@link ConditionalOnTeeFramework}.
+ */
+@Slf4j
+public class OnTeeFrameworkCondition extends SpringBootCondition {
+ @Override
+ public ConditionOutcome getMatchOutcome(ConditionContext context, AnnotatedTypeMetadata metadata) {
+ final Map attributes = metadata.getAnnotationAttributes(ConditionalOnTeeFramework.class.getName());
+ final String[] activeProfiles = context.getEnvironment().getActiveProfiles();
+
+ final String beanClassName;
+
+ if (metadata instanceof AnnotationMetadata) {
+ beanClassName = ((AnnotationMetadata) metadata).getClassName();
+ } else {
+ beanClassName = ((MethodMetadata) metadata).getMethodName();
+ }
+
+ if (attributes == null) {
+ log.warn("No attribute for bean annotation, won't be loaded [bean:{}",
+ beanClassName);
+ return new ConditionOutcome(
+ false,
+ ConditionMessage.forCondition(ConditionalOnTeeFramework.class).didNotFind("any TEE frameworks").atAll());
+ }
+
+ final TeeFramework[] frameworks = (TeeFramework[]) attributes.get("frameworks");
+ if (frameworks == null || frameworks.length == 0) {
+ log.warn(
+ "No TEE framework defined for bean, won't be loaded [bean:{}]",
+ beanClassName);
+ return new ConditionOutcome(
+ false,
+ ConditionMessage.forCondition(ConditionalOnTeeFramework.class).didNotFind("any TEE frameworks").atAll());
+ }
+
+ for (String activeProfile : activeProfiles) {
+ for (TeeFramework framework : frameworks) {
+ if (activeProfile.equalsIgnoreCase(framework.name())) {
+ return new ConditionOutcome(
+ true,
+ ConditionMessage.forCondition(ConditionalOnTeeFramework.class).foundExactly(framework));
+ }
+ }
+ }
+
+ log.debug(
+ "Active profiles and condition don't match, bean won't be loaded [bean:{}]",
+ beanClassName);
+
+ return new ConditionOutcome(
+ false,
+ ConditionMessage.forCondition(ConditionalOnTeeFramework.class).didNotFind("profile", "profiles").items(Arrays.asList(frameworks)));
+ }
+}
diff --git a/src/main/java/com/iexec/sms/tee/TeeController.java b/src/main/java/com/iexec/sms/tee/TeeController.java
index 917eb875..d52a468c 100644
--- a/src/main/java/com/iexec/sms/tee/TeeController.java
+++ b/src/main/java/com/iexec/sms/tee/TeeController.java
@@ -18,16 +18,17 @@
import com.iexec.common.chain.WorkerpoolAuthorization;
-import com.iexec.sms.api.TeeSessionGenerationError;
-import com.iexec.common.tee.TeeWorkflowSharedConfiguration;
+import com.iexec.common.tee.TeeFramework;
import com.iexec.common.web.ApiResponseBody;
+import com.iexec.sms.api.TeeSessionGenerationError;
+import com.iexec.sms.api.TeeSessionGenerationResponse;
+import com.iexec.sms.api.config.TeeServicesProperties;
import com.iexec.sms.authorization.AuthorizationError;
import com.iexec.sms.authorization.AuthorizationService;
import com.iexec.sms.tee.challenge.TeeChallenge;
import com.iexec.sms.tee.challenge.TeeChallengeService;
-import com.iexec.sms.tee.session.TeeSessionGenerationException;
import com.iexec.sms.tee.session.TeeSessionService;
-import com.iexec.sms.tee.workflow.TeeWorkflowConfiguration;
+import com.iexec.sms.tee.session.generic.TeeSessionGenerationException;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
@@ -57,33 +58,45 @@ public class TeeController {
private final AuthorizationService authorizationService;
private final TeeChallengeService teeChallengeService;
private final TeeSessionService teeSessionService;
- private final TeeWorkflowConfiguration teeWorkflowConfig;
+ private final TeeServicesProperties teeServicesProperties;
public TeeController(
AuthorizationService authorizationService,
TeeChallengeService teeChallengeService,
TeeSessionService teeSessionService,
- TeeWorkflowConfiguration teeWorkflowConfig) {
+ TeeServicesProperties teeServicesProperties) {
this.authorizationService = authorizationService;
this.teeChallengeService = teeChallengeService;
this.teeSessionService = teeSessionService;
- this.teeWorkflowConfig = teeWorkflowConfig;
+ this.teeServicesProperties = teeServicesProperties;
}
/**
- * Retrieve configuration for tee workflow. This includes configuration
- * for pre-compute and post-compute stages.
- *
- * Note: Being able to read the fingerprints on this endpoint is not required
- * for the workflow but it might be convenient to keep it for
- * transparency purposes.
+ * Return which TEE framework this SMS is configured to use.
+ * @return TEE framework this SMS is configured to use.
+ */
+ @GetMapping("/framework")
+ public ResponseEntity getTeeFramework() {
+ return ResponseEntity.ok(teeServicesProperties.getTeeFramework());
+ }
+
+ /**
+ * Retrieve properties for TEE services. This includes properties
+ * for pre-compute and post-compute stages
+ * and potential TEE framework's specific data.
*
- * @return tee workflow config (pre-compute image uri, post-compute image uri,
- * pre-compute fingerprint, heap size, ...)
+ * @return TEE services properties (pre-compute image uri, post-compute image uri,
+ * heap size, ...)
*/
- @GetMapping("/workflow/config")
- public ResponseEntity getTeeWorkflowSharedConfig() {
- return ResponseEntity.ok(teeWorkflowConfig.getSharedConfiguration());
+ @GetMapping("/properties/{teeFramework}")
+ public ResponseEntity getTeeServicesProperties(
+ @PathVariable TeeFramework teeFramework) {
+ if (teeFramework != teeServicesProperties.getTeeFramework()) {
+ log.error("SMS configured to use another TeeFramework " +
+ "[required:{}, actual:{}]", teeFramework, teeServicesProperties.getTeeFramework());
+ return ResponseEntity.status(HttpStatus.CONFLICT).build();
+ }
+ return ResponseEntity.ok(teeServicesProperties);
}
/**
@@ -111,14 +124,14 @@ public ResponseEntity generateTeeChallenge(@PathVariable String chainTas
* 500 INTERNAL_SERVER_ERROR otherwise.
*/
@PostMapping("/sessions")
- public ResponseEntity> generateTeeSession(
+ public ResponseEntity> generateTeeSession(
@RequestHeader("Authorization") String authorization,
@RequestBody WorkerpoolAuthorization workerpoolAuthorization) {
String workerAddress = workerpoolAuthorization.getWorkerWallet();
String challenge = authorizationService.getChallengeForWorker(workerpoolAuthorization);
if (!authorizationService.isSignedByHimself(challenge, authorization, workerAddress)) {
- final ApiResponseBody body =
- ApiResponseBody.builder()
+ final ApiResponseBody body =
+ ApiResponseBody.builder()
.error(INVALID_AUTHORIZATION)
.build();
@@ -132,8 +145,8 @@ public ResponseEntity> genera
final TeeSessionGenerationError teeSessionGenerationError =
authorizationToGenerationError.get(authorizationError.get());
- final ApiResponseBody body =
- ApiResponseBody.builder()
+ final ApiResponseBody body =
+ ApiResponseBody.builder()
.error(teeSessionGenerationError)
.build();
@@ -147,19 +160,21 @@ public ResponseEntity> genera
log.info("TEE session request [taskId:{}, workerAddress:{}]",
taskId, workerAddress);
try {
- String sessionId = teeSessionService
+ TeeSessionGenerationResponse teeSessionGenerationResponse = teeSessionService
.generateTeeSession(taskId, workerAddress, attestingEnclave);
- if (sessionId.isEmpty()) {
+ if (teeSessionGenerationResponse == null) {
return ResponseEntity.notFound().build();
}
- return ResponseEntity.ok(ApiResponseBody.builder().data(sessionId).build());
+ return ResponseEntity.ok(ApiResponseBody.builder()
+ .data(teeSessionGenerationResponse)
+ .build());
} catch(TeeSessionGenerationException e) {
log.error("Failed to generate secure session [taskId:{}, workerAddress:{}]",
taskId, workerAddress, e);
- final ApiResponseBody body =
- ApiResponseBody.builder()
+ final ApiResponseBody body =
+ ApiResponseBody.builder()
.error(e.getError())
.build();
return ResponseEntity
@@ -168,8 +183,8 @@ public ResponseEntity> genera
} catch (Exception e) {
log.error("Failed to generate secure session with unknown reason [taskId:{}, workerAddress:{}]",
taskId, workerAddress, e);
- final ApiResponseBody body =
- ApiResponseBody.builder()
+ final ApiResponseBody body =
+ ApiResponseBody.builder()
.error(SECURE_SESSION_GENERATION_FAILED)
.build();
return ResponseEntity
diff --git a/src/main/java/com/iexec/sms/tee/TeeFrameworkConverter.java b/src/main/java/com/iexec/sms/tee/TeeFrameworkConverter.java
new file mode 100644
index 00000000..78ccd54e
--- /dev/null
+++ b/src/main/java/com/iexec/sms/tee/TeeFrameworkConverter.java
@@ -0,0 +1,17 @@
+package com.iexec.sms.tee;
+
+import com.iexec.common.tee.TeeFramework;
+import org.springframework.core.convert.converter.Converter;
+import org.springframework.stereotype.Component;
+
+/**
+ * This class is needed to have a case-insensitive `teeFramework` path variable in
+ * {@link TeeController#getTeeServicesProperties(TeeFramework)}.
+ */
+@Component
+public class TeeFrameworkConverter implements Converter {
+ @Override
+ public TeeFramework convert(String value) {
+ return TeeFramework.valueOf(value.toUpperCase());
+ }
+}
diff --git a/src/main/java/com/iexec/sms/tee/challenge/TeeChallengeService.java b/src/main/java/com/iexec/sms/tee/challenge/TeeChallengeService.java
index 992db397..551e0c61 100644
--- a/src/main/java/com/iexec/sms/tee/challenge/TeeChallengeService.java
+++ b/src/main/java/com/iexec/sms/tee/challenge/TeeChallengeService.java
@@ -29,8 +29,8 @@
@Service
public class TeeChallengeService {
- private TeeChallengeRepository teeChallengeRepository;
- private EncryptionService encryptionService;
+ private final TeeChallengeRepository teeChallengeRepository;
+ private final EncryptionService encryptionService;
public TeeChallengeService(TeeChallengeRepository teeChallengeRepository,
EncryptionService encryptionService) {
diff --git a/src/main/java/com/iexec/sms/tee/config/TeeWorkerInternalConfiguration.java b/src/main/java/com/iexec/sms/tee/config/TeeWorkerInternalConfiguration.java
new file mode 100644
index 00000000..e907ba26
--- /dev/null
+++ b/src/main/java/com/iexec/sms/tee/config/TeeWorkerInternalConfiguration.java
@@ -0,0 +1,82 @@
+package com.iexec.sms.tee.config;
+
+import com.iexec.common.tee.TeeFramework;
+import com.iexec.sms.api.config.GramineServicesProperties;
+import com.iexec.sms.api.config.SconeServicesProperties;
+import com.iexec.sms.api.config.TeeAppProperties;
+import com.iexec.sms.tee.ConditionalOnTeeFramework;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.util.unit.DataSize;
+import org.springframework.validation.annotation.Validated;
+
+import javax.validation.constraints.NotBlank;
+import javax.validation.constraints.Positive;
+
+@Configuration
+@Validated
+public class TeeWorkerInternalConfiguration {
+ @Bean
+ TeeAppProperties preComputeProperties(
+ @Value("${tee.worker.pre-compute.image}")
+ @NotBlank(message = "pre-compute image must be provided")
+ String preComputeImage,
+ @Value("${tee.worker.pre-compute.fingerprint}")
+ @NotBlank(message = "pre-compute fingerprint must be provided")
+ String preComputeFingerprint,
+ @Value("${tee.worker.pre-compute.entrypoint}")
+ @NotBlank(message = "pre-compute entrypoint must be provided")
+ String preComputeEntrypoint,
+ @Value("${tee.worker.pre-compute.heap-size-gb}")
+ @Positive(message = "pre-compute heap size must be provided")
+ long preComputeHeapSizeInGB) {
+ return new TeeAppProperties(
+ preComputeImage,
+ preComputeFingerprint,
+ preComputeEntrypoint,
+ DataSize.ofGigabytes(preComputeHeapSizeInGB).toBytes()
+ );
+ }
+
+ @Bean
+ TeeAppProperties postComputeProperties(
+ @Value("${tee.worker.post-compute.image}")
+ @NotBlank(message = "post-compute image must be provided")
+ String postComputeImage,
+ @Value("${tee.worker.post-compute.fingerprint}")
+ @NotBlank(message = "post-compute fingerprint must be provided")
+ String postComputeFingerprint,
+ @Value("${tee.worker.post-compute.entrypoint}")
+ @NotBlank(message = "post-compute entrypoint must be provided")
+ String postComputeEntrypoint,
+ @Value("${tee.worker.post-compute.heap-size-gb}")
+ @Positive(message = "post-compute heap size must be provided")
+ long postComputeHeapSizeInGB) {
+ return new TeeAppProperties(
+ postComputeImage,
+ postComputeFingerprint,
+ postComputeEntrypoint,
+ DataSize.ofGigabytes(postComputeHeapSizeInGB).toBytes()
+ );
+ }
+
+ @Bean
+ @ConditionalOnTeeFramework(frameworks = TeeFramework.GRAMINE)
+ GramineServicesProperties gramineServicesProperties(
+ TeeAppProperties preComputeProperties,
+ TeeAppProperties postComputeProperties) {
+ return new GramineServicesProperties(preComputeProperties, postComputeProperties);
+ }
+
+ @Bean
+ @ConditionalOnTeeFramework(frameworks = TeeFramework.SCONE)
+ SconeServicesProperties sconeServicesProperties(
+ TeeAppProperties preComputeProperties,
+ TeeAppProperties postComputeProperties,
+ @Value("${tee.scone.las-image}")
+ @NotBlank(message = "las image must be provided")
+ String lasImage) {
+ return new SconeServicesProperties(preComputeProperties, postComputeProperties, lasImage);
+ }
+}
diff --git a/src/main/java/com/iexec/sms/tee/session/TeeSessionLogConfiguration.java b/src/main/java/com/iexec/sms/tee/session/TeeSessionLogConfiguration.java
new file mode 100644
index 00000000..535390c2
--- /dev/null
+++ b/src/main/java/com/iexec/sms/tee/session/TeeSessionLogConfiguration.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright 2022 IEXEC BLOCKCHAIN TECH
+ *
+ * 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.
+ */
+
+package com.iexec.sms.tee.session;
+
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.stereotype.Component;
+
+import lombok.Getter;
+
+@Getter
+@Component
+public class TeeSessionLogConfiguration {
+
+ @Value("${logging.tee.display-debug-session}")
+ boolean displayDebugSessionEnabled;
+}
diff --git a/src/main/java/com/iexec/sms/tee/session/TeeSessionService.java b/src/main/java/com/iexec/sms/tee/session/TeeSessionService.java
index 22f552ea..b2b26390 100644
--- a/src/main/java/com/iexec/sms/tee/session/TeeSessionService.java
+++ b/src/main/java/com/iexec/sms/tee/session/TeeSessionService.java
@@ -17,83 +17,65 @@
package com.iexec.sms.tee.session;
import com.iexec.common.task.TaskDescription;
+import com.iexec.common.tee.TeeFramework;
+import com.iexec.sms.api.TeeSessionGenerationResponse;
import com.iexec.sms.blockchain.IexecHubService;
-import com.iexec.sms.tee.session.cas.CasClient;
-import com.iexec.sms.tee.session.palaemon.PalaemonSessionRequest;
-import com.iexec.sms.tee.session.palaemon.PalaemonSessionService;
-import lombok.extern.slf4j.Slf4j;
+import com.iexec.sms.tee.session.generic.TeeSessionGenerationException;
+import com.iexec.sms.tee.session.generic.TeeSessionHandler;
+import com.iexec.sms.tee.session.generic.TeeSessionRequest;
import org.apache.commons.lang3.RandomStringUtils;
-import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
-import static com.iexec.sms.api.TeeSessionGenerationError.*;
+import static com.iexec.sms.api.TeeSessionGenerationError.GET_TASK_DESCRIPTION_FAILED;
+import static com.iexec.sms.api.TeeSessionGenerationError.SECURE_SESSION_NO_TEE_PROVIDER;
-@Slf4j
@Service
public class TeeSessionService {
private final IexecHubService iexecHubService;
- private final CasClient casClient;
- private final PalaemonSessionService palaemonSessionService;
- private final boolean shouldDisplayDebugSession;
+ private final TeeSessionHandler teeSessionHandler;
+
public TeeSessionService(
IexecHubService iexecService,
- PalaemonSessionService palaemonSessionService,
- CasClient casClient,
- @Value("${logging.tee.display-debug-session}")
- boolean shouldDisplayDebugSession) {
+ TeeSessionHandler teeSessionHandler) {
this.iexecHubService = iexecService;
- this.palaemonSessionService = palaemonSessionService;
- this.casClient = casClient;
- this.shouldDisplayDebugSession = shouldDisplayDebugSession;
+ this.teeSessionHandler = teeSessionHandler;
}
- public String generateTeeSession(
+ public TeeSessionGenerationResponse generateTeeSession(
String taskId,
String workerAddress,
String teeChallenge) throws TeeSessionGenerationException {
-
String sessionId = createSessionId(taskId);
TaskDescription taskDescription = iexecHubService.getTaskDescription(taskId);
if (taskDescription == null) {
throw new TeeSessionGenerationException(
GET_TASK_DESCRIPTION_FAILED,
- String.format("Failed to get task description [taskId:%s]", taskId)
- );
+ String.format("Failed to get task description [taskId:%s]", taskId));
}
- PalaemonSessionRequest request = PalaemonSessionRequest.builder()
+ TeeSessionRequest request = TeeSessionRequest.builder()
.sessionId(sessionId)
.taskDescription(taskDescription)
.workerAddress(workerAddress)
.enclaveChallenge(teeChallenge)
.build();
- String sessionYmlAsString = palaemonSessionService.getSessionYml(request);
- if (sessionYmlAsString.isEmpty()) {
+
+ final TeeFramework teeFramework = taskDescription.getTeeFramework();
+ if (teeFramework == null) {
throw new TeeSessionGenerationException(
- GET_SESSION_YML_FAILED,
- String.format("Failed to get session yml [taskId:%s, workerAddress:%s]", taskId, workerAddress));
- }
- log.info("Session yml is ready [taskId:{}]", taskId);
- if (shouldDisplayDebugSession){
- log.info("Session yml content [taskId:{}]\n{}", taskId, sessionYmlAsString);
+ SECURE_SESSION_NO_TEE_PROVIDER,
+ String.format("TEE framework can't be null [taskId:%s]", taskId));
}
+
// /!\ TODO clean expired tasks sessions
- boolean isSessionGenerated = casClient
- .generateSecureSession(sessionYmlAsString.getBytes())
- .getStatusCode()
- .is2xxSuccessful();
- if (!isSessionGenerated) {
- throw new TeeSessionGenerationException(
- SECURE_SESSION_CAS_CALL_FAILED,
- String.format("Failed to generate secure session [taskId:%s, workerAddress:%s]", taskId, workerAddress)
- );
- }
- return sessionId;
+ String secretProvisioningUrl = teeSessionHandler.buildAndPostSession(request);
+ return new TeeSessionGenerationResponse(sessionId, secretProvisioningUrl);
}
private String createSessionId(String taskId) {
String randomString = RandomStringUtils.randomAlphanumeric(10);
return String.format("%s0000%s", randomString, taskId);
}
+
}
diff --git a/src/main/java/com/iexec/sms/tee/session/base/SecretEnclaveBase.java b/src/main/java/com/iexec/sms/tee/session/base/SecretEnclaveBase.java
new file mode 100644
index 00000000..6dc934b7
--- /dev/null
+++ b/src/main/java/com/iexec/sms/tee/session/base/SecretEnclaveBase.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright 2022 IEXEC BLOCKCHAIN TECH
+ *
+ * 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.
+ */
+
+package com.iexec.sms.tee.session.base;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+import lombok.*;
+
+import java.util.Map;
+
+@Builder
+@Getter
+@Setter
+@AllArgsConstructor
+@NoArgsConstructor
+public class SecretEnclaveBase {
+
+ @JsonProperty("name")
+ private String name;
+ @JsonProperty("mrenclave")
+ private String mrenclave;
+ @JsonProperty("environment")
+ private Map environment;
+
+}
diff --git a/src/main/java/com/iexec/sms/secret/web2/Web2SecretsRepository.java b/src/main/java/com/iexec/sms/tee/session/base/SecretSessionBase.java
similarity index 63%
rename from src/main/java/com/iexec/sms/secret/web2/Web2SecretsRepository.java
rename to src/main/java/com/iexec/sms/tee/session/base/SecretSessionBase.java
index 062ae118..52c68889 100644
--- a/src/main/java/com/iexec/sms/secret/web2/Web2SecretsRepository.java
+++ b/src/main/java/com/iexec/sms/tee/session/base/SecretSessionBase.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2020 IEXEC BLOCKCHAIN TECH
+ * Copyright 2022 IEXEC BLOCKCHAIN TECH
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,15 +14,19 @@
* limitations under the License.
*/
-package com.iexec.sms.secret.web2;
+package com.iexec.sms.tee.session.base;
+import lombok.*;
-import org.springframework.data.repository.CrudRepository;
+@Builder
+@Getter
+@Setter
+@AllArgsConstructor
+@NoArgsConstructor
+public class SecretSessionBase {
-import java.util.Optional;
-
-public interface Web2SecretsRepository extends CrudRepository {
-
- Optional findWeb2SecretsByOwnerAddress(String ownerAddress);
+ private SecretEnclaveBase preCompute;
+ private SecretEnclaveBase appCompute;
+ private SecretEnclaveBase postCompute;
}
diff --git a/src/main/java/com/iexec/sms/tee/session/palaemon/PalaemonSessionService.java b/src/main/java/com/iexec/sms/tee/session/base/SecretSessionBaseService.java
similarity index 54%
rename from src/main/java/com/iexec/sms/tee/session/palaemon/PalaemonSessionService.java
rename to src/main/java/com/iexec/sms/tee/session/base/SecretSessionBaseService.java
index f7a29767..c6d561d1 100644
--- a/src/main/java/com/iexec/sms/tee/session/palaemon/PalaemonSessionService.java
+++ b/src/main/java/com/iexec/sms/tee/session/base/SecretSessionBaseService.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2020 IEXEC BLOCKCHAIN TECH
+ * Copyright 2022 IEXEC BLOCKCHAIN TECH
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,167 +14,106 @@
* limitations under the License.
*/
-package com.iexec.sms.tee.session.palaemon;
+package com.iexec.sms.tee.session.base;
import com.iexec.common.task.TaskDescription;
import com.iexec.common.tee.TeeEnclaveConfiguration;
-import com.iexec.common.utils.FileHelper;
import com.iexec.common.utils.IexecEnvUtils;
-import com.iexec.sms.secret.ReservedSecretKeyName;
-import com.iexec.sms.secret.Secret;
+import com.iexec.common.utils.IexecFileHelper;
+import com.iexec.sms.api.config.TeeServicesProperties;
import com.iexec.sms.secret.compute.OnChainObjectType;
import com.iexec.sms.secret.compute.SecretOwnerRole;
import com.iexec.sms.secret.compute.TeeTaskComputeSecret;
import com.iexec.sms.secret.compute.TeeTaskComputeSecretService;
-import com.iexec.sms.secret.web2.Web2SecretsService;
+import com.iexec.sms.secret.web2.Web2SecretService;
import com.iexec.sms.secret.web3.Web3SecretService;
import com.iexec.sms.tee.challenge.TeeChallenge;
import com.iexec.sms.tee.challenge.TeeChallengeService;
-import com.iexec.sms.tee.session.TeeSessionGenerationException;
-import com.iexec.sms.tee.session.attestation.AttestationSecurityConfig;
-import com.iexec.sms.tee.workflow.TeeWorkflowConfiguration;
+import com.iexec.sms.tee.session.base.SecretEnclaveBase.SecretEnclaveBaseBuilder;
+import com.iexec.sms.tee.session.base.SecretSessionBase.SecretSessionBaseBuilder;
+import com.iexec.sms.tee.session.generic.TeeSessionGenerationException;
+import com.iexec.sms.tee.session.generic.TeeSessionRequest;
import com.iexec.sms.utils.EthereumCredentials;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
-import org.apache.velocity.Template;
-import org.apache.velocity.VelocityContext;
-import org.apache.velocity.app.VelocityEngine;
-import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
-import javax.annotation.PostConstruct;
-import java.io.FileNotFoundException;
-import java.io.StringWriter;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.Map;
-import java.util.Optional;
-import java.util.stream.Collectors;
+import java.util.*;
import static com.iexec.common.chain.DealParams.DROPBOX_RESULT_STORAGE_PROVIDER;
-import static com.iexec.common.precompute.PreComputeUtils.IEXEC_DATASET_KEY;
import static com.iexec.common.precompute.PreComputeUtils.IS_DATASET_REQUIRED;
import static com.iexec.common.tee.TeeUtils.booleanToYesNo;
import static com.iexec.common.worker.result.ResultUtils.*;
import static com.iexec.sms.api.TeeSessionGenerationError.*;
-import static com.iexec.sms.secret.ReservedSecretKeyName.IEXEC_RESULT_ENCRYPTION_PUBLIC_KEY;
+import static com.iexec.sms.secret.ReservedSecretKeyName.*;
@Slf4j
@Service
-public class PalaemonSessionService {
+public class SecretSessionBaseService {
- public static final String EMPTY_YML_VALUE = "";
+ static final String EMPTY_YML_VALUE = "";
- // Internal values required for setting up a palaemon session
- // Generic
- static final String SESSION_ID = "SESSION_ID";
- static final String INPUT_FILE_URLS = "INPUT_FILE_URLS";
- static final String INPUT_FILE_NAMES = "INPUT_FILE_NAMES";
- static final String TOLERATED_INSECURE_OPTIONS = "TOLERATED_INSECURE_OPTIONS";
- static final String IGNORED_SGX_ADVISORIES = "IGNORED_SGX_ADVISORIES";
+ public static final String INPUT_FILE_URLS = "INPUT_FILE_URLS";
+ public static final String INPUT_FILE_NAMES = "INPUT_FILE_NAMES";
// PreCompute
static final String IS_PRE_COMPUTE_REQUIRED = "IS_PRE_COMPUTE_REQUIRED";
- static final String PRE_COMPUTE_MRENCLAVE = "PRE_COMPUTE_MRENCLAVE";
- static final String PRE_COMPUTE_ENTRYPOINT = "PRE_COMPUTE_ENTRYPOINT";
+ public static final String PRE_COMPUTE_MRENCLAVE = "PRE_COMPUTE_MRENCLAVE";
+ static final String IEXEC_PRE_COMPUTE_OUT = "IEXEC_PRE_COMPUTE_OUT";
+ static final String IEXEC_DATASET_KEY = "IEXEC_DATASET_KEY";
// Compute
- static final String APP_MRENCLAVE = "APP_MRENCLAVE";
- static final String APP_ARGS = "APP_ARGS";
+ public static final String APP_MRENCLAVE = "APP_MRENCLAVE";
// PostCompute
- static final String POST_COMPUTE_MRENCLAVE = "POST_COMPUTE_MRENCLAVE";
- static final String POST_COMPUTE_ENTRYPOINT = "POST_COMPUTE_ENTRYPOINT";
- // Secrets
- static final String REQUESTER_SECRETS = "REQUESTER_SECRETS";
- // Env
- private static final String ENV_PROPERTY = "env";
+ public static final String POST_COMPUTE_MRENCLAVE = "POST_COMPUTE_MRENCLAVE";
private final Web3SecretService web3SecretService;
- private final Web2SecretsService web2SecretsService;
+ private final Web2SecretService web2SecretService;
private final TeeChallengeService teeChallengeService;
- private final TeeWorkflowConfiguration teeWorkflowConfig;
- private final AttestationSecurityConfig attestationSecurityConfig;
+ private final TeeServicesProperties teeServicesConfig;
private final TeeTaskComputeSecretService teeTaskComputeSecretService;
- @Value("${scone.cas.palaemon}")
- private String palaemonTemplateFilePath;
-
- public PalaemonSessionService(
+ public SecretSessionBaseService(
Web3SecretService web3SecretService,
- Web2SecretsService web2SecretsService,
+ Web2SecretService web2SecretService,
TeeChallengeService teeChallengeService,
- TeeWorkflowConfiguration teeWorkflowConfig,
- AttestationSecurityConfig attestationSecurityConfig,
+ TeeServicesProperties teeServicesConfig,
TeeTaskComputeSecretService teeTaskComputeSecretService) {
this.web3SecretService = web3SecretService;
- this.web2SecretsService = web2SecretsService;
+ this.web2SecretService = web2SecretService;
this.teeChallengeService = teeChallengeService;
- this.teeWorkflowConfig = teeWorkflowConfig;
- this.attestationSecurityConfig = attestationSecurityConfig;
+ this.teeServicesConfig = teeServicesConfig;
this.teeTaskComputeSecretService = teeTaskComputeSecretService;
}
- @PostConstruct
- void postConstruct() throws FileNotFoundException {
- if (StringUtils.isEmpty(palaemonTemplateFilePath)) {
- throw new IllegalArgumentException("Missing palaemon template filepath");
- }
- if (!FileHelper.exists(palaemonTemplateFilePath)) {
- throw new FileNotFoundException("Missing palaemon template file");
- }
- }
-
/**
- * Collect tokens required for different compute stages (pre, in, post)
- * and build the yaml config of the TEE session.
- *
- * TODO: Read onchain available infos from enclave instead of copying
- * public vars to palaemon.yml. It needs ssl call from enclave to eth
- * node (only ethereum node address required inside palaemon.yml)
+ * Collect tokens required for different compute stages (pre, in, post).
*
* @param request session request details
- * @return session config in yaml string format
+ * @return All common tokens for a session, whatever TEE technology is used
*/
- public String getSessionYml(PalaemonSessionRequest request) throws TeeSessionGenerationException {
+ public SecretSessionBase getSecretsTokens(TeeSessionRequest request) throws TeeSessionGenerationException {
if (request == null) {
throw new TeeSessionGenerationException(
NO_SESSION_REQUEST,
- "Session request must not be null"
- );
+ "Session request must not be null");
}
if (request.getTaskDescription() == null) {
throw new TeeSessionGenerationException(
NO_TASK_DESCRIPTION,
- "Task description must not be null"
- );
+ "Task description must not be null");
}
-
+ SecretSessionBaseBuilder sessionBase = SecretSessionBase.builder();
TaskDescription taskDescription = request.getTaskDescription();
- Map palaemonTokens = new HashMap<>();
- palaemonTokens.put(SESSION_ID, request.getSessionId());
// pre-compute
boolean isPreComputeRequired = taskDescription.containsDataset() ||
!taskDescription.getInputFiles().isEmpty();
- palaemonTokens.put(IS_PRE_COMPUTE_REQUIRED, isPreComputeRequired);
if (isPreComputeRequired) {
- palaemonTokens.putAll(getPreComputePalaemonTokens(request));
+ sessionBase.preCompute(getPreComputeTokens(request));
}
// app
- palaemonTokens.putAll(getAppPalaemonTokens(request));
+ sessionBase.appCompute(getAppTokens(request));
// post compute
- palaemonTokens.putAll(getPostComputePalaemonTokens(request));
- // env variables
- Map env = IexecEnvUtils.getAllIexecEnv(taskDescription);
- // Null value should be replaced by an empty string.
- env.forEach((key, value) -> env.replace(key, null, EMPTY_YML_VALUE));
- palaemonTokens.put(ENV_PROPERTY, env);
- // Add attestation security config
- String toleratedInsecureOptions =
- String.join(",", attestationSecurityConfig.getToleratedInsecureOptions());
- String ignoredSgxAdvisories =
- String.join(",", attestationSecurityConfig.getIgnoredSgxAdvisories());
- palaemonTokens.put(TOLERATED_INSECURE_OPTIONS, toleratedInsecureOptions);
- palaemonTokens.put(IGNORED_SGX_ADVISORIES, ignoredSgxAdvisories);
- // Merge template with tokens and return the result
- return getFilledPalaemonTemplate(this.palaemonTemplateFilePath, palaemonTokens);
+ sessionBase.postCompute(getPostComputeTokens(request));
+ return sessionBase.build();
}
/**
@@ -183,51 +122,63 @@ public String getSessionYml(PalaemonSessionRequest request) throws TeeSessionGen
* @return map of pre-compute tokens
* @throws TeeSessionGenerationException if dataset secret is not found.
*/
- Map getPreComputePalaemonTokens(PalaemonSessionRequest request)
+ public SecretEnclaveBase getPreComputeTokens(TeeSessionRequest request)
throws TeeSessionGenerationException {
+ SecretEnclaveBaseBuilder enclaveBase = SecretEnclaveBase.builder();
+ enclaveBase.name("pre-compute");
+ Map tokens = new HashMap<>();
TaskDescription taskDescription = request.getTaskDescription();
String taskId = taskDescription.getChainTaskId();
- Map tokens = new HashMap<>();
- String fingerprint = teeWorkflowConfig.getPreComputeFingerprint();
- tokens.put(PRE_COMPUTE_MRENCLAVE, fingerprint);
- String entrypoint = teeWorkflowConfig.getPreComputeEntrypoint();
- tokens.put(PRE_COMPUTE_ENTRYPOINT, entrypoint);
+ enclaveBase.mrenclave(teeServicesConfig.getPreComputeProperties().getFingerprint());
+ tokens.put(IEXEC_PRE_COMPUTE_OUT, IexecFileHelper.SLASH_IEXEC_IN);
+ // `IS_DATASET_REQUIRED` still meaningful?
tokens.put(IS_DATASET_REQUIRED, taskDescription.containsDataset());
- tokens.put(IEXEC_DATASET_KEY, EMPTY_YML_VALUE);
+
+ List trustedEnv = new ArrayList<>();
if (taskDescription.containsDataset()) {
String datasetKey = web3SecretService
- .getSecret(taskDescription.getDatasetAddress(), true)
+ .getDecryptedValue(taskDescription.getDatasetAddress())
.orElseThrow(() -> new TeeSessionGenerationException(
PRE_COMPUTE_GET_DATASET_SECRET_FAILED,
- "Empty dataset secret - taskId: " + taskId
- ))
- .getTrimmedValue();
+ "Empty dataset secret - taskId: " + taskId));
tokens.put(IEXEC_DATASET_KEY, datasetKey);
+ trustedEnv.addAll(List.of(
+ IexecEnvUtils.IEXEC_DATASET_URL,
+ IexecEnvUtils.IEXEC_DATASET_FILENAME,
+ IexecEnvUtils.IEXEC_DATASET_CHECKSUM));
} else {
log.info("No dataset key needed for this task [taskId:{}]", taskId);
}
- // extract
- // this map will be empty (not null) if no input file is found
- Map inputFileUrls = IexecEnvUtils.getAllIexecEnv(taskDescription)
+ trustedEnv.addAll(List.of(
+ IexecEnvUtils.IEXEC_TASK_ID,
+ IexecEnvUtils.IEXEC_INPUT_FILES_FOLDER,
+ IexecEnvUtils.IEXEC_INPUT_FILES_NUMBER));
+ IexecEnvUtils.getAllIexecEnv(taskDescription)
.entrySet()
.stream()
- .filter(e -> e.getKey().contains(IexecEnvUtils.IEXEC_INPUT_FILE_URL_PREFIX))
- .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
- tokens.put(INPUT_FILE_URLS, inputFileUrls);
- return tokens;
+ .filter(e ->
+ // extract trusted en vars to include
+ trustedEnv.contains(e.getKey())
+ // extract
+ || e.getKey().startsWith(IexecEnvUtils.IEXEC_INPUT_FILE_URL_PREFIX))
+ .forEach(e -> tokens.put(e.getKey(), e.getValue()));
+ return enclaveBase
+ .environment(tokens)
+ .build();
}
/*
* Compute (App)
*/
- Map getAppPalaemonTokens(PalaemonSessionRequest request)
- throws TeeSessionGenerationException{
+ public SecretEnclaveBase getAppTokens(TeeSessionRequest request)
+ throws TeeSessionGenerationException {
+ SecretEnclaveBaseBuilder enclaveBase = SecretEnclaveBase.builder();
+ enclaveBase.name("app");
TaskDescription taskDescription = request.getTaskDescription();
if (taskDescription == null) {
throw new TeeSessionGenerationException(
NO_TASK_DESCRIPTION,
- "Task description must no be null"
- );
+ "Task description must not be null");
}
Map tokens = new HashMap<>();
@@ -235,36 +186,31 @@ Map getAppPalaemonTokens(PalaemonSessionRequest request)
if (enclaveConfig == null) {
throw new TeeSessionGenerationException(
APP_COMPUTE_NO_ENCLAVE_CONFIG,
- "Enclave configuration must no be null"
- );
+ "Enclave configuration must not be null");
}
- if (!enclaveConfig.getValidator().isValid()){
+ if (!enclaveConfig.getValidator().isValid()) {
throw new TeeSessionGenerationException(
APP_COMPUTE_INVALID_ENCLAVE_CONFIG,
"Invalid enclave configuration: " +
- enclaveConfig.getValidator().validate().toString()
- );
+ enclaveConfig.getValidator().validate().toString());
}
- tokens.put(APP_MRENCLAVE, enclaveConfig.getFingerprint());
- String appArgs = enclaveConfig.getEntrypoint();
- if (!StringUtils.isEmpty(taskDescription.getCmd())) {
- appArgs = appArgs + " " + taskDescription.getCmd();
- }
- tokens.put(APP_ARGS, appArgs);
+ enclaveBase.mrenclave(enclaveConfig.getFingerprint());
// extract
// this map will be empty (not null) if no input file is found
- Map inputFileNames = IexecEnvUtils.getComputeStageEnvMap(taskDescription)
+ IexecEnvUtils.getComputeStageEnvMap(taskDescription)
.entrySet()
.stream()
- .filter(e -> e.getKey().contains(IexecEnvUtils.IEXEC_INPUT_FILE_NAME_PREFIX))
- .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
- tokens.put(INPUT_FILE_NAMES, inputFileNames);
+ .filter(e -> e.getKey().startsWith(IexecEnvUtils.IEXEC_INPUT_FILE_NAME_PREFIX))
+ .forEach(e -> tokens.put(e.getKey(), e.getValue()));
final Map computeSecrets = getApplicationComputeSecrets(taskDescription);
tokens.putAll(computeSecrets);
-
- return tokens;
+ // trusted env variables (not confidential)
+ tokens.putAll(IexecEnvUtils.getComputeStageEnvMap(taskDescription));
+ return enclaveBase
+ .environment(tokens)
+ .build();
}
private Map getApplicationComputeSecrets(TaskDescription taskDescription) {
@@ -273,25 +219,26 @@ private Map getApplicationComputeSecrets(TaskDescription taskDes
if (applicationAddress != null) {
final String secretIndex = "1";
- String appDeveloperSecret =
- teeTaskComputeSecretService.getSecret(
- OnChainObjectType.APPLICATION,
- applicationAddress.toLowerCase(),
- SecretOwnerRole.APPLICATION_DEVELOPER,
- "",
- secretIndex)
- .map(TeeTaskComputeSecret::getValue)
- .orElse(EMPTY_YML_VALUE);
- tokens.put(IexecEnvUtils.IEXEC_APP_DEVELOPER_SECRET_PREFIX + secretIndex, appDeveloperSecret);
+ String appDeveloperSecret = teeTaskComputeSecretService.getSecret(
+ OnChainObjectType.APPLICATION,
+ applicationAddress.toLowerCase(),
+ SecretOwnerRole.APPLICATION_DEVELOPER,
+ "",
+ secretIndex)
+ .map(TeeTaskComputeSecret::getValue)
+ .orElse(EMPTY_YML_VALUE);
+ if (!StringUtils.isEmpty(appDeveloperSecret)) {
+ tokens.put("IEXEC_APP_DEVELOPER_SECRET", appDeveloperSecret);
+ tokens.put(IexecEnvUtils.IEXEC_APP_DEVELOPER_SECRET_PREFIX + secretIndex, appDeveloperSecret);
+ }
}
if (taskDescription.getSecrets() == null || taskDescription.getRequester() == null) {
- tokens.put(REQUESTER_SECRETS, Collections.emptyMap());
return tokens;
}
final HashMap requesterSecrets = new HashMap<>();
- for (Map.Entry secretEntry: taskDescription.getSecrets().entrySet()) {
+ for (Map.Entry secretEntry : taskDescription.getSecrets().entrySet()) {
try {
int requesterSecretIndex = Integer.parseInt(secretEntry.getKey());
if (requesterSecretIndex <= 0) {
@@ -300,49 +247,47 @@ private Map getApplicationComputeSecrets(TaskDescription taskDes
log.warn(message);
throw new NumberFormatException(message);
}
- } catch(NumberFormatException e) {
+ } catch (NumberFormatException e) {
log.warn("Invalid entry found in deal parameters secrets map", e);
continue;
}
String requesterSecret = teeTaskComputeSecretService.getSecret(
- OnChainObjectType.APPLICATION,
- "",
- SecretOwnerRole.REQUESTER,
- taskDescription.getRequester().toLowerCase(),
- secretEntry.getValue())
+ OnChainObjectType.APPLICATION,
+ "",
+ SecretOwnerRole.REQUESTER,
+ taskDescription.getRequester().toLowerCase(),
+ secretEntry.getValue())
.map(TeeTaskComputeSecret::getValue)
.orElse(EMPTY_YML_VALUE);
requesterSecrets.put(IexecEnvUtils.IEXEC_REQUESTER_SECRET_PREFIX + secretEntry.getKey(), requesterSecret);
}
- tokens.put(REQUESTER_SECRETS, requesterSecrets);
-
+ tokens.putAll(requesterSecrets);
return tokens;
}
/*
* Post-Compute (Result)
*/
- Map getPostComputePalaemonTokens(PalaemonSessionRequest request)
+ public SecretEnclaveBase getPostComputeTokens(TeeSessionRequest request)
throws TeeSessionGenerationException {
+ SecretEnclaveBaseBuilder enclaveBase = SecretEnclaveBase.builder()
+ .name("post-compute")
+ .mrenclave(teeServicesConfig.getPostComputeProperties().getFingerprint());
+ Map tokens = new HashMap<>();
TaskDescription taskDescription = request.getTaskDescription();
if (taskDescription == null) {
throw new TeeSessionGenerationException(NO_TASK_DESCRIPTION, "Task description must not be null");
}
-
- Map tokens = new HashMap<>();
- String teePostComputeFingerprint = teeWorkflowConfig.getPostComputeFingerprint();
// ###############################################################################
// TODO: activate this when user specific post-compute is properly
- // supported. See https://github.com/iExecBlockchainComputing/iexec-sms/issues/52.
+ // supported. See
+ // https://github.com/iExecBlockchainComputing/iexec-sms/issues/52.
// ###############################################################################
// // Use specific post-compute image if requested.
- //if (taskDescription.containsPostCompute()) {
- // teePostComputeFingerprint = taskDescription.getTeePostComputeFingerprint();
- // //add entrypoint too
- //}
- tokens.put(POST_COMPUTE_MRENCLAVE, teePostComputeFingerprint);
- String entrypoint = teeWorkflowConfig.getPostComputeEntrypoint();
- tokens.put(POST_COMPUTE_ENTRYPOINT, entrypoint);
+ // if (taskDescription.containsPostCompute()) {
+ // teePostComputeFingerprint = taskDescription.getTeePostComputeFingerprint();
+ // //add entrypoint too
+ // }
// encryption
Map encryptionTokens = getPostComputeEncryptionTokens(request);
tokens.putAll(encryptionTokens);
@@ -352,10 +297,12 @@ Map getPostComputePalaemonTokens(PalaemonSessionRequest request)
// enclave signature
Map signTokens = getPostComputeSignTokens(request);
tokens.putAll(signTokens);
- return tokens;
+ return enclaveBase
+ .environment(tokens)
+ .build();
}
- Map getPostComputeEncryptionTokens(PalaemonSessionRequest request)
+ public Map getPostComputeEncryptionTokens(TeeSessionRequest request)
throws TeeSessionGenerationException {
TaskDescription taskDescription = request.getTaskDescription();
String taskId = taskDescription.getChainTaskId();
@@ -367,17 +314,15 @@ Map getPostComputeEncryptionTokens(PalaemonSessionRequest reques
if (!shouldEncrypt) {
return tokens;
}
- Optional beneficiaryResultEncryptionKeySecret = web2SecretsService.getSecret(
+ Optional beneficiaryResultEncryptionKeySecret = web2SecretService.getDecryptedValue(
taskDescription.getBeneficiary(),
- IEXEC_RESULT_ENCRYPTION_PUBLIC_KEY,
- true);
+ IEXEC_RESULT_ENCRYPTION_PUBLIC_KEY);
if (beneficiaryResultEncryptionKeySecret.isEmpty()) {
throw new TeeSessionGenerationException(
POST_COMPUTE_GET_ENCRYPTION_TOKENS_FAILED_EMPTY_BENEFICIARY_KEY,
- "Empty beneficiary encryption key - taskId: " + taskId
- );
+ "Empty beneficiary encryption key - taskId: " + taskId);
}
- String publicKeyValue = beneficiaryResultEncryptionKeySecret.get().getTrimmedValue();
+ String publicKeyValue = beneficiaryResultEncryptionKeySecret.get();
tokens.put(RESULT_ENCRYPTION_PUBLIC_KEY, publicKeyValue); // base64 encoded by client
return tokens;
}
@@ -386,7 +331,7 @@ Map getPostComputeEncryptionTokens(PalaemonSessionRequest reques
// to the beneficiary private storage space waiting for
// that feature we only allow to push to the requester
// private storage space
- Map getPostComputeStorageTokens(PalaemonSessionRequest request)
+ public Map getPostComputeStorageTokens(TeeSessionRequest request)
throws TeeSessionGenerationException {
TaskDescription taskDescription = request.getTaskDescription();
String taskId = taskDescription.getChainTaskId();
@@ -402,10 +347,11 @@ Map getPostComputeStorageTokens(PalaemonSessionRequest request)
String storageProvider = taskDescription.getResultStorageProvider();
String storageProxy = taskDescription.getResultStorageProxy();
String keyName = storageProvider.equals(DROPBOX_RESULT_STORAGE_PROVIDER)
- ? ReservedSecretKeyName.IEXEC_RESULT_DROPBOX_TOKEN
- : ReservedSecretKeyName.IEXEC_RESULT_IEXEC_IPFS_TOKEN;
- Optional requesterStorageTokenSecret =
- web2SecretsService.getSecret(taskDescription.getRequester(), keyName, true);
+ ? IEXEC_RESULT_DROPBOX_TOKEN
+ : IEXEC_RESULT_IEXEC_IPFS_TOKEN;
+ Optional requesterStorageTokenSecret = web2SecretService.getDecryptedValue(
+ taskDescription.getRequester(),
+ keyName);
if (requesterStorageTokenSecret.isEmpty()) {
log.error("Failed to get storage token [taskId:{}, storageProvider:{}, requester:{}]",
taskId, storageProvider, taskDescription.getRequester());
@@ -413,14 +359,14 @@ Map getPostComputeStorageTokens(PalaemonSessionRequest request)
POST_COMPUTE_GET_STORAGE_TOKENS_FAILED,
"Empty requester storage token - taskId: " + taskId);
}
- String requesterStorageToken = requesterStorageTokenSecret.get().getTrimmedValue();
+ String requesterStorageToken = requesterStorageTokenSecret.get();
tokens.put(RESULT_STORAGE_PROVIDER, storageProvider);
tokens.put(RESULT_STORAGE_PROXY, storageProxy);
tokens.put(RESULT_STORAGE_TOKEN, requesterStorageToken);
return tokens;
}
- Map getPostComputeSignTokens(PalaemonSessionRequest request)
+ public Map getPostComputeSignTokens(TeeSessionRequest request)
throws TeeSessionGenerationException {
String taskId = request.getTaskDescription().getChainTaskId();
String workerAddress = request.getWorkerAddress();
@@ -428,28 +374,24 @@ Map getPostComputeSignTokens(PalaemonSessionRequest request)
if (StringUtils.isEmpty(workerAddress)) {
throw new TeeSessionGenerationException(
POST_COMPUTE_GET_SIGNATURE_TOKENS_FAILED_EMPTY_WORKER_ADDRESS,
- "Empty worker address - taskId: " + taskId
- );
+ "Empty worker address - taskId: " + taskId);
}
if (StringUtils.isEmpty(request.getEnclaveChallenge())) {
throw new TeeSessionGenerationException(
POST_COMPUTE_GET_SIGNATURE_TOKENS_FAILED_EMPTY_PUBLIC_ENCLAVE_CHALLENGE,
- "Empty public enclave challenge - taskId: " + taskId
- );
+ "Empty public enclave challenge - taskId: " + taskId);
}
Optional teeChallenge = teeChallengeService.getOrCreate(taskId, true);
if (teeChallenge.isEmpty()) {
throw new TeeSessionGenerationException(
POST_COMPUTE_GET_SIGNATURE_TOKENS_FAILED_EMPTY_TEE_CHALLENGE,
- "Empty TEE challenge - taskId: " + taskId
- );
+ "Empty TEE challenge - taskId: " + taskId);
}
EthereumCredentials enclaveCredentials = teeChallenge.get().getCredentials();
if (enclaveCredentials == null || enclaveCredentials.getPrivateKey().isEmpty()) {
throw new TeeSessionGenerationException(
POST_COMPUTE_GET_SIGNATURE_TOKENS_FAILED_EMPTY_TEE_CREDENTIALS,
- "Empty TEE challenge credentials - taskId: " + taskId
- );
+ "Empty TEE challenge credentials - taskId: " + taskId);
}
tokens.put(RESULT_TASK_ID, taskId);
tokens.put(RESULT_SIGN_WORKER_ADDRESS, workerAddress);
@@ -457,14 +399,4 @@ Map getPostComputeSignTokens(PalaemonSessionRequest request)
return tokens;
}
- private String getFilledPalaemonTemplate(String templatePath, Map tokens) {
- VelocityEngine ve = new VelocityEngine();
- ve.init();
- Template template = ve.getTemplate(templatePath);
- VelocityContext context = new VelocityContext();
- tokens.forEach(context::put); // copy all data from the tokens into context
- StringWriter writer = new StringWriter();
- template.merge(context, writer);
- return writer.toString();
- }
}
diff --git a/src/main/java/com/iexec/sms/tee/session/cas/CasConfigurationController.java b/src/main/java/com/iexec/sms/tee/session/cas/CasConfigurationController.java
deleted file mode 100644
index cfe8dfd5..00000000
--- a/src/main/java/com/iexec/sms/tee/session/cas/CasConfigurationController.java
+++ /dev/null
@@ -1,42 +0,0 @@
-/*
- * Copyright 2021 IEXEC BLOCKCHAIN TECH
- *
- * 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.
- */
-
-package com.iexec.sms.tee.session.cas;
-
-import org.springframework.web.bind.annotation.GetMapping;
-import org.springframework.web.bind.annotation.RequestMapping;
-import org.springframework.web.bind.annotation.RestController;
-
-@RestController
-@RequestMapping("/cas")
-public class CasConfigurationController {
-
- private final CasConfiguration casConfiguration;
-
- public CasConfigurationController(CasConfiguration casConfiguration) {
- this.casConfiguration = casConfiguration;
- }
-
- /**
- * Get CAS public url intended for enclaves.
- *
- * @return enclave dedicated url
- */
- @GetMapping("/url")
- public String getCasEnclaveUrl() {
- return casConfiguration.getEnclaveUrl();
- }
-}
diff --git a/src/main/java/com/iexec/sms/tee/session/TeeSessionGenerationException.java b/src/main/java/com/iexec/sms/tee/session/generic/TeeSessionGenerationException.java
similarity index 95%
rename from src/main/java/com/iexec/sms/tee/session/TeeSessionGenerationException.java
rename to src/main/java/com/iexec/sms/tee/session/generic/TeeSessionGenerationException.java
index 04fa9761..f02c257e 100644
--- a/src/main/java/com/iexec/sms/tee/session/TeeSessionGenerationException.java
+++ b/src/main/java/com/iexec/sms/tee/session/generic/TeeSessionGenerationException.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.iexec.sms.tee.session;
+package com.iexec.sms.tee.session.generic;
import com.iexec.sms.api.TeeSessionGenerationError;
diff --git a/src/main/java/com/iexec/sms/tee/session/generic/TeeSessionHandler.java b/src/main/java/com/iexec/sms/tee/session/generic/TeeSessionHandler.java
new file mode 100644
index 00000000..f69d83bb
--- /dev/null
+++ b/src/main/java/com/iexec/sms/tee/session/generic/TeeSessionHandler.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright 2022 IEXEC BLOCKCHAIN TECH
+ *
+ * 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
+ *
+ * https://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.
+ */
+
+package com.iexec.sms.tee.session.generic;
+
+public interface TeeSessionHandler {
+
+ /**
+ * Build and post secret session on secret provisioning service.
+ *
+ * @param request tee session generation request
+ * @return String secret provisioning service url
+ * @throws TeeSessionGenerationException
+ */
+ String buildAndPostSession(TeeSessionRequest request) throws TeeSessionGenerationException;
+
+}
diff --git a/src/main/java/com/iexec/sms/tee/session/palaemon/PalaemonSessionRequest.java b/src/main/java/com/iexec/sms/tee/session/generic/TeeSessionRequest.java
similarity index 92%
rename from src/main/java/com/iexec/sms/tee/session/palaemon/PalaemonSessionRequest.java
rename to src/main/java/com/iexec/sms/tee/session/generic/TeeSessionRequest.java
index 16c5b86a..117d62ef 100644
--- a/src/main/java/com/iexec/sms/tee/session/palaemon/PalaemonSessionRequest.java
+++ b/src/main/java/com/iexec/sms/tee/session/generic/TeeSessionRequest.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.iexec.sms.tee.session.palaemon;
+package com.iexec.sms.tee.session.generic;
import com.iexec.common.task.TaskDescription;
import lombok.AllArgsConstructor;
@@ -26,7 +26,7 @@
@Builder
@NoArgsConstructor
@AllArgsConstructor
-public class PalaemonSessionRequest {
+public class TeeSessionRequest {
private String sessionId;
private TaskDescription taskDescription;
diff --git a/src/main/java/com/iexec/sms/tee/session/gramine/GramineSessionHandlerService.java b/src/main/java/com/iexec/sms/tee/session/gramine/GramineSessionHandlerService.java
new file mode 100644
index 00000000..862899be
--- /dev/null
+++ b/src/main/java/com/iexec/sms/tee/session/gramine/GramineSessionHandlerService.java
@@ -0,0 +1,72 @@
+/*
+ * Copyright 2022 IEXEC BLOCKCHAIN TECH
+ *
+ * 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.
+ */
+
+package com.iexec.sms.tee.session.gramine;
+
+import com.iexec.common.tee.TeeFramework;
+import com.iexec.sms.api.TeeSessionGenerationError;
+import com.iexec.sms.tee.ConditionalOnTeeFramework;
+import com.iexec.sms.tee.session.TeeSessionLogConfiguration;
+import com.iexec.sms.tee.session.generic.TeeSessionGenerationException;
+import com.iexec.sms.tee.session.generic.TeeSessionHandler;
+import com.iexec.sms.tee.session.generic.TeeSessionRequest;
+import com.iexec.sms.tee.session.gramine.sps.GramineSession;
+import com.iexec.sms.tee.session.gramine.sps.SpsConfiguration;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.stereotype.Component;
+
+@Slf4j
+@Component
+@ConditionalOnTeeFramework(frameworks = TeeFramework.GRAMINE)
+public class GramineSessionHandlerService implements TeeSessionHandler {
+ private final GramineSessionMakerService sessionService;
+ private final SpsConfiguration spsConfiguration;
+ private final TeeSessionLogConfiguration teeSessionLogConfiguration;
+
+ public GramineSessionHandlerService(GramineSessionMakerService sessionService,
+ SpsConfiguration spsConfiguration,
+ TeeSessionLogConfiguration teeSessionLogConfiguration) {
+ this.sessionService = sessionService;
+ this.spsConfiguration = spsConfiguration;
+ this.teeSessionLogConfiguration = teeSessionLogConfiguration;
+ }
+
+ /**
+ * Build and post secret session on secret provisioning service.
+ *
+ * @param request tee session generation request
+ * @return String secret provisioning service url
+ * @throws TeeSessionGenerationException
+ */
+ @Override
+ public String buildAndPostSession(TeeSessionRequest request)
+ throws TeeSessionGenerationException {
+ GramineSession session = sessionService.generateSession(request);
+ if (teeSessionLogConfiguration.isDisplayDebugSessionEnabled()) {
+ log.info("Session content [taskId:{}]\n{}",
+ request.getTaskDescription().getChainTaskId(), session);
+ }
+
+ try {
+ spsConfiguration.getInstance().postSession(session);
+ return spsConfiguration.getEnclaveHost();
+ } catch (Exception e) {
+ throw new TeeSessionGenerationException(
+ TeeSessionGenerationError.SECURE_SESSION_STORAGE_CALL_FAILED,
+ "Failed to post session: " + e.getMessage());
+ }
+ }
+}
diff --git a/src/main/java/com/iexec/sms/tee/session/gramine/GramineSessionMakerService.java b/src/main/java/com/iexec/sms/tee/session/gramine/GramineSessionMakerService.java
new file mode 100644
index 00000000..86de236e
--- /dev/null
+++ b/src/main/java/com/iexec/sms/tee/session/gramine/GramineSessionMakerService.java
@@ -0,0 +1,82 @@
+/*
+ * Copyright 2022 IEXEC BLOCKCHAIN TECH
+ *
+ * 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.
+ */
+
+package com.iexec.sms.tee.session.gramine;
+
+import com.iexec.common.tee.TeeFramework;
+import com.iexec.sms.tee.ConditionalOnTeeFramework;
+import com.iexec.sms.tee.session.base.SecretEnclaveBase;
+import com.iexec.sms.tee.session.base.SecretSessionBase;
+import com.iexec.sms.tee.session.base.SecretSessionBaseService;
+import com.iexec.sms.tee.session.generic.TeeSessionGenerationException;
+import com.iexec.sms.tee.session.generic.TeeSessionRequest;
+import com.iexec.sms.tee.session.gramine.sps.GramineEnclave;
+import com.iexec.sms.tee.session.gramine.sps.GramineSession;
+import com.iexec.sms.tee.session.gramine.sps.GramineSession.GramineSessionBuilder;
+import lombok.NonNull;
+import org.springframework.stereotype.Service;
+
+import java.util.List;
+
+@Service
+@ConditionalOnTeeFramework(frameworks = TeeFramework.GRAMINE)
+public class GramineSessionMakerService {
+
+ private final SecretSessionBaseService secretSessionBaseService;
+
+ public GramineSessionMakerService(SecretSessionBaseService secretSessionBaseService) {
+ this.secretSessionBaseService = secretSessionBaseService;
+ }
+
+ /**
+ * Collect tokens required for different compute stages (pre, in, post)
+ * and build the JSON config of the TEE session.
+ *
+ * @param request session request details
+ * @return session config
+ */
+ @NonNull
+ public GramineSession generateSession(TeeSessionRequest request) throws TeeSessionGenerationException {
+ SecretSessionBase baseSession = secretSessionBaseService.getSecretsTokens(request);
+ GramineSessionBuilder gramineSession = GramineSession.builder()
+ .session(request.getSessionId());
+ GramineEnclave gramineAppEnclave = toGramineEnclave(baseSession.getAppCompute());
+ GramineEnclave graminePostEnclave = toGramineEnclave(baseSession.getPostCompute());
+
+ // TODO: Validate command-line arguments from the host
+ // (https://github.com/gramineproject/gsc/issues/13)
+ gramineAppEnclave.setCommand("");
+ graminePostEnclave.setCommand("");
+ // TODO: Remove useless volumes when SPS is ready
+ gramineAppEnclave.setVolumes(List.of());
+ graminePostEnclave.setVolumes(List.of());
+
+ return gramineSession.enclaves(List.of(
+ // No pre-compute for now
+ gramineAppEnclave,
+ graminePostEnclave))
+ .build();
+ }
+
+ private GramineEnclave toGramineEnclave(SecretEnclaveBase enclaveBase) {
+ return GramineEnclave.builder()
+ .name(enclaveBase.getName())
+ .mrenclave(enclaveBase.getMrenclave())
+ .environment(enclaveBase.getEnvironment())
+ .build();
+ }
+
+}
diff --git a/src/main/java/com/iexec/sms/tee/session/gramine/sps/GramineEnclave.java b/src/main/java/com/iexec/sms/tee/session/gramine/sps/GramineEnclave.java
new file mode 100644
index 00000000..35f78060
--- /dev/null
+++ b/src/main/java/com/iexec/sms/tee/session/gramine/sps/GramineEnclave.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright 2022 IEXEC BLOCKCHAIN TECH
+ *
+ * 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.
+ */
+
+package com.iexec.sms.tee.session.gramine.sps;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+import lombok.*;
+
+import java.util.List;
+import java.util.Map;
+
+@Builder
+@Getter
+@Setter
+@AllArgsConstructor
+@NoArgsConstructor
+public class GramineEnclave {
+
+ @JsonProperty("name")
+ private String name;
+ @JsonProperty("mrenclave")
+ private String mrenclave;
+ @JsonProperty("command")
+ private String command;
+ @JsonProperty("environment")
+ private Map environment;
+ @JsonProperty("volumes")
+ private List volumes;
+
+}
diff --git a/src/main/java/com/iexec/sms/tee/session/gramine/sps/GramineSession.java b/src/main/java/com/iexec/sms/tee/session/gramine/sps/GramineSession.java
new file mode 100644
index 00000000..36c12edc
--- /dev/null
+++ b/src/main/java/com/iexec/sms/tee/session/gramine/sps/GramineSession.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright 2022 IEXEC BLOCKCHAIN TECH
+ *
+ * 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.
+ */
+
+package com.iexec.sms.tee.session.gramine.sps;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import lombok.*;
+import lombok.extern.slf4j.Slf4j;
+
+import java.util.List;
+
+@Slf4j
+@Builder
+@Getter
+@Setter
+@AllArgsConstructor
+@NoArgsConstructor
+public class GramineSession {
+
+ @JsonProperty("session")
+ private String session;
+ @JsonProperty("enclaves")
+ private List enclaves;
+
+ @Override
+ public String toString() {
+ try {
+ return new ObjectMapper().writeValueAsString(this);
+ } catch (JsonProcessingException e) {
+ log.error("Failed to write SPS session as string [session:{}]", session, e);
+ return "";
+ }
+ }
+
+}
diff --git a/src/test/java/com/iexec/sms/secret/web3/Web3SecretsServiceTests.java b/src/main/java/com/iexec/sms/tee/session/gramine/sps/SpsApiClient.java
similarity index 70%
rename from src/test/java/com/iexec/sms/secret/web3/Web3SecretsServiceTests.java
rename to src/main/java/com/iexec/sms/tee/session/gramine/sps/SpsApiClient.java
index 15135ea0..daff5d02 100644
--- a/src/test/java/com/iexec/sms/secret/web3/Web3SecretsServiceTests.java
+++ b/src/main/java/com/iexec/sms/tee/session/gramine/sps/SpsApiClient.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2020 IEXEC BLOCKCHAIN TECH
+ * Copyright 2022 IEXEC BLOCKCHAIN TECH
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,8 +14,13 @@
* limitations under the License.
*/
-package com.iexec.sms.secret.web3;
+package com.iexec.sms.tee.session.gramine.sps;
-class Web3SecretsServiceTests {
+import feign.RequestLine;
-}
\ No newline at end of file
+public interface SpsApiClient {
+
+ @RequestLine("POST /api/session")
+ String postSession(GramineSession spsSession);
+
+}
diff --git a/src/main/java/com/iexec/sms/tee/session/gramine/sps/SpsConfiguration.java b/src/main/java/com/iexec/sms/tee/session/gramine/sps/SpsConfiguration.java
new file mode 100644
index 00000000..bb2e628a
--- /dev/null
+++ b/src/main/java/com/iexec/sms/tee/session/gramine/sps/SpsConfiguration.java
@@ -0,0 +1,68 @@
+/*
+ * Copyright 2022 IEXEC BLOCKCHAIN TECH
+ *
+ * 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.
+ */
+
+package com.iexec.sms.tee.session.gramine.sps;
+
+import com.iexec.common.tee.TeeFramework;
+import com.iexec.common.utils.FeignBuilder;
+import com.iexec.sms.tee.ConditionalOnTeeFramework;
+import feign.Logger.Level;
+import lombok.Getter;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.context.annotation.Configuration;
+
+@Configuration
+@ConditionalOnTeeFramework(frameworks = TeeFramework.GRAMINE)
+@Getter
+public class SpsConfiguration {
+ @Value("${tee.secret-provisioner.web.hostname}")
+ private String webHost;
+
+ @Value("${tee.secret-provisioner.web.port}")
+ private String webPort;
+
+ @Value("${tee.gramine.sps.login}")
+ private String webLogin;
+
+ @Value("${tee.gramine.sps.password}")
+ private String webPassword;
+
+ @Value("${tee.secret-provisioner.enclave.hostname}")
+ private String enclaveHostName;
+
+ @Value("${tee.secret-provisioner.enclave.port}")
+ private String enclavePort;
+
+ private SpsApiClient spsApiClient;
+
+ public String getWebUrl() {
+ return "http://" + webHost + ":" + webPort;
+ }
+
+ public String getEnclaveHost() {
+ return enclaveHostName + ":" + enclavePort;
+ }
+
+ public SpsApiClient getInstance() {
+ if (spsApiClient == null) {
+ spsApiClient = FeignBuilder.createBuilderWithBasicAuth(Level.FULL,
+ webLogin, webPassword)
+ .target(SpsApiClient.class, getWebUrl());
+ }
+ return spsApiClient;
+ }
+
+}
diff --git a/src/main/java/com/iexec/sms/tee/session/scone/SconeSessionHandlerService.java b/src/main/java/com/iexec/sms/tee/session/scone/SconeSessionHandlerService.java
new file mode 100644
index 00000000..a77e31c0
--- /dev/null
+++ b/src/main/java/com/iexec/sms/tee/session/scone/SconeSessionHandlerService.java
@@ -0,0 +1,84 @@
+/*
+ * Copyright 2022 IEXEC BLOCKCHAIN TECH
+ *
+ * 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.
+ */
+
+package com.iexec.sms.tee.session.scone;
+
+import com.iexec.common.tee.TeeFramework;
+import com.iexec.sms.api.TeeSessionGenerationError;
+import com.iexec.sms.tee.ConditionalOnTeeFramework;
+import com.iexec.sms.tee.session.TeeSessionLogConfiguration;
+import com.iexec.sms.tee.session.generic.TeeSessionGenerationException;
+import com.iexec.sms.tee.session.generic.TeeSessionHandler;
+import com.iexec.sms.tee.session.generic.TeeSessionRequest;
+import com.iexec.sms.tee.session.scone.cas.CasClient;
+import com.iexec.sms.tee.session.scone.cas.CasConfiguration;
+import com.iexec.sms.tee.session.scone.cas.SconeSession;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.http.ResponseEntity;
+import org.springframework.stereotype.Component;
+
+@Slf4j
+@Component
+@ConditionalOnTeeFramework(frameworks = TeeFramework.SCONE)
+public class SconeSessionHandlerService implements TeeSessionHandler {
+ private final SconeSessionMakerService sessionService;
+ private final CasClient apiClient;
+ private final TeeSessionLogConfiguration teeSessionLogConfiguration;
+ private final CasConfiguration casConfiguration;
+
+ public SconeSessionHandlerService(SconeSessionMakerService sessionService,
+ CasClient apiClient,
+ TeeSessionLogConfiguration teeSessionLogConfiguration,
+ CasConfiguration casConfiguration) {
+ this.sessionService = sessionService;
+ this.apiClient = apiClient;
+ this.teeSessionLogConfiguration = teeSessionLogConfiguration;
+ this.casConfiguration = casConfiguration;
+ }
+
+ /**
+ * Build and post secret session on secret provisioning service.
+ *
+ * @param request tee session generation request
+ * @return String secret provisioning service url
+ * @throws TeeSessionGenerationException if call to CAS failed
+ */
+ @Override
+ public String buildAndPostSession(TeeSessionRequest request)
+ throws TeeSessionGenerationException {
+ SconeSession session = sessionService.generateSession(request);
+ if (teeSessionLogConfiguration.isDisplayDebugSessionEnabled()) {
+ log.info("Session content [taskId:{}]\n{}",
+ request.getTaskDescription().getChainTaskId(), session);
+ }
+ ResponseEntity postSession = apiClient.postSession(session.toString());
+
+ if (postSession == null) {
+ throw new TeeSessionGenerationException(
+ TeeSessionGenerationError.SECURE_SESSION_STORAGE_CALL_FAILED,
+ "Failed to post session, no return from CAS.");
+ }
+
+ int httpCode = postSession.getStatusCodeValue();
+ if (httpCode != 201) {
+ throw new TeeSessionGenerationException(
+ TeeSessionGenerationError.SECURE_SESSION_STORAGE_CALL_FAILED,
+ "Failed to post session: " + httpCode);
+ }
+ return casConfiguration.getEnclaveHost();
+ }
+
+}
diff --git a/src/main/java/com/iexec/sms/tee/session/scone/SconeSessionMakerService.java b/src/main/java/com/iexec/sms/tee/session/scone/SconeSessionMakerService.java
new file mode 100644
index 00000000..52b48cf1
--- /dev/null
+++ b/src/main/java/com/iexec/sms/tee/session/scone/SconeSessionMakerService.java
@@ -0,0 +1,159 @@
+/*
+ * Copyright 2020 IEXEC BLOCKCHAIN TECH
+ *
+ * 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.
+ */
+
+package com.iexec.sms.tee.session.scone;
+
+import com.iexec.common.tee.TeeFramework;
+import com.iexec.sms.api.config.TeeServicesProperties;
+import com.iexec.sms.tee.ConditionalOnTeeFramework;
+import com.iexec.sms.tee.session.base.SecretEnclaveBase;
+import com.iexec.sms.tee.session.base.SecretSessionBase;
+import com.iexec.sms.tee.session.base.SecretSessionBaseService;
+import com.iexec.sms.tee.session.generic.TeeSessionGenerationException;
+import com.iexec.sms.tee.session.generic.TeeSessionRequest;
+import com.iexec.sms.tee.session.scone.cas.SconeEnclave;
+import com.iexec.sms.tee.session.scone.cas.SconeSession;
+import com.iexec.sms.tee.session.scone.cas.SconeSession.AccessPolicy;
+import com.iexec.sms.tee.session.scone.cas.SconeSession.Image;
+import com.iexec.sms.tee.session.scone.cas.SconeSession.Image.Volume;
+import com.iexec.sms.tee.session.scone.cas.SconeSession.Security;
+import com.iexec.sms.tee.session.scone.cas.SconeSession.Volumes;
+import lombok.NonNull;
+import org.springframework.stereotype.Service;
+
+import java.util.*;
+
+//TODO Rename and move
+@Service
+@ConditionalOnTeeFramework(frameworks = TeeFramework.SCONE)
+public class SconeSessionMakerService {
+
+ // Internal values required for setting up a palaemon session
+ // Generic
+ static final String TOLERATED_INSECURE_OPTIONS = "TOLERATED_INSECURE_OPTIONS";
+ static final String IGNORED_SGX_ADVISORIES = "IGNORED_SGX_ADVISORIES";
+ static final String APP_ARGS = "APP_ARGS";
+
+ // PreCompute
+ static final String PRE_COMPUTE_ENTRYPOINT = "PRE_COMPUTE_ENTRYPOINT";
+ // PostCompute
+ static final String POST_COMPUTE_ENTRYPOINT = "POST_COMPUTE_ENTRYPOINT";
+
+ private final SecretSessionBaseService secretSessionBaseService;
+ private final TeeServicesProperties teeServicesConfig;
+ private final SconeSessionSecurityConfig attestationSecurityConfig;
+
+ public SconeSessionMakerService(
+ SecretSessionBaseService secretSessionBaseService,
+ TeeServicesProperties teeServicesConfig,
+ SconeSessionSecurityConfig attestationSecurityConfig) {
+ this.secretSessionBaseService = secretSessionBaseService;
+ this.teeServicesConfig = teeServicesConfig;
+ this.attestationSecurityConfig = attestationSecurityConfig;
+ }
+
+ /**
+ * Collect tokens required for different compute stages (pre, in, post)
+ * and build the yaml config of the TEE session.
+ *
+ * TODO: Read onchain available infos from enclave instead of copying
+ * public vars to palaemon.yml. It needs ssl call from enclave to eth
+ * node (only ethereum node address required inside palaemon.yml)
+ *
+ * @param request session request details
+ * @return session config in yaml string format
+ */
+ @NonNull
+ public SconeSession generateSession(TeeSessionRequest request)
+ throws TeeSessionGenerationException {
+ List policy = Arrays.asList("CREATOR");
+ Volume iexecInVolume = new Volume("iexec_in", "/iexec_in");
+ Volume iexecOutVolume = new Volume("iexec_out", "/iexec_out");
+ Volume postComputeTmpVolume = new Volume("post-compute-tmp",
+ "/post-compute-tmp");
+ List services = new ArrayList<>();
+ List images = new ArrayList<>();
+
+ SecretSessionBase baseSession = secretSessionBaseService
+ .getSecretsTokens(request);
+
+ // pre (optional)
+ if (baseSession.getPreCompute() != null) {
+ SconeEnclave sconePreEnclave = toSconeEnclave(
+ baseSession.getPreCompute());
+ sconePreEnclave
+ .setCommand(teeServicesConfig.getPreComputeProperties().getEntrypoint());
+ addJavaEnvVars(sconePreEnclave);
+ services.add(sconePreEnclave);
+ images.add(new SconeSession.Image(sconePreEnclave.getImageName(),
+ Arrays.asList(iexecInVolume)));
+ }
+ // app
+ SconeEnclave sconeAppEnclave = toSconeEnclave(
+ baseSession.getAppCompute());
+ sconeAppEnclave
+ .setCommand(request.getTaskDescription().getAppCommand());
+ services.add(sconeAppEnclave);
+ images.add(new SconeSession.Image(sconeAppEnclave.getImageName(),
+ Arrays.asList(iexecInVolume, iexecOutVolume)));
+ // post
+ SconeEnclave sconePostEnclave = toSconeEnclave(
+ baseSession.getPostCompute());
+ sconePostEnclave
+ .setCommand(teeServicesConfig.getPostComputeProperties().getEntrypoint());
+ addJavaEnvVars(sconePostEnclave);
+ services.add(sconePostEnclave);
+ images.add(new SconeSession.Image(sconePostEnclave.getImageName(),
+ Arrays.asList(iexecOutVolume,
+ postComputeTmpVolume)));
+
+ return SconeSession.builder()
+ .name(request.getSessionId())
+ .version("0.3")
+ .accessPolicy(new AccessPolicy(policy, policy))
+ .services(services)
+ .images(images)
+ .volumes(Arrays.asList(new Volumes(iexecInVolume.getName()),
+ new Volumes(iexecOutVolume.getName()),
+ new Volumes(postComputeTmpVolume.getName())))
+ .security(new Security(
+ attestationSecurityConfig.getToleratedInsecureOptions(),
+ attestationSecurityConfig.getIgnoredSgxAdvisories()))
+ .build();
+ }
+
+ private void addJavaEnvVars(SconeEnclave sconeEnclave) {
+ Map additionalJavaEnv = Map.of("LD_LIBRARY_PATH",
+ "/usr/lib/jvm/java-11-openjdk/lib/server:/usr/lib/jvm/java-11-openjdk/lib:/usr/lib/jvm/java-11-openjdk/../lib",
+ "JAVA_TOOL_OPTIONS", "-Xmx256m");
+ HashMap newEnvironment = new HashMap<>();
+ newEnvironment.putAll(sconeEnclave.getEnvironment());
+ newEnvironment.putAll(additionalJavaEnv);
+ sconeEnclave.setEnvironment(newEnvironment);
+ }
+
+ private SconeEnclave toSconeEnclave(SecretEnclaveBase enclaveBase) {
+ return SconeEnclave.builder()
+ .name(enclaveBase.getName())
+ .imageName(enclaveBase.getName() + "-image")
+ .mrenclaves(Arrays.asList(enclaveBase.getMrenclave()))
+ .pwd("/")
+ // TODO .command(command)
+ .environment(enclaveBase.getEnvironment())
+ .build();
+ }
+
+}
diff --git a/src/main/java/com/iexec/sms/tee/session/attestation/AttestationSecurityConfig.java b/src/main/java/com/iexec/sms/tee/session/scone/SconeSessionSecurityConfig.java
similarity index 71%
rename from src/main/java/com/iexec/sms/tee/session/attestation/AttestationSecurityConfig.java
rename to src/main/java/com/iexec/sms/tee/session/scone/SconeSessionSecurityConfig.java
index ca750cb1..1124f157 100644
--- a/src/main/java/com/iexec/sms/tee/session/attestation/AttestationSecurityConfig.java
+++ b/src/main/java/com/iexec/sms/tee/session/scone/SconeSessionSecurityConfig.java
@@ -14,8 +14,10 @@
* limitations under the License.
*/
-package com.iexec.sms.tee.session.attestation;
+package com.iexec.sms.tee.session.scone;
+import com.iexec.common.tee.TeeFramework;
+import com.iexec.sms.tee.ConditionalOnTeeFramework;
import lombok.Getter;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Configuration;
@@ -23,13 +25,14 @@
import java.util.List;
@Configuration
-public class AttestationSecurityConfig {
+@ConditionalOnTeeFramework(frameworks = TeeFramework.SCONE)
+public class SconeSessionSecurityConfig {
- @Value("${scone.attestation.tolerated-insecure-options}")
+ @Value("${tee.scone.attestation.tolerated-insecure-options}")
@Getter
private List toleratedInsecureOptions;
- @Value("${scone.attestation.ignored-sgx-advisories}")
+ @Value("${tee.scone.attestation.ignored-sgx-advisories}")
@Getter
private List ignoredSgxAdvisories;
}
diff --git a/src/main/java/com/iexec/sms/tee/session/cas/CasClient.java b/src/main/java/com/iexec/sms/tee/session/scone/cas/CasClient.java
similarity index 77%
rename from src/main/java/com/iexec/sms/tee/session/cas/CasClient.java
rename to src/main/java/com/iexec/sms/tee/session/scone/cas/CasClient.java
index acffcda3..0ed6f964 100644
--- a/src/main/java/com/iexec/sms/tee/session/cas/CasClient.java
+++ b/src/main/java/com/iexec/sms/tee/session/scone/cas/CasClient.java
@@ -14,31 +14,36 @@
* limitations under the License.
*/
-package com.iexec.sms.tee.session.cas;
+package com.iexec.sms.tee.session.scone.cas;
+import com.iexec.common.tee.TeeFramework;
import com.iexec.sms.ssl.TwoWaySslClient;
+import com.iexec.sms.tee.ConditionalOnTeeFramework;
import org.springframework.http.HttpEntity;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;
+import java.nio.charset.StandardCharsets;
+
@Service
+@ConditionalOnTeeFramework(frameworks = TeeFramework.SCONE)
public class CasClient {
private final CasConfiguration casConfiguration;
private final TwoWaySslClient twoWaySslClient;
public CasClient(CasConfiguration teeCasConfiguration,
- TwoWaySslClient twoWaySslClient) {
+ TwoWaySslClient twoWaySslClient) {
this.casConfiguration = teeCasConfiguration;
this.twoWaySslClient = twoWaySslClient;
}
/*
* POST /session of CAS requires 2-way SSL authentication
- * */
- public ResponseEntity generateSecureSession(byte[] palaemonFile) {
+ */
+ public ResponseEntity postSession(String palaemonFile) {
String url = casConfiguration.getUrl() + "/session";
- HttpEntity request = new HttpEntity<>(palaemonFile);
+ HttpEntity request = new HttpEntity<>(palaemonFile.getBytes(StandardCharsets.UTF_8));
return twoWaySslClient
.getRestTemplate()
.postForEntity(url, request, String.class);
diff --git a/src/main/java/com/iexec/sms/tee/session/cas/CasConfiguration.java b/src/main/java/com/iexec/sms/tee/session/scone/cas/CasConfiguration.java
similarity index 78%
rename from src/main/java/com/iexec/sms/tee/session/cas/CasConfiguration.java
rename to src/main/java/com/iexec/sms/tee/session/scone/cas/CasConfiguration.java
index f471b6ef..eeb96ac0 100644
--- a/src/main/java/com/iexec/sms/tee/session/cas/CasConfiguration.java
+++ b/src/main/java/com/iexec/sms/tee/session/scone/cas/CasConfiguration.java
@@ -14,8 +14,10 @@
* limitations under the License.
*/
-package com.iexec.sms.tee.session.cas;
+package com.iexec.sms.tee.session.scone.cas;
+import com.iexec.common.tee.TeeFramework;
+import com.iexec.sms.tee.ConditionalOnTeeFramework;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
@@ -29,33 +31,34 @@
* When a service wants to access those secrets, it sends a quote with its MREnclave.
* The CAS attests the quote through Intel Attestation Service and sends the secrets
* if the MREnclave is as expected.
- *
+ *
* MREnclave: an enclave identifier, created by hashing all its
* code. It guarantees that a code behaves exactly as expected.
*/
@Component
+@ConditionalOnTeeFramework(frameworks = TeeFramework.SCONE)
@Getter
@NoArgsConstructor
@AllArgsConstructor
public class CasConfiguration {
- @Value("${scone.cas.host}")
+ @Value("${tee.secret-provisioner.web.hostname}")
private String host;
- @Value("${scone.cas.port}")
+ @Value("${tee.secret-provisioner.web.port}")
private String port;
- @Value("${scone.cas.public-host}")
+ @Value("${tee.secret-provisioner.enclave.hostname}")
private String publicHost;
- @Value("${scone.cas.enclave-port}")
+ @Value("${tee.secret-provisioner.enclave.port}")
private String enclavePort;
public String getUrl() {
return "https://" + host + ":" + port;
}
- public String getEnclaveUrl() {
+ public String getEnclaveHost() {
return publicHost + ":" + enclavePort;
}
}
diff --git a/src/main/java/com/iexec/sms/tee/session/scone/cas/SconeEnclave.java b/src/main/java/com/iexec/sms/tee/session/scone/cas/SconeEnclave.java
new file mode 100644
index 00000000..f41709c0
--- /dev/null
+++ b/src/main/java/com/iexec/sms/tee/session/scone/cas/SconeEnclave.java
@@ -0,0 +1,59 @@
+/*
+ * Copyright 2022 IEXEC BLOCKCHAIN TECH
+ *
+ * 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.
+ */
+
+package com.iexec.sms.tee.session.scone.cas;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import lombok.*;
+import lombok.extern.slf4j.Slf4j;
+
+import java.util.List;
+import java.util.Map;
+
+@Slf4j
+@Builder
+@Getter
+@Setter
+@AllArgsConstructor
+@NoArgsConstructor
+public class SconeEnclave {
+
+ @JsonProperty("name")
+ private String name;
+ @JsonProperty("image_name")
+ private String imageName;
+ @JsonProperty("mrenclaves")
+ private List mrenclaves;
+ @JsonProperty("pwd")
+ private String pwd;
+ @JsonProperty("command")
+ private String command;
+ @JsonProperty("environment")
+ private Map environment;
+
+ @Override
+ public String toString() {
+ try {
+ return new ObjectMapper().writeValueAsString(this);
+ } catch (JsonProcessingException e) {
+ log.error("Failed to write CAS session as string [session:{}]", name, e);
+ return "";
+ }
+ }
+
+}
diff --git a/src/main/java/com/iexec/sms/tee/session/scone/cas/SconeSession.java b/src/main/java/com/iexec/sms/tee/session/scone/cas/SconeSession.java
new file mode 100644
index 00000000..7f837e04
--- /dev/null
+++ b/src/main/java/com/iexec/sms/tee/session/scone/cas/SconeSession.java
@@ -0,0 +1,112 @@
+/*
+ * Copyright 2022 IEXEC BLOCKCHAIN TECH
+ *
+ * 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.
+ */
+
+package com.iexec.sms.tee.session.scone.cas;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.dataformat.yaml.YAMLMapper;
+import lombok.*;
+import lombok.extern.slf4j.Slf4j;
+
+import java.util.List;
+
+@Slf4j
+@Builder
+@Getter
+@Setter
+@AllArgsConstructor
+@NoArgsConstructor
+public class SconeSession {
+
+ @JsonProperty("name")
+ private String name;
+ @JsonProperty("version")
+ private String version;
+ @JsonProperty("access_policy")
+ private AccessPolicy accessPolicy;
+ @JsonProperty("services")
+ private List services;
+ @JsonProperty("images")
+ private List images;
+ @JsonProperty("volumes")
+ private List volumes;
+ @JsonProperty("security")
+ private Security security;
+
+ @AllArgsConstructor
+ @Getter
+ public static class AccessPolicy {
+ @JsonProperty("read")
+ private List read;
+ @JsonProperty("update")
+ private List update;
+ }
+
+ @AllArgsConstructor
+ public static class Image {
+ @JsonProperty("name")
+ private String name;
+ @JsonProperty("volumes")
+ private List volumes;
+
+ @Getter
+ @AllArgsConstructor
+ public static class Volume {
+ @JsonProperty("name")
+ private String name;
+ @JsonProperty("path")
+ private String path;
+ }
+ }
+
+ @AllArgsConstructor
+ public static class Volumes {
+ @JsonProperty("name")
+ private String name;
+ }
+
+ @AllArgsConstructor
+ @Getter
+ public static class Security {
+ @JsonProperty("attestation")
+ private Attestation attestation;
+
+ public Security(List tolerate, List ignoreAdvisories) {
+ this.attestation = new Attestation(tolerate, ignoreAdvisories);
+ }
+
+ @AllArgsConstructor
+ @Getter
+ public class Attestation {
+ @JsonProperty("tolerate")
+ private List tolerate;
+ @JsonProperty("ignore_advisories")
+ private List ignoreAdvisories;
+ }
+ }
+
+ @Override
+ public String toString() {
+ try {
+ return new YAMLMapper().writeValueAsString(this);
+ } catch (JsonProcessingException e) {
+ log.error("Failed to write SPS session as string [session:{}]", name, e);
+ return "";
+ }
+ }
+
+}
diff --git a/src/main/java/com/iexec/sms/tee/workflow/TeeWorkflowConfiguration.java b/src/main/java/com/iexec/sms/tee/workflow/TeeWorkflowConfiguration.java
deleted file mode 100644
index 9c5859a7..00000000
--- a/src/main/java/com/iexec/sms/tee/workflow/TeeWorkflowConfiguration.java
+++ /dev/null
@@ -1,101 +0,0 @@
-/*
- * Copyright 2020 IEXEC BLOCKCHAIN TECH
- *
- * 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.
- */
-
-package com.iexec.sms.tee.workflow;
-
-import com.iexec.common.tee.TeeWorkflowSharedConfiguration;
-import lombok.AccessLevel;
-import lombok.Getter;
-import org.springframework.beans.factory.annotation.Value;
-import org.springframework.context.annotation.Configuration;
-import org.springframework.util.unit.DataSize;
-
-import javax.annotation.PostConstruct;
-import javax.validation.ConstraintViolationException;
-import javax.validation.Validator;
-import javax.validation.constraints.NotBlank;
-import javax.validation.constraints.Positive;
-
-@Configuration
-@Getter
-public class TeeWorkflowConfiguration {
-
- @Value("${tee.workflow.las-image}")
- @NotBlank(message = "las image must be provided")
- String lasImage;
-
- @Value("${tee.workflow.pre-compute.image}")
- @NotBlank(message = "pre-compute image must be provided")
- String preComputeImage;
-
- @Value("${tee.workflow.pre-compute.fingerprint}")
- @NotBlank(message = "pre-compute fingerprint must be provided")
- String preComputeFingerprint;
-
- @Value("${tee.workflow.pre-compute.entrypoint}")
- @NotBlank(message = "pre-compute entrypoint must be provided")
- String preComputeEntrypoint;
-
- @Value("${tee.workflow.pre-compute.heap-size-gb}")
- @Positive(message = "pre-compute heap size must be provided")
- int preComputeHeapSizeGb;
-
- @Value("${tee.workflow.post-compute.image}")
- @NotBlank(message = "post-compute image must be provided")
- String postComputeImage;
-
- @Value("${tee.workflow.post-compute.fingerprint}")
- @NotBlank(message = "post-compute fingerprint must be provided")
- String postComputeFingerprint;
-
- @Value("${tee.workflow.post-compute.entrypoint}")
- @NotBlank(message = "post-compute entrypoint must be provided")
- String postComputeEntrypoint;
-
- @Value("${tee.workflow.post-compute.heap-size-gb}")
- @Positive(message = "post-compute heap size must be provided")
- int postComputeHeapSizeGb;
-
- @Getter(AccessLevel.NONE) // no getter
- private Validator validator;
-
- public TeeWorkflowConfiguration(Validator validator) {
- this.validator = validator;
- }
-
- @PostConstruct
- private void validate() {
- if (!validator.validate(this).isEmpty()) {
- throw new ConstraintViolationException(validator.validate(this));
- }
- }
-
- public TeeWorkflowSharedConfiguration getSharedConfiguration() {
- return TeeWorkflowSharedConfiguration.builder()
- .lasImage(lasImage)
- .preComputeImage(preComputeImage)
- .preComputeEntrypoint(preComputeEntrypoint)
- .preComputeHeapSize(DataSize
- .ofGigabytes(preComputeHeapSizeGb)
- .toBytes())
- .postComputeImage(postComputeImage)
- .postComputeEntrypoint(postComputeEntrypoint)
- .postComputeHeapSize(DataSize
- .ofGigabytes(postComputeHeapSizeGb)
- .toBytes())
- .build();
- }
-}
diff --git a/src/main/java/com/iexec/sms/untee/UnTeeController.java b/src/main/java/com/iexec/sms/untee/UnTeeController.java
deleted file mode 100644
index 3f5161f9..00000000
--- a/src/main/java/com/iexec/sms/untee/UnTeeController.java
+++ /dev/null
@@ -1,83 +0,0 @@
-/*
- * Copyright 2020 IEXEC BLOCKCHAIN TECH
- *
- * 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.
- */
-
-package com.iexec.sms.untee;
-
-
-import java.util.Optional;
-
-import com.iexec.common.chain.WorkerpoolAuthorization;
-import com.iexec.common.sms.secret.SmsSecretResponse;
-import com.iexec.common.sms.secret.SmsSecretResponseData;
-import com.iexec.common.sms.secret.TaskSecrets;
-import com.iexec.sms.authorization.AuthorizationService;
-import com.iexec.sms.untee.secret.UnTeeSecretService;
-
-import org.springframework.http.HttpStatus;
-import org.springframework.http.ResponseEntity;
-import org.springframework.web.bind.annotation.PostMapping;
-import org.springframework.web.bind.annotation.RequestBody;
-import org.springframework.web.bind.annotation.RequestHeader;
-import org.springframework.web.bind.annotation.RestController;
-
-@RestController
-public class UnTeeController {
-
- private UnTeeSecretService unTeeSecretService;
- private AuthorizationService authorizationService;
-
- public UnTeeController(
- AuthorizationService authorizationService,
- UnTeeSecretService unTeeSecretService) {
- this.authorizationService = authorizationService;
- this.unTeeSecretService = unTeeSecretService;
- }
-
- /*
- * Retrieve secrets when non-tee execution : We shouldn't do this..
- * */
- @PostMapping("/untee/secrets")
- public ResponseEntity> getUnTeeSecrets(@RequestHeader("Authorization") String authorization,
- @RequestBody WorkerpoolAuthorization workerpoolAuthorization) {
- String workerAddress = workerpoolAuthorization.getWorkerWallet();
- String challenge = authorizationService.getChallengeForWorker(workerpoolAuthorization);
- if (!authorizationService.isSignedByHimself(challenge, authorization, workerAddress)) {
- return ResponseEntity.status(HttpStatus.UNAUTHORIZED).build();
- }
-
- if (!authorizationService.isAuthorizedOnExecution(workerpoolAuthorization, false)) {
- return ResponseEntity.status(HttpStatus.UNAUTHORIZED).build();
- }
-
- Optional unTeeTaskSecrets = unTeeSecretService.getUnTeeTaskSecrets(workerpoolAuthorization.getChainTaskId());
- if (unTeeTaskSecrets.isEmpty()) {
- return ResponseEntity.status(HttpStatus.NOT_FOUND).build();
- }
-
- SmsSecretResponseData smsSecretResponseData = SmsSecretResponseData.builder()
- .secrets(unTeeTaskSecrets.get())
- .build();
-
- SmsSecretResponse smsSecretResponse = SmsSecretResponse.builder()
- .data(smsSecretResponseData)
- .build();
-
- return Optional.of(smsSecretResponse).map(ResponseEntity::ok).orElseGet(() -> ResponseEntity.notFound().build());
- }
-
-
-}
-
diff --git a/src/main/java/com/iexec/sms/untee/secret/UnTeeSecretService.java b/src/main/java/com/iexec/sms/untee/secret/UnTeeSecretService.java
deleted file mode 100644
index 7e97b60c..00000000
--- a/src/main/java/com/iexec/sms/untee/secret/UnTeeSecretService.java
+++ /dev/null
@@ -1,97 +0,0 @@
-/*
- * Copyright 2020 IEXEC BLOCKCHAIN TECH
- *
- * 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.
- */
-
-package com.iexec.sms.untee.secret;
-
-import com.iexec.common.chain.ChainDeal;
-import com.iexec.common.chain.ChainTask;
-import com.iexec.common.sms.secret.SmsSecret;
-import com.iexec.common.sms.secret.TaskSecrets;
-import com.iexec.sms.blockchain.IexecHubService;
-import com.iexec.sms.secret.Secret;
-import com.iexec.sms.secret.web2.Web2SecretsService;
-import com.iexec.sms.secret.web3.Web3Secret;
-import com.iexec.sms.secret.web3.Web3SecretService;
-import lombok.extern.slf4j.Slf4j;
-import org.springframework.stereotype.Service;
-
-import java.util.Optional;
-
-import static com.iexec.sms.secret.ReservedSecretKeyName.IEXEC_RESULT_ENCRYPTION_PUBLIC_KEY;
-
-@Service
-@Slf4j
-public class UnTeeSecretService {
-
- private final IexecHubService iexecHubService;
- private final Web3SecretService web3SecretService;
- private final Web2SecretsService web2SecretsService;
-
- public UnTeeSecretService(IexecHubService iexecHubService,
- Web3SecretService web3SecretService,
- Web2SecretsService web2SecretsService
- ) {
- this.iexecHubService = iexecHubService;
- this.web3SecretService = web3SecretService;
- this.web2SecretsService = web2SecretsService;
- }
-
- /*
- * Untested yet
- */
- public Optional getUnTeeTaskSecrets(String chainTaskId) {
- TaskSecrets.TaskSecretsBuilder taskSecretsBuilder = TaskSecrets.builder();
-
- // TODO use taskDescription instead of chainDeal
- Optional oChainTask = iexecHubService.getChainTask(chainTaskId);
- if (oChainTask.isEmpty()) {
- log.error("getUnTeeTaskSecrets failed (getChainTask failed) [chainTaskId:{}]", chainTaskId);
- return Optional.empty();
- }
- ChainTask chainTask = oChainTask.get();
- Optional oChainDeal = iexecHubService.getChainDeal(chainTask.getDealid());
- if (oChainDeal.isEmpty()) {
- log.error("getUnTeeTaskSecrets failed (getChainDeal failed) [chainTaskId:{}]", chainTaskId);
- return Optional.empty();
- }
- ChainDeal chainDeal = oChainDeal.get();
- String chainDatasetId = chainDeal.getChainDataset().getChainDatasetId();
-
- Optional datasetSecret = web3SecretService.getSecret(chainDatasetId, true);
- if (datasetSecret.isEmpty()) {
- log.error("getUnTeeTaskSecrets failed (datasetSecret failed) [chainTaskId:{}]", chainTaskId);
- return Optional.empty();
- }
- taskSecretsBuilder.datasetSecret(SmsSecret.builder()
- .address(datasetSecret.get().getAddress())
- .secret(datasetSecret.get().getValue())
- .build());
-
- Optional beneficiarySecret = web2SecretsService.getSecret(chainDeal.getBeneficiary(),
- IEXEC_RESULT_ENCRYPTION_PUBLIC_KEY, true);
- if (beneficiarySecret.isEmpty()) {
- log.error("getUnTeeTaskSecrets failed (beneficiarySecret empty) [chainTaskId:{}]", chainTaskId);
- return Optional.empty();
- }
- taskSecretsBuilder.beneficiarySecret(SmsSecret.builder()
- .address(beneficiarySecret.get().getAddress())
- .secret(beneficiarySecret.get().getValue())
- .build());
-
- return Optional.of(taskSecretsBuilder.build());
- }
-
-}
diff --git a/src/main/resources/Dockerfile b/src/main/resources/Dockerfile
deleted file mode 100644
index 7327ed5c..00000000
--- a/src/main/resources/Dockerfile
+++ /dev/null
@@ -1,55 +0,0 @@
-FROM nexus.iex.ec/sconecuratedimages-apps-java:jdk-alpine-scone3.0
-
-ARG jar
-
-RUN test -n "$jar"
-
-COPY $jar /app/iexec-sms.jar
-COPY build/resources/main/palaemonTemplate.vm /app/palaemonTemplate.vm
-COPY src/main/resources/ssl-keystore-dev.p12 /app/ssl-keystore-dev.p12
-
-# #### Docker ENV vars should be placed in palaemon conf for Scone ###
-
-RUN SCONE_MODE=sim SCONE_HEAP=128M && \
- # This path must match the one in palaemon session.
- FSPF_PB_FILE=/fspf.pb && \
- # This path can be changed safely.
- FSPF_PB_FILE_KEYTAG=/fspf.keytag && \
- # This path can be change safely.
- FINGERPRINT_FILE=/fingerprint.txt && \
- # This value must match the heap size value
- # provided at runtime.
- RUNTIME_HEAP_SIZE=3G && \
- # Save the current file system state in fspf.pb file.
- scone fspf create ${FSPF_PB_FILE} && \
- scone fspf addr ${FSPF_PB_FILE} / --not-protected --kernel / && \
- scone fspf addr ${FSPF_PB_FILE} /usr --authenticated --kernel /usr && \
- scone fspf addf ${FSPF_PB_FILE} /usr /usr && \
- scone fspf addr ${FSPF_PB_FILE} /bin --authenticated --kernel /bin && \
- scone fspf addf ${FSPF_PB_FILE} /bin /bin && \
- scone fspf addr ${FSPF_PB_FILE} /lib --authenticated --kernel /lib && \
- scone fspf addf ${FSPF_PB_FILE} /lib /lib && \
- scone fspf addr ${FSPF_PB_FILE} /etc/ssl --authenticated --kernel /etc/ssl && \
- scone fspf addf ${FSPF_PB_FILE} /etc/ssl /etc/ssl && \
- scone fspf addr ${FSPF_PB_FILE} /sbin --authenticated --kernel /sbin && \
- scone fspf addf ${FSPF_PB_FILE} /sbin /sbin && \
- scone fspf addr ${FSPF_PB_FILE} /app --authenticated --kernel /app && \
- scone fspf addf ${FSPF_PB_FILE} /app /app && \
- # Encrypt fspf.pb file with a randomly generated key
- scone fspf encrypt ${FSPF_PB_FILE} > ${FSPF_PB_FILE_KEYTAG} && \
- # Get the runtime mrenclave
- MRENCLAVE="$(SCONE_HASH=1 SCONE_HEAP=${RUNTIME_HEAP_SIZE} java)" && \
- # Get fspf.pb file tag
- FSPF_TAG=$(awk '{print $9}' ${FSPF_PB_FILE_KEYTAG}) && \
- # Get fspf.pb file key
- FSPF_KEY=$(awk '{print $11}' ${FSPF_PB_FILE_KEYTAG}) && \
- # The complete fingerprint is composed of 3 parts
- FINGERPRINT="${FSPF_KEY}|${FSPF_TAG}|${MRENCLAVE}" && \
- echo ${FINGERPRINT} > ${FINGERPRINT_FILE} && \
- echo "${MRENCLAVE}" && \
- echo "${FSPF_KEY}" && \
- echo "${FSPF_TAG}" && \
- echo "${FINGERPRINT}"
-
-# /!\ This should match the "command" entry in the palaemon config
-ENTRYPOINT [ "/bin/sh", "-c", "java -jar /app/iexec-sms.jar" ]
diff --git a/src/main/resources/Dockerfile.untrusted b/src/main/resources/Dockerfile.untrusted
deleted file mode 100644
index 1616e59a..00000000
--- a/src/main/resources/Dockerfile.untrusted
+++ /dev/null
@@ -1,22 +0,0 @@
-FROM openjdk:11.0.15-jre-slim
-
-ARG jar
-
-RUN test -n "$jar"
-
-RUN apt-get update \
- && apt-get install -y curl \
- && rm -rf /var/lib/apt/lists/*
-
-COPY $jar /app/iexec-sms.jar
-COPY build/resources/main/palaemonTemplate.vm /app/palaemonTemplate.vm
-COPY src/main/resources/ssl-keystore-dev.p12 /app/ssl-keystore-dev.p12
-
-ENV IEXEC_PALAEMON_TEMPLATE=/app/palaemonTemplate.vm
-ENV IEXEC_SMS_SSL_KEYSTORE=/app/ssl-keystore-dev.p12
-ENV IEXEC_SMS_STORAGE_ENCRYPTION_AES_KEY_PATH=/scone/iexec-sms-aes.key
-ENV IEXEC_SMS_BLOCKCHAIN_NODE_ADDRESS=http://chain:8545
-ENV IEXEC_SCONE_CAS_HOST=iexec-cas
-ENV IEXEC_SMS_H2_URL=jdbc:h2:file:/scone/sms-h2
-
-ENTRYPOINT [ "/bin/sh", "-c", "java -jar /app/iexec-sms.jar" ]
diff --git a/src/main/resources/application-gramine.yml b/src/main/resources/application-gramine.yml
new file mode 100644
index 00000000..e505ae7c
--- /dev/null
+++ b/src/main/resources/application-gramine.yml
@@ -0,0 +1,25 @@
+tee:
+ secret-provisioner:
+ web:
+ hostname: ${IEXEC_SECRET_PROVISIONER_WEB_HOSTNAME:localhost}
+ port: ${IEXEC_SECRET_PROVISIONER_WEB_PORT:8080}
+ enclave:
+ hostname: ${IEXEC_SECRET_PROVISIONER_ENCLAVE_HOSTNAME:localhost}
+ port: ${IEXEC_SECRET_PROVISIONER_ENCLAVE_PORT:4433}
+
+ worker:
+ pre-compute:
+ image: ${IEXEC_TEE_WORKER_PRE_COMPUTE_IMAGE:} # e.g.: docker.io/iexechub/tee-worker-pre-compute:x.y.z-production
+ fingerprint: ${IEXEC_TEE_WORKER_PRE_COMPUTE_FINGERPRINT:}
+ heap-size-gb: ${IEXEC_TEE_WORKER_PRE_COMPUTE_HEAP_SIZE_GB:3}
+ entrypoint: ${IEXEC_TEE_WORKER_PRE_COMPUTE_ENTRYPOINT:/bin/bash /apploader.sh}
+ post-compute:
+ image: ${IEXEC_TEE_WORKER_POST_COMPUTE_IMAGE:} # e.g.: docker.io/iexechub/tee-worker-post-compute:x.y.z-production
+ fingerprint: ${IEXEC_TEE_WORKER_POST_COMPUTE_FINGERPRINT:}
+ heap-size-gb: ${IEXEC_TEE_WORKER_POST_COMPUTE_HEAP_SIZE_GB:3}
+ entrypoint: ${IEXEC_TEE_WORKER_POST_COMPUTE_ENTRYPOINT:/bin/bash /apploader.sh}
+
+ gramine:
+ sps:
+ login: ${IEXEC_GRAMINE_SPS_WEB_LOGIN:admin}
+ password: ${IEXEC_GRAMINE_SPS_WEB_PASSWORD:admin}
diff --git a/src/main/resources/application-scone.yml b/src/main/resources/application-scone.yml
new file mode 100644
index 00000000..eec7fc96
--- /dev/null
+++ b/src/main/resources/application-scone.yml
@@ -0,0 +1,33 @@
+tee:
+ secret-provisioner:
+ web:
+ hostname: ${IEXEC_SECRET_PROVISIONER_WEB_HOSTNAME:localhost}
+ port: ${IEXEC_SECRET_PROVISIONER_WEB_PORT:8081}
+ enclave:
+ hostname: ${IEXEC_SECRET_PROVISIONER_ENCLAVE_HOSTNAME:localhost}
+ port: ${IEXEC_SECRET_PROVISIONER_ENCLAVE_PORT:18765}
+
+ worker:
+ pre-compute:
+ image: ${IEXEC_TEE_WORKER_PRE_COMPUTE_IMAGE:} # e.g.: docker.io/iexechub/tee-worker-pre-compute:x.y.z-production
+ fingerprint: ${IEXEC_TEE_WORKER_PRE_COMPUTE_FINGERPRINT:}
+ heap-size-gb: ${IEXEC_TEE_WORKER_PRE_COMPUTE_HEAP_SIZE_GB:3}
+ entrypoint: ${IEXEC_TEE_WORKER_PRE_COMPUTE_ENTRYPOINT:java -jar /app/app.jar}
+ post-compute:
+ image: ${IEXEC_TEE_WORKER_POST_COMPUTE_IMAGE:} # e.g.: docker.io/iexechub/tee-worker-post-compute:x.y.z-production
+ fingerprint: ${IEXEC_TEE_WORKER_POST_COMPUTE_FINGERPRINT:}
+ heap-size-gb: ${IEXEC_TEE_WORKER_POST_COMPUTE_HEAP_SIZE_GB:3}
+ entrypoint: ${IEXEC_TEE_WORKER_POST_COMPUTE_ENTRYPOINT:java -jar /app/app.jar}
+
+ ssl:
+ key-store: ${IEXEC_SMS_SSL_KEYSTORE:/app/ssl-keystore-dev.p12} #iexec-core dev certificate for dev
+ key-store-password: ${IEXEC_SMS_SSL_KEYSTORE_PASSWORD:whatever}
+ key-store-type: ${IEXEC_SMS_SSL_KEYSTORE_TYPE:PKCS12}
+ key-alias: ${IEXEC_SMS_SSL_KEYSTORE_ALIAS:iexec-core}
+ client-auth: need
+
+ scone:
+ las-image: ${IEXEC_SMS_IMAGE_LAS_IMAGE:} # e.g.: registry.scontain.com:5050/scone-production/iexec-las:x.y.z
+ attestation:
+ tolerated-insecure-options: ${IEXEC_SCONE_TOLERATED_INSECURE_OPTIONS:} # e.g.: hyperthreading,software-hardening-needed,insecure-igpu,outdated-tcb,debug-mode
+ ignored-sgx-advisories: ${IEXEC_IGNORED_SGX_ADVISORIES:} # e.g.: INTEL-SA-00220,INTEL-SA-00270
diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml
index 1d4cb585..63c00e9c 100644
--- a/src/main/resources/application.yml
+++ b/src/main/resources/application.yml
@@ -1,14 +1,5 @@
server:
- port: ${IEXEC_SMS_PORT:15443}
- http:
- enabled: ${IEXEC_SMS_HTTP_ENABLED:true}
- port: ${IEXEC_SMS_HTTP_PORT:13300}
- ssl:
- key-store: ${IEXEC_SMS_SSL_KEYSTORE:./src/main/resources/ssl-keystore-dev.p12} #iexec-core dev certificate for dev
- key-store-password: ${IEXEC_SMS_SSL_KEYSTORE_PASSWORD:whatever}
- key-store-type: ${IEXEC_SMS_SSL_KEYSTORE_TYPE:PKCS12}
- key-alias: ${IEXEC_SMS_SSL_KEYSTORE_ALIAS:iexec-core}
- client-auth: need
+ port: ${IEXEC_SMS_PORT:13300}
# Not sure it's a good idea but here is a link for an embedded mongodb
# https://www.baeldung.com/spring-boot-embedded-mongodb
@@ -22,8 +13,10 @@ server:
# Embedded H2 inside JVM
spring:
+ profiles:
+ active: ${IEXEC_SMS_TEE_RUNTIME_FRAMEWORK:} # gramine/scone
datasource:
- url: ${IEXEC_SMS_H2_URL:jdbc:h2:file:/tmp/h2/sms-h2} # will get or create /tmp/h2/sms-h2.mv.db
+ url: ${IEXEC_SMS_H2_URL:jdbc:h2:file:/data/sms-h2} # will get or create /data/sms-h2.mv.db
driver-class-name: org.h2.Driver
username: sa
password:
@@ -39,41 +32,16 @@ spring:
encryption:
# Will get previous key or else create one on this path
# this file shouldn't be clearly readable outside the enclave (but encrypted content could be copied outside)
- aesKeyPath: ${IEXEC_SMS_STORAGE_ENCRYPTION_AES_KEY_PATH:./src/main/resources/iexec-sms-aes.key} # /scone/iexec-sms-aes.key
+ aesKeyPath: ${IEXEC_SMS_STORAGE_ENCRYPTION_AES_KEY_PATH:/data/iexec-sms-aes.key}
blockchain:
id: ${IEXEC_CHAIN_ID:17}
node-address: ${IEXEC_SMS_BLOCKCHAIN_NODE_ADDRESS:http://localhost:8545}
hub-address: ${IEXEC_HUB_ADDRESS:0xBF6B2B07e47326B7c8bfCb4A5460bef9f0Fd2002}
- gas-price-multiplier: ${IEXEC_GAS_PRICE_MULTIPLIER:1.0} # txs will be sent with networkGasPrice*gasPriceMultiplier, 4.0 means super fast
+ gas-price-multiplier: ${IEXEC_GAS_PRICE_MULTIPLIER:1.0} # txs will be sent with networkGasPrice*gasPriceMultiplier, 4.0 means superfast
gas-price-cap: ${IEXEC_GAS_PRICE_CAP:22000000000} #in Wei, will be used for txs if networkGasPrice*gasPriceMultiplier > gasPriceCap
is-sidechain: ${IEXEC_IS_SIDECHAIN:false}
-scone:
- cas:
- host: ${IEXEC_SCONE_CAS_HOST:localhost}
- port: ${IEXEC_SCONE_CAS_PORT:8081}
- public-host: ${IEXEC_SCONE_CAS_PUBLIC_HOST:localhost}
- enclave-port: ${IEXEC_SCONE_CAS_ENCLAVE_PORT:18765}
- palaemon: ${IEXEC_PALAEMON_TEMPLATE:./src/main/resources/palaemonTemplate.vm}
- attestation:
- tolerated-insecure-options: ${IEXEC_SCONE_TOLERATED_INSECURE_OPTIONS:} # e.g.: hyperthreading,software-hardening-needed,insecure-igpu,outdated-tcb,debug-mode
- ignored-sgx-advisories: ${IEXEC_IGNORED_SGX_ADVISORIES:} # e.g.: INTEL-SA-00220,INTEL-SA-00270
-
-# TODO /!\ remove the option of env variable for releases.
-tee.workflow:
- las-image: ${IEXEC_SMS_IMAGE_LAS_IMAGE:} # e.g.: registry.scontain.com:5050/scone-production/iexec-las:x.y.z
- pre-compute:
- image: ${IEXEC_TEE_WORKER_PRE_COMPUTE_IMAGE:} # e.g.: docker.io/iexechub/tee-worker-pre-compute:x.y.z-production
- fingerprint: ${IEXEC_TEE_WORKER_PRE_COMPUTE_FINGERPRINT:}
- heap-size-gb: ${IEXEC_TEE_WORKER_PRE_COMPUTE_HEAP_SIZE_GB:4}
- entrypoint: ${IEXEC_TEE_WORKER_PRE_COMPUTE_ENTRYPOINT:java -jar /app/app.jar}
- post-compute:
- image: ${IEXEC_TEE_WORKER_POST_COMPUTE_IMAGE:} # e.g.: docker.io/iexechub/tee-worker-post-compute:x.y.z-production
- fingerprint: ${IEXEC_TEE_WORKER_POST_COMPUTE_FINGERPRINT:}
- heap-size-gb: ${IEXEC_TEE_WORKER_POST_COMPUTE_HEAP_SIZE_GB:4}
- entrypoint: ${IEXEC_TEE_WORKER_POST_COMPUTE_ENTRYPOINT:java -jar /app/app.jar}
-
logging:
tee.display-debug-session: ${IEXEC_SMS_DISPLAY_DEBUG_SESSION:false}
# level:
@@ -82,4 +50,4 @@ logging:
springdoc:
packagesToScan: com.iexec.sms
- pathsToMatch: /**
\ No newline at end of file
+ pathsToMatch: /**
diff --git a/src/main/resources/banner.txt b/src/main/resources/banner.txt
new file mode 100644
index 00000000..2bb249c3
--- /dev/null
+++ b/src/main/resources/banner.txt
@@ -0,0 +1,7 @@
+${Ansi.YELLOW} _ _____ ______
+${Ansi.YELLOW} __/\__ (_) ____|_ _____ ___ \ \ \ \
+${Ansi.YELLOW} \ / | | _| \ \/ / _ \/ __| \ \ \ \
+${Ansi.YELLOW} /_ _\ | | |___ > < __/ (__ / / / /
+${Ansi.YELLOW} \/ |_|_____/_/\_\___|\___| /_/_/_/
+${Ansi.YELLOW} =========
+${Ansi.YELLOW} :: ${application.title}${application.formatted-version} built with Spring Boot${spring-boot.formatted-version} :: ${Ansi.DEFAULT}
\ No newline at end of file
diff --git a/src/main/resources/logback-spring.xml b/src/main/resources/logback-spring.xml
new file mode 100644
index 00000000..411ed485
--- /dev/null
+++ b/src/main/resources/logback-spring.xml
@@ -0,0 +1,28 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/main/resources/palaemonTemplate.vm b/src/main/resources/palaemonTemplate.vm
deleted file mode 100644
index 645c75fe..00000000
--- a/src/main/resources/palaemonTemplate.vm
+++ /dev/null
@@ -1,143 +0,0 @@
-#*
- It is safer to keep all values single-quoted to escape yaml
- special characters (in dataset names for example). We also
- ran into some parsing issues on the CAS's side when integers
- were not quoted ("invalid type: integer `0`, expected a string").
-
- Null values should be replaced by an empty string in the code
- otherwise we end up with 'null' instead of ''.
-*#
-
-name: '$SESSION_ID'
-version: '0.3'
-
-access_policy:
- read:
- - CREATOR
- update:
- - CREATOR
-
-services:
-
- #*
- Pre-compute enclave.
- Only when dataset is requested.
- *#
- #if ($IS_PRE_COMPUTE_REQUIRED)
- - name: 'pre-compute'
- image_name: 'pre-compute-image'
- mrenclaves: ['$PRE_COMPUTE_MRENCLAVE']
- pwd: '/'
- command: '$PRE_COMPUTE_ENTRYPOINT'
- environment:
- LD_LIBRARY_PATH: '/usr/lib/jvm/java-11-openjdk/lib/server:/usr/lib/jvm/java-11-openjdk/lib:/usr/lib/jvm/java-11-openjdk/../lib'
- JAVA_TOOL_OPTIONS: '-Xmx256m'
- IEXEC_TASK_ID: '$env.get("IEXEC_TASK_ID")'
- IEXEC_PRE_COMPUTE_OUT: '/iexec_in'
- #* dataset *#
- IS_DATASET_REQUIRED: '$IS_DATASET_REQUIRED'
- IEXEC_DATASET_KEY: '$IEXEC_DATASET_KEY'
- IEXEC_DATASET_URL: '$env.get("IEXEC_DATASET_URL")'
- IEXEC_DATASET_FILENAME: '$env.get("IEXEC_DATASET_FILENAME")'
- IEXEC_DATASET_CHECKSUM: '$env.get("IEXEC_DATASET_CHECKSUM")'
- #* input files *#
- IEXEC_INPUT_FILES_FOLDER: '$env.get("IEXEC_INPUT_FILES_FOLDER")'
- IEXEC_INPUT_FILES_NUMBER: '$env.get("IEXEC_INPUT_FILES_NUMBER")'
- #foreach($key in $INPUT_FILE_URLS.keySet())
- $key: '$INPUT_FILE_URLS.get($key)'
- #end
- #end
-
- #*
- Application enclave
- *#
- - name: 'app'
- image_name: 'app-image'
- mrenclaves: ['$APP_MRENCLAVE']
- pwd: '/'
- command: '$APP_ARGS'
- environment:
- IEXEC_TASK_ID: '$env.get("IEXEC_TASK_ID")'
- IEXEC_IN: '$env.get("IEXEC_IN")'
- IEXEC_OUT: '$env.get("IEXEC_OUT")'
- #* dataset *#
- IEXEC_DATASET_ADDRESS: '$env.get("IEXEC_DATASET_ADDRESS")'
- IEXEC_DATASET_FILENAME: '$env.get("IEXEC_DATASET_FILENAME")'
- #* BoT *#
- IEXEC_BOT_SIZE: '$env.get("IEXEC_BOT_SIZE")'
- IEXEC_BOT_FIRST_INDEX: '$env.get("IEXEC_BOT_FIRST_INDEX")'
- IEXEC_BOT_TASK_INDEX: '$env.get("IEXEC_BOT_TASK_INDEX")'
- #* input files *#
- IEXEC_INPUT_FILES_FOLDER: '$env.get("IEXEC_INPUT_FILES_FOLDER")'
- IEXEC_INPUT_FILES_NUMBER: '$env.get("IEXEC_INPUT_FILES_NUMBER")'
- #foreach($key in $INPUT_FILE_NAMES.keySet())
- $key: '$INPUT_FILE_NAMES.get($key)'
- #end
- IEXEC_APP_DEVELOPER_SECRET: '$IEXEC_APP_DEVELOPER_SECRET_1'
- IEXEC_APP_DEVELOPER_SECRET_1: '$IEXEC_APP_DEVELOPER_SECRET_1'
- #foreach($key in $REQUESTER_SECRETS.keySet())
- $key: '$REQUESTER_SECRETS.get($key)'
- #end
-
- #*
- Post-compute enclave
- *#
- - name: 'post-compute'
- image_name: 'post-compute-image'
- mrenclaves: ['$POST_COMPUTE_MRENCLAVE']
- pwd: '/'
- command: '$POST_COMPUTE_ENTRYPOINT'
- environment:
- LD_LIBRARY_PATH: '/usr/lib/jvm/java-11-openjdk/lib/server:/usr/lib/jvm/java-11-openjdk/lib:/usr/lib/jvm/java-11-openjdk/../lib'
- JAVA_TOOL_OPTIONS: '-Xmx256m'
- RESULT_TASK_ID: '$RESULT_TASK_ID'
- RESULT_ENCRYPTION: '$RESULT_ENCRYPTION'
- RESULT_ENCRYPTION_PUBLIC_KEY: '$RESULT_ENCRYPTION_PUBLIC_KEY'
- RESULT_STORAGE_PROVIDER: '$RESULT_STORAGE_PROVIDER'
- RESULT_STORAGE_PROXY: '$RESULT_STORAGE_PROXY'
- RESULT_STORAGE_TOKEN: '$RESULT_STORAGE_TOKEN'
- RESULT_STORAGE_CALLBACK: '$RESULT_STORAGE_CALLBACK'
- RESULT_SIGN_WORKER_ADDRESS: '$RESULT_SIGN_WORKER_ADDRESS'
- RESULT_SIGN_TEE_CHALLENGE_PRIVATE_KEY: '$RESULT_SIGN_TEE_CHALLENGE_PRIVATE_KEY'
-
-#**
- Images used by each service
-*#
-images:
- #* pre-compute. Only when dataset is requested *#
- #if ($IS_PRE_COMPUTE_REQUIRED)
- - name: pre-compute-image
- volumes:
- - name: 'iexec_in'
- path: '/iexec_in'
- #end
-
- #* application *#
- - name: app-image
- volumes:
- - name: 'iexec_in'
- path: '/iexec_in'
- - name: 'iexec_out'
- path: '/iexec_out'
-
- #* post-compute *#
- - name: post-compute-image
- volumes:
- - name: 'iexec_out'
- path: '/iexec_out'
- - name: 'post-compute-tmp'
- path: '/post-compute-tmp'
-
-#**
- Volumes that will be protected
- for each service.
-*#
-volumes:
- - name: iexec_in
- - name: iexec_out
- - name: post-compute-tmp
-
-security:
- attestation:
- tolerate: [$TOLERATED_INSECURE_OPTIONS]
- ignore_advisories: [$IGNORED_SGX_ADVISORIES]
diff --git a/src/main/resources/sms-palaemon-conf.yml.template b/src/main/resources/sms-palaemon-conf.yml.template
deleted file mode 100644
index 781dba24..00000000
--- a/src/main/resources/sms-palaemon-conf.yml.template
+++ /dev/null
@@ -1,34 +0,0 @@
-## Palaemon config ##
-name: s1
-digest: create
-
-services:
- - name: @IEXEC_SMS_PALAEMON_SERVICE_NAME@
- image_name: image3
- mrenclaves: [@IEXEC_SMS_MRENCLAVE@]
- tags: [demo]
- pwd: /
- command: java -jar /app/iexec-sms.jar
- fspf_path: /fspf.pb
- fspf_key: @IEXEC_SMS_FSPF_KEY@
- fspf_tag: @IEXEC_SMS_FSPF_TAG@
- environment:
- LD_LIBRARY_PATH: '/usr/lib/jvm/java-11-openjdk/lib/server:/usr/lib/jvm/java-11-openjdk/lib:/usr/lib/jvm/java-11-openjdk/../lib'
- JAVA_TOOL_OPTIONS: -Xmx256m
- IEXEC_SMS_BLOCKCHAIN_NODE_ADDRESS: http://chain:8545
- IEXEC_SCONE_CAS_HOST: iexec-cas
- IEXEC_PALAEMON_TEMPLATE: /palaemonTemplate.vm
- IEXEC_SMS_SSL_KEYSTORE: /ssl-keystore-dev.p12
- IEXEC_SMS_STORAGE_ENCRYPTION_AES_KEY_PATH: /scone/iexec-sms-aes.key
- IEXEC_SMS_H2_URL: jdbc:h2:file:/scone/sms-h2
-
-images:
- - name: image3
- mrenclaves: [@IEXEC_SMS_MRENCLAVE@]
- tags: [demo]
- volumes:
- - name: scone
- path: /scone
-
-volumes:
- - name: scone
diff --git a/src/test/java/com/iexec/sms/authorization/AuthorizationServiceTests.java b/src/test/java/com/iexec/sms/authorization/AuthorizationServiceTests.java
index 67322d33..0ff228b8 100644
--- a/src/test/java/com/iexec/sms/authorization/AuthorizationServiceTests.java
+++ b/src/test/java/com/iexec/sms/authorization/AuthorizationServiceTests.java
@@ -16,21 +16,12 @@
package com.iexec.sms.authorization;
-import static com.iexec.common.chain.ChainTaskStatus.ACTIVE;
-import static com.iexec.common.chain.ChainTaskStatus.UNSET;
-import static com.iexec.sms.authorization.AuthorizationError.*;
-import static org.assertj.core.api.Assertions.assertThat;
-import static org.mockito.Mockito.when;
-
-import java.util.Optional;
-
import com.iexec.common.chain.ChainDeal;
import com.iexec.common.chain.ChainTask;
import com.iexec.common.chain.WorkerpoolAuthorization;
import com.iexec.common.security.Signature;
import com.iexec.common.utils.TestUtils;
import com.iexec.sms.blockchain.IexecHubService;
-
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
@@ -38,6 +29,14 @@
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
+import java.util.Optional;
+
+import static com.iexec.common.chain.ChainTaskStatus.ACTIVE;
+import static com.iexec.common.chain.ChainTaskStatus.UNSET;
+import static com.iexec.sms.authorization.AuthorizationError.*;
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.mockito.Mockito.when;
+
class AuthorizationServiceTests {
@Mock
@@ -169,7 +168,6 @@ void shouldNotBeAuthorizedOnExecutionOfTeeTaskWhenTaskNotActiveWithDetails() {
@Test
void shouldNotBeAuthorizedOnExecutionOfTeeTaskWhenGetDealFailedWithDetails() {
- ChainDeal chainDeal = TestUtils.getChainDeal();
ChainTask chainTask = TestUtils.getChainTask(ACTIVE);
WorkerpoolAuthorization auth = TestUtils.getTeeWorkerpoolAuth();
auth.setSignature(new Signature(TestUtils.POOL_WRONG_SIGNATURE));
diff --git a/src/test/java/com/iexec/sms/secret/SecretControllerTests.java b/src/test/java/com/iexec/sms/secret/SecretControllerTests.java
new file mode 100644
index 00000000..e644d7c1
--- /dev/null
+++ b/src/test/java/com/iexec/sms/secret/SecretControllerTests.java
@@ -0,0 +1,247 @@
+/*
+ * Copyright 2022 IEXEC BLOCKCHAIN TECH
+ *
+ * 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.
+ */
+
+package com.iexec.sms.secret;
+
+import com.iexec.sms.authorization.AuthorizationService;
+import com.iexec.sms.secret.web2.*;
+import com.iexec.sms.secret.web3.Web3SecretService;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.mockito.InjectMocks;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.ResponseEntity;
+
+import java.nio.charset.StandardCharsets;
+import java.security.SecureRandom;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.mockito.Mockito.*;
+
+class SecretControllerTests {
+
+ private static final String AUTHORIZATION = "AUTHORIZATION";
+ private static final String CHALLENGE = "CHALLENGE";
+ private static final String WEB2_OWNER_ADDRESS = "WEB2_OWNER_ADDRESS";
+ private static final String WEB2_SECRET_NAME = "WEB2_SECRET_NAME";
+ private static final String WEB2_SECRET_VALUE = "WEB2_SECRET_VALUE";
+ private static final String WEB3_SECRET_ADDRESS = "WEB3_SECRET_ADDRESS";
+ private static final String WEB3_SECRET_VALUE = "WEB3_SECRET_VALUE";
+
+ @Mock
+ private AuthorizationService authorizationService;
+
+ @Mock
+ private Web2SecretService web2SecretService;
+
+ @Mock
+ private Web3SecretService web3SecretService;
+
+ @InjectMocks
+ private SecretController secretController;
+
+ private static final SecureRandom seed = new SecureRandom();
+
+ @BeforeEach
+ public void init() {
+ MockitoAnnotations.openMocks(this);
+ }
+
+ //region isWeb3SecretSet
+ @Test
+ void shouldReturnNoContentWhenWeb3SecretExists() {
+ when(web3SecretService.isSecretPresent(WEB3_SECRET_ADDRESS))
+ .thenReturn(true);
+ assertThat(secretController.isWeb3SecretSet(WEB3_SECRET_ADDRESS))
+ .isEqualTo(ResponseEntity.noContent().build());
+ verifyNoInteractions(authorizationService, web2SecretService);
+ }
+
+ @Test
+ void shouldReturnNotFoundWhenWeb3SecretDoesNotExist() {
+ when(web3SecretService.isSecretPresent(WEB3_SECRET_ADDRESS))
+ .thenReturn(false);
+ assertThat(secretController.isWeb3SecretSet(WEB3_SECRET_ADDRESS))
+ .isEqualTo(ResponseEntity.notFound().build());
+ verifyNoInteractions(authorizationService, web2SecretService);
+ }
+ //endregion
+
+ //region addWeb3Secret
+ @Test
+ void failToAddWeb3SecretWhenPayloadTooLarge() {
+ assertThat(secretController.addWeb3Secret(AUTHORIZATION, WEB3_SECRET_ADDRESS, getRandomString(4097)))
+ .isEqualTo(ResponseEntity.status(HttpStatus.PAYLOAD_TOO_LARGE).build());
+ verifyNoInteractions(authorizationService, web2SecretService, web3SecretService);
+ }
+
+ @Test
+ void failToAddWeb3SecretWhenBadAuthorization() {
+ when(authorizationService.getChallengeForSetWeb3Secret(WEB3_SECRET_ADDRESS, WEB3_SECRET_VALUE))
+ .thenReturn(CHALLENGE);
+ when(authorizationService.isSignedByOwner(CHALLENGE, AUTHORIZATION, WEB3_SECRET_ADDRESS))
+ .thenReturn(false);
+ assertThat(secretController.addWeb3Secret(AUTHORIZATION, WEB3_SECRET_ADDRESS, WEB3_SECRET_VALUE))
+ .isEqualTo(ResponseEntity.status(HttpStatus.UNAUTHORIZED).build());
+ verifyNoInteractions(web2SecretService, web3SecretService);
+ }
+
+ @Test
+ void failToAddWeb3SecretWhenSecretAlreadyExists() {
+ when(authorizationService.getChallengeForSetWeb3Secret(WEB3_SECRET_ADDRESS, WEB3_SECRET_VALUE))
+ .thenReturn(CHALLENGE);
+ when(authorizationService.isSignedByOwner(CHALLENGE, AUTHORIZATION, WEB3_SECRET_ADDRESS))
+ .thenReturn(true);
+ when(web3SecretService.addSecret(WEB3_SECRET_ADDRESS, WEB3_SECRET_VALUE))
+ .thenReturn(false);
+ assertThat(secretController.addWeb3Secret(AUTHORIZATION, WEB3_SECRET_ADDRESS, WEB3_SECRET_VALUE))
+ .isEqualTo(ResponseEntity.status(HttpStatus.CONFLICT).build());
+ verifyNoInteractions(web2SecretService);
+ verify(web3SecretService).addSecret(WEB3_SECRET_ADDRESS, WEB3_SECRET_VALUE);
+ }
+
+ @Test
+ void addWeb3Secret() {
+ when(authorizationService.getChallengeForSetWeb3Secret(WEB3_SECRET_ADDRESS, WEB3_SECRET_VALUE))
+ .thenReturn(CHALLENGE);
+ when(authorizationService.isSignedByOwner(CHALLENGE, AUTHORIZATION, WEB3_SECRET_ADDRESS))
+ .thenReturn(true);
+ when(web3SecretService.addSecret(WEB3_SECRET_ADDRESS, WEB3_SECRET_VALUE))
+ .thenReturn(true);
+ assertThat(secretController.addWeb3Secret(AUTHORIZATION, WEB3_SECRET_ADDRESS, WEB3_SECRET_VALUE))
+ .isEqualTo(ResponseEntity.noContent().build());
+ verifyNoInteractions(web2SecretService);
+ verify(web3SecretService).addSecret(WEB3_SECRET_ADDRESS, WEB3_SECRET_VALUE);
+ }
+ //endregion
+
+ //region isWeb2SecretSet
+ @Test
+ void shouldReturnNoContentWhenWeb2SecretExists() {
+ when(web2SecretService.isSecretPresent(WEB2_OWNER_ADDRESS, WEB2_SECRET_NAME))
+ .thenReturn(true);
+ assertThat(secretController.isWeb2SecretSet(WEB2_OWNER_ADDRESS, WEB2_SECRET_NAME))
+ .isEqualTo(ResponseEntity.noContent().build());
+ verifyNoInteractions(authorizationService, web3SecretService);
+ }
+
+ @Test
+ void shouldReturnNotFoundWhenWeb2SecretDoesNotExist() {
+ when(web2SecretService.isSecretPresent(WEB2_OWNER_ADDRESS, WEB2_SECRET_NAME))
+ .thenReturn(false);
+ assertThat(secretController.isWeb2SecretSet(WEB2_OWNER_ADDRESS, WEB2_SECRET_NAME))
+ .isEqualTo(ResponseEntity.notFound().build());
+ verifyNoInteractions(authorizationService, web3SecretService);
+ }
+ //endregion
+
+ //region addWeb2Secret
+ @Test
+ void failToAddWeb2SecretWhenPayloadTooLarge() {
+ assertThat(secretController.addWeb2Secret(AUTHORIZATION, WEB2_OWNER_ADDRESS, WEB2_SECRET_NAME, getRandomString(4097)))
+ .isEqualTo(ResponseEntity.status(HttpStatus.PAYLOAD_TOO_LARGE).build());
+ verifyNoInteractions(web2SecretService, web3SecretService);
+ }
+
+ @Test
+ void failToAddWeb2SecretWhenBadAuthorization() {
+ when(authorizationService.getChallengeForSetWeb2Secret(WEB2_OWNER_ADDRESS, WEB2_SECRET_NAME, WEB2_SECRET_VALUE))
+ .thenReturn(CHALLENGE);
+ when(authorizationService.isSignedByHimself(CHALLENGE, AUTHORIZATION, WEB2_OWNER_ADDRESS))
+ .thenReturn(false);
+ assertThat(secretController.addWeb2Secret(AUTHORIZATION, WEB2_OWNER_ADDRESS, WEB2_SECRET_NAME, WEB2_SECRET_VALUE))
+ .isEqualTo(ResponseEntity.status(HttpStatus.UNAUTHORIZED).build());
+ verifyNoInteractions(web2SecretService, web3SecretService);
+ }
+
+ @Test
+ void failToAddWeb2SecretWhenSecretAlreadyExists() throws SecretAlreadyExistsException {
+ when(authorizationService.getChallengeForSetWeb2Secret(WEB2_OWNER_ADDRESS, WEB2_SECRET_NAME, WEB2_SECRET_VALUE))
+ .thenReturn(CHALLENGE);
+ when(authorizationService.isSignedByHimself(CHALLENGE, AUTHORIZATION, WEB2_OWNER_ADDRESS))
+ .thenReturn(true);
+ when(web2SecretService.addSecret(WEB2_OWNER_ADDRESS, WEB2_SECRET_NAME, WEB2_SECRET_VALUE))
+ .thenThrow(new SecretAlreadyExistsException(WEB2_OWNER_ADDRESS, WEB2_SECRET_NAME));
+ assertThat(secretController.addWeb2Secret(AUTHORIZATION, WEB2_OWNER_ADDRESS, WEB2_SECRET_NAME, WEB2_SECRET_VALUE))
+ .isEqualTo(ResponseEntity.status(HttpStatus.CONFLICT).build());
+ verify(web2SecretService).addSecret(WEB2_OWNER_ADDRESS, WEB2_SECRET_NAME, WEB2_SECRET_VALUE);
+ }
+
+ @Test
+ void addWeb2Secret() throws SecretAlreadyExistsException {
+ when(authorizationService.getChallengeForSetWeb2Secret(WEB2_OWNER_ADDRESS, WEB2_SECRET_NAME, WEB2_SECRET_VALUE))
+ .thenReturn(CHALLENGE);
+ when(authorizationService.isSignedByHimself(CHALLENGE, AUTHORIZATION, WEB2_OWNER_ADDRESS))
+ .thenReturn(true);
+ when(web2SecretService.addSecret(WEB2_OWNER_ADDRESS, WEB2_SECRET_NAME, WEB2_SECRET_VALUE))
+ .thenReturn(new Web2Secret(WEB2_OWNER_ADDRESS, WEB2_SECRET_NAME, WEB2_SECRET_VALUE));
+ assertThat(secretController.addWeb2Secret(AUTHORIZATION, WEB2_OWNER_ADDRESS, WEB2_SECRET_NAME, WEB2_SECRET_VALUE))
+ .isEqualTo(ResponseEntity.noContent().build());
+ verify(web2SecretService).addSecret(WEB2_OWNER_ADDRESS, WEB2_SECRET_NAME, WEB2_SECRET_VALUE);
+ }
+ //endregion
+
+ //region updateWeb2Secret
+ @Test
+ void failToUpdateWeb2SecretWhenPayloadTooLarge() {
+ assertThat(secretController.updateWeb2Secret(AUTHORIZATION, WEB2_OWNER_ADDRESS, WEB2_SECRET_NAME, getRandomString(4097)))
+ .isEqualTo(ResponseEntity.status(HttpStatus.PAYLOAD_TOO_LARGE).build());
+ verifyNoInteractions(web2SecretService, web3SecretService);
+ }
+
+ @Test
+ void failToUpdateWeb2SecretWhenBadAuthorization() {
+ when(authorizationService.getChallengeForSetWeb2Secret(WEB2_OWNER_ADDRESS, WEB2_SECRET_NAME, WEB2_SECRET_VALUE))
+ .thenReturn(CHALLENGE);
+ when(authorizationService.isSignedByHimself(CHALLENGE, AUTHORIZATION, WEB2_OWNER_ADDRESS))
+ .thenReturn(false);
+ assertThat(secretController.updateWeb2Secret(AUTHORIZATION, WEB2_OWNER_ADDRESS, WEB2_SECRET_NAME, WEB2_SECRET_VALUE))
+ .isEqualTo(ResponseEntity.status(HttpStatus.UNAUTHORIZED).build());
+ verifyNoInteractions(web2SecretService, web3SecretService);
+ }
+
+ @Test
+ void failToUpdateWeb2SecretWhenSecretIsMissing() throws NotAnExistingSecretException, SameSecretException {
+ when(authorizationService.getChallengeForSetWeb2Secret(WEB2_OWNER_ADDRESS, WEB2_SECRET_NAME, WEB2_SECRET_VALUE))
+ .thenReturn(CHALLENGE);
+ when(authorizationService.isSignedByHimself(CHALLENGE, AUTHORIZATION, WEB2_OWNER_ADDRESS))
+ .thenReturn(true);
+ doThrow(NotAnExistingSecretException.class).when(web2SecretService)
+ .updateSecret(WEB2_OWNER_ADDRESS, WEB2_SECRET_NAME, WEB2_SECRET_VALUE);
+ assertThat(secretController.updateWeb2Secret(AUTHORIZATION, WEB2_OWNER_ADDRESS, WEB2_SECRET_NAME, WEB2_SECRET_VALUE))
+ .isEqualTo(ResponseEntity.notFound().build());
+ }
+
+ @Test
+ void updateWeb2Secret() {
+ when(authorizationService.getChallengeForSetWeb2Secret(WEB2_OWNER_ADDRESS, WEB2_SECRET_NAME, WEB2_SECRET_VALUE))
+ .thenReturn(CHALLENGE);
+ when(authorizationService.isSignedByHimself(CHALLENGE, AUTHORIZATION, WEB2_OWNER_ADDRESS))
+ .thenReturn(true);
+ assertThat(secretController.updateWeb2Secret(AUTHORIZATION, WEB2_OWNER_ADDRESS, WEB2_SECRET_NAME, WEB2_SECRET_VALUE))
+ .isEqualTo(ResponseEntity.noContent().build());
+ }
+ //endregion
+
+ String getRandomString(int size) {
+ byte[] bytes = new byte[size];
+ seed.nextBytes(bytes);
+ return new String(bytes, StandardCharsets.UTF_8);
+ }
+
+}
diff --git a/src/test/java/com/iexec/sms/secret/compute/TeeTaskComputeSecretHeaderTests.java b/src/test/java/com/iexec/sms/secret/compute/TeeTaskComputeSecretHeaderTests.java
new file mode 100644
index 00000000..c6088d3c
--- /dev/null
+++ b/src/test/java/com/iexec/sms/secret/compute/TeeTaskComputeSecretHeaderTests.java
@@ -0,0 +1,117 @@
+/*
+ * Copyright 2022 IEXEC BLOCKCHAIN TECH
+ *
+ * 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.
+ */
+
+package com.iexec.sms.secret.compute;
+
+import org.assertj.core.api.Assertions;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.EnumSource;
+
+import javax.validation.ValidationException;
+
+class TeeTaskComputeSecretHeaderTests {
+ private final static String ON_CHAIN_OBJECT_ADDRESS = "onChainObjectAddress";
+ private final static String FIXED_SECRET_OWNER = "fixedSecretOwner";
+ private final static String KEY = "key";
+
+ // region Valid constructions
+ @ParameterizedTest
+ @EnumSource(OnChainObjectType.class)
+ void shouldConstructNewApplicationDeveloperSecretHeader(OnChainObjectType objectType) {
+ Assertions.assertThatNoException().isThrownBy(() -> new TeeTaskComputeSecretHeader(
+ objectType,
+ ON_CHAIN_OBJECT_ADDRESS,
+ SecretOwnerRole.APPLICATION_DEVELOPER,
+ "",
+ KEY
+ ));
+ }
+
+ @ParameterizedTest
+ @EnumSource(OnChainObjectType.class)
+ void shouldConstructNewRequesterSecretHeader(OnChainObjectType objectType) {
+ Assertions.assertThatNoException().isThrownBy(() -> new TeeTaskComputeSecretHeader(
+ objectType,
+ "",
+ SecretOwnerRole.REQUESTER,
+ FIXED_SECRET_OWNER,
+ KEY
+ ));
+ }
+ // endregion
+
+ // region Invalid construction
+ @Test
+ void shouldNotConstructNewSecretHeaderBecauseOnChainObjectTypeIsNull() {
+ Assertions.assertThatThrownBy(() -> new TeeTaskComputeSecretHeader(
+ null,
+ ON_CHAIN_OBJECT_ADDRESS,
+ SecretOwnerRole.APPLICATION_DEVELOPER,
+ "",
+ KEY
+ )).isInstanceOf(ValidationException.class);
+ }
+
+ @ParameterizedTest
+ @EnumSource(OnChainObjectType.class)
+ void shouldNotConstructSecretHeaderBecauseSecretOwnerRoleIsNull(OnChainObjectType objectType) {
+ Assertions.assertThatThrownBy(() -> new TeeTaskComputeSecretHeader(
+ objectType,
+ ON_CHAIN_OBJECT_ADDRESS,
+ null,
+ FIXED_SECRET_OWNER,
+ KEY
+ )).isInstanceOf(ValidationException.class);
+ }
+
+ @ParameterizedTest
+ @EnumSource(OnChainObjectType.class)
+ void shouldNotConstructSecretHeaderBecauseKeyIsNull(OnChainObjectType objectType) {
+ Assertions.assertThatThrownBy(() -> new TeeTaskComputeSecretHeader(
+ objectType,
+ ON_CHAIN_OBJECT_ADDRESS,
+ SecretOwnerRole.APPLICATION_DEVELOPER,
+ "",
+ null
+ )).isInstanceOf(ValidationException.class);
+ }
+
+ @ParameterizedTest
+ @EnumSource(OnChainObjectType.class)
+ void shouldNotConstructNewApplicationDeveloperSecretHeaderBecauseFixedSecretOwnerNotEmpty(OnChainObjectType objectType) {
+ Assertions.assertThatThrownBy(() -> new TeeTaskComputeSecretHeader(
+ objectType,
+ ON_CHAIN_OBJECT_ADDRESS,
+ SecretOwnerRole.APPLICATION_DEVELOPER,
+ FIXED_SECRET_OWNER,
+ KEY
+ )).isInstanceOf(ValidationException.class);
+ }
+
+ @ParameterizedTest
+ @EnumSource(OnChainObjectType.class)
+ void shouldNotConstructNewRequesterSecretHeaderBecauseOnChainObjectAddressNotEmpty(OnChainObjectType objectType) {
+ Assertions.assertThatThrownBy(() -> new TeeTaskComputeSecretHeader(
+ objectType,
+ ON_CHAIN_OBJECT_ADDRESS,
+ SecretOwnerRole.REQUESTER,
+ "",
+ KEY
+ )).isInstanceOf(ValidationException.class);
+ }
+ // endregion
+}
diff --git a/src/test/java/com/iexec/sms/secret/compute/TeeTaskComputeSecretServiceTest.java b/src/test/java/com/iexec/sms/secret/compute/TeeTaskComputeSecretServiceTest.java
index f586952a..a9434c58 100644
--- a/src/test/java/com/iexec/sms/secret/compute/TeeTaskComputeSecretServiceTest.java
+++ b/src/test/java/com/iexec/sms/secret/compute/TeeTaskComputeSecretServiceTest.java
@@ -19,6 +19,7 @@ class TeeTaskComputeSecretServiceTest {
.onChainObjectType(OnChainObjectType.APPLICATION)
.onChainObjectAddress(APP_ADDRESS.toLowerCase())
.secretOwnerRole(SecretOwnerRole.APPLICATION_DEVELOPER)
+ .fixedSecretOwner("")
.key("0")
.value(ENCRYPTED_SECRET_VALUE)
.build();
@@ -53,8 +54,8 @@ void shouldAddSecret() {
verify(teeTaskComputeSecretRepository, times(1)).save(computeSecretCaptor.capture());
final TeeTaskComputeSecret savedTeeTaskComputeSecret = computeSecretCaptor.getValue();
- Assertions.assertThat(savedTeeTaskComputeSecret.getKey()).isEqualTo("0");
- Assertions.assertThat(savedTeeTaskComputeSecret.getOnChainObjectAddress()).isEqualTo(APP_ADDRESS.toLowerCase());
+ Assertions.assertThat(savedTeeTaskComputeSecret.getHeader().getKey()).isEqualTo("0");
+ Assertions.assertThat(savedTeeTaskComputeSecret.getHeader().getOnChainObjectAddress()).isEqualTo(APP_ADDRESS.toLowerCase());
Assertions.assertThat(savedTeeTaskComputeSecret.getValue()).isEqualTo(ENCRYPTED_SECRET_VALUE);
}
@@ -72,15 +73,15 @@ void shouldNotAddSecretSinceAlreadyExist() {
// region getSecret
@Test
void shouldGetSecret() {
- when(teeTaskComputeSecretRepository.findOne(any()))
+ when(teeTaskComputeSecretRepository.findById(any()))
.thenReturn(Optional.of(COMPUTE_SECRET));
when(encryptionService.decrypt(ENCRYPTED_SECRET_VALUE))
.thenReturn(DECRYPTED_SECRET_VALUE);
Optional decryptedSecret = teeTaskComputeSecretService.getSecret(OnChainObjectType.APPLICATION, APP_ADDRESS, SecretOwnerRole.APPLICATION_DEVELOPER, "", "0");
Assertions.assertThat(decryptedSecret).isPresent();
- Assertions.assertThat(decryptedSecret.get().getKey()).isEqualTo("0");
- Assertions.assertThat(decryptedSecret.get().getOnChainObjectAddress()).isEqualTo(APP_ADDRESS.toLowerCase());
+ Assertions.assertThat(decryptedSecret.get().getHeader().getKey()).isEqualTo("0");
+ Assertions.assertThat(decryptedSecret.get().getHeader().getOnChainObjectAddress()).isEqualTo(APP_ADDRESS.toLowerCase());
Assertions.assertThat(decryptedSecret.get().getValue()).isEqualTo(DECRYPTED_SECRET_VALUE);
verify(encryptionService, Mockito.times(1)).decrypt(any());
}
diff --git a/src/test/java/com/iexec/sms/secret/compute/TeeTaskComputeSecretTest.java b/src/test/java/com/iexec/sms/secret/compute/TeeTaskComputeSecretTest.java
index 8fff237f..c4a2be1f 100644
--- a/src/test/java/com/iexec/sms/secret/compute/TeeTaskComputeSecretTest.java
+++ b/src/test/java/com/iexec/sms/secret/compute/TeeTaskComputeSecretTest.java
@@ -1,17 +1,14 @@
package com.iexec.sms.secret.compute;
-import lombok.extern.slf4j.Slf4j;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest;
-import org.springframework.dao.DataIntegrityViolationException;
import javax.validation.ConstraintViolationException;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatThrownBy;
-@Slf4j
@DataJpaTest
public class TeeTaskComputeSecretTest {
@@ -43,85 +40,6 @@ private TeeTaskComputeSecret getRequesterSecret() {
.build();
}
- @Test
- void shouldNotSaveSecretFromDefaultBuilder() {
- log.info("shouldNotSaveEntity");
- TeeTaskComputeSecret secret = TeeTaskComputeSecret.builder().build();
- assertThat(secret).hasAllNullFieldsOrProperties();
- assertThatThrownBy(() -> teeTaskComputeSecretRepository.saveAndFlush(secret))
- .isInstanceOf(ConstraintViolationException.class);
- }
-
- @Test
- void shouldNotSaveSecretWhenOnChainObjectAddressIsNull() {
- TeeTaskComputeSecret secret = TeeTaskComputeSecret.builder()
- //.onChainObjectAddress("")
- .onChainObjectType(OnChainObjectType.APPLICATION)
- .secretOwnerRole(SecretOwnerRole.APPLICATION_DEVELOPER)
- .fixedSecretOwner("")
- .key("")
- .value("")
- .build();
- assertThatThrownBy(() -> teeTaskComputeSecretRepository.saveAndFlush(secret))
- .isInstanceOf(ConstraintViolationException.class);
- }
-
- @Test
- void shouldNotSaveSecretWhenOnChainObjectTypeIsNull() {
- TeeTaskComputeSecret secret = TeeTaskComputeSecret.builder()
- .onChainObjectAddress("")
- //.onChainObjectType(OnChainObjectType.APPLICATION)
- .secretOwnerRole(SecretOwnerRole.APPLICATION_DEVELOPER)
- .fixedSecretOwner("")
- .key("")
- .value("")
- .build();
- assertThatThrownBy(() -> teeTaskComputeSecretRepository.saveAndFlush(secret))
- .isInstanceOf(ConstraintViolationException.class);
- }
-
- @Test
- void shouldNotSaveSecretWhenSecretOwnerRoleIsNull() {
- TeeTaskComputeSecret secret = TeeTaskComputeSecret.builder()
- .onChainObjectAddress("")
- .onChainObjectType(OnChainObjectType.APPLICATION)
- //.secretOwnerRole(SecretOwnerRole.APPLICATION_DEVELOPER)
- .fixedSecretOwner("")
- .key("")
- .value("")
- .build();
- assertThatThrownBy(() -> teeTaskComputeSecretRepository.saveAndFlush(secret))
- .isInstanceOf(ConstraintViolationException.class);
- }
-
- @Test
- void shouldNotSaveSecretWhenFixedSecretOwnerIsNull() {
- TeeTaskComputeSecret secret = TeeTaskComputeSecret.builder()
- .onChainObjectAddress("")
- .onChainObjectType(OnChainObjectType.APPLICATION)
- .secretOwnerRole(SecretOwnerRole.APPLICATION_DEVELOPER)
- //.fixedSecretOwner("")
- .key("")
- .value("")
- .build();
- assertThatThrownBy(() -> teeTaskComputeSecretRepository.saveAndFlush(secret))
- .isInstanceOf(ConstraintViolationException.class);
- }
-
- @Test
- void shouldNotSaveSecretWhenKeyIsNull() {
- TeeTaskComputeSecret secret = TeeTaskComputeSecret.builder()
- .onChainObjectAddress("")
- .onChainObjectType(OnChainObjectType.APPLICATION)
- .secretOwnerRole(SecretOwnerRole.APPLICATION_DEVELOPER)
- .fixedSecretOwner("")
- //.key("")
- .value("")
- .build();
- assertThatThrownBy(() -> teeTaskComputeSecretRepository.saveAndFlush(secret))
- .isInstanceOf(ConstraintViolationException.class);
- }
-
@Test
void shouldNotSaveSecretWhenValueIsNull() {
TeeTaskComputeSecret secret = TeeTaskComputeSecret.builder()
@@ -129,41 +47,25 @@ void shouldNotSaveSecretWhenValueIsNull() {
.onChainObjectType(OnChainObjectType.APPLICATION)
.secretOwnerRole(SecretOwnerRole.APPLICATION_DEVELOPER)
.fixedSecretOwner("")
- .key("")
+ .key("key")
//.value("")
.build();
assertThatThrownBy(() -> teeTaskComputeSecretRepository.saveAndFlush(secret))
.isInstanceOf(ConstraintViolationException.class);
}
- @Test
- void shouldFailToSaveSameAppDeveloperSecretTwice() {
- log.info("AppDeveloperSecret");
- teeTaskComputeSecretRepository.saveAndFlush(getAppDeveloperSecret());
- assertThatThrownBy(() -> teeTaskComputeSecretRepository.saveAndFlush(getAppDeveloperSecret()))
- .isInstanceOf(DataIntegrityViolationException.class);
- }
-
- @Test
- void shouldFailToSaveSameRequesterSecretTwice() {
- log.info("RequesterSecret");
- teeTaskComputeSecretRepository.saveAndFlush(getRequesterSecret());
- assertThatThrownBy(() -> teeTaskComputeSecretRepository.saveAndFlush(getRequesterSecret()))
- .isInstanceOf(DataIntegrityViolationException.class);
- }
-
@Test
void shouldSaveAppDeveloperAndRequesterSecrets() {
TeeTaskComputeSecret appDeveloperSecret = getAppDeveloperSecret();
teeTaskComputeSecretRepository.save(appDeveloperSecret);
- String appDeveloperSecretId = appDeveloperSecret.getId();
+ final TeeTaskComputeSecretHeader appDeveloperSecretHeader = appDeveloperSecret.getHeader();
TeeTaskComputeSecret requesterSecret = getRequesterSecret();
teeTaskComputeSecretRepository.save(requesterSecret);
- String requesterSecretId = requesterSecret.getId();
+ final TeeTaskComputeSecretHeader requesterSecretHeader = requesterSecret.getHeader();
assertThat(teeTaskComputeSecretRepository.count()).isEqualTo(2);
- assertThat(teeTaskComputeSecretRepository.getById(appDeveloperSecretId))
+ assertThat(teeTaskComputeSecretRepository.getById(appDeveloperSecretHeader))
.isEqualTo(appDeveloperSecret);
- assertThat(teeTaskComputeSecretRepository.getById(requesterSecretId))
+ assertThat(teeTaskComputeSecretRepository.getById(requesterSecretHeader))
.isEqualTo(requesterSecret);
}
diff --git a/src/test/java/com/iexec/sms/secret/web2/Web2SecretServiceTests.java b/src/test/java/com/iexec/sms/secret/web2/Web2SecretServiceTests.java
new file mode 100644
index 00000000..8b5e0e4d
--- /dev/null
+++ b/src/test/java/com/iexec/sms/secret/web2/Web2SecretServiceTests.java
@@ -0,0 +1,199 @@
+/*
+ *
+ * * Copyright 2022 IEXEC BLOCKCHAIN TECH
+ * *
+ * * 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.
+ *
+ */
+
+package com.iexec.sms.secret.web2;
+
+import com.iexec.sms.encryption.EncryptionService;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.mockito.InjectMocks;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.util.Optional;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.junit.jupiter.api.Assertions.*;
+import static org.mockito.Mockito.*;
+
+class Web2SecretServiceTests {
+ private static final String OWNER_ADDRESS = "ownerAddress";
+ private static final String SECRET_ADDRESS = "secretAddress";
+ private static final String PLAIN_SECRET_VALUE = "plainSecretValue";
+ private static final String ENCRYPTED_SECRET_VALUE = "encryptedSecretValue";
+
+ @Mock
+ private Web2SecretRepository web2SecretRepository;
+
+ @Mock
+ private EncryptionService encryptionService;
+
+ @InjectMocks
+ private Web2SecretService web2SecretService;
+
+ @BeforeEach
+ void beforeEach() {
+ MockitoAnnotations.openMocks(this);
+ }
+
+ // region getSecret
+ @Test
+ void shouldGetDecryptedValue() {
+ final Web2Secret encryptedSecret = new Web2Secret(OWNER_ADDRESS, SECRET_ADDRESS, ENCRYPTED_SECRET_VALUE);
+
+ when(web2SecretRepository.findById(any(Web2SecretHeader.class)))
+ .thenReturn(Optional.of(encryptedSecret));
+ when(encryptionService.decrypt(ENCRYPTED_SECRET_VALUE))
+ .thenReturn(PLAIN_SECRET_VALUE);
+
+ final Optional result = web2SecretService.getDecryptedValue(OWNER_ADDRESS, SECRET_ADDRESS);
+ assertThat(result)
+ .isNotEmpty()
+ .get().isEqualTo(PLAIN_SECRET_VALUE);
+ }
+
+ @Test
+ void shouldGetEncryptedSecret() {
+ final Web2Secret encryptedSecret = new Web2Secret(OWNER_ADDRESS, SECRET_ADDRESS, ENCRYPTED_SECRET_VALUE);
+
+ when(web2SecretRepository.findById(any(Web2SecretHeader.class)))
+ .thenReturn(Optional.of(encryptedSecret));
+
+ final Optional result = web2SecretService.getSecret(OWNER_ADDRESS, SECRET_ADDRESS);
+ assertThat(result).isNotEmpty();
+ assertAll(
+ () -> assertThat(result).get().extracting(Web2Secret::getHeader).usingRecursiveComparison().isEqualTo(new Web2SecretHeader(OWNER_ADDRESS, SECRET_ADDRESS)),
+ () -> assertThat(result).get().extracting(Web2Secret::getValue).isEqualTo(ENCRYPTED_SECRET_VALUE),
+ () -> verifyNoInteractions(encryptionService)
+ );
+ }
+
+ @Test
+ void shouldGetEmptySecretIfSecretNotPresent() {
+ when(web2SecretRepository.findById(any(Web2SecretHeader.class)))
+ .thenReturn(Optional.empty());
+
+ assertThat(web2SecretService.getSecret(OWNER_ADDRESS, SECRET_ADDRESS)).isEmpty();
+ verify(web2SecretRepository, times(1))
+ .findById(any(Web2SecretHeader.class));
+ verifyNoInteractions(encryptionService);
+ }
+
+ @Test
+ void shouldGetEmptyDecryptedValueIfSecretNotPresent() {
+ when(web2SecretRepository.findById(any(Web2SecretHeader.class)))
+ .thenReturn(Optional.empty());
+
+ assertThat(web2SecretService.getDecryptedValue(OWNER_ADDRESS, SECRET_ADDRESS)).isEmpty();
+ verify(web2SecretRepository, times(1))
+ .findById(any(Web2SecretHeader.class));
+ verifyNoInteractions(encryptionService);
+ }
+ // endregion
+
+ // region addSecret
+ @Test
+ void shouldAddSecret() throws SecretAlreadyExistsException {
+ when(web2SecretRepository.findById(any(Web2SecretHeader.class)))
+ .thenReturn(Optional.empty());
+ when(encryptionService.encrypt(PLAIN_SECRET_VALUE)).thenReturn(ENCRYPTED_SECRET_VALUE);
+ when(web2SecretRepository.save(any())).thenReturn(new Web2Secret(OWNER_ADDRESS, SECRET_ADDRESS, ENCRYPTED_SECRET_VALUE));
+
+ final Web2Secret newSecret = web2SecretService.addSecret(OWNER_ADDRESS, SECRET_ADDRESS, PLAIN_SECRET_VALUE);
+ assertAll(
+ () -> assertThat(newSecret).extracting(Web2Secret::getHeader).usingRecursiveComparison().isEqualTo(new Web2SecretHeader(OWNER_ADDRESS, SECRET_ADDRESS)),
+ () -> assertThat(newSecret).extracting(Web2Secret::getValue).isEqualTo(ENCRYPTED_SECRET_VALUE),
+
+ () -> verify(encryptionService).encrypt(any()),
+ () -> verify(web2SecretRepository).save(any())
+ );
+ }
+
+ @Test
+ void shouldNotAddSecretIfPresent() {
+ final Web2Secret secret = new Web2Secret(OWNER_ADDRESS, SECRET_ADDRESS, ENCRYPTED_SECRET_VALUE);
+ when(web2SecretRepository.findById(any(Web2SecretHeader.class)))
+ .thenReturn(Optional.of(secret));
+
+ final SecretAlreadyExistsException exception = assertThrows(SecretAlreadyExistsException.class,
+ () -> web2SecretService.addSecret(OWNER_ADDRESS, SECRET_ADDRESS, ENCRYPTED_SECRET_VALUE));
+ assertAll(
+ () -> assertEquals(OWNER_ADDRESS, exception.getOwnerAddress()),
+ () -> assertEquals(SECRET_ADDRESS, exception.getSecretAddress()),
+ () -> verify(encryptionService, never()).encrypt(PLAIN_SECRET_VALUE),
+ () -> verify(web2SecretRepository, never()).save(any())
+ );
+ }
+ // endregion
+
+ // region updateSecret
+ @Test
+ void shouldUpdateSecret() throws NotAnExistingSecretException, SameSecretException {
+ final Web2Secret encryptedSecret = new Web2Secret(OWNER_ADDRESS, SECRET_ADDRESS, ENCRYPTED_SECRET_VALUE);
+ final String newSecretValue = "newSecretValue";
+ final String newEncryptedSecretValue = "newEncryptedSecretValue";
+ when(web2SecretRepository.findById(any(Web2SecretHeader.class)))
+ .thenReturn(Optional.of(encryptedSecret));
+ when(encryptionService.encrypt(newSecretValue))
+ .thenReturn(newEncryptedSecretValue);
+ final Web2Secret savedSecret = new Web2Secret(OWNER_ADDRESS, SECRET_ADDRESS, newEncryptedSecretValue);
+ when(web2SecretRepository.save(any()))
+ .thenReturn(savedSecret);
+
+ final Web2Secret newSecret = web2SecretService.updateSecret(OWNER_ADDRESS, SECRET_ADDRESS, newSecretValue);
+ assertAll(
+ () -> assertThat(newSecret).extracting(Web2Secret::getHeader).usingRecursiveComparison().isEqualTo(new Web2SecretHeader(OWNER_ADDRESS, SECRET_ADDRESS)),
+ () -> assertThat(newSecret).extracting(Web2Secret::getValue).isEqualTo(newEncryptedSecretValue),
+
+ () -> verify(web2SecretRepository, never()).save(encryptedSecret), // Current object should not be updated
+ () -> verify(web2SecretRepository, times(1)).save(any()) // A new object should be created with the same ID
+ );
+ }
+
+ @Test
+ void shouldNotUpdateSecretIfMissing() {
+ when(web2SecretRepository.findById(any(Web2SecretHeader.class)))
+ .thenReturn(Optional.empty());
+
+ final NotAnExistingSecretException exception = assertThrows(NotAnExistingSecretException.class,
+ () -> web2SecretService.updateSecret(OWNER_ADDRESS, SECRET_ADDRESS, PLAIN_SECRET_VALUE));
+ assertAll(
+ () -> assertEquals(OWNER_ADDRESS, exception.getOwnerAddress()),
+ () -> assertEquals(SECRET_ADDRESS, exception.getSecretAddress()),
+ () -> verify(web2SecretRepository, never()).save(any())
+ );
+ }
+
+ @Test
+ void shouldNotUpdateSecretIfSameValue() {
+ final Web2Secret encryptedSecret = new Web2Secret(OWNER_ADDRESS, SECRET_ADDRESS, ENCRYPTED_SECRET_VALUE);
+ when(web2SecretRepository.findById(any(Web2SecretHeader.class)))
+ .thenReturn(Optional.of(encryptedSecret));
+ when(encryptionService.encrypt(PLAIN_SECRET_VALUE))
+ .thenReturn(ENCRYPTED_SECRET_VALUE);
+
+ final SameSecretException exception = assertThrows(SameSecretException.class,
+ () -> web2SecretService.updateSecret(OWNER_ADDRESS, SECRET_ADDRESS, PLAIN_SECRET_VALUE));
+ assertAll(
+ () -> assertEquals(OWNER_ADDRESS, exception.getOwnerAddress()),
+ () -> assertEquals(SECRET_ADDRESS, exception.getSecretAddress()),
+ () -> verify(web2SecretRepository, never()).save(any())
+ );
+ }
+ // endregion
+}
diff --git a/src/test/java/com/iexec/sms/secret/web2/Web2SecretTests.java b/src/test/java/com/iexec/sms/secret/web2/Web2SecretTests.java
new file mode 100644
index 00000000..6be72f64
--- /dev/null
+++ b/src/test/java/com/iexec/sms/secret/web2/Web2SecretTests.java
@@ -0,0 +1,39 @@
+/*
+ *
+ * * Copyright 2022 IEXEC BLOCKCHAIN TECH
+ * *
+ * * 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.
+ *
+ */
+
+package com.iexec.sms.secret.web2;
+
+import org.assertj.core.api.Assertions;
+import org.junit.jupiter.api.Test;
+
+class Web2SecretTests {
+ private static final String OWNER_ADDRESS = "ownerAddress";
+ private static final String SECRET_ADDRESS = "secretAddress";
+
+ private static final String VALUE = "value";
+ private static final String NEW_VALUE = "newValue";
+ private static final Web2Secret SECRET = new Web2Secret(OWNER_ADDRESS, SECRET_ADDRESS, VALUE);
+ private static final Web2Secret NEW_SECRET = new Web2Secret(OWNER_ADDRESS, SECRET_ADDRESS, NEW_VALUE);
+
+ @Test
+ void withValue() {
+ Assertions.assertThat(SECRET.withValue(NEW_VALUE))
+ .usingRecursiveComparison()
+ .isEqualTo(NEW_SECRET);
+ }
+}
diff --git a/src/test/java/com/iexec/sms/secret/web2/Web2SecretsServiceTests.java b/src/test/java/com/iexec/sms/secret/web2/Web2SecretsServiceTests.java
deleted file mode 100644
index 1d17bec1..00000000
--- a/src/test/java/com/iexec/sms/secret/web2/Web2SecretsServiceTests.java
+++ /dev/null
@@ -1,97 +0,0 @@
-/*
- * Copyright 2020 IEXEC BLOCKCHAIN TECH
- *
- * 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.
- */
-
-package com.iexec.sms.secret.web2;
-
-import com.iexec.sms.encryption.EncryptionService;
-import com.iexec.sms.secret.Secret;
-import org.junit.jupiter.api.BeforeEach;
-import org.junit.jupiter.api.Test;
-import org.mockito.InjectMocks;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
-
-import java.util.List;
-import java.util.Optional;
-
-import static org.assertj.core.api.Assertions.assertThat;
-import static org.mockito.Mockito.*;
-
-class Web2SecretsServiceTests {
-
- String ownerAddress = "ownerAddress";
- String secretAddress = "secretAddress";
- String plainSecretValue = "plainSecretValue";
- String encryptedSecretValue = "encryptedSecretValue";
-
- @Mock
- private Web2SecretsRepository web2SecretsRepository;
-
- @Mock
- private EncryptionService encryptionService;
-
- @InjectMocks
- private Web2SecretsService web2SecretsService;
-
- @BeforeEach
- void beforeEach() {
- MockitoAnnotations.openMocks(this);
- }
-
- @Test
- void shouldGetAndDecryptWeb2Secrets() {
- ownerAddress = ownerAddress.toLowerCase();
- Secret encryptedSecret = new Secret(secretAddress, encryptedSecretValue);
- encryptedSecret.setEncryptedValue(true);
- List secretList = List.of(encryptedSecret);
- Web2Secrets web2SecretsMock = new Web2Secrets(ownerAddress);
- web2SecretsMock.setSecrets(secretList);
- when(web2SecretsRepository.findWeb2SecretsByOwnerAddress(ownerAddress))
- .thenReturn(Optional.of(web2SecretsMock));
- when(encryptionService.decrypt(encryptedSecret.getValue()))
- .thenReturn(plainSecretValue);
-
- Optional result = web2SecretsService.getSecret(ownerAddress, secretAddress, true);
- assertThat(result.get().getAddress()).isEqualTo(secretAddress);
- assertThat(result.get().getValue()).isEqualTo(plainSecretValue);
- }
-
- @Test
- void shouldAddSecret() {
- ownerAddress = ownerAddress.toLowerCase();
- web2SecretsService.addSecret(ownerAddress, secretAddress, plainSecretValue);
- verify(web2SecretsRepository, times(1)).save(any());
- }
-
- @Test
- void shouldUpdateSecret() {
- ownerAddress = ownerAddress.toLowerCase();
- Secret encryptedSecret = new Secret(secretAddress, encryptedSecretValue);
- encryptedSecret.setEncryptedValue(true);
- String newSecretValue = "newSecretValue";
- String newEncryptedSecretValue = "newEncryptedSecretValue";
- List secretList = List.of(encryptedSecret);
- Web2Secrets web2SecretsMock = new Web2Secrets(ownerAddress);
- web2SecretsMock.setSecrets(secretList);
- when(web2SecretsRepository.findWeb2SecretsByOwnerAddress(ownerAddress))
- .thenReturn(Optional.of(web2SecretsMock));
- when(encryptionService.encrypt(newSecretValue))
- .thenReturn(newEncryptedSecretValue);
-
- web2SecretsService.updateSecret(ownerAddress, secretAddress, newSecretValue);
- verify(web2SecretsRepository, times(1)).save(web2SecretsMock);
- }
-}
\ No newline at end of file
diff --git a/src/test/java/com/iexec/sms/secret/web3/Web3SecretServiceTests.java b/src/test/java/com/iexec/sms/secret/web3/Web3SecretServiceTests.java
new file mode 100644
index 00000000..42b73461
--- /dev/null
+++ b/src/test/java/com/iexec/sms/secret/web3/Web3SecretServiceTests.java
@@ -0,0 +1,110 @@
+/*
+ * Copyright 2020 IEXEC BLOCKCHAIN TECH
+ *
+ * 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.
+ */
+
+package com.iexec.sms.secret.web3;
+
+import com.iexec.sms.encryption.EncryptionService;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.mockito.InjectMocks;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.util.Optional;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.*;
+
+class Web3SecretServiceTests {
+
+ String secretAddress = "secretAddress".toLowerCase();
+ String plainSecretValue = "plainSecretValue";
+ String encryptedSecretValue = "encryptedSecretValue";
+
+ @Mock
+ private EncryptionService encryptionService;
+ @Mock
+ private Web3SecretRepository web3SecretRepository;
+ @InjectMocks
+ private Web3SecretService web3SecretService;
+
+ @BeforeEach
+ void init() {
+ MockitoAnnotations.openMocks(this);
+ }
+
+ @Test
+ void shouldNotAddSecretIfPresent() {
+ Web3Secret web3Secret = new Web3Secret(secretAddress, encryptedSecretValue);
+ when(web3SecretRepository.findById(any(Web3SecretHeader.class))).thenReturn(Optional.of(web3Secret));
+ assertThat(web3SecretService.addSecret(secretAddress, plainSecretValue)).isFalse();
+ verifyNoInteractions(encryptionService);
+ verify(web3SecretRepository, never()).save(any());
+ }
+
+ @Test
+ void shouldAddSecret() {
+ when(web3SecretRepository.findById(any(Web3SecretHeader.class))).thenReturn(Optional.empty());
+ when(encryptionService.encrypt(plainSecretValue)).thenReturn(encryptedSecretValue);
+ assertThat(web3SecretService.addSecret(secretAddress, plainSecretValue)).isTrue();
+ verify(encryptionService).encrypt(any());
+ verify(web3SecretRepository).save(any());
+ }
+
+ @Test
+ void shouldGetDecryptedValue() {
+ Web3Secret encryptedSecret = new Web3Secret(secretAddress, encryptedSecretValue);
+ when(web3SecretRepository.findById(any(Web3SecretHeader.class))).thenReturn(Optional.of(encryptedSecret));
+ when(encryptionService.decrypt(encryptedSecretValue)).thenReturn(plainSecretValue);
+
+ Optional result = web3SecretService.getDecryptedValue(secretAddress);
+ assertThat(result)
+ .isPresent()
+ .get().isEqualTo(plainSecretValue);
+
+ verify(web3SecretRepository).findById(any(Web3SecretHeader.class));
+ verify(encryptionService).decrypt(any());
+ }
+
+ @Test
+ void shouldGetEncryptedSecret() {
+ Web3Secret encryptedSecret = new Web3Secret(secretAddress, encryptedSecretValue);
+ when(web3SecretRepository.findById(any(Web3SecretHeader.class))).thenReturn(Optional.of(encryptedSecret));
+ Optional result = web3SecretService.getSecret(secretAddress);
+ assertThat(result)
+ .contains(encryptedSecret);
+ verify(web3SecretRepository, times(1)).findById(any(Web3SecretHeader.class));
+ verifyNoInteractions(encryptionService);
+ }
+
+ @Test
+ void shouldGetEmptySecretIfSecretNotPresent() {
+ when(web3SecretRepository.findById(any(Web3SecretHeader.class))).thenReturn(Optional.empty());
+ assertThat(web3SecretService.getSecret(secretAddress)).isEmpty();
+ verify(web3SecretRepository, times(1)).findById(any(Web3SecretHeader.class));
+ verifyNoInteractions(encryptionService);
+ }
+
+ @Test
+ void shouldGetEmptyValueIfSecretNotPresent() {
+ when(web3SecretRepository.findById(any(Web3SecretHeader.class))).thenReturn(Optional.empty());
+ assertThat(web3SecretService.getDecryptedValue(secretAddress)).isEmpty();
+ verify(web3SecretRepository, times(1)).findById(any(Web3SecretHeader.class));
+ verifyNoInteractions(encryptionService);
+ }
+
+}
\ No newline at end of file
diff --git a/src/test/java/com/iexec/sms/tee/ConditionalOnTeeFrameworkTests.java b/src/test/java/com/iexec/sms/tee/ConditionalOnTeeFrameworkTests.java
new file mode 100644
index 00000000..be828dba
--- /dev/null
+++ b/src/test/java/com/iexec/sms/tee/ConditionalOnTeeFrameworkTests.java
@@ -0,0 +1,189 @@
+package com.iexec.sms.tee;
+
+import com.iexec.common.tee.TeeFramework;
+import com.iexec.sms.tee.session.generic.TeeSessionHandler;
+import com.iexec.sms.tee.session.gramine.GramineSessionHandlerService;
+import com.iexec.sms.tee.session.gramine.GramineSessionMakerService;
+import com.iexec.sms.tee.session.gramine.sps.SpsConfiguration;
+import com.iexec.sms.tee.session.scone.SconeSessionHandlerService;
+import com.iexec.sms.tee.session.scone.SconeSessionMakerService;
+import com.iexec.sms.tee.session.scone.SconeSessionSecurityConfig;
+import com.iexec.sms.tee.session.scone.cas.CasClient;
+import com.iexec.sms.tee.session.scone.cas.CasConfiguration;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.Arguments;
+import org.junit.jupiter.params.provider.MethodSource;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.springframework.context.annotation.ConditionContext;
+import org.springframework.core.env.Environment;
+import org.springframework.core.type.AnnotationMetadata;
+
+import java.util.Map;
+import java.util.function.Function;
+import java.util.stream.Stream;
+
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+import static org.mockito.Mockito.when;
+
+class ConditionalOnTeeFrameworkTests {
+ @ConditionalOnTeeFramework(frameworks = {})
+ static class NoFrameworksSet {}
+
+ @Mock
+ Environment environment;
+ @Mock
+ ConditionContext context;
+ @Mock
+ AnnotationMetadata metadata;
+ OnTeeFrameworkCondition condition = new OnTeeFrameworkCondition();
+
+ @BeforeEach
+ void init() {
+ MockitoAnnotations.openMocks(this);
+ }
+
+ static Stream sconeBeansClasses() {
+ return Stream.of(
+// Arguments.of(SconeServicesProperties.class), // Can't test this bean as it is declared through a method, not a class
+ Arguments.of(SconeSessionHandlerService.class),
+ Arguments.of(SconeSessionMakerService.class),
+ Arguments.of(SconeSessionSecurityConfig.class),
+ Arguments.of(CasClient.class),
+ Arguments.of(CasConfiguration.class)
+ );
+ }
+
+ static Stream gramineBeansClasses() {
+ return Stream.of(
+// Arguments.of(GramineServicesProperties.class), // Can't test this bean as it is declared through a method, not a class
+ Arguments.of(GramineSessionHandlerService.class),
+ Arguments.of(GramineSessionMakerService.class),
+ Arguments.of(SpsConfiguration.class)
+ );
+ }
+
+ static Stream sconeAndGramineBeansClasses() {
+ return Stream.of(sconeBeansClasses(), gramineBeansClasses())
+ .flatMap(Function.identity());
+ }
+
+ static Stream nonAnnotatedClasses() {
+ return Stream.of(
+ Arguments.of(TeeSessionHandler.class)
+ );
+ }
+
+ // region shouldMatch
+ @ParameterizedTest
+ @MethodSource("sconeBeansClasses")
+ void shouldMatchScone(Class> clazz) {
+ when(context.getEnvironment()).thenReturn(environment);
+ when(environment.getActiveProfiles()).thenReturn(new String[]{"scone"});
+ when(metadata.getClassName()).thenReturn(clazz.getName());
+ setAttributesForMetadataMock(clazz);
+
+ assertTrue(condition.matches(context, metadata));
+ }
+
+ @ParameterizedTest
+ @MethodSource("gramineBeansClasses")
+ void shouldMatchGramine(Class> clazz) {
+ when(context.getEnvironment()).thenReturn(environment);
+ when(environment.getActiveProfiles()).thenReturn(new String[]{"gramine"});
+ when(metadata.getClassName()).thenReturn(clazz.getName());
+ setAttributesForMetadataMock(clazz);
+
+ assertTrue(condition.matches(context, metadata));
+ }
+
+ @ParameterizedTest
+ @MethodSource("sconeAndGramineBeansClasses")
+ void shouldMatchSconeAndGramine(Class> clazz) {
+ when(context.getEnvironment()).thenReturn(environment);
+ when(environment.getActiveProfiles()).thenReturn(new String[]{"scone", "gramine"});
+ when(metadata.getClassName()).thenReturn(clazz.getName());
+ setAttributesForMetadataMock(clazz);
+
+ assertTrue(condition.matches(context, metadata));
+ }
+ // endregion
+
+ // region shouldNotMatch
+ @ParameterizedTest
+ @MethodSource("sconeBeansClasses")
+ void shouldNotMatchSconeSinceGramineProfile(Class> clazz) {
+ when(context.getEnvironment()).thenReturn(environment);
+ when(environment.getActiveProfiles()).thenReturn(new String[]{"gramine"});
+ when(metadata.getClassName()).thenReturn(clazz.getName());
+ setAttributesForMetadataMock(clazz);
+
+ assertFalse(condition.matches(context, metadata));
+ }
+
+ @ParameterizedTest
+ @MethodSource("gramineBeansClasses")
+ void shouldNotMatchGramineSinceSconeProfile(Class> clazz) {
+ when(context.getEnvironment()).thenReturn(environment);
+ when(environment.getActiveProfiles()).thenReturn(new String[]{"scone"});
+ when(metadata.getClassName()).thenReturn(clazz.getName());
+ setAttributesForMetadataMock(clazz);
+
+ assertFalse(condition.matches(context, metadata));
+ }
+
+ @ParameterizedTest
+ @MethodSource("sconeAndGramineBeansClasses")
+ void shouldNotMatchAnySinceNoProfile(Class> clazz) {
+ when(context.getEnvironment()).thenReturn(environment);
+ when(environment.getActiveProfiles()).thenReturn(new String[]{});
+ when(metadata.getClassName()).thenReturn(clazz.getName());
+ setAttributesForMetadataMock(clazz);
+
+ assertFalse(condition.matches(context, metadata));
+ }
+
+ @ParameterizedTest
+ @MethodSource("nonAnnotatedClasses")
+ void shouldNotMatchAnySinceNoAnnotation(Class> clazz) {
+ when(context.getEnvironment()).thenReturn(environment);
+ when(environment.getActiveProfiles()).thenReturn(new String[]{});
+ when(metadata.getClassName()).thenReturn(clazz.getName());
+
+ assertFalse(condition.matches(context, metadata));
+ }
+
+ @Test
+ void shouldNotMatchAnySinceNoFrameworkDefined() {
+ final Class> clazz = NoFrameworksSet.class;
+
+ when(context.getEnvironment()).thenReturn(environment);
+ when(environment.getActiveProfiles()).thenReturn(new String[]{"scone", "gramine"});
+ when(metadata.getClassName()).thenReturn(clazz.getName());
+ setAttributesForMetadataMock(clazz);
+
+ assertFalse(condition.matches(context, metadata));
+ }
+
+ @Test
+ void shouldNotMatchAnySinceClassDoesNotExist() {
+ final String className = "Not an existing class";
+
+ when(context.getEnvironment()).thenReturn(environment);
+ when(environment.getActiveProfiles()).thenReturn(new String[]{"scone", "gramine"});
+ when(metadata.getClassName()).thenReturn(className);
+
+ assertFalse(condition.matches(context, metadata));
+ }
+ // endregion
+
+ void setAttributesForMetadataMock(Class> clazz) {
+ ConditionalOnTeeFramework annotation = clazz.getAnnotation(ConditionalOnTeeFramework.class);
+ TeeFramework[] frameworks = annotation.frameworks();
+ when(metadata.getAnnotationAttributes(ConditionalOnTeeFramework.class.getName()))
+ .thenReturn(Map.of("frameworks", frameworks));
+ }
+}
diff --git a/src/test/java/com/iexec/sms/tee/TeeControllerTests.java b/src/test/java/com/iexec/sms/tee/TeeControllerTests.java
index 2b2d09d6..252efb71 100644
--- a/src/test/java/com/iexec/sms/tee/TeeControllerTests.java
+++ b/src/test/java/com/iexec/sms/tee/TeeControllerTests.java
@@ -1,14 +1,19 @@
package com.iexec.sms.tee;
import com.iexec.common.chain.WorkerpoolAuthorization;
-import com.iexec.sms.api.TeeSessionGenerationError;
+import com.iexec.common.tee.TeeFramework;
import com.iexec.common.web.ApiResponseBody;
+import com.iexec.sms.api.TeeSessionGenerationError;
+import com.iexec.sms.api.TeeSessionGenerationResponse;
+import com.iexec.sms.api.config.GramineServicesProperties;
+import com.iexec.sms.api.config.SconeServicesProperties;
+import com.iexec.sms.api.config.TeeAppProperties;
+import com.iexec.sms.api.config.TeeServicesProperties;
import com.iexec.sms.authorization.AuthorizationError;
import com.iexec.sms.authorization.AuthorizationService;
import com.iexec.sms.tee.challenge.TeeChallengeService;
-import com.iexec.sms.tee.session.TeeSessionGenerationException;
import com.iexec.sms.tee.session.TeeSessionService;
-import com.iexec.sms.tee.workflow.TeeWorkflowConfiguration;
+import com.iexec.sms.tee.session.generic.TeeSessionGenerationException;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
@@ -36,6 +41,9 @@ class TeeControllerTests {
private final static String AUTHORIZATION = "0x2";
private final static String CHALLENGE = "CHALLENGE";
private final static String SESSION_ID = "SESSION_ID";
+ private static final String SECRET_PROVISIONING_URL = "https://secretProvisioningUrl";
+ private static final String LAS_IMAGE = "lasImage";
+
@Mock
AuthorizationService authorizationService;
@@ -44,7 +52,12 @@ class TeeControllerTests {
@Mock
TeeSessionService teeSessionService;
@Mock
- TeeWorkflowConfiguration teeWorkflowConfig;
+ TeeServicesProperties teeServicesConfig;
+
+ @Mock
+ TeeAppProperties preComputeProperties;
+ @Mock
+ TeeAppProperties postComputeProperties;
@InjectMocks
TeeController teeController;
@@ -54,6 +67,137 @@ void setUp() {
MockitoAnnotations.openMocks(this);
}
+ // region getTeeFramework
+ @Test
+ void shouldGetSconeFramework() {
+ final TeeServicesProperties properties = new SconeServicesProperties(
+ preComputeProperties,
+ postComputeProperties,
+ LAS_IMAGE
+ );
+
+ final TeeController teeController = new TeeController(
+ authorizationService, teeChallengeService, teeSessionService, properties
+ );
+
+ final ResponseEntity response =
+ teeController.getTeeFramework();
+
+ assertEquals(HttpStatus.OK, response.getStatusCode());
+
+ final TeeFramework result = response.getBody();
+ assertEquals(TeeFramework.SCONE, result);
+ }
+
+ @Test
+ void shouldGetGramineFramework() {
+ final TeeServicesProperties properties = new GramineServicesProperties(
+ preComputeProperties,
+ postComputeProperties
+ );
+
+ final TeeController teeController = new TeeController(
+ authorizationService, teeChallengeService, teeSessionService, properties
+ );
+
+ final ResponseEntity response =
+ teeController.getTeeFramework();
+
+ assertEquals(HttpStatus.OK, response.getStatusCode());
+
+ final TeeFramework result = response.getBody();
+ assertEquals(TeeFramework.GRAMINE, result);
+ }
+ // endregion
+
+ // region getTeeServicesConfig
+ @Test
+ void shouldGetSconeProperties() {
+ final TeeServicesProperties properties = new SconeServicesProperties(
+ preComputeProperties,
+ postComputeProperties,
+ LAS_IMAGE
+ );
+
+ final TeeController teeController = new TeeController(
+ authorizationService, teeChallengeService, teeSessionService, properties
+ );
+
+ final ResponseEntity response =
+ teeController.getTeeServicesProperties(TeeFramework.SCONE);
+
+ assertEquals(HttpStatus.OK, response.getStatusCode());
+
+ final TeeServicesProperties result = response.getBody();
+ assertNotNull(result);
+ assertInstanceOf(SconeServicesProperties.class, result);
+ assertEquals(TeeFramework.SCONE, result.getTeeFramework());
+ assertEquals(preComputeProperties, result.getPreComputeProperties());
+ assertEquals(postComputeProperties, result.getPostComputeProperties());
+ assertEquals(postComputeProperties, result.getPostComputeProperties());
+ assertEquals(LAS_IMAGE, ((SconeServicesProperties) result).getLasImage());
+ }
+
+ @Test
+ void shouldGetGramineProperties() {
+ final TeeServicesProperties properties = new GramineServicesProperties(
+ preComputeProperties,
+ postComputeProperties
+ );
+
+ final TeeController teeController = new TeeController(
+ authorizationService, teeChallengeService, teeSessionService, properties
+ );
+
+ final ResponseEntity response =
+ teeController.getTeeServicesProperties(TeeFramework.GRAMINE);
+
+ assertEquals(HttpStatus.OK, response.getStatusCode());
+
+ final TeeServicesProperties result = response.getBody();
+ assertNotNull(result);
+ assertInstanceOf(GramineServicesProperties.class, result);
+ assertEquals(TeeFramework.GRAMINE, result.getTeeFramework());
+ assertEquals(preComputeProperties, result.getPreComputeProperties());
+ assertEquals(postComputeProperties, result.getPostComputeProperties());
+ }
+
+ @Test
+ void shouldNotGetSconePropertiesSinceGramineSms() {
+ final TeeServicesProperties properties = new SconeServicesProperties(
+ preComputeProperties,
+ postComputeProperties,
+ LAS_IMAGE
+ );
+
+ final TeeController teeController = new TeeController(
+ authorizationService, teeChallengeService, teeSessionService, properties
+ );
+
+ final ResponseEntity response =
+ teeController.getTeeServicesProperties(TeeFramework.GRAMINE);
+
+ assertEquals(HttpStatus.CONFLICT, response.getStatusCode());
+ }
+
+ @Test
+ void shouldNotGetGraminePropertiesSinceSconeSms() {
+ final TeeServicesProperties properties = new GramineServicesProperties(
+ preComputeProperties,
+ postComputeProperties
+ );
+
+ final TeeController teeController = new TeeController(
+ authorizationService, teeChallengeService, teeSessionService, properties
+ );
+
+ final ResponseEntity response =
+ teeController.getTeeServicesProperties(TeeFramework.SCONE);
+
+ assertEquals(HttpStatus.CONFLICT, response.getStatusCode());
+ }
+ // endregion
+
// region generateTeeSession
@Test
void shouldGenerateTeeSession() throws TeeSessionGenerationException {
@@ -66,15 +210,18 @@ void shouldGenerateTeeSession() throws TeeSessionGenerationException {
when(authorizationService.getChallengeForWorker(workerpoolAuthorization)).thenReturn(CHALLENGE);
when(authorizationService.isSignedByHimself(CHALLENGE, AUTHORIZATION, WORKER_ADDRESS)).thenReturn(true);
- when(authorizationService.isAuthorizedOnExecutionWithDetailedIssue(workerpoolAuthorization, true)).thenReturn(Optional.empty());
- when(teeSessionService.generateTeeSession(TASK_ID, Keys.toChecksumAddress(WORKER_ADDRESS), ENCLAVE_CHALLENGE)).thenReturn(SESSION_ID);
+ when(authorizationService.isAuthorizedOnExecutionWithDetailedIssue(workerpoolAuthorization, true))
+ .thenReturn(Optional.empty());
+ when(teeSessionService.generateTeeSession(TASK_ID, Keys.toChecksumAddress(WORKER_ADDRESS), ENCLAVE_CHALLENGE))
+ .thenReturn(new TeeSessionGenerationResponse(SESSION_ID, SECRET_PROVISIONING_URL));
- final ResponseEntity> response =
- teeController.generateTeeSession(AUTHORIZATION, workerpoolAuthorization);
+ final ResponseEntity> response = teeController
+ .generateTeeSession(AUTHORIZATION, workerpoolAuthorization);
assertEquals(HttpStatus.OK, response.getStatusCode());
- assertNotEquals(null, response.getBody());
- assertNotEquals(null, response.getBody().getData());
- assertNotEquals("", response.getBody().getData());
+ assertNotNull(response.getBody());
+ assertNotNull(response.getBody().getData());
+ assertEquals(SESSION_ID, response.getBody().getData().getSessionId());
+ assertEquals(SECRET_PROVISIONING_URL, response.getBody().getData().getSecretProvisioningUrl());
assertNull(response.getBody().getError());
}
@@ -90,8 +237,8 @@ void shouldNotGenerateTeeSessionSinceNotSignedByHimself() {
when(authorizationService.getChallengeForWorker(workerpoolAuthorization)).thenReturn(CHALLENGE);
when(authorizationService.isSignedByHimself(CHALLENGE, AUTHORIZATION, WORKER_ADDRESS)).thenReturn(false);
- final ResponseEntity> response =
- teeController.generateTeeSession(AUTHORIZATION, workerpoolAuthorization);
+ final ResponseEntity> response = teeController
+ .generateTeeSession(AUTHORIZATION, workerpoolAuthorization);
assertEquals(HttpStatus.UNAUTHORIZED, response.getStatusCode());
assertNotEquals(null, response.getBody());
assertNull(response.getBody().getData());
@@ -106,13 +253,13 @@ private static Stream notAuthorizedParams() {
Arguments.of(GET_CHAIN_TASK_FAILED, EXECUTION_NOT_AUTHORIZED_GET_CHAIN_TASK_FAILED),
Arguments.of(TASK_NOT_ACTIVE, EXECUTION_NOT_AUTHORIZED_TASK_NOT_ACTIVE),
Arguments.of(GET_CHAIN_DEAL_FAILED, EXECUTION_NOT_AUTHORIZED_GET_CHAIN_DEAL_FAILED),
- Arguments.of(INVALID_SIGNATURE, EXECUTION_NOT_AUTHORIZED_INVALID_SIGNATURE)
- );
+ Arguments.of(INVALID_SIGNATURE, EXECUTION_NOT_AUTHORIZED_INVALID_SIGNATURE));
}
@ParameterizedTest
@MethodSource("notAuthorizedParams")
- void shouldNotGenerateTeeSessionSinceNotAuthorized(AuthorizationError cause, TeeSessionGenerationError consequence) {
+ void shouldNotGenerateTeeSessionSinceNotAuthorized(AuthorizationError cause,
+ TeeSessionGenerationError consequence) {
final WorkerpoolAuthorization workerpoolAuthorization = WorkerpoolAuthorization
.builder()
.chainTaskId(TASK_ID)
@@ -122,10 +269,11 @@ void shouldNotGenerateTeeSessionSinceNotAuthorized(AuthorizationError cause, Tee
when(authorizationService.getChallengeForWorker(workerpoolAuthorization)).thenReturn(CHALLENGE);
when(authorizationService.isSignedByHimself(CHALLENGE, AUTHORIZATION, WORKER_ADDRESS)).thenReturn(true);
- when(authorizationService.isAuthorizedOnExecutionWithDetailedIssue(workerpoolAuthorization, true)).thenReturn(Optional.of(cause));
+ when(authorizationService.isAuthorizedOnExecutionWithDetailedIssue(workerpoolAuthorization, true))
+ .thenReturn(Optional.of(cause));
- final ResponseEntity> response =
- teeController.generateTeeSession(AUTHORIZATION, workerpoolAuthorization);
+ final ResponseEntity> response = teeController
+ .generateTeeSession(AUTHORIZATION, workerpoolAuthorization);
assertEquals(HttpStatus.UNAUTHORIZED, response.getStatusCode());
assertNotEquals(null, response.getBody());
assertNull(response.getBody().getData());
@@ -134,7 +282,7 @@ void shouldNotGenerateTeeSessionSinceNotAuthorized(AuthorizationError cause, Tee
}
@Test
- void shouldNotGenerateTeeSessionSinceEmptySessionId() throws TeeSessionGenerationException {
+ void shouldNotGenerateTeeSessionSinceEmptyResponse() throws TeeSessionGenerationException {
final WorkerpoolAuthorization workerpoolAuthorization = WorkerpoolAuthorization
.builder()
.chainTaskId(TASK_ID)
@@ -144,11 +292,13 @@ void shouldNotGenerateTeeSessionSinceEmptySessionId() throws TeeSessionGeneratio
when(authorizationService.getChallengeForWorker(workerpoolAuthorization)).thenReturn(CHALLENGE);
when(authorizationService.isSignedByHimself(CHALLENGE, AUTHORIZATION, WORKER_ADDRESS)).thenReturn(true);
- when(authorizationService.isAuthorizedOnExecutionWithDetailedIssue(workerpoolAuthorization, true)).thenReturn(Optional.empty());
- when(teeSessionService.generateTeeSession(TASK_ID, Keys.toChecksumAddress(WORKER_ADDRESS), ENCLAVE_CHALLENGE)).thenReturn("");
+ when(authorizationService.isAuthorizedOnExecutionWithDetailedIssue(workerpoolAuthorization, true))
+ .thenReturn(Optional.empty());
+ when(teeSessionService.generateTeeSession(TASK_ID, Keys.toChecksumAddress(WORKER_ADDRESS), ENCLAVE_CHALLENGE))
+ .thenReturn(null);
- final ResponseEntity> response =
- teeController.generateTeeSession(AUTHORIZATION, workerpoolAuthorization);
+ final ResponseEntity> response = teeController
+ .generateTeeSession(AUTHORIZATION, workerpoolAuthorization);
assertEquals(HttpStatus.NOT_FOUND, response.getStatusCode());
assertNull(response.getBody());
}
@@ -158,11 +308,9 @@ private static Stream exceptionOnSessionIdGeneration() {
Arguments.of(
new TeeSessionGenerationException(
SECURE_SESSION_GENERATION_FAILED,
- String.format("Failed to generate secure session [taskId:%s, workerAddress:%s]", TASK_ID, WORKER_ADDRESS)
- )
- ),
- Arguments.of(new RuntimeException())
- );
+ String.format("Failed to generate secure session [taskId:%s, workerAddress:%s]",
+ TASK_ID, WORKER_ADDRESS))),
+ Arguments.of(new RuntimeException()));
}
/**
@@ -172,7 +320,8 @@ private static Stream exceptionOnSessionIdGeneration() {
*/
@ParameterizedTest
@MethodSource("exceptionOnSessionIdGeneration")
- void shouldNotGenerateTeeSessionSinceSessionIdGenerationFailed(Exception exception) throws TeeSessionGenerationException {
+ void shouldNotGenerateTeeSessionSinceSessionIdGenerationFailed(Exception exception)
+ throws TeeSessionGenerationException {
final WorkerpoolAuthorization workerpoolAuthorization = WorkerpoolAuthorization
.builder()
.chainTaskId(TASK_ID)
@@ -182,11 +331,13 @@ void shouldNotGenerateTeeSessionSinceSessionIdGenerationFailed(Exception excepti
when(authorizationService.getChallengeForWorker(workerpoolAuthorization)).thenReturn(CHALLENGE);
when(authorizationService.isSignedByHimself(CHALLENGE, AUTHORIZATION, WORKER_ADDRESS)).thenReturn(true);
- when(authorizationService.isAuthorizedOnExecutionWithDetailedIssue(workerpoolAuthorization, true)).thenReturn(Optional.empty());
- when(teeSessionService.generateTeeSession(TASK_ID, Keys.toChecksumAddress(WORKER_ADDRESS), ENCLAVE_CHALLENGE)).thenThrow(exception);
+ when(authorizationService.isAuthorizedOnExecutionWithDetailedIssue(workerpoolAuthorization, true))
+ .thenReturn(Optional.empty());
+ when(teeSessionService.generateTeeSession(TASK_ID, Keys.toChecksumAddress(WORKER_ADDRESS), ENCLAVE_CHALLENGE))
+ .thenThrow(exception);
- final ResponseEntity> response =
- teeController.generateTeeSession(AUTHORIZATION, workerpoolAuthorization);
+ final ResponseEntity> response = teeController
+ .generateTeeSession(AUTHORIZATION, workerpoolAuthorization);
assertEquals(HttpStatus.INTERNAL_SERVER_ERROR, response.getStatusCode());
assertNotEquals(null, response.getBody());
assertNull(response.getBody().getData());
diff --git a/src/test/java/com/iexec/sms/tee/TeeFrameworkConverterTests.java b/src/test/java/com/iexec/sms/tee/TeeFrameworkConverterTests.java
new file mode 100644
index 00000000..02697da5
--- /dev/null
+++ b/src/test/java/com/iexec/sms/tee/TeeFrameworkConverterTests.java
@@ -0,0 +1,38 @@
+package com.iexec.sms.tee;
+
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.ValueSource;
+
+import static com.iexec.common.tee.TeeFramework.GRAMINE;
+import static com.iexec.common.tee.TeeFramework.SCONE;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+
+class TeeFrameworkConverterTests {
+ private final TeeFrameworkConverter converter = new TeeFrameworkConverter();
+
+ // region convert
+ @Test
+ void shouldConvertScone() {
+ assertEquals(SCONE, converter.convert("scone"));
+ assertEquals(SCONE, converter.convert("Scone"));
+ assertEquals(SCONE, converter.convert("SCONE"));
+ assertEquals(SCONE, converter.convert("sCoNe"));
+ }
+
+ @Test
+ void shouldConvertGramine() {
+ assertEquals(GRAMINE, converter.convert("gramine"));
+ assertEquals(GRAMINE, converter.convert("Gramine"));
+ assertEquals(GRAMINE, converter.convert("GRAMINE"));
+ assertEquals(GRAMINE, converter.convert("gRaMiNe"));
+ }
+
+ @ParameterizedTest
+ @ValueSource(strings = {"", "azer"})
+ void shouldNotConvertBadValue(String value) {
+ assertThrows(IllegalArgumentException.class, () -> converter.convert(value));
+ }
+ // endregion
+}
diff --git a/src/test/java/com/iexec/sms/tee/challenge/TeeChallengeServiceTests.java b/src/test/java/com/iexec/sms/tee/challenge/TeeChallengeServiceTests.java
index 6cc471b4..59680917 100644
--- a/src/test/java/com/iexec/sms/tee/challenge/TeeChallengeServiceTests.java
+++ b/src/test/java/com/iexec/sms/tee/challenge/TeeChallengeServiceTests.java
@@ -36,9 +36,9 @@
class TeeChallengeServiceTests {
- private final static String TASK_ID = "0x123";
- private final static String PLAIN_PRIVATE = "plainPrivate";
- private final static String ENC_PRIVATE = "encPrivate";
+ private static final String TASK_ID = "0x123";
+ private static final String PLAIN_PRIVATE = "plainPrivate";
+ private static final String ENC_PRIVATE = "encPrivate";
@Mock
private TeeChallengeRepository teeChallengeRepository;
diff --git a/src/test/java/com/iexec/sms/tee/config/TeeWorkerInternalConfigurationTests.java b/src/test/java/com/iexec/sms/tee/config/TeeWorkerInternalConfigurationTests.java
new file mode 100644
index 00000000..28554630
--- /dev/null
+++ b/src/test/java/com/iexec/sms/tee/config/TeeWorkerInternalConfigurationTests.java
@@ -0,0 +1,87 @@
+package com.iexec.sms.tee.config;
+
+import com.iexec.common.tee.TeeFramework;
+import com.iexec.sms.api.config.GramineServicesProperties;
+import com.iexec.sms.api.config.SconeServicesProperties;
+import com.iexec.sms.api.config.TeeAppProperties;
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.mockito.Mockito.mock;
+
+class TeeWorkerInternalConfigurationTests {
+ private final static String IMAGE = "image";
+ private final static String FINGERPRINT = "fingerprint";
+ private final static String ENTRYPOINT = "entrypoint";
+ private final static String LAS_IMAGE = "lasImage";
+ private final static long HEAP_SIZE_GB = 3;
+ private final static long HEAP_SIZE_B = 3221225472L;
+
+ private final TeeWorkerInternalConfiguration teeWorkerInternalConfiguration =
+ new TeeWorkerInternalConfiguration();
+
+ // region preComputeProperties
+ @Test
+ void preComputeProperties() {
+ TeeAppProperties properties = teeWorkerInternalConfiguration.preComputeProperties(
+ IMAGE,
+ FINGERPRINT,
+ ENTRYPOINT,
+ HEAP_SIZE_GB
+ );
+
+ assertEquals(IMAGE, properties.getImage());
+ assertEquals(FINGERPRINT, properties.getFingerprint());
+ assertEquals(ENTRYPOINT, properties.getEntrypoint());
+ assertEquals(HEAP_SIZE_B, properties.getHeapSizeInBytes());
+ }
+ // endregion
+
+ // region postComputeProperties
+ @Test
+ void postComputeProperties() {
+ TeeAppProperties properties = teeWorkerInternalConfiguration.postComputeProperties(
+ IMAGE,
+ FINGERPRINT,
+ ENTRYPOINT,
+ HEAP_SIZE_GB
+ );
+
+ assertEquals(IMAGE, properties.getImage());
+ assertEquals(FINGERPRINT, properties.getFingerprint());
+ assertEquals(ENTRYPOINT, properties.getEntrypoint());
+ assertEquals(HEAP_SIZE_B, properties.getHeapSizeInBytes());
+ }
+ // endregion
+
+ // region gramineServicesProperties
+ @Test
+ void gramineServicesProperties() {
+ TeeAppProperties preComputeProperties = mock(TeeAppProperties.class);
+ TeeAppProperties postComputeProperties = mock(TeeAppProperties.class);
+
+ GramineServicesProperties properties =
+ teeWorkerInternalConfiguration.gramineServicesProperties(preComputeProperties, postComputeProperties);
+
+ assertEquals(TeeFramework.GRAMINE, properties.getTeeFramework());
+ assertEquals(preComputeProperties, properties.getPreComputeProperties());
+ assertEquals(postComputeProperties, properties.getPostComputeProperties());
+ }
+ // endregion
+
+ // region sconeServicesProperties
+ @Test
+ void sconeServicesProperties() {
+ TeeAppProperties preComputeProperties = mock(TeeAppProperties.class);
+ TeeAppProperties postComputeProperties = mock(TeeAppProperties.class);
+
+ SconeServicesProperties properties =
+ teeWorkerInternalConfiguration.sconeServicesProperties(preComputeProperties, postComputeProperties, LAS_IMAGE);
+
+ assertEquals(TeeFramework.SCONE, properties.getTeeFramework());
+ assertEquals(preComputeProperties, properties.getPreComputeProperties());
+ assertEquals(postComputeProperties, properties.getPostComputeProperties());
+ assertEquals(LAS_IMAGE, properties.getLasImage());
+ }
+ // endregion
+}
\ No newline at end of file
diff --git a/src/test/java/com/iexec/sms/tee/session/TeeSessionServiceTests.java b/src/test/java/com/iexec/sms/tee/session/TeeSessionServiceTests.java
index 30225d59..4acc4227 100644
--- a/src/test/java/com/iexec/sms/tee/session/TeeSessionServiceTests.java
+++ b/src/test/java/com/iexec/sms/tee/session/TeeSessionServiceTests.java
@@ -1,87 +1,113 @@
package com.iexec.sms.tee.session;
import com.iexec.common.task.TaskDescription;
+import com.iexec.common.tee.TeeFramework;
+import com.iexec.sms.api.TeeSessionGenerationError;
+import com.iexec.sms.api.TeeSessionGenerationResponse;
import com.iexec.sms.blockchain.IexecHubService;
-import com.iexec.sms.tee.session.cas.CasClient;
-import com.iexec.sms.tee.session.palaemon.PalaemonSessionService;
+import com.iexec.sms.tee.session.generic.TeeSessionGenerationException;
+import com.iexec.sms.tee.session.gramine.GramineSessionHandlerService;
+import com.iexec.sms.tee.session.scone.SconeSessionHandlerService;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
-import org.springframework.http.ResponseEntity;
-import static com.iexec.sms.api.TeeSessionGenerationError.*;
import static org.junit.jupiter.api.Assertions.*;
import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.Mockito.when;
+import static org.mockito.Mockito.*;
class TeeSessionServiceTests {
private final static String TASK_ID = "0x0";
private final static String WORKER_ADDRESS = "0x1";
private final static String TEE_CHALLENGE = "0x2";
-
+ private static final String SECRET_PROVISIONING_URL = "https://secretProvisioningUrl";
@Mock
- IexecHubService iexecHubService;
-
+ private SconeSessionHandlerService sconeService;
@Mock
- CasClient casClient;
-
+ private GramineSessionHandlerService gramineService;
@Mock
- PalaemonSessionService palaemonSessionService;
-
- TeeSessionService teeSessionService;
+ private IexecHubService iexecHubService;
@BeforeEach
void setUp() {
MockitoAnnotations.openMocks(this);
- teeSessionService = new TeeSessionService(iexecHubService, palaemonSessionService, casClient, false);
}
@Test
- void shouldGenerateTeeSession() throws TeeSessionGenerationException {
- final TaskDescription taskDescription = TaskDescription.builder().chainTaskId(TASK_ID).build();
- final String sessionYmlAsString = "YML session";
-
+ void shouldGenerateSconeSession()
+ throws TeeSessionGenerationException {
+ final TeeSessionService teeSessionService = new TeeSessionService(iexecHubService, sconeService);
+
+ final TaskDescription taskDescription = TaskDescription.builder()
+ .chainTaskId(TASK_ID)
+ .teeFramework(TeeFramework.SCONE)
+ .build();
when(iexecHubService.getTaskDescription(TASK_ID)).thenReturn(taskDescription);
- when(palaemonSessionService.getSessionYml(any())).thenReturn(sessionYmlAsString);
- when(casClient.generateSecureSession(sessionYmlAsString.getBytes())).thenReturn(ResponseEntity.ok(null));
-
- final String teeSession = assertDoesNotThrow(() -> teeSessionService.generateTeeSession(TASK_ID, WORKER_ADDRESS, TEE_CHALLENGE));
- assertNotNull(teeSession);
+ when(sconeService.buildAndPostSession(any())).thenReturn(SECRET_PROVISIONING_URL);
+
+ final TeeSessionGenerationResponse teeSessionReponse = assertDoesNotThrow(
+ () -> teeSessionService.generateTeeSession(TASK_ID, WORKER_ADDRESS, TEE_CHALLENGE));
+ verify(sconeService, times(1))
+ .buildAndPostSession(any());
+ assertFalse(teeSessionReponse.getSessionId().isEmpty());
+ assertEquals(SECRET_PROVISIONING_URL, teeSessionReponse.getSecretProvisioningUrl());
}
@Test
- void shouldNotGenerateTeeSessionSinceCantGetTaskDescription() {
- when(iexecHubService.getTaskDescription(TASK_ID)).thenReturn(null);
-
- final TeeSessionGenerationException teeSessionGenerationException = assertThrows(TeeSessionGenerationException.class, () -> teeSessionService.generateTeeSession(TASK_ID, WORKER_ADDRESS, TEE_CHALLENGE));
- assertEquals(GET_TASK_DESCRIPTION_FAILED, teeSessionGenerationException.getError());
- assertEquals(String.format("Failed to get task description [taskId:%s]", TASK_ID), teeSessionGenerationException.getMessage());
+ void shouldGenerateGramineSession()
+ throws TeeSessionGenerationException {
+ final TeeSessionService teeSessionService = new TeeSessionService(iexecHubService, gramineService);
+
+ final TaskDescription taskDescription = TaskDescription.builder()
+ .chainTaskId(TASK_ID)
+ .teeFramework(TeeFramework.GRAMINE)
+ .build();
+ when(iexecHubService.getTaskDescription(TASK_ID)).thenReturn(taskDescription);
+ when(gramineService.buildAndPostSession(any())).thenReturn(SECRET_PROVISIONING_URL);
+
+ final TeeSessionGenerationResponse teeSessionReponse = assertDoesNotThrow(
+ () -> teeSessionService.generateTeeSession(TASK_ID, WORKER_ADDRESS, TEE_CHALLENGE));
+ verify(gramineService, times(1))
+ .buildAndPostSession(any());
+ assertFalse(teeSessionReponse.getSessionId().isEmpty());
+ assertEquals(SECRET_PROVISIONING_URL, teeSessionReponse.getSecretProvisioningUrl());
}
@Test
- void shouldNotGenerateTeeSessionSinceCantGetSessionYml() throws TeeSessionGenerationException {
- final TaskDescription taskDescription = TaskDescription.builder().chainTaskId(TASK_ID).build();
+ void shouldNotGenerateTeeSessionSinceCantGetTaskDescription() {
+ final TeeSessionService teeSessionService = new TeeSessionService(iexecHubService, sconeService);
- when(iexecHubService.getTaskDescription(TASK_ID)).thenReturn(taskDescription);
- when(palaemonSessionService.getSessionYml(any())).thenReturn("");
+ when(iexecHubService.getTaskDescription(TASK_ID)).thenReturn(null);
- final TeeSessionGenerationException teeSessionGenerationException = assertThrows(TeeSessionGenerationException.class, () -> teeSessionService.generateTeeSession(TASK_ID, WORKER_ADDRESS, TEE_CHALLENGE));
- assertEquals(GET_SESSION_YML_FAILED, teeSessionGenerationException.getError());
- assertEquals(String.format("Failed to get session yml [taskId:%s, workerAddress:%s]", TASK_ID, WORKER_ADDRESS), teeSessionGenerationException.getMessage());
+ final TeeSessionGenerationException teeSessionGenerationException = assertThrows(
+ TeeSessionGenerationException.class,
+ () -> teeSessionService.generateTeeSession(TASK_ID, WORKER_ADDRESS, TEE_CHALLENGE));
+ assertEquals(TeeSessionGenerationError.GET_TASK_DESCRIPTION_FAILED,
+ teeSessionGenerationException.getError());
+ assertEquals(String.format("Failed to get task description [taskId:%s]", TASK_ID),
+ teeSessionGenerationException.getMessage());
}
@Test
- void shouldNotGenerateTeeSessionSinceCantGenerateSecureSession() throws TeeSessionGenerationException {
- final TaskDescription taskDescription = TaskDescription.builder().chainTaskId(TASK_ID).build();
- final String sessionYmlAsString = "YML session";
+ void shouldNotGenerateTeeSessionSinceNoTeeFramework() {
+ final TeeSessionService teeSessionService = new TeeSessionService(iexecHubService, sconeService);
+
+ final TaskDescription taskDescription = TaskDescription.builder()
+ .chainTaskId(TASK_ID)
+ .teeFramework(null)
+ .build();
when(iexecHubService.getTaskDescription(TASK_ID)).thenReturn(taskDescription);
- when(palaemonSessionService.getSessionYml(any())).thenReturn(sessionYmlAsString);
- when(casClient.generateSecureSession(sessionYmlAsString.getBytes())).thenReturn(ResponseEntity.notFound().build());
- final TeeSessionGenerationException teeSessionGenerationException = assertThrows(TeeSessionGenerationException.class, () -> teeSessionService.generateTeeSession(TASK_ID, WORKER_ADDRESS, TEE_CHALLENGE));
- assertEquals(SECURE_SESSION_CAS_CALL_FAILED, teeSessionGenerationException.getError());
- assertEquals(String.format("Failed to generate secure session [taskId:%s, workerAddress:%s]", TASK_ID, WORKER_ADDRESS), teeSessionGenerationException.getMessage());
+ final TeeSessionGenerationException teeSessionGenerationException = assertThrows(
+ TeeSessionGenerationException.class,
+ () -> teeSessionService.generateTeeSession(TASK_ID, WORKER_ADDRESS, TEE_CHALLENGE));
+ assertEquals(TeeSessionGenerationError.SECURE_SESSION_NO_TEE_PROVIDER,
+ teeSessionGenerationException.getError());
+ assertEquals(String.format("TEE framework can't be null [taskId:%s]",
+ TASK_ID),
+ teeSessionGenerationException.getMessage());
}
+
}
\ No newline at end of file
diff --git a/src/test/java/com/iexec/sms/tee/session/TeeSessionTestUtils.java b/src/test/java/com/iexec/sms/tee/session/TeeSessionTestUtils.java
new file mode 100644
index 00000000..ad3cfdf2
--- /dev/null
+++ b/src/test/java/com/iexec/sms/tee/session/TeeSessionTestUtils.java
@@ -0,0 +1,201 @@
+/*
+ * Copyright 2022 IEXEC BLOCKCHAIN TECH
+ *
+ * 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.
+ */
+
+package com.iexec.sms.tee.session;
+
+import com.iexec.common.precompute.PreComputeUtils;
+import com.iexec.common.task.TaskDescription;
+import com.iexec.common.tee.TeeEnclaveConfiguration;
+import com.iexec.common.utils.IexecEnvUtils;
+import com.iexec.sms.secret.compute.OnChainObjectType;
+import com.iexec.sms.secret.compute.SecretOwnerRole;
+import com.iexec.sms.secret.compute.TeeTaskComputeSecret;
+import com.iexec.sms.tee.session.base.SecretSessionBaseService;
+import com.iexec.sms.tee.session.generic.TeeSessionRequest;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.lang3.ClassUtils;
+
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import static com.iexec.common.worker.result.ResultUtils.*;
+import static com.iexec.sms.Web3jUtils.createEthereumAddress;
+import static com.iexec.sms.tee.session.base.SecretSessionBaseService.*;
+import static org.assertj.core.api.Assertions.assertThat;
+
+@Slf4j
+public class TeeSessionTestUtils {
+ public static final String TASK_ID = "taskId";
+ public static final String SESSION_ID = "sessionId";
+ public static final String WORKER_ADDRESS = "workerAddress";
+ public static final String ENCLAVE_CHALLENGE = "enclaveChallenge";
+ // pre-compute
+ public static final String PRE_COMPUTE_FINGERPRINT = "mrEnclave1";
+ public static final String PRE_COMPUTE_ENTRYPOINT = "entrypoint1";
+ public static final String DATASET_ADDRESS = "0xDatasetAddress";
+ public static final String DATASET_NAME = "datasetName";
+ public static final String DATASET_CHECKSUM = "datasetChecksum";
+ public static final String DATASET_URL = "http://datasetUrl"; // 0x687474703a2f2f646174617365742d75726c in hex
+ // keys with leading/trailing \n should not break the workflow
+ public static final String DATASET_KEY = "\ndatasetKey\n";
+ // app
+ public static final String APP_DEVELOPER_SECRET_INDEX = "1";
+ public static final String APP_DEVELOPER_SECRET_VALUE = "appDeveloperSecretValue";
+ public static final String REQUESTER_SECRET_KEY_1 = "requesterSecretKey1";
+ public static final String REQUESTER_SECRET_VALUE_1 = "requesterSecretValue1";
+ public static final String REQUESTER_SECRET_KEY_2 = "requesterSecretKey2";
+ public static final String REQUESTER_SECRET_VALUE_2 = "requesterSecretValue2";
+ public static final String APP_URI = "appUri";
+ public static final String APP_FINGERPRINT = "01ba4719c80b6fe911b091a7c05124b64eeece964e09c058ef8f9805daca546b";
+ public static final String APP_ENTRYPOINT = "appEntrypoint";
+ public static final String ARGS = "args";
+ public static final String IEXEC_APP_DEVELOPER_SECRET_1 = "IEXEC_APP_DEVELOPER_SECRET_1";
+ // post-compute
+ public static final String POST_COMPUTE_FINGERPRINT = "mrEnclave3";
+ public static final String POST_COMPUTE_ENTRYPOINT = "entrypoint3";
+ public static final String STORAGE_PROVIDER = "ipfs";
+ public static final String STORAGE_PROXY = "storageProxy";
+ public static final String STORAGE_TOKEN = "storageToken";
+ public static final String ENCRYPTION_PUBLIC_KEY = "encryptionPublicKey";
+ public static final String TEE_CHALLENGE_PRIVATE_KEY = "teeChallengePrivateKey";
+ // input files
+ public static final String INPUT_FILE_URL_1 = "http://host/file1";
+ public static final String INPUT_FILE_NAME_1 = "file1";
+ public static final String INPUT_FILE_URL_2 = "http://host/file2";
+ public static final String INPUT_FILE_NAME_2 = "file2";
+
+ //region utils
+ public static TeeTaskComputeSecret getApplicationDeveloperSecret(String appAddress) {
+ return TeeTaskComputeSecret.builder()
+ .onChainObjectType(OnChainObjectType.APPLICATION)
+ .onChainObjectAddress(appAddress)
+ .secretOwnerRole(SecretOwnerRole.APPLICATION_DEVELOPER)
+ .fixedSecretOwner("")
+ .key(APP_DEVELOPER_SECRET_INDEX)
+ .value(APP_DEVELOPER_SECRET_VALUE)
+ .build();
+ }
+
+ public static TeeTaskComputeSecret getRequesterSecret(String requesterAddress, String secretKey, String secretValue) {
+ return TeeTaskComputeSecret.builder()
+ .onChainObjectType(OnChainObjectType.APPLICATION)
+ .onChainObjectAddress("")
+ .secretOwnerRole(SecretOwnerRole.REQUESTER)
+ .fixedSecretOwner(requesterAddress)
+ .key(secretKey)
+ .value(secretValue)
+ .build();
+ }
+
+ public static TeeSessionRequest createSessionRequest(TaskDescription taskDescription) {
+ return TeeSessionRequest.builder()
+ .sessionId(SESSION_ID)
+ .workerAddress(WORKER_ADDRESS)
+ .enclaveChallenge(ENCLAVE_CHALLENGE)
+ .taskDescription(taskDescription)
+ .build();
+ }
+
+ public static TaskDescription createTaskDescription(TeeEnclaveConfiguration enclaveConfig) {
+ String appAddress = createEthereumAddress();
+ String requesterAddress = createEthereumAddress();
+ String beneficiaryAddress = createEthereumAddress();
+ return TaskDescription.builder()
+ .chainTaskId(TASK_ID)
+ .appUri(APP_URI)
+ .appAddress(appAddress)
+ .appEnclaveConfiguration(enclaveConfig)
+ .datasetAddress(DATASET_ADDRESS)
+ .datasetUri(DATASET_URL)
+ .datasetName(DATASET_NAME)
+ .datasetChecksum(DATASET_CHECKSUM)
+ .requester(requesterAddress)
+ .beneficiary(beneficiaryAddress)
+ .cmd(ARGS)
+ .inputFiles(List.of(INPUT_FILE_URL_1, INPUT_FILE_URL_2))
+ .isResultEncryption(true)
+ .resultStorageProvider(STORAGE_PROVIDER)
+ .resultStorageProxy(STORAGE_PROXY)
+ .secrets(Map.of("1", REQUESTER_SECRET_KEY_1, "2", REQUESTER_SECRET_KEY_2))
+ .botSize(1)
+ .botFirstIndex(0)
+ .botIndex(0)
+ .build();
+ }
+
+ public static Map getPreComputeTokens() {
+ return Map.of(
+ PRE_COMPUTE_MRENCLAVE, PRE_COMPUTE_FINGERPRINT,
+ PreComputeUtils.IS_DATASET_REQUIRED, true,
+ PreComputeUtils.IEXEC_DATASET_KEY, DATASET_KEY.trim(),
+ INPUT_FILE_URLS, Map.of(
+ IexecEnvUtils.IEXEC_INPUT_FILE_URL_PREFIX + "1", INPUT_FILE_URL_1,
+ IexecEnvUtils.IEXEC_INPUT_FILE_URL_PREFIX + "2", INPUT_FILE_URL_2));
+ }
+
+ public static Map getAppTokens() {
+ return Map.of(
+ APP_MRENCLAVE, APP_FINGERPRINT,
+ SecretSessionBaseService.INPUT_FILE_NAMES, Map.of(
+ IexecEnvUtils.IEXEC_INPUT_FILE_NAME_PREFIX + "1", INPUT_FILE_NAME_1,
+ IexecEnvUtils.IEXEC_INPUT_FILE_NAME_PREFIX + "2", INPUT_FILE_NAME_2));
+ }
+
+ public static Map getPostComputeTokens() {
+ Map map = new HashMap<>();
+ map.put(POST_COMPUTE_MRENCLAVE, POST_COMPUTE_FINGERPRINT);
+ map.put(RESULT_TASK_ID, TASK_ID);
+ map.put(RESULT_ENCRYPTION, "yes");
+ map.put(RESULT_ENCRYPTION_PUBLIC_KEY, ENCRYPTION_PUBLIC_KEY);
+ map.put(RESULT_STORAGE_PROVIDER, STORAGE_PROVIDER);
+ map.put(RESULT_STORAGE_PROXY, STORAGE_PROXY);
+ map.put(RESULT_STORAGE_TOKEN, STORAGE_TOKEN);
+ map.put(RESULT_STORAGE_CALLBACK, "no");
+ map.put(RESULT_SIGN_WORKER_ADDRESS, WORKER_ADDRESS);
+ map.put(RESULT_SIGN_TEE_CHALLENGE_PRIVATE_KEY, TEE_CHALLENGE_PRIVATE_KEY);
+ return map;
+ }
+
+ public static void assertRecursively(Object expected, Object actual) {
+ if (expected == null ||
+ expected instanceof String ||
+ ClassUtils.isPrimitiveOrWrapper(expected.getClass())) {
+ log.info("Comparing [actual:{}, expected:{}]", expected, actual);
+ assertThat(expected).isEqualTo(actual);
+ return;
+ }
+ if (expected instanceof List) {
+ List> actualList = (List>) expected;
+ List> expectedList = (List>) actual;
+ for (int i = 0; i < actualList.size(); i++) {
+ assertRecursively(actualList.get(i), expectedList.get(i));
+ }
+ return;
+ }
+ if (expected instanceof Map) {
+ Map, ?> actualMap = (Map, ?>) expected;
+ Map, ?> expectedMap = (Map, ?>) actual;
+ actualMap.keySet().forEach((key) -> {
+ final Object expectedObject = expectedMap.get(key);
+ final Object actualObject = actualMap.get(key);
+ log.info("Checking expected map contains valid '{}' key [expected value:{}, actual value:{}]", key, expectedObject, actualObject);
+ assertRecursively(expectedObject, actualObject);
+ });
+ }
+ }
+ //endregion
+}
diff --git a/src/test/java/com/iexec/sms/tee/session/base/SecretSessionBaseServiceTests.java b/src/test/java/com/iexec/sms/tee/session/base/SecretSessionBaseServiceTests.java
new file mode 100644
index 00000000..9030e295
--- /dev/null
+++ b/src/test/java/com/iexec/sms/tee/session/base/SecretSessionBaseServiceTests.java
@@ -0,0 +1,693 @@
+package com.iexec.sms.tee.session.base;
+
+import com.iexec.common.chain.DealParams;
+import com.iexec.common.task.TaskDescription;
+import com.iexec.common.tee.TeeEnclaveConfiguration;
+import com.iexec.common.tee.TeeFramework;
+import com.iexec.common.utils.IexecEnvUtils;
+import com.iexec.sms.api.TeeSessionGenerationError;
+import com.iexec.sms.api.config.TeeAppProperties;
+import com.iexec.sms.api.config.TeeServicesProperties;
+import com.iexec.sms.secret.ReservedSecretKeyName;
+import com.iexec.sms.secret.compute.OnChainObjectType;
+import com.iexec.sms.secret.compute.SecretOwnerRole;
+import com.iexec.sms.secret.compute.TeeTaskComputeSecret;
+import com.iexec.sms.secret.compute.TeeTaskComputeSecretService;
+import com.iexec.sms.secret.web2.Web2SecretService;
+import com.iexec.sms.secret.web3.Web3SecretService;
+import com.iexec.sms.tee.challenge.TeeChallenge;
+import com.iexec.sms.tee.challenge.TeeChallengeService;
+import com.iexec.sms.tee.session.generic.TeeSessionGenerationException;
+import com.iexec.sms.tee.session.generic.TeeSessionRequest;
+import com.iexec.sms.utils.EthereumCredentials;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.NullSource;
+import org.junit.jupiter.params.provider.ValueSource;
+import org.mockito.InjectMocks;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.security.GeneralSecurityException;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+
+import static com.iexec.sms.tee.session.TeeSessionTestUtils.*;
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.junit.Assert.assertThrows;
+import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.*;
+
+class SecretSessionBaseServiceTests {
+
+ private static final TeeEnclaveConfiguration enclaveConfig = TeeEnclaveConfiguration.builder()
+ .framework(TeeFramework.SCONE)// any would be fine
+ .entrypoint(APP_ENTRYPOINT)
+ .fingerprint(APP_FINGERPRINT)
+ .heapSize(1)
+ .build();
+
+ @Mock
+ private Web3SecretService web3SecretService;
+ @Mock
+ private Web2SecretService web2SecretService;
+ @Mock
+ private TeeChallengeService teeChallengeService;
+ @Mock
+ private TeeAppProperties preComputeProperties;
+ @Mock
+ private TeeAppProperties postComputeProperties;
+ @Mock
+ private TeeServicesProperties teeServicesConfig;
+ @Mock
+ private TeeTaskComputeSecretService teeTaskComputeSecretService;
+
+ @InjectMocks
+ private SecretSessionBaseService teeSecretsService;
+
+ @BeforeEach
+ void beforeEach() {
+ MockitoAnnotations.openMocks(this);
+ when(teeServicesConfig.getPreComputeProperties()).thenReturn(preComputeProperties);
+ when(teeServicesConfig.getPostComputeProperties()).thenReturn(postComputeProperties);
+ }
+
+ // region getSecretsTokens
+ @Test
+ void shouldGetSecretsTokens() throws Exception {
+ TaskDescription taskDescription = createTaskDescription(enclaveConfig);
+ TeeSessionRequest request = createSessionRequest(taskDescription);
+ String beneficiary = request.getTaskDescription().getBeneficiary();
+
+ // pre
+ when(preComputeProperties.getFingerprint())
+ .thenReturn(PRE_COMPUTE_FINGERPRINT);
+ when(web3SecretService.getDecryptedValue(DATASET_ADDRESS))
+ .thenReturn(Optional.of(DATASET_KEY));
+ // post
+ when(postComputeProperties.getFingerprint())
+ .thenReturn(POST_COMPUTE_FINGERPRINT);
+ when(web2SecretService.getDecryptedValue(
+ beneficiary,
+ ReservedSecretKeyName.IEXEC_RESULT_ENCRYPTION_PUBLIC_KEY))
+ .thenReturn(Optional.of(ENCRYPTION_PUBLIC_KEY));
+ when(web2SecretService.getDecryptedValue(taskDescription.getRequester(),
+ ReservedSecretKeyName.IEXEC_RESULT_IEXEC_IPFS_TOKEN))
+ .thenReturn(Optional.of(STORAGE_TOKEN));
+ TeeChallenge challenge = TeeChallenge.builder()
+ .credentials(EthereumCredentials.generate())
+ .build();
+ when(teeChallengeService.getOrCreate(TASK_ID, true))
+ .thenReturn(Optional.of(challenge));
+
+ SecretSessionBase sessionBase = teeSecretsService.getSecretsTokens(request);
+
+ SecretEnclaveBase preComputeBase = sessionBase.getPreCompute();
+ assertEquals("pre-compute", preComputeBase.getName());
+ assertEquals(PRE_COMPUTE_FINGERPRINT, preComputeBase.getMrenclave());
+ // environment content checks are handled in dedicated tests below
+ assertEquals(teeSecretsService.getPreComputeTokens(request).getEnvironment(),
+ preComputeBase.getEnvironment());
+
+ SecretEnclaveBase appComputeBase = sessionBase.getAppCompute();
+ assertEquals("app", appComputeBase.getName());
+ assertEquals(APP_FINGERPRINT, appComputeBase.getMrenclave());
+ // environment content checks are handled in dedicated tests below
+ assertEquals(teeSecretsService.getAppTokens(request).getEnvironment(),
+ appComputeBase.getEnvironment());
+
+ SecretEnclaveBase postComputeBase = sessionBase.getPostCompute();
+ assertEquals("post-compute", postComputeBase.getName());
+ assertEquals(POST_COMPUTE_FINGERPRINT, postComputeBase.getMrenclave());
+ // environment content checks are handled in dedicated tests below
+ assertEquals(teeSecretsService.getPostComputeTokens(request).getEnvironment(),
+ postComputeBase.getEnvironment());
+ }
+
+ @Test
+ void shouldNotGetSecretsTokensSinceRequestIsNull() {
+ final TeeSessionGenerationException exception = assertThrows(
+ TeeSessionGenerationException.class,
+ () -> teeSecretsService.getSecretsTokens(null));
+ assertEquals(TeeSessionGenerationError.NO_SESSION_REQUEST, exception.getError());
+ assertEquals("Session request must not be null", exception.getMessage());
+ }
+
+ @Test
+ void shouldNotGetSecretsTokensSinceTaskDescriptionIsMissing() {
+ TeeSessionRequest request = TeeSessionRequest.builder().build();
+
+ final TeeSessionGenerationException exception = assertThrows(
+ TeeSessionGenerationException.class,
+ () -> teeSecretsService.getSecretsTokens(request));
+ assertEquals(TeeSessionGenerationError.NO_TASK_DESCRIPTION, exception.getError());
+ assertEquals("Task description must not be null", exception.getMessage());
+ }
+ // endregion
+
+ // region getPreComputeTokens
+ @Test
+ void shouldGetPreComputeTokens() throws Exception {
+ TaskDescription taskDescription = createTaskDescription(enclaveConfig);
+ TeeSessionRequest request = createSessionRequest(taskDescription);
+ when(preComputeProperties.getFingerprint())
+ .thenReturn(PRE_COMPUTE_FINGERPRINT);
+ when(web3SecretService.getDecryptedValue(DATASET_ADDRESS))
+ .thenReturn(Optional.of(DATASET_KEY));
+
+ SecretEnclaveBase enclaveBase = teeSecretsService.getPreComputeTokens(request);
+ assertThat(enclaveBase.getName()).isEqualTo("pre-compute");
+ assertThat(enclaveBase.getMrenclave()).isEqualTo(PRE_COMPUTE_FINGERPRINT);
+ Map expectedTokens = new HashMap<>();
+ expectedTokens.put("IEXEC_TASK_ID", TASK_ID);
+ expectedTokens.put("IEXEC_PRE_COMPUTE_OUT", "/iexec_in");
+ expectedTokens.put("IS_DATASET_REQUIRED", true);
+ expectedTokens.put("IEXEC_DATASET_KEY", DATASET_KEY);
+ expectedTokens.put("IEXEC_DATASET_URL", DATASET_URL);
+ expectedTokens.put("IEXEC_DATASET_FILENAME", DATASET_NAME);
+ expectedTokens.put("IEXEC_DATASET_CHECKSUM", DATASET_CHECKSUM);
+ expectedTokens.put("IEXEC_INPUT_FILES_FOLDER", "/iexec_in");
+ expectedTokens.put("IEXEC_INPUT_FILES_NUMBER", "2");
+ expectedTokens.put("IEXEC_INPUT_FILE_URL_1", INPUT_FILE_URL_1);
+ expectedTokens.put("IEXEC_INPUT_FILE_URL_2", INPUT_FILE_URL_2);
+ assertThat(enclaveBase.getEnvironment()).containsExactlyInAnyOrderEntriesOf(expectedTokens);
+ }
+
+ @Test
+ void shouldGetPreComputeTokensWithoutDataset() throws Exception {
+ TeeSessionRequest request = TeeSessionRequest.builder()
+ .sessionId(SESSION_ID)
+ .workerAddress(WORKER_ADDRESS)
+ .enclaveChallenge(ENCLAVE_CHALLENGE)
+ .taskDescription(TaskDescription.builder()
+ .chainTaskId(TASK_ID)
+ .inputFiles(List.of(INPUT_FILE_URL_1, INPUT_FILE_URL_2))
+ .build())
+ .build();
+ when(preComputeProperties.getFingerprint())
+ .thenReturn(PRE_COMPUTE_FINGERPRINT);
+
+ SecretEnclaveBase enclaveBase = teeSecretsService.getPreComputeTokens(request);
+ assertThat(enclaveBase.getName()).isEqualTo("pre-compute");
+ assertThat(enclaveBase.getMrenclave()).isEqualTo(PRE_COMPUTE_FINGERPRINT);
+ Map expectedTokens = new HashMap<>();
+ expectedTokens.put("IEXEC_TASK_ID", TASK_ID);
+ expectedTokens.put("IEXEC_PRE_COMPUTE_OUT", "/iexec_in");
+ expectedTokens.put("IS_DATASET_REQUIRED", false);
+ expectedTokens.put("IEXEC_INPUT_FILES_FOLDER", "/iexec_in");
+ expectedTokens.put("IEXEC_INPUT_FILES_NUMBER", "2");
+ expectedTokens.put("IEXEC_INPUT_FILE_URL_1", INPUT_FILE_URL_1);
+ expectedTokens.put("IEXEC_INPUT_FILE_URL_2", INPUT_FILE_URL_2);
+ assertThat(enclaveBase.getEnvironment()).containsExactlyInAnyOrderEntriesOf(expectedTokens);
+ }
+ // endregion
+
+ // region getAppTokens
+ @Test
+ void shouldGetAppTokensForAdvancedTaskDescription() throws TeeSessionGenerationException {
+ TeeSessionRequest request = createSessionRequest(createTaskDescription(enclaveConfig));
+
+ String appAddress = request.getTaskDescription().getAppAddress();
+ String requesterAddress = request.getTaskDescription().getRequester();
+
+ addApplicationDeveloperSecret(appAddress);
+ addRequesterSecret(requesterAddress, REQUESTER_SECRET_KEY_1, REQUESTER_SECRET_VALUE_1);
+ addRequesterSecret(requesterAddress, REQUESTER_SECRET_KEY_2, REQUESTER_SECRET_VALUE_2);
+
+ SecretEnclaveBase enclaveBase = teeSecretsService.getAppTokens(request);
+ assertThat(enclaveBase.getName()).isEqualTo("app");
+ assertThat(enclaveBase.getMrenclave()).isEqualTo(APP_FINGERPRINT);
+ Map expectedTokens = new HashMap<>();
+ expectedTokens.put("IEXEC_TASK_ID", TASK_ID);
+ expectedTokens.put("IEXEC_IN", "/iexec_in");
+ expectedTokens.put("IEXEC_OUT", "/iexec_out");
+ expectedTokens.put("IEXEC_DATASET_ADDRESS", DATASET_ADDRESS);
+ expectedTokens.put("IEXEC_DATASET_FILENAME", DATASET_NAME);
+ expectedTokens.put("IEXEC_BOT_SIZE", "1");
+ expectedTokens.put("IEXEC_BOT_FIRST_INDEX", "0");
+ expectedTokens.put("IEXEC_BOT_TASK_INDEX", "0");
+ expectedTokens.put("IEXEC_INPUT_FILES_FOLDER", "/iexec_in");
+ expectedTokens.put("IEXEC_INPUT_FILES_NUMBER", "2");
+ expectedTokens.put("IEXEC_INPUT_FILE_NAME_1", INPUT_FILE_NAME_1);
+ expectedTokens.put("IEXEC_INPUT_FILE_NAME_2", INPUT_FILE_NAME_2);
+ expectedTokens.put("IEXEC_APP_DEVELOPER_SECRET", APP_DEVELOPER_SECRET_VALUE);
+ expectedTokens.put("IEXEC_APP_DEVELOPER_SECRET_1", APP_DEVELOPER_SECRET_VALUE);
+ expectedTokens.put("IEXEC_REQUESTER_SECRET_1", REQUESTER_SECRET_VALUE_1);
+ expectedTokens.put("IEXEC_REQUESTER_SECRET_2", REQUESTER_SECRET_VALUE_2);
+ assertThat(enclaveBase.getEnvironment()).containsExactlyInAnyOrderEntriesOf(expectedTokens);
+
+ verify(teeTaskComputeSecretService).getSecret(OnChainObjectType.APPLICATION, appAddress,
+ SecretOwnerRole.APPLICATION_DEVELOPER, "", APP_DEVELOPER_SECRET_INDEX);
+ verify(teeTaskComputeSecretService).getSecret(OnChainObjectType.APPLICATION, "", SecretOwnerRole.REQUESTER,
+ requesterAddress, REQUESTER_SECRET_KEY_1);
+ verify(teeTaskComputeSecretService).getSecret(OnChainObjectType.APPLICATION, "", SecretOwnerRole.REQUESTER,
+ requesterAddress, REQUESTER_SECRET_KEY_2);
+ }
+
+ @Test
+ void shouldGetTokensWithEmptyAppComputeSecretWhenSecretsDoNotExist() throws TeeSessionGenerationException {
+ final String appAddress = "0xapp";
+ final String requesterAddress = "0xrequester";
+ final TaskDescription taskDescription = TaskDescription.builder()
+ .chainTaskId(TASK_ID)
+ .appUri(APP_URI)
+ .appAddress(appAddress)
+ .appEnclaveConfiguration(enclaveConfig)
+ .datasetAddress(DATASET_ADDRESS)
+ .datasetUri(DATASET_URL)
+ .datasetName(DATASET_NAME)
+ .datasetChecksum(DATASET_CHECKSUM)
+ .requester(requesterAddress)
+ .cmd(ARGS)
+ .inputFiles(List.of(INPUT_FILE_URL_1, INPUT_FILE_URL_2))
+ .isResultEncryption(true)
+ .resultStorageProvider(STORAGE_PROVIDER)
+ .resultStorageProxy(STORAGE_PROXY)
+ .botSize(1)
+ .botFirstIndex(0)
+ .botIndex(0)
+ .build();
+ TeeSessionRequest request = createSessionRequest(taskDescription);
+
+ when(teeTaskComputeSecretService.getSecret(
+ OnChainObjectType.APPLICATION,
+ appAddress,
+ SecretOwnerRole.APPLICATION_DEVELOPER,
+ "",
+ APP_DEVELOPER_SECRET_INDEX))
+ .thenReturn(Optional.empty());
+
+ SecretEnclaveBase enclaveBase = teeSecretsService.getAppTokens(request);
+ assertThat(enclaveBase.getName()).isEqualTo("app");
+ assertThat(enclaveBase.getMrenclave()).isEqualTo(APP_FINGERPRINT);
+ Map expectedTokens = new HashMap<>();
+ expectedTokens.put("IEXEC_TASK_ID", TASK_ID);
+ expectedTokens.put("IEXEC_IN", "/iexec_in");
+ expectedTokens.put("IEXEC_OUT", "/iexec_out");
+ expectedTokens.put("IEXEC_DATASET_ADDRESS", DATASET_ADDRESS);
+ expectedTokens.put("IEXEC_DATASET_FILENAME", DATASET_NAME);
+ expectedTokens.put("IEXEC_BOT_SIZE", "1");
+ expectedTokens.put("IEXEC_BOT_FIRST_INDEX", "0");
+ expectedTokens.put("IEXEC_BOT_TASK_INDEX", "0");
+ expectedTokens.put("IEXEC_INPUT_FILES_FOLDER", "/iexec_in");
+ expectedTokens.put("IEXEC_INPUT_FILES_NUMBER", "2");
+ expectedTokens.put("IEXEC_INPUT_FILE_NAME_1", INPUT_FILE_NAME_1);
+ expectedTokens.put("IEXEC_INPUT_FILE_NAME_2", INPUT_FILE_NAME_2);
+ assertThat(enclaveBase.getEnvironment()).containsExactlyInAnyOrderEntriesOf(expectedTokens);
+
+ verify(teeTaskComputeSecretService).getSecret(eq(OnChainObjectType.APPLICATION), eq(appAddress),
+ eq(SecretOwnerRole.APPLICATION_DEVELOPER), eq(""), any());
+ verify(teeTaskComputeSecretService, never()).getSecret(eq(OnChainObjectType.APPLICATION), eq(""),
+ eq(SecretOwnerRole.REQUESTER), any(), any());
+ }
+
+ @Test
+ void shouldFailToGetAppTokensSinceNoTaskDescription() {
+ TeeSessionRequest request = TeeSessionRequest.builder()
+ .build();
+ TeeSessionGenerationException exception = assertThrows(TeeSessionGenerationException.class,
+ () -> teeSecretsService.getAppTokens(request));
+ Assertions.assertEquals(TeeSessionGenerationError.NO_TASK_DESCRIPTION, exception.getError());
+ Assertions.assertEquals("Task description must not be null", exception.getMessage());
+ }
+
+ @Test
+ void shouldFailToGetAppTokensSinceNoEnclaveConfig() {
+ TeeSessionRequest request = TeeSessionRequest.builder()
+ .sessionId(SESSION_ID)
+ .workerAddress(WORKER_ADDRESS)
+ .enclaveChallenge(ENCLAVE_CHALLENGE)
+ .taskDescription(TaskDescription.builder().build())
+ .build();
+ TeeSessionGenerationException exception = assertThrows(TeeSessionGenerationException.class,
+ () -> teeSecretsService.getAppTokens(request));
+ Assertions.assertEquals(TeeSessionGenerationError.APP_COMPUTE_NO_ENCLAVE_CONFIG, exception.getError());
+ Assertions.assertEquals("Enclave configuration must not be null", exception.getMessage());
+ }
+
+ @Test
+ void shouldFailToGetAppTokensInvalidEnclaveConfig() {
+ // invalid enclave config
+ TeeSessionRequest request = createSessionRequest(createTaskDescription(TeeEnclaveConfiguration.builder().build()));
+
+ TeeSessionGenerationException exception = assertThrows(TeeSessionGenerationException.class,
+ () -> teeSecretsService.getAppTokens(request));
+ Assertions.assertEquals(TeeSessionGenerationError.APP_COMPUTE_INVALID_ENCLAVE_CONFIG, exception.getError());
+ }
+
+ @Test
+ void shouldAddMultipleRequesterSecrets() {
+ TeeSessionRequest request = createSessionRequest(createTaskDescription(enclaveConfig));
+ String requesterAddress = request.getTaskDescription().getRequester();
+
+ addRequesterSecret(requesterAddress, REQUESTER_SECRET_KEY_1, REQUESTER_SECRET_VALUE_1);
+ addRequesterSecret(requesterAddress, REQUESTER_SECRET_KEY_2, REQUESTER_SECRET_VALUE_2);
+ SecretEnclaveBase enclaveBase = assertDoesNotThrow(() -> teeSecretsService.getAppTokens(request));
+ verify(teeTaskComputeSecretService, times(2))
+ .getSecret(eq(OnChainObjectType.APPLICATION), eq(""), eq(SecretOwnerRole.REQUESTER), any(), any());
+ verify(teeTaskComputeSecretService).getSecret(OnChainObjectType.APPLICATION, "", SecretOwnerRole.REQUESTER,
+ requesterAddress, REQUESTER_SECRET_KEY_1);
+ verify(teeTaskComputeSecretService).getSecret(OnChainObjectType.APPLICATION, "", SecretOwnerRole.REQUESTER,
+ requesterAddress, REQUESTER_SECRET_KEY_2);
+ assertThat(enclaveBase.getEnvironment()).containsAllEntriesOf(
+ Map.of(
+ IexecEnvUtils.IEXEC_REQUESTER_SECRET_PREFIX + "1", REQUESTER_SECRET_VALUE_1,
+ IexecEnvUtils.IEXEC_REQUESTER_SECRET_PREFIX + "2", REQUESTER_SECRET_VALUE_2));
+ }
+
+ @Test
+ void shouldFilterRequesterSecretIndexLowerThanZero() {
+ TeeSessionRequest request = createSessionRequest(createTaskDescription(enclaveConfig));
+ String requesterAddress = request.getTaskDescription().getRequester();
+
+ request.getTaskDescription()
+ .setSecrets(Map.of("1", REQUESTER_SECRET_KEY_1, "-1", "out-of-bound-requester-secret"));
+ addRequesterSecret(requesterAddress, REQUESTER_SECRET_KEY_1, REQUESTER_SECRET_VALUE_1);
+ SecretEnclaveBase enclaveBase = assertDoesNotThrow(() -> teeSecretsService.getAppTokens(request));
+ verify(teeTaskComputeSecretService).getSecret(eq(OnChainObjectType.APPLICATION), eq(""),
+ eq(SecretOwnerRole.REQUESTER), any(), any());
+ assertThat(enclaveBase.getEnvironment()).containsAllEntriesOf(
+ Map.of(IexecEnvUtils.IEXEC_REQUESTER_SECRET_PREFIX + "1", REQUESTER_SECRET_VALUE_1));
+ }
+ // endregion
+
+ // region getPostComputeTokens
+ @Test
+ void shouldGetPostComputeTokens() throws Exception {
+ TeeSessionRequest request = createSessionRequest(createTaskDescription(enclaveConfig));
+
+ String requesterAddress = request.getTaskDescription().getRequester();
+
+ when(postComputeProperties.getFingerprint())
+ .thenReturn(POST_COMPUTE_FINGERPRINT);
+ when(web2SecretService.getDecryptedValue(
+ request.getTaskDescription().getBeneficiary(),
+ ReservedSecretKeyName.IEXEC_RESULT_ENCRYPTION_PUBLIC_KEY))
+ .thenReturn(Optional.of(ENCRYPTION_PUBLIC_KEY));
+ when(web2SecretService.getDecryptedValue(
+ requesterAddress,
+ ReservedSecretKeyName.IEXEC_RESULT_IEXEC_IPFS_TOKEN))
+ .thenReturn(Optional.of(STORAGE_TOKEN));
+
+ TeeChallenge challenge = TeeChallenge.builder()
+ .credentials(EthereumCredentials.generate())
+ .build();
+ when(teeChallengeService.getOrCreate(TASK_ID, true))
+ .thenReturn(Optional.of(challenge));
+
+ SecretEnclaveBase enclaveBase = teeSecretsService.getPostComputeTokens(request);
+ assertThat(enclaveBase.getName()).isEqualTo("post-compute");
+ assertThat(enclaveBase.getMrenclave()).isEqualTo(POST_COMPUTE_FINGERPRINT);
+ Map expectedTokens = new HashMap<>();
+ // encryption tokens
+ expectedTokens.put("RESULT_ENCRYPTION", "yes");
+ expectedTokens.put("RESULT_ENCRYPTION_PUBLIC_KEY", ENCRYPTION_PUBLIC_KEY);
+ // storage tokens
+ expectedTokens.put("RESULT_STORAGE_CALLBACK", "no");
+ expectedTokens.put("RESULT_STORAGE_PROVIDER", STORAGE_PROVIDER);
+ expectedTokens.put("RESULT_STORAGE_PROXY", STORAGE_PROXY);
+ expectedTokens.put("RESULT_STORAGE_TOKEN", STORAGE_TOKEN);
+ // sign tokens
+ expectedTokens.put("RESULT_TASK_ID", TASK_ID);
+ expectedTokens.put("RESULT_SIGN_WORKER_ADDRESS", WORKER_ADDRESS);
+ expectedTokens.put("RESULT_SIGN_TEE_CHALLENGE_PRIVATE_KEY", challenge.getCredentials().getPrivateKey());
+
+ assertThat(enclaveBase.getEnvironment()).containsExactlyEntriesOf(expectedTokens);
+ }
+
+ @Test
+ void shouldNotGetPostComputeTokensSinceTaskDescriptionMissing() {
+ TeeSessionRequest request = TeeSessionRequest.builder().build();
+
+ final TeeSessionGenerationException exception = assertThrows(
+ TeeSessionGenerationException.class,
+ () -> teeSecretsService.getPostComputeTokens(request));
+ assertEquals(TeeSessionGenerationError.NO_TASK_DESCRIPTION, exception.getError());
+ assertEquals("Task description must not be null", exception.getMessage());
+ }
+ // endregion
+
+ // region getPostComputeEncryptionTokens
+ @Test
+ void shouldGetPostComputeStorageTokensWithCallback() {
+ final TeeSessionRequest sessionRequest = createSessionRequest(createTaskDescription(enclaveConfig));
+ sessionRequest.getTaskDescription().setCallback("callback");
+
+ final Map tokens = assertDoesNotThrow(
+ () -> teeSecretsService.getPostComputeStorageTokens(sessionRequest));
+
+ assertThat(tokens)
+ .containsExactlyInAnyOrderEntriesOf(
+ Map.of(
+ "RESULT_STORAGE_CALLBACK", "yes",
+ "RESULT_STORAGE_PROVIDER", "",
+ "RESULT_STORAGE_PROXY", "",
+ "RESULT_STORAGE_TOKEN", ""));
+ }
+
+ @Test
+ void shouldGetPostComputeStorageTokensOnIpfs() {
+ final TeeSessionRequest sessionRequest = createSessionRequest(createTaskDescription(enclaveConfig));
+ final TaskDescription taskDescription = sessionRequest.getTaskDescription();
+
+ final String secretValue = "Secret value";
+ when(web2SecretService.getDecryptedValue(taskDescription.getRequester(),
+ ReservedSecretKeyName.IEXEC_RESULT_IEXEC_IPFS_TOKEN))
+ .thenReturn(Optional.of(secretValue));
+
+ final Map tokens = assertDoesNotThrow(
+ () -> teeSecretsService.getPostComputeStorageTokens(sessionRequest));
+
+ assertThat(tokens)
+ .containsExactlyInAnyOrderEntriesOf(
+ Map.of(
+ "RESULT_STORAGE_CALLBACK", "no",
+ "RESULT_STORAGE_PROVIDER", STORAGE_PROVIDER,
+ "RESULT_STORAGE_PROXY", STORAGE_PROXY,
+ "RESULT_STORAGE_TOKEN", secretValue));
+ }
+
+ @Test
+ void shouldGetPostComputeStorageTokensOnDropbox() {
+ final TeeSessionRequest sessionRequest = createSessionRequest(createTaskDescription(enclaveConfig));
+ final TaskDescription taskDescription = sessionRequest.getTaskDescription();
+ taskDescription.setResultStorageProvider(DealParams.DROPBOX_RESULT_STORAGE_PROVIDER);
+
+ final String secretValue = "Secret value";
+ when(web2SecretService.getDecryptedValue(taskDescription.getRequester(),
+ ReservedSecretKeyName.IEXEC_RESULT_DROPBOX_TOKEN))
+ .thenReturn(Optional.of(secretValue));
+
+ final Map tokens = assertDoesNotThrow(
+ () -> teeSecretsService.getPostComputeStorageTokens(sessionRequest));
+
+ assertThat(tokens)
+ .containsExactlyInAnyOrderEntriesOf(
+ Map.of(
+ "RESULT_STORAGE_CALLBACK", "no",
+ "RESULT_STORAGE_PROVIDER", "dropbox",
+ "RESULT_STORAGE_PROXY", STORAGE_PROXY,
+ "RESULT_STORAGE_TOKEN", secretValue));
+ }
+
+ @Test
+ void shouldNotGetPostComputeStorageTokensSinceNoSecret() {
+ final TeeSessionRequest sessionRequest = createSessionRequest(createTaskDescription(enclaveConfig));
+ final TaskDescription taskDescription = sessionRequest.getTaskDescription();
+
+ when(web2SecretService.getDecryptedValue(taskDescription.getRequester(),
+ ReservedSecretKeyName.IEXEC_RESULT_IEXEC_IPFS_TOKEN))
+ .thenReturn(Optional.empty());
+
+ final TeeSessionGenerationException exception = assertThrows(
+ TeeSessionGenerationException.class,
+ () -> teeSecretsService.getPostComputeStorageTokens(sessionRequest));
+
+ assertThat(exception.getError()).isEqualTo(TeeSessionGenerationError.POST_COMPUTE_GET_STORAGE_TOKENS_FAILED);
+ assertThat(exception.getMessage())
+ .isEqualTo("Empty requester storage token - taskId: " + taskDescription.getChainTaskId());
+ }
+
+ @Test
+ void shouldGetPostComputeSignTokens() throws GeneralSecurityException {
+ final TeeSessionRequest sessionRequest = createSessionRequest(createTaskDescription(enclaveConfig));
+ final TaskDescription taskDescription = sessionRequest.getTaskDescription();
+ final String taskId = taskDescription.getChainTaskId();
+ final EthereumCredentials credentials = EthereumCredentials.generate();
+
+ when(teeChallengeService.getOrCreate(taskId, true))
+ .thenReturn(Optional.of(TeeChallenge.builder().credentials(credentials).build()));
+
+ final Map tokens = assertDoesNotThrow(
+ () -> teeSecretsService.getPostComputeSignTokens(sessionRequest));
+
+ assertThat(tokens)
+ .containsExactlyInAnyOrderEntriesOf(
+ Map.of(
+ "RESULT_TASK_ID", taskId,
+ "RESULT_SIGN_WORKER_ADDRESS", sessionRequest.getWorkerAddress(),
+ "RESULT_SIGN_TEE_CHALLENGE_PRIVATE_KEY", credentials.getPrivateKey()));
+ }
+
+ @ParameterizedTest
+ @NullSource
+ @ValueSource(strings = { "" })
+ void shouldNotGetPostComputeSignTokensSinceNoWorkerAddress(String emptyWorkerAddress) {
+ final TeeSessionRequest sessionRequest = createSessionRequest(createTaskDescription(enclaveConfig));
+ final String taskId = sessionRequest.getTaskDescription().getChainTaskId();
+ sessionRequest.setWorkerAddress(emptyWorkerAddress);
+
+ final TeeSessionGenerationException exception = assertThrows(
+ TeeSessionGenerationException.class,
+ () -> teeSecretsService.getPostComputeSignTokens(sessionRequest));
+
+ assertThat(exception.getError())
+ .isEqualTo(TeeSessionGenerationError.POST_COMPUTE_GET_SIGNATURE_TOKENS_FAILED_EMPTY_WORKER_ADDRESS);
+ assertThat(exception.getMessage()).isEqualTo("Empty worker address - taskId: " + taskId);
+ }
+
+ @ParameterizedTest
+ @NullSource
+ @ValueSource(strings = { "" })
+ void shouldNotGetPostComputeSignTokensSinceNoEnclaveChallenge(String emptyEnclaveChallenge) {
+ final TeeSessionRequest sessionRequest = createSessionRequest(createTaskDescription(enclaveConfig));
+ final String taskId = sessionRequest.getTaskDescription().getChainTaskId();
+ sessionRequest.setEnclaveChallenge(emptyEnclaveChallenge);
+
+ final TeeSessionGenerationException exception = assertThrows(
+ TeeSessionGenerationException.class,
+ () -> teeSecretsService.getPostComputeSignTokens(sessionRequest));
+
+ assertThat(exception.getError()).isEqualTo(
+ TeeSessionGenerationError.POST_COMPUTE_GET_SIGNATURE_TOKENS_FAILED_EMPTY_PUBLIC_ENCLAVE_CHALLENGE);
+ assertThat(exception.getMessage()).isEqualTo("Empty public enclave challenge - taskId: " + taskId);
+ }
+
+ @Test
+ void shouldNotGetPostComputeSignTokensSinceNoTeeChallenge() {
+ final TeeSessionRequest sessionRequest = createSessionRequest(createTaskDescription(enclaveConfig));
+ final String taskId = sessionRequest.getTaskDescription().getChainTaskId();
+
+ when(teeChallengeService.getOrCreate(taskId, true))
+ .thenReturn(Optional.empty());
+
+ final TeeSessionGenerationException exception = assertThrows(
+ TeeSessionGenerationException.class,
+ () -> teeSecretsService.getPostComputeSignTokens(sessionRequest));
+
+ assertThat(exception.getError())
+ .isEqualTo(TeeSessionGenerationError.POST_COMPUTE_GET_SIGNATURE_TOKENS_FAILED_EMPTY_TEE_CHALLENGE);
+ assertThat(exception.getMessage()).isEqualTo("Empty TEE challenge - taskId: " + taskId);
+ }
+
+ @Test
+ void shouldNotGetPostComputeSignTokensSinceNoEnclaveCredentials() {
+ final TeeSessionRequest sessionRequest = createSessionRequest(createTaskDescription(enclaveConfig));
+ final String taskId = sessionRequest.getTaskDescription().getChainTaskId();
+
+ when(teeChallengeService.getOrCreate(taskId, true))
+ .thenReturn(Optional.of(TeeChallenge.builder().credentials(null).build()));
+
+ final TeeSessionGenerationException exception = assertThrows(
+ TeeSessionGenerationException.class,
+ () -> teeSecretsService.getPostComputeSignTokens(sessionRequest));
+
+ assertThat(exception.getError())
+ .isEqualTo(TeeSessionGenerationError.POST_COMPUTE_GET_SIGNATURE_TOKENS_FAILED_EMPTY_TEE_CREDENTIALS);
+ assertThat(exception.getMessage()).isEqualTo("Empty TEE challenge credentials - taskId: " + taskId);
+ }
+
+ @Test
+ void shouldNotGetPostComputeSignTokensSinceNoEnclaveCredentialsPrivateKey() {
+ final TeeSessionRequest sessionRequest = createSessionRequest(createTaskDescription(enclaveConfig));
+ final String taskId = sessionRequest.getTaskDescription().getChainTaskId();
+
+ when(teeChallengeService.getOrCreate(taskId, true))
+ .thenReturn(Optional
+ .of(TeeChallenge.builder().credentials(new EthereumCredentials("", "", false, "")).build()));
+
+ final TeeSessionGenerationException exception = assertThrows(
+ TeeSessionGenerationException.class,
+ () -> teeSecretsService.getPostComputeSignTokens(sessionRequest));
+
+ assertThat(exception.getError())
+ .isEqualTo(TeeSessionGenerationError.POST_COMPUTE_GET_SIGNATURE_TOKENS_FAILED_EMPTY_TEE_CREDENTIALS);
+ assertThat(exception.getMessage()).isEqualTo("Empty TEE challenge credentials - taskId: " + taskId);
+ }
+
+ @Test
+ void shouldGetPostComputeEncryptionTokensWithEncryption() {
+ TeeSessionRequest request = createSessionRequest(createTaskDescription(enclaveConfig));
+
+ String beneficiary = request.getTaskDescription().getBeneficiary();
+ when(web2SecretService.getDecryptedValue(
+ beneficiary,
+ ReservedSecretKeyName.IEXEC_RESULT_ENCRYPTION_PUBLIC_KEY))
+ .thenReturn(Optional.of(ENCRYPTION_PUBLIC_KEY));
+
+ final Map encryptionTokens = assertDoesNotThrow(
+ () -> teeSecretsService.getPostComputeEncryptionTokens(request));
+ assertThat(encryptionTokens)
+ .containsExactlyInAnyOrderEntriesOf(
+ Map.of(
+ "RESULT_ENCRYPTION", "yes",
+ "RESULT_ENCRYPTION_PUBLIC_KEY", ENCRYPTION_PUBLIC_KEY));
+ }
+
+ @Test
+ void shouldGetPostComputeEncryptionTokensWithoutEncryption() {
+ TeeSessionRequest request = createSessionRequest(createTaskDescription(enclaveConfig));
+ request.getTaskDescription().setResultEncryption(false);
+
+ final Map encryptionTokens = assertDoesNotThrow(
+ () -> teeSecretsService.getPostComputeEncryptionTokens(request));
+ assertThat(encryptionTokens)
+ .containsExactlyInAnyOrderEntriesOf(
+ Map.of(
+ "RESULT_ENCRYPTION", "no",
+ "RESULT_ENCRYPTION_PUBLIC_KEY", ""));
+ }
+
+ @Test
+ void shouldNotGetPostComputeEncryptionTokensSinceEmptyBeneficiaryKey() {
+ TeeSessionRequest request = createSessionRequest(createTaskDescription(enclaveConfig));
+
+ when(web2SecretService.getDecryptedValue(
+ request.getTaskDescription().getBeneficiary(),
+ ReservedSecretKeyName.IEXEC_RESULT_ENCRYPTION_PUBLIC_KEY))
+ .thenReturn(Optional.empty());
+
+ final TeeSessionGenerationException exception = assertThrows(
+ TeeSessionGenerationException.class,
+ () -> teeSecretsService.getPostComputeEncryptionTokens(request));
+ assertEquals(TeeSessionGenerationError.POST_COMPUTE_GET_ENCRYPTION_TOKENS_FAILED_EMPTY_BENEFICIARY_KEY,
+ exception.getError());
+ assertEquals("Empty beneficiary encryption key - taskId: taskId", exception.getMessage());
+ }
+
+ // endregion
+
+ // region utils
+ private void addApplicationDeveloperSecret(String appAddress) {
+ TeeTaskComputeSecret applicationDeveloperSecret = getApplicationDeveloperSecret(appAddress);
+ when(teeTaskComputeSecretService.getSecret(OnChainObjectType.APPLICATION, appAddress,
+ SecretOwnerRole.APPLICATION_DEVELOPER, "", APP_DEVELOPER_SECRET_INDEX))
+ .thenReturn(Optional.of(applicationDeveloperSecret));
+ }
+
+ private void addRequesterSecret(String requesterAddress, String secretKey, String secretValue) {
+ TeeTaskComputeSecret requesterSecret = getRequesterSecret(requesterAddress, secretKey, secretValue);
+ when(teeTaskComputeSecretService.getSecret(OnChainObjectType.APPLICATION, "", SecretOwnerRole.REQUESTER,
+ requesterAddress, secretKey))
+ .thenReturn(Optional.of(requesterSecret));
+ }
+ // endregion
+
+}
\ No newline at end of file
diff --git a/src/test/java/com/iexec/sms/tee/session/gramine/GramineSessionHandlerServiceTests.java b/src/test/java/com/iexec/sms/tee/session/gramine/GramineSessionHandlerServiceTests.java
new file mode 100644
index 00000000..6d9a8a9f
--- /dev/null
+++ b/src/test/java/com/iexec/sms/tee/session/gramine/GramineSessionHandlerServiceTests.java
@@ -0,0 +1,112 @@
+/*
+ * Copyright 2022 IEXEC BLOCKCHAIN TECH
+ *
+ * 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.
+ */
+
+package com.iexec.sms.tee.session.gramine;
+
+import com.iexec.common.task.TaskDescription;
+import com.iexec.sms.api.TeeSessionGenerationError;
+import com.iexec.sms.tee.session.TeeSessionLogConfiguration;
+import com.iexec.sms.tee.session.generic.TeeSessionGenerationException;
+import com.iexec.sms.tee.session.generic.TeeSessionRequest;
+import com.iexec.sms.tee.session.gramine.sps.GramineSession;
+import com.iexec.sms.tee.session.gramine.sps.SpsApiClient;
+import com.iexec.sms.tee.session.gramine.sps.SpsConfiguration;
+import feign.FeignException;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.mockito.InjectMocks;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.springframework.boot.test.system.CapturedOutput;
+import org.springframework.boot.test.system.OutputCaptureExtension;
+
+import static org.junit.Assert.assertThrows;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+@ExtendWith(OutputCaptureExtension.class)
+class GramineSessionHandlerServiceTests {
+
+ private static final String SPS_URL = "spsUrl";
+ @Mock
+ private GramineSessionMakerService sessionService;
+ @Mock
+ private SpsConfiguration spsConfiguration;
+ @Mock
+ private TeeSessionLogConfiguration teeSessionLogConfiguration;
+ @InjectMocks
+ private GramineSessionHandlerService sessionHandlerService;
+
+ @BeforeEach
+ void beforeEach() {
+ MockitoAnnotations.openMocks(this);
+ when(spsConfiguration.getEnclaveHost()).thenReturn(SPS_URL);
+ }
+
+ @Test
+ void shouldBuildAndPostSession(CapturedOutput output) throws TeeSessionGenerationException {
+ TeeSessionRequest request = mock(TeeSessionRequest.class);
+ TaskDescription taskDescription = mock(TaskDescription.class);
+ when(request.getTaskDescription()).thenReturn(taskDescription);
+ GramineSession spsSession = mock(GramineSession.class);
+ when(spsSession.toString()).thenReturn("sessionContent");
+ when(sessionService.generateSession(request)).thenReturn(spsSession);
+ when(teeSessionLogConfiguration.isDisplayDebugSessionEnabled()).thenReturn(true);
+ SpsApiClient spsClient = mock(SpsApiClient.class);
+ when(spsClient.postSession(spsSession)).thenReturn("sessionId");
+ when(spsConfiguration.getInstance()).thenReturn(spsClient);
+
+ assertEquals(SPS_URL, sessionHandlerService.buildAndPostSession(request));
+ // Testing output here since it reflects a business feature (ability to catch a
+ // session in debug mode)
+ assertTrue(output.getOut().contains("Session content [taskId:null]\nsessionContent\n"));
+ }
+
+ @Test
+ void shouldNotBuildAndPostSessionSinceBuildSessionFailed()
+ throws TeeSessionGenerationException {
+ TeeSessionRequest request = mock(TeeSessionRequest.class);
+ TeeSessionGenerationException teeSessionGenerationException = new TeeSessionGenerationException(
+ TeeSessionGenerationError.SECURE_SESSION_GENERATION_FAILED, "some error");
+ when(sessionService.generateSession(request)).thenThrow(teeSessionGenerationException);
+
+ assertThrows(teeSessionGenerationException.getClass(),
+ () -> sessionHandlerService.buildAndPostSession(request));
+ }
+
+ @Test
+ void shouldNotBuildAndPostSessionSincePostSessionFailed()
+ throws TeeSessionGenerationException {
+ TeeSessionRequest request = mock(TeeSessionRequest.class);
+ TaskDescription taskDescription = mock(TaskDescription.class);
+ when(request.getTaskDescription()).thenReturn(taskDescription);
+ GramineSession spsSession = mock(GramineSession.class);
+ when(spsSession.toString()).thenReturn("sessionContent");
+ when(sessionService.generateSession(request)).thenReturn(spsSession);
+ when(teeSessionLogConfiguration.isDisplayDebugSessionEnabled()).thenReturn(true);
+ SpsApiClient spsClient = mock(SpsApiClient.class);
+ when(spsConfiguration.getInstance()).thenReturn(spsClient);
+ FeignException apiClientException = mock(FeignException.class);
+ when(spsClient.postSession(spsSession)).thenThrow(apiClientException);
+
+ assertThrows(TeeSessionGenerationException.class,
+ () -> sessionHandlerService.buildAndPostSession(request));
+ }
+
+}
diff --git a/src/test/java/com/iexec/sms/tee/session/gramine/GramineSessionMakerServiceTests.java b/src/test/java/com/iexec/sms/tee/session/gramine/GramineSessionMakerServiceTests.java
new file mode 100644
index 00000000..9fedc989
--- /dev/null
+++ b/src/test/java/com/iexec/sms/tee/session/gramine/GramineSessionMakerServiceTests.java
@@ -0,0 +1,102 @@
+package com.iexec.sms.tee.session.gramine;
+
+import com.iexec.common.tee.TeeEnclaveConfiguration;
+import com.iexec.common.utils.FileHelper;
+import com.iexec.sms.api.config.GramineServicesProperties;
+import com.iexec.sms.api.config.TeeAppProperties;
+import com.iexec.sms.tee.session.base.SecretEnclaveBase;
+import com.iexec.sms.tee.session.base.SecretSessionBase;
+import com.iexec.sms.tee.session.base.SecretSessionBaseService;
+import com.iexec.sms.tee.session.generic.TeeSessionRequest;
+import com.iexec.sms.tee.session.gramine.sps.GramineSession;
+import lombok.extern.slf4j.Slf4j;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.mockito.InjectMocks;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.testcontainers.shaded.org.yaml.snakeyaml.Yaml;
+
+import java.util.Map;
+
+import static com.iexec.sms.tee.session.TeeSessionTestUtils.*;
+import static org.mockito.Mockito.when;
+
+@Slf4j
+class GramineSessionMakerServiceTests {
+ @Mock
+ private TeeAppProperties preComputeProperties;
+ @Mock
+ private TeeAppProperties postComputeProperties;
+ @Mock
+ private GramineServicesProperties teeServicesConfig;
+ @Mock
+ private SecretSessionBaseService teeSecretsService;
+ @InjectMocks
+ private GramineSessionMakerService gramineSessionService;
+
+ @BeforeEach
+ void beforeEach() {
+ MockitoAnnotations.openMocks(this);
+ when(teeServicesConfig.getPreComputeProperties()).thenReturn(preComputeProperties);
+ when(teeServicesConfig.getPostComputeProperties()).thenReturn(postComputeProperties);
+ }
+
+ // region getSessionYml
+ @Test
+ void shouldGetSessionJson() throws Exception {
+ TeeEnclaveConfiguration enclaveConfig = TeeEnclaveConfiguration.builder()
+ .fingerprint(APP_FINGERPRINT)
+ .build();
+ TeeSessionRequest request = createSessionRequest(createTaskDescription(enclaveConfig));
+
+ when(postComputeProperties.getFingerprint()).thenReturn(POST_COMPUTE_FINGERPRINT);
+ when(postComputeProperties.getEntrypoint()).thenReturn(POST_COMPUTE_ENTRYPOINT);
+
+ SecretEnclaveBase appCompute = SecretEnclaveBase.builder()
+ .name("app")
+ .mrenclave(APP_FINGERPRINT)
+ .environment(Map.ofEntries(
+ Map.entry("IEXEC_TASK_ID", "taskId"),
+ Map.entry("IEXEC_IN", "/iexec_in"),
+ Map.entry("IEXEC_OUT", "/iexec_out"),
+ Map.entry("IEXEC_DATASET_ADDRESS", "0xDatasetAddress"),
+ Map.entry("IEXEC_DATASET_FILENAME", "datasetName"),
+ Map.entry("IEXEC_BOT_SIZE", "1"),
+ Map.entry("IEXEC_BOT_FIRST_INDEX", "0"),
+ Map.entry("IEXEC_BOT_TASK_INDEX", "0"),
+ Map.entry("IEXEC_INPUT_FILES_FOLDER", "/iexec_in"),
+ Map.entry("IEXEC_INPUT_FILES_NUMBER", "2"),
+ Map.entry("IEXEC_INPUT_FILE_NAME_1", "file1"),
+ Map.entry("IEXEC_INPUT_FILE_NAME_2", "file2")))
+ .build();
+ SecretEnclaveBase postCompute = SecretEnclaveBase.builder()
+ .name("post-compute")
+ .mrenclave("mrEnclave3")
+ .environment(Map.ofEntries(
+ Map.entry("RESULT_TASK_ID", "taskId"),
+ Map.entry("RESULT_ENCRYPTION", "yes"),
+ Map.entry("RESULT_ENCRYPTION_PUBLIC_KEY", "encryptionPublicKey"),
+ Map.entry("RESULT_STORAGE_PROVIDER", "ipfs"),
+ Map.entry("RESULT_STORAGE_PROXY", "storageProxy"),
+ Map.entry("RESULT_STORAGE_TOKEN", "storageToken"),
+ Map.entry("RESULT_STORAGE_CALLBACK", "no"),
+ Map.entry("RESULT_SIGN_WORKER_ADDRESS", "workerAddress"),
+ Map.entry("RESULT_SIGN_TEE_CHALLENGE_PRIVATE_KEY", "teeChallengePrivateKey")))
+ .build();
+
+ when(teeSecretsService.getSecretsTokens(request))
+ .thenReturn(SecretSessionBase.builder()
+ .appCompute(appCompute)
+ .postCompute(postCompute)
+ .build());
+
+ GramineSession actualSpsSession = gramineSessionService.generateSession(request);
+ log.info(actualSpsSession.toString());
+ Map actualJsonMap = new Yaml().load(actualSpsSession.toString());
+ String expectedJsonString = FileHelper.readFile("src/test/resources/gramine-tee-session.json");
+ Map expectedYmlMap = new Yaml().load(expectedJsonString);
+ assertRecursively(expectedYmlMap, actualJsonMap);
+ }
+ // endregion
+}
\ No newline at end of file
diff --git a/src/test/java/com/iexec/sms/tee/session/palaemon/PalaemonSessionServiceTests.java b/src/test/java/com/iexec/sms/tee/session/palaemon/PalaemonSessionServiceTests.java
deleted file mode 100644
index fd8b8d82..00000000
--- a/src/test/java/com/iexec/sms/tee/session/palaemon/PalaemonSessionServiceTests.java
+++ /dev/null
@@ -1,859 +0,0 @@
-/*
- * Copyright 2020 IEXEC BLOCKCHAIN TECH
- *
- * 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.
- */
-
-package com.iexec.sms.tee.session.palaemon;
-
-import com.iexec.common.precompute.PreComputeUtils;
-import com.iexec.common.task.TaskDescription;
-import com.iexec.common.tee.TeeEnclaveConfiguration;
-import com.iexec.common.tee.TeeEnclaveConfigurationValidator;
-import com.iexec.common.utils.FileHelper;
-import com.iexec.common.utils.IexecEnvUtils;
-import com.iexec.common.worker.result.ResultUtils;
-import com.iexec.sms.api.TeeSessionGenerationError;
-import com.iexec.sms.secret.ReservedSecretKeyName;
-import com.iexec.sms.secret.Secret;
-import com.iexec.sms.secret.compute.OnChainObjectType;
-import com.iexec.sms.secret.compute.SecretOwnerRole;
-import com.iexec.sms.secret.compute.TeeTaskComputeSecret;
-import com.iexec.sms.secret.compute.TeeTaskComputeSecretService;
-import com.iexec.sms.secret.web2.Web2SecretsService;
-import com.iexec.sms.secret.web3.Web3Secret;
-import com.iexec.sms.secret.web3.Web3SecretService;
-import com.iexec.sms.tee.challenge.TeeChallenge;
-import com.iexec.sms.tee.challenge.TeeChallengeService;
-import com.iexec.sms.tee.session.TeeSessionGenerationException;
-import com.iexec.sms.tee.session.attestation.AttestationSecurityConfig;
-import com.iexec.sms.tee.workflow.TeeWorkflowConfiguration;
-import com.iexec.sms.utils.EthereumCredentials;
-import lombok.extern.slf4j.Slf4j;
-import org.apache.commons.lang3.ClassUtils;
-import org.junit.jupiter.api.Assertions;
-import org.junit.jupiter.api.BeforeEach;
-import org.junit.jupiter.api.Test;
-import org.junit.jupiter.params.ParameterizedTest;
-import org.junit.jupiter.params.provider.NullSource;
-import org.junit.jupiter.params.provider.ValueSource;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
-import org.springframework.test.util.ReflectionTestUtils;
-import org.yaml.snakeyaml.Yaml;
-
-import java.security.GeneralSecurityException;
-import java.util.*;
-
-import static com.iexec.common.chain.DealParams.DROPBOX_RESULT_STORAGE_PROVIDER;
-import static com.iexec.common.worker.result.ResultUtils.*;
-import static com.iexec.sms.Web3jUtils.createEthereumAddress;
-import static com.iexec.sms.api.TeeSessionGenerationError.*;
-import static com.iexec.sms.tee.session.palaemon.PalaemonSessionService.*;
-import static org.assertj.core.api.Assertions.assertThat;
-import static org.junit.jupiter.api.Assertions.*;
-import static org.mockito.Mockito.*;
-
-@Slf4j
-class PalaemonSessionServiceTests {
-
- private static final String TEMPLATE_SESSION_FILE = "src/main/resources/palaemonTemplate.vm";
- private static final String EXPECTED_SESSION_FILE = "src/test/resources/tee-session.yml";
-
- private static final String TASK_ID = "taskId";
- private static final String SESSION_ID = "sessionId";
- private static final String WORKER_ADDRESS = "workerAddress";
- private static final String ENCLAVE_CHALLENGE = "enclaveChallenge";
- // pre-compute
- private static final String PRE_COMPUTE_FINGERPRINT = "mrEnclave1";
- private static final String PRE_COMPUTE_ENTRYPOINT = "entrypoint1";
- private static final String DATASET_ADDRESS = "0xDatasetAddress";
- private static final String DATASET_NAME = "datasetName";
- private static final String DATASET_CHECKSUM = "datasetChecksum";
- private static final String DATASET_URL = "http://datasetUrl"; // 0x687474703a2f2f646174617365742d75726c in hex
- // keys with leading/trailing \n should not break the workflow
- private static final String DATASET_KEY = "\ndatasetKey\n";
- // app
- private static final String APP_DEVELOPER_SECRET_INDEX = "1";
- private static final String APP_DEVELOPER_SECRET_VALUE = "appDeveloperSecretValue";
- private static final String REQUESTER_SECRET_KEY_1 = "requesterSecretKey1";
- private static final String REQUESTER_SECRET_VALUE_1 = "requesterSecretValue1";
- private static final String REQUESTER_SECRET_KEY_2 = "requesterSecretKey2";
- private static final String REQUESTER_SECRET_VALUE_2 = "requesterSecretValue2";
- private static final String APP_URI = "appUri";
- private static final String APP_FINGERPRINT = "01ba4719c80b6fe911b091a7c05124b64eeece964e09c058ef8f9805daca546b";
- private static final String APP_ENTRYPOINT = "appEntrypoint";
- private static final TeeEnclaveConfiguration enclaveConfig =
- mock(TeeEnclaveConfiguration.class);
- private static final String ARGS = "args";
- private static final String IEXEC_APP_DEVELOPER_SECRET_1 = "IEXEC_APP_DEVELOPER_SECRET_1";
- // post-compute
- private static final String POST_COMPUTE_FINGERPRINT = "mrEnclave3";
- private static final String POST_COMPUTE_ENTRYPOINT = "entrypoint3";
- private static final String STORAGE_PROVIDER = "ipfs";
- private static final String STORAGE_PROXY = "storageProxy";
- private static final String STORAGE_TOKEN = "storageToken";
- private static final String ENCRYPTION_PUBLIC_KEY = "encryptionPublicKey";
- private static final String TEE_CHALLENGE_PRIVATE_KEY = "teeChallengePrivateKey";
- // input files
- private static final String INPUT_FILE_URL_1 = "http://host/file1";
- private static final String INPUT_FILE_NAME_1 = "file1";
- private static final String INPUT_FILE_URL_2 = "http://host/file2";
- private static final String INPUT_FILE_NAME_2 = "file2";
-
- private String appAddress;
- private String requesterAddress;
-
- @Mock
- private Web3SecretService web3SecretService;
- @Mock
- private Web2SecretsService web2SecretsService;
- @Mock
- private TeeChallengeService teeChallengeService;
- @Mock
- private TeeWorkflowConfiguration teeWorkflowConfig;
- @Mock
- private AttestationSecurityConfig attestationSecurityConfig;
- @Mock private TeeTaskComputeSecretService teeTaskComputeSecretService;
-
- private PalaemonSessionService palaemonSessionService;
-
- @BeforeEach
- void beforeEach() {
- MockitoAnnotations.openMocks(this);
- // spy is needed to mock some internal calls of the tested
- // class when relevant
- palaemonSessionService = spy(new PalaemonSessionService(
- web3SecretService,
- web2SecretsService,
- teeChallengeService,
- teeWorkflowConfig,
- attestationSecurityConfig,
- teeTaskComputeSecretService
- ));
- ReflectionTestUtils.setField(palaemonSessionService, "palaemonTemplateFilePath", TEMPLATE_SESSION_FILE);
- when(enclaveConfig.getFingerprint()).thenReturn(APP_FINGERPRINT);
- when(enclaveConfig.getEntrypoint()).thenReturn(APP_ENTRYPOINT);
- }
-
- //region getSessionYml
- @Test
- void shouldGetSessionYml() throws Exception {
- PalaemonSessionRequest request = createSessionRequest(createTaskDescription());
- doReturn(getPreComputeTokens()).when(palaemonSessionService)
- .getPreComputePalaemonTokens(request);
- doReturn(getAppTokens()).when(palaemonSessionService)
- .getAppPalaemonTokens(request);
- doReturn(getPostComputeTokens()).when(palaemonSessionService)
- .getPostComputePalaemonTokens(request);
- when(attestationSecurityConfig.getToleratedInsecureOptions())
- .thenReturn(List.of("hyperthreading", "debug-mode"));
- when(attestationSecurityConfig.getIgnoredSgxAdvisories())
- .thenReturn(List.of("INTEL-SA-00161", "INTEL-SA-00289"));
-
- String actualYmlString = palaemonSessionService.getSessionYml(request);
- Map actualYmlMap = new Yaml().load(actualYmlString);
- String expectedYamlString = FileHelper.readFile(EXPECTED_SESSION_FILE);
- Map expectedYmlMap = new Yaml().load(expectedYamlString);
- assertRecursively(expectedYmlMap, actualYmlMap);
- }
-
- @Test
- void shouldNotGetSessionYmlSinceRequestIsNull() {
- final TeeSessionGenerationException exception = assertThrows(
- TeeSessionGenerationException.class,
- () -> palaemonSessionService.getSessionYml(null)
- );
- assertEquals(NO_SESSION_REQUEST, exception.getError());
- assertEquals("Session request must not be null", exception.getMessage());
- }
-
- @Test
- void shouldNotGetSessionYmlSinceTaskDescriptionIsMissing() {
- PalaemonSessionRequest request = PalaemonSessionRequest.builder().build();
-
- final TeeSessionGenerationException exception = assertThrows(
- TeeSessionGenerationException.class,
- () -> palaemonSessionService.getSessionYml(request)
- );
- assertEquals(NO_TASK_DESCRIPTION, exception.getError());
- assertEquals("Task description must not be null", exception.getMessage());
- }
- //endregion
-
- //region getPreComputePalaemonTokens
- @Test
- void shouldGetPreComputePalaemonTokens() throws Exception {
- PalaemonSessionRequest request = createSessionRequest(createTaskDescription());
- when(teeWorkflowConfig.getPreComputeFingerprint())
- .thenReturn(PRE_COMPUTE_FINGERPRINT);
- when(teeWorkflowConfig.getPreComputeEntrypoint())
- .thenReturn(PRE_COMPUTE_ENTRYPOINT);
- Web3Secret secret = new Web3Secret(DATASET_ADDRESS, DATASET_KEY);
- when(web3SecretService.getSecret(DATASET_ADDRESS, true))
- .thenReturn(Optional.of(secret));
-
- Map tokens =
- palaemonSessionService.getPreComputePalaemonTokens(request);
- assertThat(tokens)
- .containsExactlyInAnyOrderEntriesOf(
- Map.of(
- PalaemonSessionService.PRE_COMPUTE_MRENCLAVE, PRE_COMPUTE_FINGERPRINT,
- PalaemonSessionService.PRE_COMPUTE_ENTRYPOINT, PRE_COMPUTE_ENTRYPOINT,
- PreComputeUtils.IEXEC_DATASET_KEY, secret.getTrimmedValue(),
- PreComputeUtils.IS_DATASET_REQUIRED, true,
- PalaemonSessionService.INPUT_FILE_URLS,
- Map.of(
- IexecEnvUtils.IEXEC_INPUT_FILE_URL_PREFIX + "1", INPUT_FILE_URL_1,
- IexecEnvUtils.IEXEC_INPUT_FILE_URL_PREFIX + "2", INPUT_FILE_URL_2)
-
- )
- );
- }
-
- @Test
- void shouldGetPreComputePalaemonTokensWithoutDataset() throws Exception {
- PalaemonSessionRequest request = PalaemonSessionRequest.builder()
- .sessionId(SESSION_ID)
- .workerAddress(WORKER_ADDRESS)
- .enclaveChallenge(ENCLAVE_CHALLENGE)
- .taskDescription(TaskDescription.builder()
- .chainTaskId(TASK_ID)
- .inputFiles(List.of(INPUT_FILE_URL_1, INPUT_FILE_URL_2))
- .build())
- .build();
- when(teeWorkflowConfig.getPreComputeFingerprint())
- .thenReturn(PRE_COMPUTE_FINGERPRINT);
- when(teeWorkflowConfig.getPreComputeEntrypoint())
- .thenReturn(PRE_COMPUTE_ENTRYPOINT);
-
- Map tokens =
- palaemonSessionService.getPreComputePalaemonTokens(request);
- assertThat(tokens).isNotEmpty()
- .containsExactlyInAnyOrderEntriesOf(
- Map.of(
- PalaemonSessionService.PRE_COMPUTE_MRENCLAVE, PRE_COMPUTE_FINGERPRINT,
- PalaemonSessionService.PRE_COMPUTE_ENTRYPOINT, PRE_COMPUTE_ENTRYPOINT,
- PreComputeUtils.IEXEC_DATASET_KEY, "",
- PreComputeUtils.IS_DATASET_REQUIRED, false,
- PalaemonSessionService.INPUT_FILE_URLS,
- Map.of(
- IexecEnvUtils.IEXEC_INPUT_FILE_URL_PREFIX + "1", INPUT_FILE_URL_1,
- IexecEnvUtils.IEXEC_INPUT_FILE_URL_PREFIX + "2", INPUT_FILE_URL_2)
-
- )
- );
- }
- //endregion
-
- //region getAppPalaemonTokens
- @Test
- void shouldGetAppPalaemonTokensForAdvancedTaskDescription() {
- PalaemonSessionRequest request = createSessionRequest(createTaskDescription());
- TeeEnclaveConfigurationValidator validator = mock(TeeEnclaveConfigurationValidator.class);
- when(enclaveConfig.getValidator()).thenReturn(validator);
- when(validator.isValid()).thenReturn(true);
- addApplicationDeveloperSecret();
- addRequesterSecret(REQUESTER_SECRET_KEY_1, REQUESTER_SECRET_VALUE_1);
- addRequesterSecret(REQUESTER_SECRET_KEY_2, REQUESTER_SECRET_VALUE_2);
-
- Map tokens = assertDoesNotThrow(() -> palaemonSessionService.getAppPalaemonTokens(request));
-
- verify(teeTaskComputeSecretService).getSecret(OnChainObjectType.APPLICATION, appAddress, SecretOwnerRole.APPLICATION_DEVELOPER, "", APP_DEVELOPER_SECRET_INDEX);
- verify(teeTaskComputeSecretService).getSecret(OnChainObjectType.APPLICATION, "", SecretOwnerRole.REQUESTER, requesterAddress, REQUESTER_SECRET_KEY_1);
- verify(teeTaskComputeSecretService).getSecret(OnChainObjectType.APPLICATION, "", SecretOwnerRole.REQUESTER, requesterAddress, REQUESTER_SECRET_KEY_2);
-
- assertThat(tokens)
- .containsExactlyInAnyOrderEntriesOf(
- Map.of(
- PalaemonSessionService.APP_MRENCLAVE, APP_FINGERPRINT,
- PalaemonSessionService.APP_ARGS, APP_ENTRYPOINT + " " + ARGS,
- PalaemonSessionService.INPUT_FILE_NAMES,
- Map.of(
- IexecEnvUtils.IEXEC_INPUT_FILE_NAME_PREFIX + "1", "file1",
- IexecEnvUtils.IEXEC_INPUT_FILE_NAME_PREFIX + "2", "file2"
- ),
- IEXEC_APP_DEVELOPER_SECRET_1, APP_DEVELOPER_SECRET_VALUE,
- REQUESTER_SECRETS,
- Map.of(
- IexecEnvUtils.IEXEC_REQUESTER_SECRET_PREFIX + "1", REQUESTER_SECRET_VALUE_1,
- IexecEnvUtils.IEXEC_REQUESTER_SECRET_PREFIX + "2", REQUESTER_SECRET_VALUE_2
- )
-
- )
- );
- }
-
- @Test
- void shouldGetPalaemonTokensWithEmptyAppComputeSecretWhenSecretsDoNotExist() {
- final String appAddress = createEthereumAddress();
- final String requesterAddress = createEthereumAddress();
- final TaskDescription taskDescription = TaskDescription.builder()
- .chainTaskId(TASK_ID)
- .appUri(APP_URI)
- .appAddress(appAddress)
- .appEnclaveConfiguration(enclaveConfig)
- .datasetAddress(DATASET_ADDRESS)
- .datasetUri(DATASET_URL)
- .datasetName(DATASET_NAME)
- .datasetChecksum(DATASET_CHECKSUM)
- .requester(requesterAddress)
- .cmd(ARGS)
- .inputFiles(List.of(INPUT_FILE_URL_1, INPUT_FILE_URL_2))
- .isResultEncryption(true)
- .resultStorageProvider(STORAGE_PROVIDER)
- .resultStorageProxy(STORAGE_PROXY)
- .botSize(1)
- .botFirstIndex(0)
- .botIndex(0)
- .build();
- PalaemonSessionRequest request = createSessionRequest(taskDescription);
- TeeEnclaveConfigurationValidator validator = mock(TeeEnclaveConfigurationValidator.class);
- when(enclaveConfig.getValidator()).thenReturn(validator);
- when(validator.isValid()).thenReturn(true);
- when(teeTaskComputeSecretService.getSecret(
- OnChainObjectType.APPLICATION,
- appAddress,
- SecretOwnerRole.APPLICATION_DEVELOPER,
- "",
- APP_DEVELOPER_SECRET_INDEX))
- .thenReturn(Optional.empty());
-
- Map tokens = assertDoesNotThrow(() -> palaemonSessionService.getAppPalaemonTokens(request));
- verify(teeTaskComputeSecretService).getSecret(eq(OnChainObjectType.APPLICATION), eq(appAddress), eq(SecretOwnerRole.APPLICATION_DEVELOPER), eq(""), any());
- verify(teeTaskComputeSecretService, never()).getSecret(eq(OnChainObjectType.APPLICATION), eq(""), eq(SecretOwnerRole.REQUESTER), any(), any());
-
- assertThat(tokens)
- .containsExactlyInAnyOrderEntriesOf(
- Map.of(
- PalaemonSessionService.APP_MRENCLAVE, APP_FINGERPRINT,
- PalaemonSessionService.APP_ARGS, APP_ENTRYPOINT + " " + ARGS,
- PalaemonSessionService.INPUT_FILE_NAMES,
- Map.of(
- IexecEnvUtils.IEXEC_INPUT_FILE_NAME_PREFIX + "1", "file1",
- IexecEnvUtils.IEXEC_INPUT_FILE_NAME_PREFIX + "2", "file2"
- ),
- IEXEC_APP_DEVELOPER_SECRET_1, "",
- REQUESTER_SECRETS, Collections.emptyMap()
- )
- );
- }
-
- @Test
- void shouldFailToGetAppPalaemonTokensSinceNoTaskDescription() {
- PalaemonSessionRequest request = PalaemonSessionRequest.builder()
- .build();
- TeeSessionGenerationException exception = assertThrows(TeeSessionGenerationException.class,
- () -> palaemonSessionService.getAppPalaemonTokens(request));
- Assertions.assertEquals(TeeSessionGenerationError.NO_TASK_DESCRIPTION, exception.getError());
- Assertions.assertEquals("Task description must no be null", exception.getMessage());
- }
-
- @Test
- void shouldFailToGetAppPalaemonTokensSinceNoEnclaveConfig() {
- PalaemonSessionRequest request = PalaemonSessionRequest.builder()
- .sessionId(SESSION_ID)
- .workerAddress(WORKER_ADDRESS)
- .enclaveChallenge(ENCLAVE_CHALLENGE)
- .taskDescription(TaskDescription.builder().build())
- .build();
- TeeSessionGenerationException exception = assertThrows(TeeSessionGenerationException.class,
- () -> palaemonSessionService.getAppPalaemonTokens(request));
- Assertions.assertEquals(TeeSessionGenerationError.APP_COMPUTE_NO_ENCLAVE_CONFIG, exception.getError());
- Assertions.assertEquals("Enclave configuration must no be null", exception.getMessage());
- }
-
- @Test
- void shouldFailToGetAppPalaemonTokensInvalidEnclaveConfig() {
- PalaemonSessionRequest request = createSessionRequest(createTaskDescription());
- TeeEnclaveConfigurationValidator validator = mock(TeeEnclaveConfigurationValidator.class);
- when(enclaveConfig.getValidator()).thenReturn(validator);
- String validationError = "validation error";
- when(validator.validate()).thenReturn(Collections.singletonList(validationError));
- TeeSessionGenerationException exception = assertThrows(TeeSessionGenerationException.class,
- () -> palaemonSessionService.getAppPalaemonTokens(request));
- Assertions.assertEquals(TeeSessionGenerationError.APP_COMPUTE_INVALID_ENCLAVE_CONFIG, exception.getError());
- }
-
- @Test
- void shouldAddMultipleRequesterSecrets() {
- PalaemonSessionRequest request = createSessionRequest(createTaskDescription());
- TeeEnclaveConfigurationValidator validator = mock(TeeEnclaveConfigurationValidator.class);
- when(enclaveConfig.getValidator()).thenReturn(validator);
- when(validator.isValid()).thenReturn(true);
- addRequesterSecret(REQUESTER_SECRET_KEY_1, REQUESTER_SECRET_VALUE_1);
- addRequesterSecret(REQUESTER_SECRET_KEY_2, REQUESTER_SECRET_VALUE_2);
- Map tokens = assertDoesNotThrow(() -> palaemonSessionService.getAppPalaemonTokens(request));
- verify(teeTaskComputeSecretService, times(2))
- .getSecret(eq(OnChainObjectType.APPLICATION), eq(""), eq(SecretOwnerRole.REQUESTER), any(), any());
- verify(teeTaskComputeSecretService).getSecret(OnChainObjectType.APPLICATION, "", SecretOwnerRole.REQUESTER, requesterAddress, REQUESTER_SECRET_KEY_1);
- verify(teeTaskComputeSecretService).getSecret(OnChainObjectType.APPLICATION, "", SecretOwnerRole.REQUESTER, requesterAddress, REQUESTER_SECRET_KEY_2);
- assertThat(tokens).containsEntry(REQUESTER_SECRETS,
- Map.of(
- IexecEnvUtils.IEXEC_REQUESTER_SECRET_PREFIX + "1", REQUESTER_SECRET_VALUE_1,
- IexecEnvUtils.IEXEC_REQUESTER_SECRET_PREFIX + "2", REQUESTER_SECRET_VALUE_2
- ));
- }
-
- @Test
- void shouldFilterRequesterSecretIndexLowerThanZero() {
- PalaemonSessionRequest request = createSessionRequest(createTaskDescription());
- request.getTaskDescription().setSecrets(Map.of("1", REQUESTER_SECRET_KEY_1, "-1", "out-of-bound-requester-secret"));
- TeeEnclaveConfigurationValidator validator = mock(TeeEnclaveConfigurationValidator.class);
- when(enclaveConfig.getValidator()).thenReturn(validator);
- when(validator.isValid()).thenReturn(true);
- addRequesterSecret(REQUESTER_SECRET_KEY_1, REQUESTER_SECRET_VALUE_1);
- Map tokens = assertDoesNotThrow(() -> palaemonSessionService.getAppPalaemonTokens(request));
- verify(teeTaskComputeSecretService).getSecret(eq(OnChainObjectType.APPLICATION), eq(""), eq(SecretOwnerRole.REQUESTER), any(), any());
- assertThat(tokens).containsEntry(REQUESTER_SECRETS,
- Map.of(IexecEnvUtils.IEXEC_REQUESTER_SECRET_PREFIX + "1", REQUESTER_SECRET_VALUE_1));
- }
- //endregion
-
- //region getPostComputePalaemonTokens
- @Test
- void shouldGetPostComputePalaemonTokens() throws Exception {
- PalaemonSessionRequest request = createSessionRequest(createTaskDescription());
- Secret publicKeySecret = new Secret("address", ENCRYPTION_PUBLIC_KEY);
- when(teeWorkflowConfig.getPostComputeFingerprint())
- .thenReturn(POST_COMPUTE_FINGERPRINT);
- when(teeWorkflowConfig.getPostComputeEntrypoint())
- .thenReturn(POST_COMPUTE_ENTRYPOINT);
- when(web2SecretsService.getSecret(
- request.getTaskDescription().getBeneficiary(),
- ReservedSecretKeyName.IEXEC_RESULT_ENCRYPTION_PUBLIC_KEY,
- true))
- .thenReturn(Optional.of(publicKeySecret));
- Secret storageSecret = new Secret("address", STORAGE_TOKEN);
- when(web2SecretsService.getSecret(
- requesterAddress,
- ReservedSecretKeyName.IEXEC_RESULT_IEXEC_IPFS_TOKEN,
- true))
- .thenReturn(Optional.of(storageSecret));
-
- TeeChallenge challenge = TeeChallenge.builder()
- .credentials(EthereumCredentials.generate())
- .build();
- when(teeChallengeService.getOrCreate(TASK_ID, true))
- .thenReturn(Optional.of(challenge));
-
-
- Map tokens =
- palaemonSessionService.getPostComputePalaemonTokens(request);
-
- final Map expectedTokens = new HashMap<>();
- expectedTokens.put(PalaemonSessionService.POST_COMPUTE_MRENCLAVE, POST_COMPUTE_FINGERPRINT);
- expectedTokens.put(PalaemonSessionService.POST_COMPUTE_ENTRYPOINT, POST_COMPUTE_ENTRYPOINT);
- // encryption tokens
- expectedTokens.put(ResultUtils.RESULT_ENCRYPTION, "yes");
- expectedTokens.put(ResultUtils.RESULT_ENCRYPTION_PUBLIC_KEY, ENCRYPTION_PUBLIC_KEY);
- // storage tokens
- expectedTokens.put(ResultUtils.RESULT_STORAGE_CALLBACK, "no");
- expectedTokens.put(ResultUtils.RESULT_STORAGE_PROVIDER, STORAGE_PROVIDER);
- expectedTokens.put(ResultUtils.RESULT_STORAGE_PROXY, STORAGE_PROXY);
- expectedTokens.put(ResultUtils.RESULT_STORAGE_TOKEN, STORAGE_TOKEN);
- // sign tokens
- expectedTokens.put(ResultUtils.RESULT_TASK_ID, TASK_ID);
- expectedTokens.put(ResultUtils.RESULT_SIGN_WORKER_ADDRESS, WORKER_ADDRESS);
- expectedTokens.put(ResultUtils.RESULT_SIGN_TEE_CHALLENGE_PRIVATE_KEY, challenge.getCredentials().getPrivateKey());
-
- assertThat(tokens).containsExactlyEntriesOf(expectedTokens);
- }
-
- @Test
- void shouldNotGetPostComputePalaemonTokensSinceTaskDescriptionMissing() {
- PalaemonSessionRequest request = PalaemonSessionRequest.builder().build();
-
- final TeeSessionGenerationException exception = assertThrows(
- TeeSessionGenerationException.class,
- () -> palaemonSessionService.getPostComputePalaemonTokens(request)
- );
- assertEquals(NO_TASK_DESCRIPTION, exception.getError());
- assertEquals("Task description must not be null", exception.getMessage());
- }
- //endregion
-
- //region getPostComputeEncryptionTokens
- @Test
- void shouldGetPostComputeStorageTokensWithCallback() {
- final PalaemonSessionRequest sessionRequest = createSessionRequest(createTaskDescription());
- sessionRequest.getTaskDescription().setCallback("callback");
-
- final Map tokens = assertDoesNotThrow(
- () -> palaemonSessionService.getPostComputeStorageTokens(sessionRequest));
-
- assertThat(tokens)
- .containsExactlyInAnyOrderEntriesOf(
- Map.of(
- RESULT_STORAGE_CALLBACK, "yes",
- RESULT_STORAGE_PROVIDER, EMPTY_YML_VALUE,
- RESULT_STORAGE_PROXY, EMPTY_YML_VALUE,
- RESULT_STORAGE_TOKEN, EMPTY_YML_VALUE
- )
- );
- }
-
- @Test
- void shouldGetPostComputeStorageTokensOnIpfs() {
- final PalaemonSessionRequest sessionRequest = createSessionRequest(createTaskDescription());
- final TaskDescription taskDescription = sessionRequest.getTaskDescription();
-
- final String secretValue = "Secret value";
- when(web2SecretsService
- .getSecret(taskDescription.getRequester(), ReservedSecretKeyName.IEXEC_RESULT_IEXEC_IPFS_TOKEN, true))
- .thenReturn(Optional.of(new Secret(null, secretValue)));
-
- final Map tokens = assertDoesNotThrow(
- () -> palaemonSessionService.getPostComputeStorageTokens(sessionRequest));
-
- assertThat(tokens)
- .containsExactlyInAnyOrderEntriesOf(
- Map.of(
- RESULT_STORAGE_CALLBACK, "no",
- RESULT_STORAGE_PROVIDER, STORAGE_PROVIDER,
- RESULT_STORAGE_PROXY, STORAGE_PROXY,
- RESULT_STORAGE_TOKEN, secretValue
- )
- );
- }
-
- @Test
- void shouldGetPostComputeStorageTokensOnDropbox() {
- final PalaemonSessionRequest sessionRequest = createSessionRequest(createTaskDescription());
- final TaskDescription taskDescription = sessionRequest.getTaskDescription();
- taskDescription.setResultStorageProvider(DROPBOX_RESULT_STORAGE_PROVIDER);
-
- final String secretValue = "Secret value";
- when(web2SecretsService
- .getSecret(taskDescription.getRequester(), ReservedSecretKeyName.IEXEC_RESULT_DROPBOX_TOKEN, true))
- .thenReturn(Optional.of(new Secret(null, secretValue)));
-
- final Map tokens = assertDoesNotThrow(
- () -> palaemonSessionService.getPostComputeStorageTokens(sessionRequest));
-
- assertThat(tokens)
- .containsExactlyInAnyOrderEntriesOf(
- Map.of(
- RESULT_STORAGE_CALLBACK, "no",
- RESULT_STORAGE_PROVIDER, DROPBOX_RESULT_STORAGE_PROVIDER,
- RESULT_STORAGE_PROXY, STORAGE_PROXY,
- RESULT_STORAGE_TOKEN, secretValue
- )
- );
- }
-
- @Test
- void shouldNotGetPostComputeStorageTokensSinceNoSecret() {
- final PalaemonSessionRequest sessionRequest = createSessionRequest(createTaskDescription());
- final TaskDescription taskDescription = sessionRequest.getTaskDescription();
-
- when(web2SecretsService
- .getSecret(taskDescription.getRequester(), ReservedSecretKeyName.IEXEC_RESULT_IEXEC_IPFS_TOKEN, true))
- .thenReturn(Optional.empty());
-
- final TeeSessionGenerationException exception = assertThrows(
- TeeSessionGenerationException.class,
- () -> palaemonSessionService.getPostComputeStorageTokens(sessionRequest));
-
- assertThat(exception.getError()).isEqualTo(POST_COMPUTE_GET_STORAGE_TOKENS_FAILED);
- assertThat(exception.getMessage()).isEqualTo("Empty requester storage token - taskId: " + taskDescription.getChainTaskId());
- }
-
- @Test
- void shouldGetPostComputeSignTokens() throws GeneralSecurityException {
- final PalaemonSessionRequest sessionRequest = createSessionRequest(createTaskDescription());
- final TaskDescription taskDescription = sessionRequest.getTaskDescription();
- final String taskId = taskDescription.getChainTaskId();
- final EthereumCredentials credentials = EthereumCredentials.generate();
-
- when(teeChallengeService.getOrCreate(taskId, true))
- .thenReturn(Optional.of(TeeChallenge.builder().credentials(credentials).build()));
-
- final Map tokens = assertDoesNotThrow(() -> palaemonSessionService.getPostComputeSignTokens(sessionRequest));
-
- assertThat(tokens)
- .containsExactlyInAnyOrderEntriesOf(
- Map.of(
- RESULT_TASK_ID, taskId,
- RESULT_SIGN_WORKER_ADDRESS, sessionRequest.getWorkerAddress(),
- RESULT_SIGN_TEE_CHALLENGE_PRIVATE_KEY, credentials.getPrivateKey()
- )
- );
- }
-
- @ParameterizedTest
- @NullSource
- @ValueSource(strings = {""})
- void shouldNotGetPostComputeSignTokensSinceNoWorkerAddress(String emptyWorkerAddress) {
- final PalaemonSessionRequest sessionRequest = createSessionRequest(createTaskDescription());
- final String taskId = sessionRequest.getTaskDescription().getChainTaskId();
- sessionRequest.setWorkerAddress(emptyWorkerAddress);
-
- final TeeSessionGenerationException exception = assertThrows(
- TeeSessionGenerationException.class,
- () -> palaemonSessionService.getPostComputeSignTokens(sessionRequest)
- );
-
- assertThat(exception.getError()).isEqualTo(POST_COMPUTE_GET_SIGNATURE_TOKENS_FAILED_EMPTY_WORKER_ADDRESS);
- assertThat(exception.getMessage()).isEqualTo("Empty worker address - taskId: " + taskId);
- }
-
- @ParameterizedTest
- @NullSource
- @ValueSource(strings = {""})
- void shouldNotGetPostComputeSignTokensSinceNoEnclaveChallenge(String emptyEnclaveChallenge) {
- final PalaemonSessionRequest sessionRequest = createSessionRequest(createTaskDescription());
- final String taskId = sessionRequest.getTaskDescription().getChainTaskId();
- sessionRequest.setEnclaveChallenge(emptyEnclaveChallenge);
-
- final TeeSessionGenerationException exception = assertThrows(
- TeeSessionGenerationException.class,
- () -> palaemonSessionService.getPostComputeSignTokens(sessionRequest)
- );
-
- assertThat(exception.getError()).isEqualTo(POST_COMPUTE_GET_SIGNATURE_TOKENS_FAILED_EMPTY_PUBLIC_ENCLAVE_CHALLENGE);
- assertThat(exception.getMessage()).isEqualTo("Empty public enclave challenge - taskId: " + taskId);
- }
-
- @Test
- void shouldNotGetPostComputeSignTokensSinceNoTeeChallenge() {
- final PalaemonSessionRequest sessionRequest = createSessionRequest(createTaskDescription());
- final String taskId = sessionRequest.getTaskDescription().getChainTaskId();
-
- when(teeChallengeService.getOrCreate(taskId, true))
- .thenReturn(Optional.empty());
-
- final TeeSessionGenerationException exception = assertThrows(
- TeeSessionGenerationException.class,
- () -> palaemonSessionService.getPostComputeSignTokens(sessionRequest)
- );
-
- assertThat(exception.getError()).isEqualTo(POST_COMPUTE_GET_SIGNATURE_TOKENS_FAILED_EMPTY_TEE_CHALLENGE);
- assertThat(exception.getMessage()).isEqualTo("Empty TEE challenge - taskId: " + taskId);
- }
-
- @Test
- void shouldNotGetPostComputeSignTokensSinceNoEnclaveCredentials() {
- final PalaemonSessionRequest sessionRequest = createSessionRequest(createTaskDescription());
- final String taskId = sessionRequest.getTaskDescription().getChainTaskId();
-
- when(teeChallengeService.getOrCreate(taskId, true))
- .thenReturn(Optional.of(TeeChallenge.builder().credentials(null).build()));
-
- final TeeSessionGenerationException exception = assertThrows(
- TeeSessionGenerationException.class,
- () -> palaemonSessionService.getPostComputeSignTokens(sessionRequest)
- );
-
- assertThat(exception.getError()).isEqualTo(POST_COMPUTE_GET_SIGNATURE_TOKENS_FAILED_EMPTY_TEE_CREDENTIALS);
- assertThat(exception.getMessage()).isEqualTo("Empty TEE challenge credentials - taskId: " + taskId);
- }
-
- @Test
- void shouldNotGetPostComputeSignTokensSinceNoEnclaveCredentialsPrivateKey() {
- final PalaemonSessionRequest sessionRequest = createSessionRequest(createTaskDescription());
- final String taskId = sessionRequest.getTaskDescription().getChainTaskId();
-
- when(teeChallengeService.getOrCreate(taskId, true))
- .thenReturn(Optional.of(TeeChallenge.builder().credentials(new EthereumCredentials("", "", false, "")).build()));
-
- final TeeSessionGenerationException exception = assertThrows(
- TeeSessionGenerationException.class,
- () -> palaemonSessionService.getPostComputeSignTokens(sessionRequest)
- );
-
- assertThat(exception.getError()).isEqualTo(POST_COMPUTE_GET_SIGNATURE_TOKENS_FAILED_EMPTY_TEE_CREDENTIALS);
- assertThat(exception.getMessage()).isEqualTo("Empty TEE challenge credentials - taskId: " + taskId);
- }
-
- // endregion
-
- @Test
- void shouldGetPostComputeEncryptionTokensWithEncryption() {
- PalaemonSessionRequest request = createSessionRequest(createTaskDescription());
-
- Secret publicKeySecret = new Secret("address", ENCRYPTION_PUBLIC_KEY);
- when(web2SecretsService.getSecret(
- request.getTaskDescription().getBeneficiary(),
- ReservedSecretKeyName.IEXEC_RESULT_ENCRYPTION_PUBLIC_KEY,
- true))
- .thenReturn(Optional.of(publicKeySecret));
-
- final Map encryptionTokens = assertDoesNotThrow(() -> palaemonSessionService.getPostComputeEncryptionTokens(request));
- assertThat(encryptionTokens)
- .containsExactlyInAnyOrderEntriesOf(
- Map.of(
- RESULT_ENCRYPTION, "yes",
- RESULT_ENCRYPTION_PUBLIC_KEY, ENCRYPTION_PUBLIC_KEY
- )
- );
- }
-
- @Test
- void shouldGetPostComputeEncryptionTokensWithoutEncryption() {
- PalaemonSessionRequest request = createSessionRequest(createTaskDescription());
- request.getTaskDescription().setResultEncryption(false);
-
- final Map encryptionTokens = assertDoesNotThrow(() -> palaemonSessionService.getPostComputeEncryptionTokens(request));
- assertThat(encryptionTokens)
- .containsExactlyInAnyOrderEntriesOf(
- Map.of(
- RESULT_ENCRYPTION, "no",
- RESULT_ENCRYPTION_PUBLIC_KEY, ""
- )
- );
- }
-
- @Test
- void shouldNotGetPostComputeEncryptionTokensSinceEmptyBeneficiaryKey() {
- PalaemonSessionRequest request = createSessionRequest(createTaskDescription());
-
- when(web2SecretsService.getSecret(
- request.getTaskDescription().getBeneficiary(),
- ReservedSecretKeyName.IEXEC_RESULT_ENCRYPTION_PUBLIC_KEY,
- true))
- .thenReturn(Optional.empty());
-
- final TeeSessionGenerationException exception = assertThrows(
- TeeSessionGenerationException.class,
- () -> palaemonSessionService.getPostComputeEncryptionTokens(request)
- );
- assertEquals(POST_COMPUTE_GET_ENCRYPTION_TOKENS_FAILED_EMPTY_BENEFICIARY_KEY, exception.getError());
- assertEquals("Empty beneficiary encryption key - taskId: taskId", exception.getMessage());
- }
-
- //endregion
-
- //region utils
- private void addApplicationDeveloperSecret() {
- TeeTaskComputeSecret applicationDeveloperSecret = TeeTaskComputeSecret.builder()
- .onChainObjectType(OnChainObjectType.APPLICATION)
- .onChainObjectAddress(appAddress)
- .secretOwnerRole(SecretOwnerRole.APPLICATION_DEVELOPER)
- .key(APP_DEVELOPER_SECRET_INDEX)
- .value(APP_DEVELOPER_SECRET_VALUE)
- .build();
- when(teeTaskComputeSecretService.getSecret(OnChainObjectType.APPLICATION, appAddress, SecretOwnerRole.APPLICATION_DEVELOPER, "", APP_DEVELOPER_SECRET_INDEX))
- .thenReturn(Optional.of(applicationDeveloperSecret));
- }
-
- private void addRequesterSecret(String secretKey, String secretValue) {
- TeeTaskComputeSecret requesterSecret = TeeTaskComputeSecret.builder()
- .onChainObjectType(OnChainObjectType.APPLICATION)
- .onChainObjectAddress("")
- .secretOwnerRole(SecretOwnerRole.REQUESTER)
- .fixedSecretOwner(requesterAddress)
- .key(secretKey)
- .value(secretValue)
- .build();
- when(teeTaskComputeSecretService.getSecret(OnChainObjectType.APPLICATION, "", SecretOwnerRole.REQUESTER, requesterAddress, secretKey))
- .thenReturn(Optional.of(requesterSecret));
- }
-
- private PalaemonSessionRequest createSessionRequest(TaskDescription taskDescription) {
- return PalaemonSessionRequest.builder()
- .sessionId(SESSION_ID)
- .workerAddress(WORKER_ADDRESS)
- .enclaveChallenge(ENCLAVE_CHALLENGE)
- .taskDescription(taskDescription)
- .build();
- }
-
- private TaskDescription createTaskDescription() {
- appAddress = createEthereumAddress();
- requesterAddress = createEthereumAddress();
- return TaskDescription.builder()
- .chainTaskId(TASK_ID)
- .appUri(APP_URI)
- .appAddress(appAddress)
- .appEnclaveConfiguration(enclaveConfig)
- .datasetAddress(DATASET_ADDRESS)
- .datasetUri(DATASET_URL)
- .datasetName(DATASET_NAME)
- .datasetChecksum(DATASET_CHECKSUM)
- .requester(requesterAddress)
- .cmd(ARGS)
- .inputFiles(List.of(INPUT_FILE_URL_1, INPUT_FILE_URL_2))
- .isResultEncryption(true)
- .resultStorageProvider(STORAGE_PROVIDER)
- .resultStorageProxy(STORAGE_PROXY)
- .secrets(Map.of("1", REQUESTER_SECRET_KEY_1, "2", REQUESTER_SECRET_KEY_2))
- .botSize(1)
- .botFirstIndex(0)
- .botIndex(0)
- .build();
- }
-
- private Map getPreComputeTokens() {
- return Map.of(
- PRE_COMPUTE_MRENCLAVE, PRE_COMPUTE_FINGERPRINT,
- PalaemonSessionService.PRE_COMPUTE_ENTRYPOINT, PRE_COMPUTE_ENTRYPOINT,
- PreComputeUtils.IS_DATASET_REQUIRED, true,
- PreComputeUtils.IEXEC_DATASET_KEY, DATASET_KEY.trim(),
- INPUT_FILE_URLS, Map.of(
- IexecEnvUtils.IEXEC_INPUT_FILE_URL_PREFIX + "1", INPUT_FILE_URL_1,
- IexecEnvUtils.IEXEC_INPUT_FILE_URL_PREFIX + "2", INPUT_FILE_URL_2));
- }
-
- private Map getAppTokens() {
- return Map.of(
- APP_MRENCLAVE, APP_FINGERPRINT,
- APP_ARGS, APP_ENTRYPOINT + " " + ARGS,
- INPUT_FILE_NAMES, Map.of(
- IexecEnvUtils.IEXEC_INPUT_FILE_NAME_PREFIX + "1", INPUT_FILE_NAME_1,
- IexecEnvUtils.IEXEC_INPUT_FILE_NAME_PREFIX + "2", INPUT_FILE_NAME_2));
- }
-
- private Map getPostComputeTokens() {
- Map map = new HashMap<>();
- map.put(POST_COMPUTE_MRENCLAVE, POST_COMPUTE_FINGERPRINT);
- map.put(PalaemonSessionService.POST_COMPUTE_ENTRYPOINT, POST_COMPUTE_ENTRYPOINT);
- map.put(RESULT_TASK_ID, TASK_ID);
- map.put(RESULT_ENCRYPTION, "yes");
- map.put(RESULT_ENCRYPTION_PUBLIC_KEY, ENCRYPTION_PUBLIC_KEY);
- map.put(RESULT_STORAGE_PROVIDER, STORAGE_PROVIDER);
- map.put(RESULT_STORAGE_PROXY, STORAGE_PROXY);
- map.put(RESULT_STORAGE_TOKEN, STORAGE_TOKEN);
- map.put(RESULT_STORAGE_CALLBACK, "no");
- map.put(RESULT_SIGN_WORKER_ADDRESS, WORKER_ADDRESS);
- map.put(RESULT_SIGN_TEE_CHALLENGE_PRIVATE_KEY, TEE_CHALLENGE_PRIVATE_KEY);
- return map;
- }
-
- private void assertRecursively(Object expected, Object actual) {
- if (expected == null ||
- expected instanceof String ||
- ClassUtils.isPrimitiveOrWrapper(expected.getClass())) {
- log.info("Comparing [actual:{}, expected:{}]", expected, actual);
- assertThat(expected).isEqualTo(actual);
- return;
- }
- if (expected instanceof List) {
- List> actualList = (List>) expected;
- List> expectedList = (List>) actual;
- for (int i = 0; i < actualList.size(); i++) {
- assertRecursively(actualList.get(i), expectedList.get(i));
- }
- return;
- }
- if (expected instanceof Map) {
- Map, ?> actualMap = (Map, ?>) expected;
- Map, ?> expectedMap = (Map, ?>) actual;
- actualMap.keySet().forEach((key) -> {
- log.info("Checking '{}'", key);
- assertRecursively(actualMap.get(key), expectedMap.get(key));
- });
- }
- }
- //endregion
-}
diff --git a/src/test/java/com/iexec/sms/tee/session/scone/SconeSessionHandlerServiceTests.java b/src/test/java/com/iexec/sms/tee/session/scone/SconeSessionHandlerServiceTests.java
new file mode 100644
index 00000000..a726ec46
--- /dev/null
+++ b/src/test/java/com/iexec/sms/tee/session/scone/SconeSessionHandlerServiceTests.java
@@ -0,0 +1,140 @@
+package com.iexec.sms.tee.session.scone;
+
+import com.iexec.common.task.TaskDescription;
+import com.iexec.sms.api.TeeSessionGenerationError;
+import com.iexec.sms.tee.session.TeeSessionLogConfiguration;
+import com.iexec.sms.tee.session.generic.TeeSessionGenerationException;
+import com.iexec.sms.tee.session.generic.TeeSessionRequest;
+import com.iexec.sms.tee.session.scone.cas.CasClient;
+import com.iexec.sms.tee.session.scone.cas.CasConfiguration;
+import com.iexec.sms.tee.session.scone.cas.SconeSession;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.mockito.InjectMocks;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.springframework.boot.test.system.CapturedOutput;
+import org.springframework.boot.test.system.OutputCaptureExtension;
+import org.springframework.http.ResponseEntity;
+
+import static org.junit.Assert.assertThrows;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+@ExtendWith(OutputCaptureExtension.class)
+class SconeSessionHandlerServiceTests {
+
+ private static final String CAS_URL = "casUrl";
+ @Mock
+ private SconeSessionMakerService sessionService;
+ @Mock
+ private CasClient apiClient;
+ @Mock
+ private TeeSessionLogConfiguration teeSessionLogConfiguration;
+ @Mock
+ private CasConfiguration casConfiguration;
+ @InjectMocks
+ private SconeSessionHandlerService sessionHandlerService;
+
+ @BeforeEach
+ void beforeEach() {
+ MockitoAnnotations.openMocks(this);
+ when(casConfiguration.getEnclaveHost()).thenReturn(CAS_URL);
+ }
+
+ @Test
+ void shouldBuildAndPostSessionWithLogs(CapturedOutput output)
+ throws TeeSessionGenerationException {
+ TeeSessionRequest request = mock(TeeSessionRequest.class);
+ TaskDescription taskDescription = mock(TaskDescription.class);
+ when(request.getTaskDescription()).thenReturn(taskDescription);
+ SconeSession casSession = mock(SconeSession.class);
+ when(casSession.toString()).thenReturn("sessionContent");
+ when(sessionService.generateSession(request)).thenReturn(casSession);
+ when(teeSessionLogConfiguration.isDisplayDebugSessionEnabled())
+ .thenReturn(true);
+ when(apiClient.postSession(casSession.toString()))
+ .thenReturn(ResponseEntity.created(null).body("sessionId"));
+
+ assertEquals(CAS_URL,
+ sessionHandlerService.buildAndPostSession(request));
+ // Testing output here since it reflects a business feature (ability to
+ // catch a
+ // session in debug mode)
+ assertTrue(output.getOut()
+ .contains("Session content [taskId:null]\nsessionContent\n"));
+ }
+
+ @Test
+ void shouldBuildAndPostSessionWithoutLogs(CapturedOutput output)
+ throws TeeSessionGenerationException {
+ TeeSessionRequest request = mock(TeeSessionRequest.class);
+ TaskDescription taskDescription = mock(TaskDescription.class);
+ when(request.getTaskDescription()).thenReturn(taskDescription);
+ SconeSession casSession = mock(SconeSession.class);
+ when(casSession.toString()).thenReturn("sessionContent");
+ when(sessionService.generateSession(request)).thenReturn(casSession);
+ when(teeSessionLogConfiguration.isDisplayDebugSessionEnabled())
+ .thenReturn(false);
+ when(apiClient.postSession(casSession.toString()))
+ .thenReturn(ResponseEntity.created(null).body("sessionId"));
+
+ assertEquals(CAS_URL,
+ sessionHandlerService.buildAndPostSession(request));
+ // Testing output here since it reflects a business feature (ability to
+ // catch a
+ // session in debug mode)
+ assertTrue(output.getOut().isEmpty());
+ }
+
+ @Test
+ void shouldNotBuildAndPostSessionSinceBuildSessionFailed()
+ throws TeeSessionGenerationException {
+ TeeSessionRequest request = mock(TeeSessionRequest.class);
+ TeeSessionGenerationException teeSessionGenerationException = new TeeSessionGenerationException(
+ TeeSessionGenerationError.SECURE_SESSION_GENERATION_FAILED,
+ "some error");
+ when(sessionService.generateSession(request))
+ .thenThrow(teeSessionGenerationException);
+
+ assertThrows(teeSessionGenerationException.getClass(),
+ () -> sessionHandlerService.buildAndPostSession(request));
+ }
+
+ @Test
+ void shouldNotBuildAndPostSessionSincePostSessionFailed()
+ throws TeeSessionGenerationException {
+ TeeSessionRequest request = mock(TeeSessionRequest.class);
+ TaskDescription taskDescription = mock(TaskDescription.class);
+ when(request.getTaskDescription()).thenReturn(taskDescription);
+ SconeSession casSession = mock(SconeSession.class);
+ when(sessionService.generateSession(request)).thenReturn(casSession);
+ when(teeSessionLogConfiguration.isDisplayDebugSessionEnabled())
+ .thenReturn(true);
+ when(apiClient.postSession(casSession.toString()))
+ .thenReturn(ResponseEntity.internalServerError().build());
+
+ assertThrows(TeeSessionGenerationException.class,
+ () -> sessionHandlerService.buildAndPostSession(request));
+ }
+
+ @Test
+ void shouldNotBuildAndPostSessionSinceNoResponse()
+ throws TeeSessionGenerationException {
+ TeeSessionRequest request = mock(TeeSessionRequest.class);
+ TaskDescription taskDescription = mock(TaskDescription.class);
+ when(request.getTaskDescription()).thenReturn(taskDescription);
+ SconeSession casSession = mock(SconeSession.class);
+ when(sessionService.generateSession(request)).thenReturn(casSession);
+ when(teeSessionLogConfiguration.isDisplayDebugSessionEnabled())
+ .thenReturn(true);
+ when(apiClient.postSession(casSession.toString()))
+ .thenReturn(null);
+
+ assertThrows(TeeSessionGenerationException.class,
+ () -> sessionHandlerService.buildAndPostSession(request));
+ }
+}
diff --git a/src/test/java/com/iexec/sms/tee/session/scone/SconeSessionMakerServiceTests.java b/src/test/java/com/iexec/sms/tee/session/scone/SconeSessionMakerServiceTests.java
new file mode 100644
index 00000000..1f7e77df
--- /dev/null
+++ b/src/test/java/com/iexec/sms/tee/session/scone/SconeSessionMakerServiceTests.java
@@ -0,0 +1,158 @@
+/*
+ * Copyright 2020 IEXEC BLOCKCHAIN TECH
+ *
+ * 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.
+ */
+
+package com.iexec.sms.tee.session.scone;
+
+import com.iexec.common.tee.TeeEnclaveConfiguration;
+import com.iexec.common.utils.FileHelper;
+import com.iexec.sms.api.config.SconeServicesProperties;
+import com.iexec.sms.api.config.TeeAppProperties;
+import com.iexec.sms.tee.session.base.SecretEnclaveBase;
+import com.iexec.sms.tee.session.base.SecretSessionBase;
+import com.iexec.sms.tee.session.base.SecretSessionBaseService;
+import com.iexec.sms.tee.session.generic.TeeSessionRequest;
+import com.iexec.sms.tee.session.scone.cas.SconeSession;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.mockito.InjectMocks;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.yaml.snakeyaml.Yaml;
+
+import java.util.List;
+import java.util.Map;
+
+import static com.iexec.sms.tee.session.TeeSessionTestUtils.*;
+import static org.mockito.Mockito.when;
+
+class SconeSessionMakerServiceTests {
+
+ private static final String PRE_COMPUTE_ENTRYPOINT = "entrypoint1";
+ private static final String APP_FINGERPRINT = "01ba4719c80b6fe911b091a7c05124b64eeece964e09c058ef8f9805daca546b";
+ private static final String APP_ENTRYPOINT = "appEntrypoint";
+ private static final String POST_COMPUTE_ENTRYPOINT = "entrypoint3";
+
+ @Mock
+ private TeeAppProperties preComputeProperties;
+ @Mock
+ private TeeAppProperties postComputeProperties;
+ @Mock
+ private SconeServicesProperties teeServicesConfig;
+ @Mock
+ private SecretSessionBaseService teeSecretsService;
+ @Mock
+ private SconeSessionSecurityConfig attestationSecurityConfig;
+
+ @InjectMocks
+ private SconeSessionMakerService palaemonSessionService;
+
+ @BeforeEach
+ void beforeEach() {
+ MockitoAnnotations.openMocks(this);
+ when(teeServicesConfig.getPreComputeProperties()).thenReturn(preComputeProperties);
+ when(teeServicesConfig.getPostComputeProperties()).thenReturn(postComputeProperties);
+ }
+
+ // region getSessionYml
+ @Test
+ void shouldGetSessionYml() throws Exception {
+ TeeEnclaveConfiguration enclaveConfig = TeeEnclaveConfiguration.builder()
+ .fingerprint(APP_FINGERPRINT)
+ .entrypoint(APP_ENTRYPOINT)
+ .build();
+ TeeSessionRequest request = createSessionRequest(createTaskDescription(enclaveConfig));
+
+ when(preComputeProperties.getEntrypoint()).thenReturn(PRE_COMPUTE_ENTRYPOINT);
+ when(postComputeProperties.getEntrypoint()).thenReturn(POST_COMPUTE_ENTRYPOINT);
+
+ SecretEnclaveBase preCompute = SecretEnclaveBase.builder()
+ .name("pre-compute")
+ .mrenclave("mrEnclave1")
+ .environment(Map.ofEntries(
+ // Keeping these test env vars for now
+ // (could be less but keeping same resource file for now)
+ Map.entry("IEXEC_TASK_ID", "taskId"),
+ Map.entry("IEXEC_PRE_COMPUTE_OUT", "/iexec_in"),
+ Map.entry("IS_DATASET_REQUIRED", "true"),
+ Map.entry("IEXEC_DATASET_KEY", "datasetKey"),
+ Map.entry("IEXEC_DATASET_URL", "http://datasetUrl"),
+ Map.entry("IEXEC_DATASET_FILENAME", "datasetName"),
+ Map.entry("IEXEC_DATASET_CHECKSUM", "datasetChecksum"),
+ Map.entry("IEXEC_INPUT_FILES_FOLDER", "/iexec_in"),
+ Map.entry("IEXEC_INPUT_FILES_NUMBER", "2"),
+ Map.entry("IEXEC_INPUT_FILE_URL_1", "http://host/file1"),
+ Map.entry("IEXEC_INPUT_FILE_URL_2", "http://host/file2")))
+ .build();
+ SecretEnclaveBase appCompute = SecretEnclaveBase.builder()
+ .name("app")
+ .mrenclave(APP_FINGERPRINT)
+ .environment(Map.ofEntries(
+ Map.entry("IEXEC_TASK_ID", "taskId"),
+ Map.entry("IEXEC_IN", "/iexec_in"),
+ Map.entry("IEXEC_OUT", "/iexec_out"),
+ Map.entry("IEXEC_DATASET_ADDRESS", "0xDatasetAddress"),
+ Map.entry("IEXEC_DATASET_FILENAME", "datasetName"),
+ Map.entry("IEXEC_BOT_SIZE", "1"),
+ Map.entry("IEXEC_BOT_FIRST_INDEX", "0"),
+ Map.entry("IEXEC_BOT_TASK_INDEX", "0"),
+ Map.entry("IEXEC_INPUT_FILES_FOLDER", "/iexec_in"),
+ Map.entry("IEXEC_INPUT_FILES_NUMBER", "2"),
+ Map.entry("IEXEC_INPUT_FILE_NAME_1", "file1"),
+ Map.entry("IEXEC_INPUT_FILE_NAME_2", "file2")))
+ .build();
+ SecretEnclaveBase postCompute = SecretEnclaveBase.builder()
+ .name("post-compute")
+ .mrenclave("mrEnclave3")
+ .environment(Map.ofEntries(
+ Map.entry("RESULT_TASK_ID", "taskId"),
+ Map.entry("RESULT_ENCRYPTION", "yes"),
+ Map.entry("RESULT_ENCRYPTION_PUBLIC_KEY", "encryptionPublicKey"),
+ Map.entry("RESULT_STORAGE_PROVIDER", "ipfs"),
+ Map.entry("RESULT_STORAGE_PROXY", "storageProxy"),
+ Map.entry("RESULT_STORAGE_TOKEN", "storageToken"),
+ Map.entry("RESULT_STORAGE_CALLBACK", "no"),
+ Map.entry("RESULT_SIGN_WORKER_ADDRESS", "workerAddress"),
+ Map.entry("RESULT_SIGN_TEE_CHALLENGE_PRIVATE_KEY", "teeChallengePrivateKey")))
+ .build();
+
+ when(teeSecretsService.getSecretsTokens(request))
+ .thenReturn(SecretSessionBase.builder()
+ .preCompute(preCompute)
+ .appCompute(appCompute)
+ .postCompute(postCompute)
+ .build());
+
+ when(attestationSecurityConfig.getToleratedInsecureOptions())
+ .thenReturn(List.of("hyperthreading", "debug-mode"));
+ when(attestationSecurityConfig.getIgnoredSgxAdvisories())
+ .thenReturn(List.of("INTEL-SA-00161", "INTEL-SA-00289"));
+
+ when(teeSecretsService.getSecretsTokens(request))
+ .thenReturn(SecretSessionBase.builder()
+ .preCompute(preCompute)
+ .appCompute(appCompute)
+ .postCompute(postCompute)
+ .build());
+
+ SconeSession actualCasSession = palaemonSessionService.generateSession(request);
+ System.out.println(actualCasSession.toString());
+ Map actualYmlMap = new Yaml().load(actualCasSession.toString());
+ String expectedYamlString = FileHelper.readFile("src/test/resources/palaemon-tee-session.yml");
+ Map expectedYmlMap = new Yaml().load(expectedYamlString);
+ assertRecursively(expectedYmlMap, actualYmlMap);
+ }
+ // endregion
+}
diff --git a/src/test/java/com/iexec/sms/tee/workflow/TeeWorkflowConfigurationTests.java b/src/test/java/com/iexec/sms/tee/workflow/TeeWorkflowConfigurationTests.java
deleted file mode 100644
index fd930878..00000000
--- a/src/test/java/com/iexec/sms/tee/workflow/TeeWorkflowConfigurationTests.java
+++ /dev/null
@@ -1,51 +0,0 @@
-package com.iexec.sms.tee.workflow;
-
-import com.iexec.common.tee.TeeWorkflowSharedConfiguration;
-import org.junit.jupiter.api.BeforeEach;
-import org.junit.jupiter.api.Test;
-import org.springframework.test.util.ReflectionTestUtils;
-import org.springframework.util.unit.DataSize;
-
-import static org.assertj.core.api.Assertions.assertThat;
-
-class TeeWorkflowConfigurationTests {
-
- private static final String LAS_IMAGE = "lasImage";
- private static final String PRE_COMPUTE_IMAGE = "preComputeImage";
- private static final String PRE_COMPUTE_FINGERPRINT = "preComputeFingerprint";
- private static final String PRE_COMPUTE_ENTRYPOINT = "preComputeEntrypoint";
- private static final int PRE_COMPUTE_HEAP_GB = 1;
- private static final String POST_COMPUTE_IMAGE = "postComputeImage";
- private static final String POST_COMPUTE_FINGERPRINT = "postComputeFingerprint";
- private static final String POST_COMPUTE_ENTRYPOINT = "postComputeEntrypoint";
- private static final int POST_COMPUTE_HEAP_GB = 2;
-
- TeeWorkflowConfiguration teeWorkflowConfiguration = new TeeWorkflowConfiguration(null);
-
- @BeforeEach
- void beforeEach() {
- ReflectionTestUtils.setField(teeWorkflowConfiguration, "lasImage", LAS_IMAGE);
- ReflectionTestUtils.setField(teeWorkflowConfiguration, "preComputeImage", PRE_COMPUTE_IMAGE);
- ReflectionTestUtils.setField(teeWorkflowConfiguration, "preComputeFingerprint", PRE_COMPUTE_FINGERPRINT);
- ReflectionTestUtils.setField(teeWorkflowConfiguration, "preComputeEntrypoint", PRE_COMPUTE_ENTRYPOINT);
- ReflectionTestUtils.setField(teeWorkflowConfiguration, "preComputeHeapSizeGb", PRE_COMPUTE_HEAP_GB);
- ReflectionTestUtils.setField(teeWorkflowConfiguration, "postComputeImage", POST_COMPUTE_IMAGE);
- ReflectionTestUtils.setField(teeWorkflowConfiguration, "postComputeFingerprint", POST_COMPUTE_FINGERPRINT);
- ReflectionTestUtils.setField(teeWorkflowConfiguration, "postComputeEntrypoint", POST_COMPUTE_ENTRYPOINT);
- ReflectionTestUtils.setField(teeWorkflowConfiguration, "postComputeHeapSizeGb", POST_COMPUTE_HEAP_GB);
- }
-
- @Test
- void shouldGetPublicConfiguration() {
- assertThat(teeWorkflowConfiguration.getSharedConfiguration())
- .isEqualTo(TeeWorkflowSharedConfiguration.builder()
- .lasImage(LAS_IMAGE)
- .preComputeImage(PRE_COMPUTE_IMAGE)
- .preComputeEntrypoint(PRE_COMPUTE_ENTRYPOINT)
- .preComputeHeapSize(DataSize.ofGigabytes(PRE_COMPUTE_HEAP_GB).toBytes())
- .postComputeImage(POST_COMPUTE_IMAGE)
- .postComputeEntrypoint(POST_COMPUTE_ENTRYPOINT)
- .postComputeHeapSize(DataSize.ofGigabytes(POST_COMPUTE_HEAP_GB).toBytes())
- .build());
- }
-}
diff --git a/src/test/resources/gramine-tee-session.json b/src/test/resources/gramine-tee-session.json
new file mode 100644
index 00000000..2e89e314
--- /dev/null
+++ b/src/test/resources/gramine-tee-session.json
@@ -0,0 +1,45 @@
+{
+ "session": "sessionId",
+ "enclaves": [
+ {
+ "name": "app",
+ "mrenclave": "01ba4719c80b6fe911b091a7c05124b64eeece964e09c058ef8f9805daca546b",
+ "command": "",
+ "environment": {
+ "IEXEC_TASK_ID": "taskId",
+ "IEXEC_IN": "/iexec_in",
+ "IEXEC_OUT": "/iexec_out",
+
+ "IEXEC_DATASET_ADDRESS": "0xDatasetAddress",
+ "IEXEC_DATASET_FILENAME": "datasetName",
+
+ "IEXEC_BOT_SIZE": "1",
+ "IEXEC_BOT_FIRST_INDEX": "0",
+ "IEXEC_BOT_TASK_INDEX": "0",
+
+ "IEXEC_INPUT_FILES_FOLDER": "/iexec_in",
+ "IEXEC_INPUT_FILES_NUMBER": "2",
+ "IEXEC_INPUT_FILE_NAME_1": "file1",
+ "IEXEC_INPUT_FILE_NAME_2": "file2"
+ },
+ "volumes": []
+ },
+ {
+ "name": "post-compute",
+ "mrenclave": "mrEnclave3",
+ "command": "",
+ "environment": {
+ "RESULT_TASK_ID": "taskId",
+ "RESULT_ENCRYPTION": "yes",
+ "RESULT_ENCRYPTION_PUBLIC_KEY": "encryptionPublicKey",
+ "RESULT_STORAGE_PROVIDER": "ipfs",
+ "RESULT_STORAGE_PROXY": "storageProxy",
+ "RESULT_STORAGE_TOKEN": "storageToken",
+ "RESULT_STORAGE_CALLBACK": "no",
+ "RESULT_SIGN_WORKER_ADDRESS": "workerAddress",
+ "RESULT_SIGN_TEE_CHALLENGE_PRIVATE_KEY": "teeChallengePrivateKey"
+ },
+ "volumes": []
+ }
+ ]
+}
\ No newline at end of file
diff --git a/src/test/resources/tee-session.yml b/src/test/resources/palaemon-tee-session.yml
similarity index 100%
rename from src/test/resources/tee-session.yml
rename to src/test/resources/palaemon-tee-session.yml