Skip to content

Commit a662681

Browse files
annahiletaAaronWDS
andauthored
Added HMAC validate code example (#124)
* added a new code example * refactoring the code * fixed links to examples * added a skip for authentication for Connect example * fix for fileSystem link * fix for linter * dependabot requested fixes --------- Co-authored-by: AaronWDS <[email protected]>
1 parent d8b6c4a commit a662681

File tree

15 files changed

+224
-71
lines changed

15 files changed

+224
-71
lines changed

Quick_ACG/pom.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -116,7 +116,7 @@
116116
<dependency>
117117
<groupId>org.springframework.boot</groupId>
118118
<artifactId>spring-boot-autoconfigure</artifactId>
119-
<version>2.4.4</version>
119+
<version>2.5.15</version>
120120
</dependency>
121121
</dependencies>
122122

pom.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -133,7 +133,7 @@
133133
<dependency>
134134
<groupId>org.json</groupId>
135135
<artifactId>json</artifactId>
136-
<version>20230618</version>
136+
<version>20231013</version>
137137
</dependency>
138138

139139
<!-- To compile JSP files -->

src/main/java/com/docusign/WebSecurityConfig.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
2828
try {
2929
authorize
3030
.antMatchers("/", "/error**", "/assets/**", "/ds/mustAuthenticate**",
31-
"/ds/authenticate**", "/ds/selectApi**")
31+
"/ds/authenticate**", "/ds/selectApi**", "/con001")
3232
.permitAll()
3333
.anyRequest().authenticated()
3434
.and()

src/main/java/com/docusign/common/ApiIndex.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,8 @@ public enum ApiIndex {
55
ROOMS("/pages/rooms/index", "/restapi", "/r001", "/r"),
66
CLICK("/pages/click/index", "/clickapi", "/c001", "/c"),
77
MONITOR("/pages/monitor/index", "", "/m001", "/m"),
8-
ADMIN("/pages/admin/index", "/management", "/a001", "/a");
9-
8+
ADMIN("/pages/admin/index", "/management", "/a001", "/a"),
9+
CONNECT("/pages/connect/index", "", "/con001", "/con");
1010
private final String indexPath;
1111

1212
private final String baseUrlSuffix;

src/main/java/com/docusign/common/WorkArguments.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -171,4 +171,8 @@ public class WorkArguments {
171171
private String targetAccountLastName;
172172

173173
private String targetAccountEmail;
174+
175+
private String hmacSecret;
176+
177+
private String jsonPayload;
174178
}
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
package com.docusign.controller.connect.examples;
2+
3+
import com.docusign.DSConfiguration;
4+
import com.docusign.core.controller.AbstractController;
5+
import org.springframework.stereotype.Controller;
6+
7+
/**
8+
* Abstract base class for all Connect controllers.
9+
*/
10+
@Controller
11+
public abstract class AbstractConnectController extends AbstractController {
12+
13+
private static final String EXAMPLE_PAGES_PATH = "pages/connect/examples/";
14+
15+
public AbstractConnectController(DSConfiguration config, String exampleName) {
16+
super(config, exampleName);
17+
}
18+
19+
protected String getExamplePagesPath() {
20+
return AbstractConnectController.EXAMPLE_PAGES_PATH;
21+
}
22+
}
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
package com.docusign.controller.connect.examples;
2+
3+
import com.docusign.DSConfiguration;
4+
import com.docusign.common.WorkArguments;
5+
import com.docusign.controller.connect.services.ValidateUsingHmacService;
6+
import com.docusign.core.model.DoneExample;
7+
import com.docusign.esign.client.ApiException;
8+
import org.springframework.beans.factory.annotation.Autowired;
9+
import org.springframework.stereotype.Controller;
10+
import org.springframework.ui.ModelMap;
11+
import org.springframework.web.bind.annotation.RequestMapping;
12+
13+
import javax.servlet.http.HttpServletResponse;
14+
import java.io.IOException;
15+
import java.security.InvalidKeyException;
16+
import java.security.NoSuchAlgorithmException;
17+
18+
@Controller
19+
@RequestMapping("/con001")
20+
public class EG001ControllerValidateUsingHMAC extends AbstractConnectController {
21+
22+
@Autowired
23+
public EG001ControllerValidateUsingHMAC(DSConfiguration config) {
24+
super(config, "con001");
25+
}
26+
27+
@Override
28+
protected Object doWork(WorkArguments args, ModelMap model,
29+
HttpServletResponse response) throws ApiException, IOException, NoSuchAlgorithmException, InvalidKeyException {
30+
String hashedOutput = new ValidateUsingHmacService().computeHash(
31+
args.getHmacSecret(),
32+
args.getJsonPayload().getBytes()
33+
);
34+
35+
DoneExample.createDefault(getTextForCodeExampleByApiType().ExampleName)
36+
.withMessage(getTextForCodeExampleByApiType().ResultsPageText
37+
.replaceFirst("\\{0}", hashedOutput)
38+
)
39+
.addToModel(model, config);
40+
return DONE_EXAMPLE_PAGE;
41+
}
42+
}
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
package com.docusign.controller.connect.services;
2+
3+
import javax.crypto.Mac;
4+
import javax.crypto.spec.SecretKeySpec;
5+
import java.io.UnsupportedEncodingException;
6+
import java.security.InvalidKeyException;
7+
import java.security.MessageDigest;
8+
import java.security.NoSuchAlgorithmException;
9+
import java.util.Base64;
10+
11+
public final class ValidateUsingHmacService {
12+
public static String computeHash(String secret, byte[] payload)
13+
throws InvalidKeyException, NoSuchAlgorithmException {
14+
String digest = "HmacSHA256";
15+
Mac mac = Mac.getInstance(digest);
16+
mac.init(new SecretKeySpec(secret.getBytes(), digest));
17+
String base64Hash = new String(Base64.getEncoder().encode(mac.doFinal(payload)));
18+
return base64Hash;
19+
}
20+
21+
public static boolean isValid(String secret, byte[] payload, String verify)
22+
throws InvalidKeyException, NoSuchAlgorithmException, UnsupportedEncodingException {
23+
String computedHash = computeHash(secret, payload);
24+
return MessageDigest.isEqual(computedHash.getBytes("UTF-8"),
25+
verify.getBytes("UTF-8"));
26+
}
27+
}

src/main/java/com/docusign/core/controller/AbstractController.java

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,7 @@ public AbstractController(DSConfiguration config, String exampleName) {
8585
}
8686

8787
public CodeExampleText getTextForCodeExampleByApiType() {
88-
ApiType apiType = ApiType.getAPITypeFromLink(this.exampleName);
88+
ApiType apiType = ApiType.giveTypeByName(this.exampleName);
8989
if (codeExampleText != null) {
9090
return codeExampleText;
9191
}
@@ -100,11 +100,11 @@ public CodeExampleText getTextForCodeExampleByApiType() {
100100

101101
@GetMapping
102102
public String get(WorkArguments args, ModelMap model) {
103-
if (isTokenExpired()) {
103+
if (ApiType.giveTypeByName(this.exampleName) != ApiType.CONNECT && isTokenExpired()) {
104104
return REDIRECT_AUTHENTICATION_PAGE;
105105
}
106106

107-
if (ApiType.getAPITypeFromLink(this.exampleName) == ApiType.MONITOR && session.getAuthTypeSelected() != AuthType.JWT) {
107+
if(ApiType.giveTypeByName(this.exampleName) == ApiType.MONITOR && session.getAuthTypeSelected() != AuthType.JWT){
108108
session.setMonitorExampleRedirect("/" + this.exampleName);
109109
return REDIRECT_AUTHENTICATION_PAGE;
110110
}
@@ -124,7 +124,7 @@ public String get(WorkArguments args, ModelMap model) {
124124

125125
@PostMapping
126126
public Object create(WorkArguments args, ModelMap model, HttpServletResponse response) {
127-
if (isTokenExpired()) {
127+
if (ApiType.giveTypeByName(this.exampleName) != ApiType.CONNECT && isTokenExpired()) {
128128
return REDIRECT_AUTHENTICATION_PAGE;
129129
}
130130

@@ -163,8 +163,10 @@ protected void onInitModel(WorkArguments args, ModelMap model) throws Exception
163163
}
164164

165165
public void setTheCurrentApiTypeAndBaseUrl() {
166-
config.setSelectedApiType(ApiType.getAPITypeFromLink(this.exampleName).toString());
167-
String basePath = this.config.getBaseUrl(config.getSelectedApiIndex(), session.getOauthAccount()) + config.getSelectedApiIndex().getBaseUrlSuffix();
166+
config.setSelectedApiType(ApiType.giveTypeByName(this.exampleName).toString());
167+
String basePath = session.getOauthAccount() != null ?
168+
this.config.getBaseUrl(config.getSelectedApiIndex(), session.getOauthAccount()) + config.getSelectedApiIndex().getBaseUrlSuffix()
169+
: null;
168170
session.setBasePath(basePath);
169171
}
170172

@@ -235,7 +237,7 @@ private boolean isTokenExpired() {
235237

236238
protected CodeExampleText getTextForCodeExample() {
237239
List<ManifestGroup> manifestGroups = config.getCodeExamplesText().APIs.stream()
238-
.filter(x -> ApiType.getAPITypeFromLink(this.exampleName).name().toLowerCase().contains(x.Name.toLowerCase()))
240+
.filter(x -> ApiType.giveTypeByName(this.exampleName).name().toLowerCase().contains(x.Name.toLowerCase()))
239241
.findFirst()
240242
.orElse(null).Groups;
241243

Lines changed: 21 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,36 +1,39 @@
11
package com.docusign.core.model;
22

3+
import java.util.Arrays;
4+
35
public enum ApiType {
4-
ESIGNATURE("eSignature API", new String[]{"signature"}),
5-
ROOMS("Rooms API", new String[]{"signature", "dtr.rooms.read", "dtr.rooms.write", "dtr.documents.read", "dtr.documents.write", "dtr.profile.read", "dtr.profile.write", "dtr.company.read", "dtr.company.write", "room_forms"}),
6-
CLICK("Click API", new String[] {"click.manage", "click.send"}),
7-
MONITOR("Monitor API", new String[] {"signature", "impersonation"}),
8-
ADMIN("Admin API", new String[] {"user_write", "signature", "impersonation", "group_read", "organization_read", "permission_read", "user_read", "account_read", "domain_read", "identity_provider_read", "user_data_redact", "asset_group_account_read", "asset_group_account_clone_write", "asset_group_account_clone_read"});
6+
ESIGNATURE("eSignature API", new String[]{"signature"}, "eg"),
7+
ROOMS("Rooms API", new String[]{"signature", "dtr.rooms.read", "dtr.rooms.write", "dtr.documents.read", "dtr.documents.write", "dtr.profile.read", "dtr.profile.write", "dtr.company.read", "dtr.company.write", "room_forms"}, "r"),
8+
CONNECT("Connect", new String[] {}, "con"),
9+
CLICK("Click API", new String[] {"click.manage", "click.send"}, "c"),
10+
MONITOR("Monitor API", new String[] {"signature", "impersonation"}, "m"),
11+
ADMIN("Admin API", new String[] {"user_write", "signature", "impersonation", "group_read", "organization_read", "permission_read", "user_read", "account_read", "domain_read", "identity_provider_read", "user_data_redact", "asset_group_account_read", "asset_group_account_clone_write", "asset_group_account_clone_read"}, "a");
912

1013
final String value;
1114

1215
final String[] scopes;
1316

14-
ApiType(String value, String[] scopes) {
17+
final String codeName;
18+
19+
ApiType(String value, String[] scopes, String codeName) {
1520
this.value = value;
1621
this.scopes = scopes;
22+
this.codeName = codeName;
1723
}
1824

19-
public static ApiType getAPITypeFromLink(String exampleName) {
20-
if (exampleName.contains("m")) {
21-
return ApiType.MONITOR;
22-
} else if (exampleName.contains("a")) {
23-
return ApiType.ADMIN;
24-
} else if (exampleName.contains("c")) {
25-
return ApiType.CLICK;
26-
} else if (exampleName.contains("r")) {
27-
return ApiType.ROOMS;
28-
}
29-
30-
return ApiType.ESIGNATURE;
25+
public static ApiType giveTypeByName(String exampleName) {
26+
return Arrays.stream(ApiType.values())
27+
.filter(x -> exampleName.contains(x.getCodeName()))
28+
.findFirst()
29+
.get();
3130
}
3231

3332
public String[] getScopes() {
3433
return scopes;
3534
}
35+
36+
public String getCodeName() {
37+
return codeName;
38+
}
3639
}

0 commit comments

Comments
 (0)