Skip to content

Commit

Permalink
Merge branch 'dev' into ue-multi-select-defaults
Browse files Browse the repository at this point in the history
  • Loading branch information
TalmizAhmed committed Nov 27, 2024
2 parents 05b7cdd + 93cf3b0 commit b22acf7
Show file tree
Hide file tree
Showing 26 changed files with 1,668 additions and 379 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
"FT_FORMS-14545",
"FT_SITES-19631",
"FT_FORMS-14255",
"FT_FORMS-14068"
"FT_FORMS-14068",
"FT_FORMS-16351"
]
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
<?xml version="1.0" encoding="UTF-8"?>
<jcr:root xmlns:jcr="http://www.jcp.org/jcr/1.0" xmlns:nt="http://www.jcp.org/jcr/nt/1.0" xmlns:sling="http://sling.apache.org/jcr/sling/1.0"
jcr:primaryType="sling:Folder"
lcFolder="{Long}0"
type="lcFolder">
<jcr:content
jcr:primaryType="nt:unstructured"
jcr:title="navigate-in-panel">
<folderThumbnail/>
</jcr:content>
</jcr:root>
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
<?xml version="1.0" encoding="UTF-8"?>
<jcr:root xmlns:jcr="http://www.jcp.org/jcr/1.0" xmlns:dam="http://www.day.com/dam/1.0" xmlns:nt="http://www.jcp.org/jcr/nt/1.0" xmlns:xmp="http://ns.adobe.com/xap/1.0/" xmlns:cq="http://www.day.com/jcr/cq/1.0" xmlns:sling="http://sling.apache.org/jcr/sling/1.0" xmlns:fd="http://www.adobe.com/aemfd/fd/1.0"
jcr:primaryType="dam:Asset">
<jcr:content
cq:conf="\0"
jcr:lastModified="{Date}2024-08-20T06:03:38.597Z"
jcr:primaryType="dam:AssetContent"
sling:resourceType="fd/fm/af/render"
guide="1"
type="guide">
<metadata
fd:version="2.1"
jcr:language="en"
jcr:primaryType="nt:unstructured"
xmp:CreatorTool="AEM Forms AF Wizard"
allowedRenderFormat="HTML"
author="admin"
dorType="none"
formmodel="none"
themeRef="/libs/fd/af/themes/canvas"
title="basic"/>
</jcr:content>
</jcr:root>
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
<?xml version="1.0" encoding="UTF-8"?>
<jcr:root xmlns:jcr="http://www.jcp.org/jcr/1.0" xmlns:dam="http://www.day.com/dam/1.0" xmlns:nt="http://www.jcp.org/jcr/nt/1.0" xmlns:xmp="http://ns.adobe.com/xap/1.0/" xmlns:cq="http://www.day.com/jcr/cq/1.0" xmlns:sling="http://sling.apache.org/jcr/sling/1.0" xmlns:fd="http://www.adobe.com/aemfd/fd/1.0"
jcr:primaryType="dam:Asset">
<jcr:content
cq:conf="\0"
jcr:lastModified="{Date}2024-08-20T10:32:12.709Z"
jcr:primaryType="dam:AssetContent"
sling:resourceType="fd/fm/af/render"
guide="1"
type="guide">
<metadata
fd:version="2.1"
jcr:language="en"
jcr:primaryType="nt:unstructured"
xmp:CreatorTool="AEM Forms AF Wizard"
allowedRenderFormat="HTML"
author="admin"
dorType="none"
formmodel="none"
themeRef="/libs/fd/af/themes/canvas"
title="blank"/>
</jcr:content>
</jcr:root>

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
<?xml version="1.0" encoding="UTF-8"?>
<jcr:root xmlns:jcr="http://www.jcp.org/jcr/1.0" xmlns:sling="http://sling.apache.org/jcr/sling/1.0"
jcr:primaryType="sling:Folder"/>

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
<?xml version="1.0" encoding="UTF-8"?>
<jcr:root xmlns:jcr="http://www.jcp.org/jcr/1.0" xmlns:nt="http://www.jcp.org/jcr/nt/1.0" xmlns:cq="http://www.day.com/jcr/cq/1.0" xmlns:sling="http://sling.apache.org/jcr/sling/1.0" xmlns:fd="http://www.adobe.com/aemfd/fd/1.0"
jcr:primaryType="cq:Page">
<jcr:content
cq:deviceGroups="[/etc/mobile/groups/responsive]"
cq:lastModified="{Date}2024-08-20T10:32:12.710Z"
cq:lastModifiedBy="admin"
cq:template="/conf/core-components-examples/settings/wcm/templates/af-blank-v2"
jcr:language="en"
jcr:primaryType="cq:PageContent"
jcr:title="blank"
sling:configRef="/conf/forms/core-components-it/samples/ruleeditor/navigate-in-panel/blank/"
sling:resourceType="forms-components-examples/components/page">
<guideContainer
fd:version="2.1"
jcr:primaryType="nt:unstructured"
sling:resourceType="forms-components-examples/components/form/container"
dorType="none"
fieldType="form"
thankYouOption="page"
themeRef="/libs/fd/af/themes/canvas"
title="blank"/>
</jcr:content>
</jcr:root>
14 changes: 14 additions & 0 deletions it/core/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,7 @@
<Import-Package>
javax.annotation;version=0.0.0,
com.adobe.cq.forms.core.components.models.form;version="[1.0.0,10.0.0)",
io.jsonwebtoken;resolution:=optional;version="[0.0.0,1.0.0)",
*
</Import-Package>
</instructions>
Expand Down Expand Up @@ -162,6 +163,19 @@ Import-Package: javax.annotation;version=0.0.0,*
<artifactId>core-forms-components-af-core</artifactId>
<version>3.0.70</version>
</dependency>
<!-- Json web token dependencies for oauth2 flow -->
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-api</artifactId>
<version>0.11.2</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-impl</artifactId>
<version>0.11.2</version>
<scope>provided</scope>
</dependency>
</dependencies>

