diff --git a/.factorypath b/.factorypath
deleted file mode 100644
index b4510fae..00000000
--- a/.factorypath
+++ /dev/null
@@ -1,84 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/.github/workflows/maven.yml b/.github/workflows/maven.yml
new file mode 100644
index 00000000..763ecc10
--- /dev/null
+++ b/.github/workflows/maven.yml
@@ -0,0 +1,39 @@
+name: Java launcher
+
+on:
+ push:
+ branches: [ "master" ]
+ pull_request:
+ branches: [ "master" ]
+
+jobs:
+ build:
+
+ runs-on: ubuntu-latest
+
+ steps:
+ - uses: actions/checkout@v3
+ - name: Set up JDK 11
+ uses: actions/setup-java@v3
+ with:
+ java-version: '11'
+ distribution: 'temurin'
+ cache: maven
+ - name: Check code with linters
+ run: mvn -U checkstyle:check
+ - name: Download dependancies
+ env:
+ CLIENT_ID: ${{ secrets.CLIENT_ID }}
+ IMPERSONATED_USER_ID: ${{ secrets.IMPERSONATED_USER_ID }}
+ PRIVATE_KEY: ${{ secrets.PRIVATE_KEY }}
+ SIGNER_EMAIL: ${{ secrets.SIGNER_EMAIL }}
+ SIGNER_NAME: ${{ secrets.SIGNER_NAME }}
+ run: mvn install
+ - name: Build with Maven
+ env:
+ CLIENT_ID: ${{ secrets.CLIENT_ID }}
+ IMPERSONATED_USER_ID: ${{ secrets.IMPERSONATED_USER_ID }}
+ PRIVATE_KEY: ${{ secrets.PRIVATE_KEY }}
+ SIGNER_EMAIL: ${{ secrets.SIGNER_EMAIL }}
+ SIGNER_NAME: ${{ secrets.SIGNER_NAME }}
+ run: mvn -B package --file pom.xml
diff --git a/.gitignore b/.gitignore
index 23e56e0d..73ec87e5 100644
--- a/.gitignore
+++ b/.gitignore
@@ -38,3 +38,16 @@ hs_err_pid*
private.key
application.properties
+/target/
+src/main/resources/application.json
+src/main/resources/application.properties
+src/main/java/com/docusign/admin/
+
+# according to gitignore.io this shouldnt be shared
+.factorypath
+src/main/resources/ExportedUserData.csv
+app.config
+Quick_ACG/target/*
+JWT Console App/target/*
+Quick_ACG/src/main/resources/application.json
+
diff --git a/.metadata/.plugins/org.eclipse.epp.logging.aeri.ide/org.eclipse.epp.logging.aeri.ide.server/server-config.json b/.metadata/.plugins/org.eclipse.epp.logging.aeri.ide/org.eclipse.epp.logging.aeri.ide.server/server-config.json
index 867c06cd..879f1b33 100644
--- a/.metadata/.plugins/org.eclipse.epp.logging.aeri.ide/org.eclipse.epp.logging.aeri.ide.server/server-config.json
+++ b/.metadata/.plugins/org.eclipse.epp.logging.aeri.ide/org.eclipse.epp.logging.aeri.ide.server/server-config.json
@@ -33,7 +33,7 @@
"com.sun.*",
"java.*",
"javafx.*",
- "javax.*",
+ "jakarta.*",
"org.apache.*",
"org.eclipse.*",
"org.fordiac.*",
diff --git a/.vscode/launch.json b/.vscode/launch.json
new file mode 100644
index 00000000..6fcb152c
--- /dev/null
+++ b/.vscode/launch.json
@@ -0,0 +1,21 @@
+{
+ // Use IntelliSense to learn about possible attributes.
+ // Hover to view descriptions of existing attributes.
+ // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
+ "version": "0.2.0",
+ "configurations": [
+ {
+ "type": "java",
+ "name": "Launch App",
+ "request": "launch",
+ "mainClass": "com.docusign.App",
+ "projectName": "code-examples-java"
+ },
+ {
+ "type": "java",
+ "name": "Launch Java Program",
+ "request": "launch",
+ "mainClass": ""
+ }
+ ]
+}
\ No newline at end of file
diff --git a/.vscode/settings.json b/.vscode/settings.json
index e0f15db2..0be1c0c6 100644
--- a/.vscode/settings.json
+++ b/.vscode/settings.json
@@ -1,3 +1,4 @@
{
- "java.configuration.updateBuildConfiguration": "automatic"
+ "java.configuration.updateBuildConfiguration": "automatic",
+ "java.compile.nullAnalysis.mode": "automatic"
}
\ No newline at end of file
diff --git a/JWT Console App/app.config.example b/JWT Console App/app.config.example
new file mode 100644
index 00000000..25f4225d
--- /dev/null
+++ b/JWT Console App/app.config.example
@@ -0,0 +1,3 @@
+clientId={INTEGRATION_KEY_JWT}
+userId={IMPERSONATED_USER_ID}
+rsaKeyFile=private.key
\ No newline at end of file
diff --git a/JWT Console App/pom.xml b/JWT Console App/pom.xml
new file mode 100644
index 00000000..50394bea
--- /dev/null
+++ b/JWT Console App/pom.xml
@@ -0,0 +1,81 @@
+
+
+ 4.0.0
+
+ docusign.jwtconsoleapp
+ code-examples-java-JWT
+ 1.0-SNAPSHOT
+
+ DocuSign Java JWT Console App
+
+
+ UTF-8
+ 11
+ 11
+ 6.0.0
+
+
+
+
+
+ commons-cli
+ commons-cli
+ 1.7.0
+
+
+
+ org.apache.commons
+ commons-math3
+ 3.6.1
+
+
+
+ com.docusign
+ docusign-esign-java
+ ${esignature.version}
+ shaded
+
+
+
+ commons-io
+ commons-io
+ 2.16.1
+
+
+
+
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-compiler-plugin
+ 3.13.0
+
+ 11
+ 11
+
+
+
+
+ org.apache.maven.plugins
+ maven-jar-plugin
+ 3.4.2
+
+
+
+ true
+ lib/
+ com.docusign.jwtconsoleapp.JWTConsoleApp
+
+
+
+
+
+
+
+
diff --git a/JWT Console App/src/main/java/com/docusign/JWTConsoleApp.java b/JWT Console App/src/main/java/com/docusign/JWTConsoleApp.java
new file mode 100644
index 00000000..7f8f0f4b
--- /dev/null
+++ b/JWT Console App/src/main/java/com/docusign/JWTConsoleApp.java
@@ -0,0 +1,146 @@
+package com.docusign.jwtconsoleapp;
+
+import org.apache.commons.cli.CommandLine;
+import org.apache.commons.cli.CommandLineParser;
+import org.apache.commons.cli.DefaultParser;
+import org.apache.commons.cli.HelpFormatter;
+import org.apache.commons.cli.Options;
+import org.apache.commons.cli.ParseException;
+
+import com.docusign.esign.api.EnvelopesApi;
+import com.docusign.esign.client.ApiClient;
+import com.docusign.esign.client.auth.OAuth;
+import com.docusign.esign.client.auth.OAuth.OAuthToken;
+import com.docusign.esign.client.auth.OAuth.UserInfo;
+import com.docusign.esign.client.auth.OAuth.Account;
+import com.docusign.esign.client.ApiException;
+import com.docusign.esign.model.Document;
+import com.docusign.esign.model.CarbonCopy;
+import com.docusign.esign.model.EnvelopeDefinition;
+import com.docusign.esign.model.EnvelopeSummary;
+import com.docusign.esign.model.Recipients;
+import com.docusign.esign.model.Signer;
+import com.docusign.esign.model.Tabs;
+import com.docusign.esign.model.SignHere;
+
+import java.nio.file.Files;
+import java.nio.file.Paths;
+import java.util.*;
+import java.io.*;
+import java.awt.Desktop;
+import java.net.URI;
+
+/**
+ * Starter class for JWTConsoleApp application.
+ */
+
+public class JWTConsoleApp {
+
+ static String DevCenterPage = "https://developers.docusign.com/platform/auth/consent";
+ /**
+ * Application entry point.
+ *
+ * @param args application command line arguments
+ */
+ public static void main(String[] args) throws java.io.IOException {
+
+ Scanner scanner = new Scanner(System. in);
+ System.out.println ("Welcome to the JWT Code example! ");
+ System.out.print("Enter the signer's email address: ");
+ String signerEmail = scanner. nextLine();
+ System.out.print("Enter the signer's name: ");
+ String signerName = scanner. nextLine();
+ System.out.print("Enter the carbon copy's email address: ");
+ String ccEmail = scanner. nextLine();
+ System.out.print("Enter the carbon copy's name: ");
+ String ccName = scanner. nextLine();
+
+ // Get information fro app.config
+ Properties prop = new Properties();
+ String fileName = "app.config";
+ FileInputStream fis = new FileInputStream(fileName);
+ prop.load(fis);
+ try
+ {
+ // Get access token and accountId
+ ApiClient apiClient = new ApiClient("https://demo.docusign.net/restapi");
+ apiClient.setOAuthBasePath("account-d.docusign.com");
+ ArrayList scopes = new ArrayList();
+ scopes.add("signature");
+ scopes.add("impersonation");
+ byte[] privateKeyBytes = Files.readAllBytes(Paths.get(prop.getProperty("rsaKeyFile")));
+ OAuthToken oAuthToken = apiClient.requestJWTUserToken(
+ prop.getProperty("clientId"),
+ prop.getProperty("userId"),
+ scopes,
+ privateKeyBytes,
+ 3600);
+ String accessToken = oAuthToken.getAccessToken();
+ UserInfo userInfo = apiClient.getUserInfo(accessToken);
+ String accountId = userInfo.getAccounts().get(0).getAccountId();
+
+ // Create envelopeDefinition object
+ EnvelopeDefinition envelope = new EnvelopeDefinition();
+ envelope.setEmailSubject("Please sign this document set");
+ envelope.setStatus("sent");
+
+ // Create tabs object
+ SignHere signHere = new SignHere();
+ signHere.setDocumentId("1");
+ signHere.setPageNumber("1");
+ signHere.setXPosition("191");
+ signHere.setYPosition("148");
+ Tabs tabs = new Tabs();
+ tabs.setSignHereTabs(Arrays.asList(signHere));
+ // Set recipients
+ Signer signer = new Signer();
+ signer.setEmail(signerEmail);
+ signer.setName(signerName);
+ signer.recipientId("1");
+ signer.setTabs(tabs);
+ CarbonCopy cc = new CarbonCopy();
+ cc.setEmail(ccEmail);
+ cc.setName(ccName);
+ cc.recipientId("2");
+ Recipients recipients = new Recipients();
+ recipients.setSigners(Arrays.asList(signer));
+ recipients.setCarbonCopies(Arrays.asList(cc));
+ envelope.setRecipients(recipients);
+
+ // Add document
+ Document document = new Document();
+ document.setDocumentBase64("VGhhbmtzIGZvciByZXZpZXdpbmcgdGhpcyEKCldlJ2xsIG1vdmUgZm9yd2FyZCBhcyBzb29uIGFzIHdlIGhlYXIgYmFjay4=");
+ document.setName("doc1.txt");
+ document.setFileExtension("txt");
+ document.setDocumentId("1");
+ envelope.setDocuments(Arrays.asList(document));
+
+ // Send envelope
+ apiClient.addDefaultHeader("Authorization", "Bearer " + accessToken);
+ EnvelopesApi envelopesApi = new EnvelopesApi(apiClient);
+ EnvelopeSummary results = envelopesApi.createEnvelope(accountId, envelope);
+ System.out.println("Successfully sent envelope with envelopeId " + results.getEnvelopeId());
+ }
+ catch (ApiException exp)
+ {
+ if (exp.getMessage().contains("consent_required"))
+ {
+ try
+ {
+ System.out.println ("Consent required, please provide consent in browser window and then run this app again.");
+ Desktop.getDesktop().browse(new URI("https://account-d.docusign.com/oauth/auth?response_type=code&scope=impersonation%20signature&client_id=" + prop.getProperty("clientId") + "&redirect_uri=" + DevCenterPage));
+ }
+ catch (Exception e)
+ {
+ System.out.print ("Error!!! ");
+ System.out.print (e.getMessage());
+ }
+ }
+ }
+ catch (Exception e)
+ {
+ System.out.print ("Error!!! ");
+ System.out.print (e.getMessage());
+ }
+ }
+}
\ No newline at end of file
diff --git a/PAYMENTS_INSTALLATION.md b/PAYMENTS_INSTALLATION.md
index 689be36d..d575c2c2 100644
--- a/PAYMENTS_INSTALLATION.md
+++ b/PAYMENTS_INSTALLATION.md
@@ -1,24 +1,21 @@
-# Configuring a DocuSign payments gateway
+# Configure a payment gateway
-DocuSign offers built-in connections to multiple payment
-gateways. The payments example uses a demo account via the Stripe
-gateway service.
+DocuSign offers built-in connections to multiple payment gateways. The payments code example uses a developer account via the Stripe gateway service.
-## Creating the payments gateway account
-1. Login to demo.docusign.net and go to the Admin Tool.
-1. On the Integrations / Payments screen, click Stripe.
-1. For development, you can skip the Stripe account application
- by using the `Skip this account form` link:
+## Create a Stripe payment gateway
+
+1. Select the Stripe button on the [**Payments**](https://admindemo.docusign.com/authenticate?goTo=payments) page in your developer account.
+
+1. For development, you can skip the Stripe account application by using the **Skip this account form** link at the top of the page.
+
+ 
+
+ An enabled Stripe payment gateway is now associated with your DocuSign developer account and is shown under **Payment Gateway**.
+
+1. Save the **Gateway Account ID** GUID to the code example launcher configuration file.
- 
-1. Next, the Admin Tool will show that an enabled Stripe
- payment gateway account has been associated with your
- DocuSign demo sandbox account.
-1. Configure the example launcher with the gateway account id shown in the Admin tool.
## Additional documentation
-See the
-[Managing Payment Gateways](https://support.docusign.com/en/guides/managing-payment-gateways)
-documentation.
-
+* [Managing Payment Gateways](https://support.docusign.com/en/guides/managing-payment-gateways)
+* [How to send a request for payment](https://developers.docusign.com/docs/esign-rest-api/how-to/request-a-payment)
diff --git a/Quick_ACG/pom.xml b/Quick_ACG/pom.xml
new file mode 100644
index 00000000..c38ee390
--- /dev/null
+++ b/Quick_ACG/pom.xml
@@ -0,0 +1,297 @@
+
+
+ 4.0.0
+
+
+ org.springframework.boot
+ spring-boot-starter-parent
+ 2.7.18
+
+
+
+ com.docusign
+ code-examples-java-acg
+ 1.0-SNAPSHOT
+
+ Docusign Java Tutorial
+
+
+ 11
+ 2.5.6
+ 2.16.1
+ 6.4.0
+ 2.0.17
+
+
+
+
+ org.springframework.boot
+ spring-boot-starter-security
+
+
+ org.springframework.boot
+ spring-boot-starter-web
+
+
+
+ org.springframework.boot
+ spring-boot-starter-oauth2-client
+
+
+
+ org.apache.commons
+ commons-lang3
+
+
+ org.freemarker
+ freemarker
+
+
+
+
+ javax.servlet
+ jstl
+
+
+
+
+ org.apache.tomcat.embed
+ tomcat-embed-jasper
+ provided
+
+
+
+ commons-io
+ commons-io
+ ${commonsio.version}
+
+
+ com.docusign
+ docusign-esign-java
+ ${esignature.version}
+ shaded
+
+
+
+ org.projectlombok
+ lombok
+ RELEASE
+ compile
+
+
+ org.springframework.boot
+ spring-boot-autoconfigure
+ 2.7.18
+
+
+
+ org.slf4j
+ slf4j-api
+ ${slf4j.version}
+
+
+
+ org.slf4j
+ slf4j-simple
+ ${slf4j.version}
+
+
+
+
+
+
+ maven-resources-plugin
+ 3.2.0
+
+
+ copy-resource-one
+ generate-sources
+
+ copy-resources
+
+
+ ${basedir}
+
+
+ ${basedir}/src/main/resources
+ ../src/main/resources
+
+ application.json
+ World_Wide_Corp_lorem.pdf
+
+
+
+ ${basedir}/src/main/resources/META-INF
+ ../src/main/resources/META-INF
+
+ spring.factories
+
+
+
+ ${basedir}/src/main/resources/public/assets
+ ../src/main/resources/public/assets
+
+ favicon.png
+ css.css
+
+
+
+ ${basedir}/src/main/java/com/docusign
+ ../src/main/java/com/docusign
+
+ JsonPropertySourceLoader.java
+ EG001ControllerEmbeddedSigning.java
+
+
+
+ ${basedir}/src/main/java/com/docusign/common
+ ../src/main/java/com/docusign/common
+
+ WorkArguments.java
+
+
+
+ ${basedir}/src/main/java/com/docusign/controller/eSignature/examples
+ ../src/main/java/com/docusign/controller/eSignature/examples
+
+ EnvelopeHelpers.java
+ ExampleException.java
+
+
+
+ ${basedir}/src/main/java/com/docusign/controller/eSignature/services
+ ../src/main/java/com/docusign/controller/eSignature/services
+
+ EmbeddedSigningService.java
+
+
+
+ ${basedir}/src/main/java/com/docusign/core/model
+ ../src/main/java/com/docusign/core/model
+
+ Locals.java
+ User.java
+ EnvelopeDocumentInfo.java
+
+
+
+ ${basedir}/src/main/java/com/docusign/core/common
+ ../src/main/java/com/docusign/core/common
+
+ Utils.java
+ DiffField.java
+
+
+
+ ${basedir}/src/main/java/com/docusign/core/utils
+ ../src/main/java/com/docusign/core/utils
+
+ AccountsConverter.java
+
+
+
+ ${basedir}/src/main/java/com/docusign/core/model/manifestModels
+ ../src/main/java/com/docusign/core/model/manifestModels
+
+ AdditionalPage.java
+ APIs.java
+ CodeExampleText.java
+ CustomErrorTexts.java
+ Forms.java
+ HelpingTexts.java
+ Inputs.java
+ LinkToAPIMethods.java
+ LoginPage.java
+ ManifestGroup.java
+ ManifestStructure.java
+ RedirectsToOtherCodeExamples.java
+ SelectAPIPage.java
+ SupportingTexts.java
+
+
+
+ ${basedir}/src/main/java/com/docusign/core/security
+ ../src/main/java/com/docusign/core/security
+
+ SecurityHelpers.java
+ JWTOAuth2User.java
+ CustomAuthenticationFailureHandler.java
+
+
+
+ ${basedir}/src/main/java/com/docusign/core/security/acg
+ ../src/main/java/com/docusign/core/security/acg
+
+ ACGAuthenticationMethod.java
+
+
+
+ ${basedir}/src/main/webapp/WEB-INF/templates/views/pages/esignature/examples
+ ../src/main/webapp/WEB-INF/templates/views/pages/esignature/examples
+
+ quickEmbeddedSigning.jsp
+
+
+
+
+
+
+
+
+
+ org.springframework.boot
+ spring-boot-maven-plugin
+
+ true
+
+
+
+
+
+
+
+
+ org.bouncycastle
+ bcprov-jdk15on
+ 1.78
+
+
+
+ org.yaml
+ snakeyaml
+ 2.2
+
+
+
+ net.minidev
+ json-smart
+ 2.5.1
+
+
+
+ com.nimbusds
+ nimbus-jose-jwt
+ 9.40
+
+
+
+ org.springframework.security
+ spring-security-core
+ 5.7.12
+
+
+
+ ch.qos.logback
+ logback-core
+ 1.5.6
+
+
+
+ ch.qos.logback
+ logback-classic
+ 1.5.6
+ test
+
+
+
+
diff --git a/Quick_ACG/src/main/java/com/docusign/App.java b/Quick_ACG/src/main/java/com/docusign/App.java
new file mode 100644
index 00000000..18bc6638
--- /dev/null
+++ b/Quick_ACG/src/main/java/com/docusign/App.java
@@ -0,0 +1,58 @@
+package com.docusign;
+
+import java.io.IOException;
+import java.net.URI;
+import java.nio.file.FileSystemAlreadyExistsException;
+import java.nio.file.FileSystems;
+import java.util.Collections;
+
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+import org.springframework.boot.autoconfigure.jmx.JmxAutoConfiguration;
+
+@Slf4j
+@SpringBootApplication(exclude={JmxAutoConfiguration.class})
+public class App {
+
+ public static void main(String[] args) throws IOException {
+ SpringApplication.run(App.class, args);
+ openHomePage();
+ initFileSystem();
+ }
+
+ private static void openHomePage() throws IOException {
+ // Each operating system opens web browsers differently so we need to determine the user's
+ // OS and open a browser to the example homepage accordingly.
+ Runtime rt = Runtime.getRuntime();
+ if (OSDetector.isMac()){
+ String[] arguments = { "osascript", "-e", "open location \"" + "http://localhost:8080" + "\"" };
+ rt.exec(arguments);
+ }
+ else if (OSDetector.isWindows()){
+ rt.exec("rundll32 url.dll,FileProtocolHandler " + "http://localhost:8080");
+ }
+ else if (OSDetector.isLinux()){
+ String [] browsers = {"firefox", "safari", "chrome", "mozilla"};
+ StringBuffer cmd = new StringBuffer();
+ for (int i=0; i= HttpURLConnection.HTTP_BAD_REQUEST) {
+ throw new Exception(httpConnection.getResponseMessage());
+ }
+
+ BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(httpConnection.getInputStream()));
+ StringBuilder stringBuilder = new StringBuilder();
+ String temp;
+ while ((temp = bufferedReader.readLine()) != null) {
+ stringBuilder.append(temp);
+ }
+ bufferedReader.close();
+ httpConnection.disconnect();
+
+ return stringBuilder.toString();
+ }
+}
diff --git a/Quick_ACG/src/main/java/com/docusign/OSDetector.java b/Quick_ACG/src/main/java/com/docusign/OSDetector.java
new file mode 100644
index 00000000..358aba40
--- /dev/null
+++ b/Quick_ACG/src/main/java/com/docusign/OSDetector.java
@@ -0,0 +1,25 @@
+package com.docusign;
+
+/*
+ * This class can be used to determine the user's operating system so that the
+ * application can successfully open a web browser to run the examples.
+ */
+public class OSDetector
+{
+ private static final boolean isWindows;
+ private static final boolean isLinux;
+ private static final boolean isMac;
+
+ static
+ {
+ String os = System.getProperty("os.name").toLowerCase();
+ isWindows = os.contains("win");
+ isLinux = os.contains("nux") || os.contains("nix");
+ isMac = os.contains("mac");
+ }
+
+ public static boolean isWindows() { return isWindows; }
+ public static boolean isLinux() { return isLinux; }
+ public static boolean isMac() { return isMac; }
+
+}
\ No newline at end of file
diff --git a/Quick_ACG/src/main/java/com/docusign/WebSecurityConfig.java b/Quick_ACG/src/main/java/com/docusign/WebSecurityConfig.java
new file mode 100644
index 00000000..6770bd1d
--- /dev/null
+++ b/Quick_ACG/src/main/java/com/docusign/WebSecurityConfig.java
@@ -0,0 +1,52 @@
+package com.docusign;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.context.annotation.Bean;
+import org.springframework.security.config.Customizer;
+import org.springframework.security.config.annotation.web.builders.HttpSecurity;
+import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
+import org.springframework.security.web.SecurityFilterChain;
+import org.springframework.security.web.authentication.LoginUrlAuthenticationEntryPoint;
+import org.springframework.security.web.savedrequest.HttpSessionRequestCache;
+import org.springframework.security.web.savedrequest.RequestCache;
+import com.docusign.core.security.CustomAuthenticationFailureHandler;
+
+@EnableWebSecurity
+public class WebSecurityConfig {
+
+ @Autowired
+ public DSConfiguration dsConfiguration;
+
+ @Bean
+ public RequestCache requestCache() {
+ return new HttpSessionRequestCache();
+ }
+
+ @Bean
+ public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
+ http
+ .authorizeHttpRequests(authorize -> {
+ try {
+ authorize
+ .antMatchers("/", "/error**", "/assets/**", "/ds/mustAuthenticate**",
+ "/ds/authenticate**", "/ds/selectApi**", "/pkce")
+ .permitAll()
+ .anyRequest().authenticated()
+ .and()
+ .exceptionHandling()
+ .authenticationEntryPoint(
+ new LoginUrlAuthenticationEntryPoint("/ds/mustAuthenticate"));
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ })
+ .requestCache().requestCache(requestCache()).and()
+ .oauth2Login(login -> login.failureHandler(new CustomAuthenticationFailureHandler()))
+ .oauth2Client(Customizer.withDefaults())
+ .logout(logout -> logout
+ .logoutSuccessUrl("/"))
+ .csrf().disable();
+
+ return http.build();
+ }
+}
diff --git a/Quick_ACG/src/main/java/com/docusign/controller/eSignature/examples/AbstractEsignatureController.java b/Quick_ACG/src/main/java/com/docusign/controller/eSignature/examples/AbstractEsignatureController.java
new file mode 100644
index 00000000..d033672a
--- /dev/null
+++ b/Quick_ACG/src/main/java/com/docusign/controller/eSignature/examples/AbstractEsignatureController.java
@@ -0,0 +1,147 @@
+package com.docusign.controller.eSignature.examples;
+
+import com.docusign.DSConfiguration;
+import com.docusign.common.WorkArguments;
+import com.docusign.core.model.User;
+import com.docusign.core.model.manifestModels.APIs;
+import com.docusign.core.model.manifestModels.CodeExampleText;
+import com.docusign.core.model.Session;
+import com.docusign.core.model.manifestModels.ManifestGroup;
+import com.docusign.esign.client.ApiClient;
+import org.apache.commons.lang3.exception.ExceptionUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.http.HttpHeaders;
+import org.springframework.stereotype.Controller;
+import org.springframework.ui.ModelMap;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PostMapping;
+
+import javax.servlet.http.HttpServletResponse;
+import java.util.List;
+import java.util.Objects;
+
+/**
+ * Abstract base class for all eSignature controllers.
+ */
+@Controller
+public abstract class AbstractEsignatureController {
+
+ protected static final String BEARER_AUTHENTICATION = "Bearer ";
+
+ @Autowired
+ protected Session session;
+
+ protected final String exampleName;
+ protected String title;
+ private final String pagePath;
+ protected final DSConfiguration config;
+ protected CodeExampleText codeExampleText;
+ private static final String EXAMPLE_PAGES_PATH = "pages/esignature/examples/";
+ private static final String EXAMPLE_TEXT = "example";
+ private static final String LAUNCHER_TEXTS = "launcherTexts";
+ protected final User user;
+
+ public AbstractEsignatureController(DSConfiguration config, String exampleName, Session session, User user) {
+ this.config = config;
+ this.session = session;
+ this.user = user;
+ this.exampleName = exampleName;
+ this.pagePath = this.getExamplePagesPath() + exampleName;
+ }
+
+ protected String getExamplePagesPath() {
+ return AbstractEsignatureController.EXAMPLE_PAGES_PATH;
+ }
+
+ /**
+ * Creates new instance of the eSignature API client.
+ * @param basePath URL to eSignature REST API
+ * @param userAccessToken user's access token
+ * @return an instance of the {@link ApiClient}
+ */
+ protected static ApiClient createApiClient(String basePath, String userAccessToken) {
+ ApiClient apiClient = new ApiClient(basePath);
+ apiClient.addDefaultHeader(HttpHeaders.AUTHORIZATION, BEARER_AUTHENTICATION + userAccessToken);
+ return apiClient;
+ }
+
+ @GetMapping
+ public String get(WorkArguments args, ModelMap model) {
+ try {
+ onInitModel(args, model);
+ return pagePath;
+ } catch (Exception exception) {
+ return handleException(exception, model);
+ }
+ }
+
+ @PostMapping
+ public Object create(WorkArguments args, ModelMap model, HttpServletResponse response) {
+ try {
+ return doWork(args, model, response);
+ } catch (Exception exception) {
+ return handleException(exception, model);
+ }
+ }
+
+ /**
+ * This method is called from the GET request and it should initialize a
+ * model. Override this method if it is necessary to add example specific
+ * attributes into the model.
+ * @param args
+ * @param model the model data
+ */
+ protected void onInitModel(WorkArguments args, ModelMap model) throws Exception {
+ this.codeExampleText = GetExampleText();
+ this.title = this.codeExampleText.ExampleName;
+
+ Class> clazz = Objects.requireNonNullElse(getClass().getEnclosingClass(), getClass());
+ String srcPath = String.join("", config.getExampleUrl(), clazz.getName().replace('.', '/'), ".java");
+ model.addAttribute("csrfToken", "");
+ model.addAttribute("title", title);
+ model.addAttribute("sourceFile", clazz.getSimpleName() + ".java");
+ model.addAttribute("sourceUrl", srcPath);
+ model.addAttribute("documentation", config.getDocumentationPath() + exampleName);
+ model.addAttribute(EXAMPLE_TEXT, this.codeExampleText);
+ model.addAttribute(LAUNCHER_TEXTS, config.getCodeExamplesText().SupportingTexts);
+ }
+
+ /**
+ * This method is called from the POST request. Example pages must override
+ * it. Overridden method should return an Object means URL to redirect.
+ * @param args the work arguments got from the current page
+ * @param model page model holder
+ * @param response for HTTP-specific functionality in sending a response
+ * @return {@link Object}. Possible types and values: null,
+ * {@link String} representation of the URL or Spring RedirectView object,
+ * (see {@link org.springframework.web.servlet.view.RedirectView RedirectView})
+ * @throws Exception if calling API has failed or if I/O operation has failed
+ */
+ protected abstract Object doWork(WorkArguments args, ModelMap model,
+ HttpServletResponse response) throws Exception;
+
+ private String handleException(Exception exception, ModelMap model) {
+ return ExceptionUtils.getStackTrace(exception);
+ }
+
+ protected CodeExampleText GetExampleText() {
+ List eSignatureManifestGroups = config.getCodeExamplesText().APIs.stream()
+ .filter(x -> "esignature".contains(((APIs) x).Name.toLowerCase()))
+ .findFirst()
+ .orElse(null).Groups;
+ var exampleNumberToSearch = 1;
+
+ for (ManifestGroup group : eSignatureManifestGroups) {
+ CodeExampleText codeExampleText = group.Examples.stream()
+ .filter(x -> ((CodeExampleText) x).ExampleNumber == exampleNumberToSearch)
+ .findFirst()
+ .orElse(null);
+
+ if (codeExampleText != null)
+ {
+ return codeExampleText;
+ }
+ }
+ return null;
+ }
+}
diff --git a/Quick_ACG/src/main/java/com/docusign/core/controller/IndexController.java b/Quick_ACG/src/main/java/com/docusign/core/controller/IndexController.java
new file mode 100644
index 00000000..33528727
--- /dev/null
+++ b/Quick_ACG/src/main/java/com/docusign/core/controller/IndexController.java
@@ -0,0 +1,213 @@
+package com.docusign.core.controller;
+
+import com.docusign.DSConfiguration;
+import com.docusign.core.model.Locals;
+import com.docusign.core.model.Session;
+import com.docusign.core.model.User;
+import java.io.IOException;
+import java.util.*;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import com.docusign.core.security.acg.ACGAuthenticationMethod;
+
+import com.docusign.core.utils.AccountsConverter;
+import org.apache.commons.lang3.StringUtils;
+import org.json.JSONObject;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.context.annotation.Scope;
+import org.springframework.security.core.context.SecurityContextHolder;
+import org.springframework.security.oauth2.client.OAuth2AuthorizedClient;
+import org.springframework.security.oauth2.client.OAuth2AuthorizedClientService;
+import org.springframework.security.oauth2.client.authentication.OAuth2AuthenticationToken;
+import org.springframework.security.oauth2.core.user.OAuth2User;
+import org.springframework.stereotype.Controller;
+import org.springframework.ui.ModelMap;
+import org.springframework.web.bind.annotation.*;
+import org.springframework.web.context.WebApplicationContext;
+import org.springframework.web.servlet.ModelAndView;
+import org.springframework.web.servlet.view.RedirectView;
+import com.docusign.esign.client.auth.OAuth;
+import java.util.stream.Collectors;
+import org.springframework.security.core.Authentication;
+
+@Controller
+@ControllerAdvice
+@Scope(WebApplicationContext.SCOPE_SESSION)
+public class IndexController {
+ private static final List ESIGNATURE_SCOPES = Arrays.asList("signature");
+ private static final String ATTR_STATE = "state";
+ private static final String ATTR_EVENT = "event";
+
+ private static final String ERROR_ACCOUNT_NOT_FOUND = "Could not find account information for the user";
+ private final DSConfiguration config;
+ private final Session session;
+ private final User user;
+ private Optional account;
+
+ @Autowired
+ private OAuth2AuthorizedClientService authorizedClientService;
+
+ public IndexController(DSConfiguration config, Session session, User user, Optional account) {
+ this.config = config;
+ this.session = session;
+ this.user = user;
+ this.account = account;
+ }
+
+ @GetMapping(path = "/")
+ public String index(ModelMap model, HttpServletResponse response) throws IOException {
+ String site = "/eg001";
+ response.setStatus(response.SC_MOVED_TEMPORARILY);
+ response.setHeader("Location", site);
+ return null;
+ }
+
+ @GetMapping(path = "/ds/mustAuthenticate")
+ public ModelAndView mustAuthenticateController(ModelMap model) throws IOException, Exception {
+ return new ModelAndView(getRedirectView());
+ }
+
+ @GetMapping(path = "/ds-return")
+ public String returnController(@RequestParam(value = ATTR_STATE, required = false) String state,
+ @RequestParam(value = ATTR_EVENT, required = false) String event,
+ @RequestParam(required = false) String envelopeId, ModelMap model, HttpServletResponse response)
+ throws IOException {
+ String site = "/eg001";
+ response.setStatus(response.SC_MOVED_TEMPORARILY);
+ response.setHeader("Location", site);
+ return null;
+ }
+
+ @GetMapping("/pkce")
+ public RedirectView pkce(String code, String state, HttpServletRequest req, HttpServletResponse resp)
+ throws Exception {
+ String redirectURL = "/";
+ RedirectView redirect;
+ try {
+ redirect = new ACGAuthenticationMethod().exchangeCodeForToken(code, config, session, redirectURL,
+ ESIGNATURE_SCOPES);
+ } catch (Exception e) {
+ redirect = new RedirectView(getLoginPath());
+ this.session.setIsPKCEWorking(false);
+ }
+
+ return redirect;
+ }
+
+ private RedirectView getRedirectView() throws Exception {
+ RedirectView redirect;
+ if (this.session.getIsPKCEWorking()) {
+ redirect = new ACGAuthenticationMethod().initiateAuthorization(config, ESIGNATURE_SCOPES);
+ } else {
+ redirect = new RedirectView(getLoginPath());
+ }
+
+ redirect.setExposeModelAttributes(false);
+ return redirect;
+ }
+
+ private String getLoginPath() {
+ return config.getAcgRedirectURL();
+ }
+
+ @ModelAttribute("documentOptions")
+ public List populateDocumentOptions() {
+ return new ArrayList<>();
+ }
+
+ @ModelAttribute("showDoc")
+ public boolean populateShowDoc() {
+ return StringUtils.isNotBlank(config.getDocumentationPath());
+ }
+
+ @ModelAttribute("locals")
+ public Object populateLocals() throws IOException {
+ Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
+
+ if (!(authentication instanceof OAuth2AuthenticationToken)) {
+ return new RedirectView("/");
+ }
+
+ OAuth2AuthenticationToken oauth = (OAuth2AuthenticationToken) authentication;
+ OAuth2User oauthUser = oauth.getPrincipal();
+ OAuth2AuthorizedClient oauthClient = authorizedClientService.loadAuthorizedClient(
+ oauth.getAuthorizedClientRegistrationId(),
+ oauthUser.getName());
+
+ if (oauth.isAuthenticated()) {
+ user.setName(oauthUser.getAttribute("name"));
+
+ if (oauthClient != null) {
+ user.setAccessToken(oauthClient.getAccessToken().getTokenValue());
+ } else {
+ user.setAccessToken(((OAuth.OAuthToken) oauthUser.getAttribute("access_token")).getAccessToken());
+ }
+
+ if (account.isEmpty()) {
+ account = Optional.ofNullable(getDefaultAccountInfo(getOAuthAccounts(oauthUser)));
+ }
+
+ OAuth.Account oauthAccount = account.orElseThrow(() -> new NoSuchElementException(ERROR_ACCOUNT_NOT_FOUND));
+ session.setAccountId(oauthAccount.getAccountId());
+ session.setAccountName(oauthAccount.getAccountName());
+ // TODO set this more efficiently with more APIs as they're added in
+ String basePath = this.getBaseUrl(oauthAccount) + "/restapi";
+ session.setBasePath(basePath);
+ }
+
+ return new Locals(config, session, user, "");
+ }
+
+ private String getBaseUrl(OAuth.Account oauthAccount) {
+ return oauthAccount.getBaseUri();
+ }
+
+ private static List getOAuthAccounts(OAuth2User user) {
+ List