Skip to content

Commit

Permalink
Merge pull request #329 from getyoti/RELEASE-3.5.0
Browse files Browse the repository at this point in the history
Release 3.5.0
  • Loading branch information
irotech authored Jun 21, 2022
2 parents e611275 + be3a930 commit bc7eb6e
Show file tree
Hide file tree
Showing 23 changed files with 784 additions and 322 deletions.
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -101,13 +101,13 @@ If you are using Maven, you need to add the following dependency:
<dependency>
<groupId>com.yoti</groupId>
<artifactId>yoti-sdk-api</artifactId>
<version>3.4.0</version>
<version>3.5.0</version>
</dependency>
```

If you are using Gradle, here is the dependency to add:

`compile group: 'com.yoti', name: 'yoti-sdk-api', version: '3.4.0'`
`compile group: 'com.yoti', name: 'yoti-sdk-api', version: '3.5.0'`

You will find all classes packaged under `com.yoti.api`

Expand Down
2 changes: 1 addition & 1 deletion examples/doc-scan/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@
<dependency>
<groupId>com.yoti</groupId>
<artifactId>yoti-sdk-api</artifactId>
<version>3.4.0</version>
<version>3.5.0</version>
</dependency>
</dependencies>

Expand Down
2 changes: 1 addition & 1 deletion pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
<groupId>com.yoti</groupId>
<artifactId>yoti-sdk</artifactId>
<packaging>pom</packaging>
<version>3.4.0</version>
<version>3.5.0</version>
<name>Yoti SDK</name>
<description>Java SDK for simple integration with the Yoti platform</description>
<url>https://github.com/getyoti/yoti-java-sdk</url>
Expand Down
2 changes: 1 addition & 1 deletion yoti-sdk-api/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
<parent>
<groupId>com.yoti</groupId>
<artifactId>yoti-sdk-parent</artifactId>
<version>3.4.0</version>
<version>3.5.0</version>
<relativePath>../yoti-sdk-parent</relativePath>
</parent>

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,11 @@
import java.security.KeyPair;
import java.security.PrivateKey;
import java.util.Base64;
import java.util.Optional;

import com.yoti.api.client.ActivityFailureException;
import com.yoti.api.client.ProfileException;
import com.yoti.api.client.spi.remote.call.ProfileResponse;
import com.yoti.api.client.spi.remote.call.ProfileService;
import com.yoti.api.client.spi.remote.call.Receipt;
import com.yoti.api.client.spi.remote.util.DecryptionHelper;
Expand All @@ -31,29 +33,37 @@ public static ReceiptFetcher newInstance() {

public Receipt fetch(String encryptedConnectToken, KeyPair keyPair, String appId) throws ProfileException {
LOG.debug("Decrypting connect token: '{}'", encryptedConnectToken);

String connectToken = decryptConnectToken(encryptedConnectToken, keyPair.getPrivate());
LOG.debug("Connect token decrypted: '{}'", connectToken);
Receipt receipt = profileService.getReceipt(keyPair, appId, connectToken);
validateReceipt(receipt, connectToken);
return receipt;

ProfileResponse profile = profileService.getProfile(keyPair, appId, connectToken);
validateReceipt(profile, connectToken);
return profile.getReceipt();
}

private String decryptConnectToken(String encryptedConnectToken, PrivateKey privateKey) throws ProfileException {
try {
byte[] byteValue = Base64.getUrlDecoder().decode(encryptedConnectToken);
byte[] decryptedToken = DecryptionHelper.decryptAsymmetric(byteValue, privateKey);
return new String(decryptedToken, DEFAULT_CHARSET);
} catch (Exception e) {
throw new ProfileException("Cannot decrypt connect token", e);
} catch (Exception ex) {
throw new ProfileException("Cannot decrypt connect token", ex);
}
}

private void validateReceipt(Receipt receipt, String connectToken) throws ProfileException {
if (receipt == null) {
throw new ProfileException("No receipt for '" + connectToken + "' was found");
}
if (receipt.getOutcome() == null || !receipt.getOutcome().isSuccessful()) {
throw new ActivityFailureException("Sharing activity unsuccessful for " + receipt.getDisplayReceiptId());
private void validateReceipt(ProfileResponse profile, String connectToken) throws ProfileException {
Receipt receipt = Optional.ofNullable(profile)
.map(ProfileResponse::getReceipt)
.orElseThrow(() -> new ProfileException("No profile for '" + connectToken + "' was found"));

if (!receipt.hasOutcome(Receipt.Outcome.SUCCESS)) {
throw new ActivityFailureException(
String.format("Sharing activity unsuccessful for %s%s",
receipt.getDisplayReceiptId(),
Optional.ofNullable(profile.getError()).map(e -> String.format(" - %s", e)).orElse("")
)
);
}
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
package com.yoti.api.client.spi.remote.call;

import java.util.Objects;

import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;

@JsonDeserialize(builder = ErrorDetails.Builder.class)
public final class ErrorDetails {

private final String code;
private final String description;

private ErrorDetails(Builder builder) {
this.code = builder.code;
this.description = builder.description;
}

public static Builder builder() {
return new Builder();
}

public String getCode() {
return code;
}

public String getDescription() {
return description;
}

@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}

ErrorDetails that = (ErrorDetails) o;
return Objects.equals(code, that.code) && Objects.equals(description, that.description);
}

@Override
public int hashCode() {
return Objects.hash(code, description);
}

@Override
public String toString() {
return String.format("Error[code='%s', description='%s']", code, description);
}

public static final class Builder {

private String code;
private String description;

private Builder() { }

@JsonProperty(Property.ERROR_CODE)
public Builder code(String code) {
this.code = code;
return this;
}

@JsonProperty(Property.DESCRIPTION)
public Builder description(String description) {
this.description = description;
return this;
}

public ErrorDetails build() {
return new ErrorDetails(this);
}

}

private static final class Property {

private static final String ERROR_CODE = "error_code";
private static final String DESCRIPTION = "description";

private Property() { }

}

}
Original file line number Diff line number Diff line change
@@ -1,20 +1,26 @@
package com.yoti.api.client.spi.remote.call;

import java.util.Objects;

import com.fasterxml.jackson.annotation.JsonProperty;

class ProfileResponse {
public class ProfileResponse {

@JsonProperty("session_data")
@JsonProperty(Property.SESSION_DATA)
private String sessionData;

@JsonProperty("receipt")
@JsonProperty(Property.RECEIPT)
private Receipt receipt;

public ProfileResponse(){}
@JsonProperty(Property.ERROR_DETAILS)
private ErrorDetails error;

public ProfileResponse() { }

private ProfileResponse(String sessionData, Receipt receipt){
private ProfileResponse(String sessionData, Receipt receipt, ErrorDetails error) {
this.sessionData = sessionData;
this.receipt = receipt;
this.error = error;
}

public String getSessionData() {
Expand All @@ -33,28 +39,39 @@ public void setReceipt(Receipt receipt) {
this.receipt = receipt;
}

public ErrorDetails getError() {
return error;
}

public void setError(ErrorDetails error) {
this.error = error;
}

@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof ProfileResponse)) return false;
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}

ProfileResponse that = (ProfileResponse) o;

if (!sessionData.equals(that.sessionData)) return false;
return receipt.equals(that.receipt);

return Objects.equals(sessionData, that.sessionData)
&& Objects.equals(receipt, that.receipt)
&& Objects.equals(error, that.error);
}

@Override
public int hashCode() {
int result = sessionData.hashCode();
result = 31 * result + receipt.hashCode();
return result;
return Objects.hash(sessionData, receipt, error);
}

public static class ProfileResponseBuilder {

private String sessionData;
private Receipt receipt;
private ErrorDetails error;

public ProfileResponseBuilder setSessionData(String sessionData) {
this.sessionData = sessionData;
Expand All @@ -66,9 +83,36 @@ public ProfileResponseBuilder setReceipt(Receipt receipt) {
return this;
}

public ProfileResponseBuilder setError(ErrorDetails error) {
this.error = error;
return this;
}

/**
* ProfileResponse Builder build
*
* @return The response Profile
* @deprecated Use {@link #build()} instead.
*/
@Deprecated
public ProfileResponse createProfileResonse() {
return new ProfileResponse(sessionData, receipt);
return new ProfileResponse(sessionData, receipt, error);
}

public ProfileResponse build() {
return new ProfileResponse(sessionData, receipt, error);
}

}

private static final class Property {

private static final String SESSION_DATA = "session_data";
private static final String RECEIPT = "receipt";
private static final String ERROR_DETAILS = "error_details";

private Property() { }

}

}
Original file line number Diff line number Diff line change
Expand Up @@ -50,15 +50,18 @@ public static ProfileService newInstance() {
}

public Receipt getReceipt(KeyPair keyPair, String appId, String connectToken) throws ProfileException {
return getProfile(keyPair, appId, connectToken).getReceipt();
}

public ProfileResponse getProfile(KeyPair keyPair, String appId, String connectToken) throws ProfileException {
notNull(keyPair, "Key pair");
notNull(appId, "Application id");
notNull(connectToken, "Connect token");

String path = unsignedPathFactory.createProfilePath(appId, connectToken);

try {
String authKey = Base64.getEncoder()
.encodeToString(keyPair.getPublic().getEncoded());
String authKey = Base64.getEncoder().encodeToString(keyPair.getPublic().getEncoded());

SignedRequest signedRequest = createSignedRequest(keyPair, path, authKey);
return fetchReceipt(signedRequest);
Expand All @@ -67,21 +70,20 @@ public Receipt getReceipt(KeyPair keyPair, String appId, String connectToken) th
}
}

private Receipt fetchReceipt(SignedRequest signedRequest) throws IOException, ProfileException {
private ProfileResponse fetchReceipt(SignedRequest signedRequest) throws IOException, ProfileException {
LOG.info("Fetching profile from resource at '{}'", signedRequest.getUri());

try {
ProfileResponse response = signedRequest.execute(ProfileResponse.class);
return response.getReceipt();
} catch (ResourceException re) {
int responseCode = re.getResponseCode();
return signedRequest.execute(ProfileResponse.class);
} catch (ResourceException ex) {
int responseCode = ex.getResponseCode();
switch (responseCode) {
case HTTP_INTERNAL_ERROR:
throw new ProfileException("Error completing sharing: " + re.getResponseBody(), re);
throw new ProfileException("Error completing sharing: " + ex.getResponseBody(), ex);
case HTTP_NOT_FOUND:
throw new ProfileException("Profile not found. This can be due to a used or expired token. Details: "
+ re.getResponseBody(), re);
throw new ProfileException("Profile not found. This can be due to a used or expired token. Details: " + ex.getResponseBody(), ex);
default:
throw new ProfileException("Unexpected response: " + responseCode + " " + re.getResponseBody(), re);
throw new ProfileException("Unexpected response: " + responseCode + " " + ex.getResponseBody(), ex);
}
}
}
Expand Down
Loading

0 comments on commit bc7eb6e

Please sign in to comment.