<!-- ====================================================================== -->
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -170,14 +170,28 @@ public ReplicationResult deliver(TransportContext transportContext, ReplicationT
// todo: publish this form model json to the external system
LOG.info("[HeadlessTransportHandler] Form Model JSON: {}", formModelJson);
/**
PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager();
connectionManager.setDefaultMaxPerRoute(100);
connectionManager.setMaxTotal(100);
CloseableHttpClient httpClient = HttpClients.custom()
.setConnectionManager(connectionManager)
.setDefaultRequestConfig(RequestConfig.custom()
.setConnectTimeout(30000)
.setSocketTimeout(30000)
.setConnectionRequestTimeout(30000)
.build())
.build();
OAuth2Client oauth2Client = new OAuth2Client(
"https://example.com/oauth2/token",
"your_client_id",
"your_client_secret",
"https://example.com/api/publish",
"your_private_key",
"your_certificate_thumbprint",
"your_resource_uri",
httpClient
);
oauth2Client.publishOrDeleteFormModelJson(formModelJson, requestSupplier);
oauth2Client.publishOrDeleteFormModelJson(formModelJson, "https://example.com/api/publish", HttpPost::new);
**/
} else {
LOG.info("[HeadlessTransportHandler] No adaptive form container found for resource {}. Skipping", resource.getPath());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,14 @@
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
package com.adobe.cq.forms.core.components.it.service;

import java.io.IOException;
import java.security.KeyFactory;
import java.security.PrivateKey;
import java.security.spec.PKCS8EncodedKeySpec;
import java.util.Base64;
import java.util.Date;
import java.util.concurrent.locks.ReentrantLock;

import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.methods.HttpRequestBase;
Expand All @@ -27,34 +35,48 @@
import javax.json.Json;
import javax.json.JsonObject;
import javax.json.JsonReader;
import java.io.IOException;
import java.io.StringReader;
import java.util.concurrent.locks.ReentrantLock;
import java.util.function.Function;


// these bundles are not present on local cloud ready sdk
// to make this work, you have to download/install 0.11.2 from here, https://mvnrepository.com/artifact/io.jsonwebtoken/jjwt-api/0.11.2
// making the import optional
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;

/**
* Uses the OAuth2 Client Credentials flow with a signed JWT (client assertion) for certificate-based authentication.
* The token request payload includes grant_type, client_id, client_assertion_type, client_assertion, and resource.
* The signed JWT includes claims such as iss, sub, aud, jti, iat, and exp.
* The JWT is signed using a private key and includes a certificate thumbprint in the header.
*
* Uses a signed JWT (client assertion) with a private key and certificate thumbprint.
* Provides enhanced security by using certificate-based authentication and a signed JWT.
*/
public class OAuth2Client {
private static final Logger LOG = LoggerFactory.getLogger(OAuth2Client.class);

private final String tokenEndpoint;
private final String clientId;
private final String clientSecret;
private final String apiEndpoint;
private final String privateKey;
private final String certificateThumbprint;
private final String resource;
private final CloseableHttpClient httpClient;

private String accessToken;
private long tokenExpirationTime;
private final ReentrantLock lock = new ReentrantLock();

public OAuth2Client(String tokenEndpoint, String clientId, String clientSecret, String apiEndpoint, CloseableHttpClient httpClient) {
public OAuth2Client(String tokenEndpoint, String clientId, String privateKey, String certificateThumbprint, String resource, CloseableHttpClient httpClient) {
this.tokenEndpoint = tokenEndpoint;
this.clientId = clientId;
this.clientSecret = clientSecret;
this.apiEndpoint = apiEndpoint;
this.privateKey = privateKey;
this.certificateThumbprint = certificateThumbprint;
this.resource = resource;
this.httpClient = httpClient;
}

public void publishOrDeleteFormModelJson(String formModelJson, Function<String, HttpRequestBase> requestSupplier) throws IOException {
public void publishOrDeleteFormModelJson(String formModelJson, String apiEndpoint, Function<String, HttpRequestBase> requestSupplier) throws IOException {
String token = getValidToken();
HttpRequestBase request = requestSupplier.apply(apiEndpoint);
request.setHeader("Authorization", "Bearer " + token);
Expand Down Expand Up @@ -94,7 +116,12 @@ private String getValidToken() throws IOException {
private String fetchOAuth2Token() throws IOException {
HttpPost post = new HttpPost(tokenEndpoint);
post.setHeader("Content-Type", "application/x-www-form-urlencoded");
post.setEntity(new StringEntity("grant_type=client_credentials&client_id=" + clientId + "&client_secret=" + clientSecret));

String clientAssertion = generateClientAssertion();

String payload = "grant_type=client_credentials&client_id=" + clientId + "&client_assertion_type=urn:ietf:params:oauth:client-assertion-type:jwt-bearer&client_assertion=" + clientAssertion + "&resource=" + resource;

post.setEntity(new StringEntity(payload));

try (CloseableHttpResponse response = httpClient.execute(post)) {
if (response.getStatusLine().getStatusCode() == 200) {
Expand All @@ -107,18 +134,7 @@ private String fetchOAuth2Token() throws IOException {
}

private String refreshOAuth2Token() throws IOException {
HttpPost post = new HttpPost(tokenEndpoint);
post.setHeader("Content-Type", "application/x-www-form-urlencoded");
post.setEntity(new StringEntity("grant_type=refresh_token&refresh_token=your_refresh_token&client_id=" + clientId + "&client_secret=" + clientSecret));

try (CloseableHttpResponse response = httpClient.execute(post)) {
if (response.getStatusLine().getStatusCode() == 200) {
String responseBody = EntityUtils.toString(response.getEntity());
return parseToken(responseBody);
} else {
throw new NotOk(response.getStatusLine().getStatusCode());
}
}
return fetchOAuth2Token(); // Assuming the same flow for refresh token
}

private String parseToken(String responseBody) {
Expand All @@ -130,9 +146,49 @@ private String parseToken(String responseBody) {
}
}

private String generateClientAssertion() {
long nowMillis = System.currentTimeMillis();
Date now = new Date(nowMillis);

// Create the JWT claims
JsonObject claims = Json.createObjectBuilder()
.add("iss", clientId)
.add("sub", clientId)
.add("aud", tokenEndpoint)
.add("jti", java.util.UUID.randomUUID().toString())
.add("iat", nowMillis / 1000)
.add("exp", (nowMillis / 1000) + 300) // 5 minutes expiration
.build();

// Create the JWT header
JsonObject header = Json.createObjectBuilder()
.add("alg", "RS256")
.add("x5t", certificateThumbprint)
.build();

// Sign the JWT
return Jwts.builder()
.setHeaderParam("x5t", certificateThumbprint)
.setClaims(claims)
.setHeaderParam("typ", "JWT")
.signWith(SignatureAlgorithm.RS256, getPrivateKey())
.compact();
}

private PrivateKey getPrivateKey() {
try {
byte[] keyBytes = Base64.getDecoder().decode(privateKey);
PKCS8EncodedKeySpec spec = new PKCS8EncodedKeySpec(keyBytes);
KeyFactory kf = KeyFactory.getInstance("RSA");
return kf.generatePrivate(spec);
} catch (Exception e) {
throw new RuntimeException("Failed to load private key", e);
}
}

private static class NotOk extends IOException {
NotOk(int status) {
super("status code = " + status);
}
}
}
}
14 changes: 7 additions & 7 deletions ui.frontend/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion ui.frontend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,6 @@
"dependencies": {
"@aemforms/af-core": "^0.22.111",
"@aemforms/af-formatters": "^0.22.109",
"@aemforms/af-custom-functions": "1.0.10"
"@aemforms/af-custom-functions": "1.0.12"
}
}
18 changes: 17 additions & 1 deletion ui.frontend/src/customFunctions.js
Original file line number Diff line number Diff line change
Expand Up @@ -98,5 +98,21 @@ export const customFunctions = {
* @param {object} globals - An object containing read-only form instance, read-only target field instance and methods for form modifications.
* @returns {string} - The captcha token.
*/
fetchCaptchaToken: cf.fetchCaptchaToken
fetchCaptchaToken: cf.fetchCaptchaToken,

/**
* Converts a date to the number of days since the Unix epoch (1970-01-01).
*
* If the input date is a number, it is assumed to represent the number of days since the epoch,
* including both integer and decimal parts. In this case, only the integer part is returned as the number of days.
*
* @param {string|Date|number} date - The date to convert.
* Can be:
* - An ISO string (yyyy-mm-dd)
* - A Date object
* - A number representing the days since the epoch, where the integer part is the number of days and the decimal part is the fraction of the day
*
* @returns {number} - The number of days since the Unix epoch
*/
dateToDaysSinceEpoch: cf.dateToDaysSinceEpoch
};
Loading

0 comments on commit b22acf7

Please sign in to comment.