diff --git a/core/pom.xml b/core/pom.xml
index ab9ecbc920..54f635f4ea 100644
--- a/core/pom.xml
+++ b/core/pom.xml
@@ -327,7 +327,7 @@
io.opentelemetry
opentelemetry-exporter-otlp
- 1.41.0
+ 1.42.0
io.opentelemetry.instrumentation
diff --git a/core/src/main/java/com/predic8/membrane/core/interceptor/oauth2/flows/TokenFlow.java b/core/src/main/java/com/predic8/membrane/core/interceptor/oauth2/flows/TokenFlow.java
index fcce2eeafd..d426a17a29 100644
--- a/core/src/main/java/com/predic8/membrane/core/interceptor/oauth2/flows/TokenFlow.java
+++ b/core/src/main/java/com/predic8/membrane/core/interceptor/oauth2/flows/TokenFlow.java
@@ -66,7 +66,7 @@ private Outcome respondWithTokenAndRedirect(Exchange exc, String token, String t
public String generateAccessToken(Client client) {
synchronized (session) {
- String token = authServer.getTokenGenerator().getToken(session.getUserName(), client.getClientId(), client.getClientSecret());
+ String token = authServer.getTokenGenerator().getToken(session.getUserName(), client.getClientId(), client.getClientSecret(), null);
authServer.getSessionFinder().addSessionForToken(token,session);
return token;
}
diff --git a/core/src/main/java/com/predic8/membrane/core/interceptor/oauth2/request/ParameterizedRequest.java b/core/src/main/java/com/predic8/membrane/core/interceptor/oauth2/request/ParameterizedRequest.java
index 3dbed4be6e..97e514f767 100644
--- a/core/src/main/java/com/predic8/membrane/core/interceptor/oauth2/request/ParameterizedRequest.java
+++ b/core/src/main/java/com/predic8/membrane/core/interceptor/oauth2/request/ParameterizedRequest.java
@@ -13,6 +13,7 @@
package com.predic8.membrane.core.interceptor.oauth2.request;
+import com.google.common.collect.ImmutableMap;
import com.predic8.membrane.core.exchange.Exchange;
import com.predic8.membrane.core.http.Header;
import com.predic8.membrane.core.http.Response;
@@ -138,12 +139,30 @@ protected boolean verifyClientThroughParams(){
}
}
- protected String createTokenForVerifiedUserAndClient(){
- return authServer.getTokenGenerator().getToken(getUsername(), getClientId(), getClientSecret());
+ protected String createTokenForVerifiedUserAndClient(Map userParams){
+ return authServer.getTokenGenerator().getToken(getUsername(), getClientId(), getClientSecret(), claimsMap(userParams));
+ }
+
+ protected Map claimsMap(Map userParams) {
+ if (userParams.containsKey("aud"))
+ return ImmutableMap.of("aud", userParams.get("aud").split(" "));
+ return ImmutableMap.of();
+ }
+
+ protected Map claimsMapForRefresh(Map userParams) {
+ if (userParams.containsKey("aud"))
+ return ImmutableMap.of("i-aud", userParams.get("aud").split(" "));
+ return ImmutableMap.of();
+ }
+
+ protected Map claimsMapFromRefresh(Map refreshClaims) {
+ if (refreshClaims.containsKey("i-aud"))
+ return ImmutableMap.of("aud", refreshClaims.get("i-aud"));
+ return ImmutableMap.of();
}
protected String createTokenForVerifiedClient(){
- return authServer.getTokenGenerator().getToken(getClientId(), getClientId(), getClientSecret());
+ return authServer.getTokenGenerator().getToken(getClientId(), getClientId(), getClientSecret(), null);
}
public String getPrompt() {
diff --git a/core/src/main/java/com/predic8/membrane/core/interceptor/oauth2/request/tokenrequest/AuthorizationCodeFlow.java b/core/src/main/java/com/predic8/membrane/core/interceptor/oauth2/request/tokenrequest/AuthorizationCodeFlow.java
index fc1b39c2a9..295728fbd7 100644
--- a/core/src/main/java/com/predic8/membrane/core/interceptor/oauth2/request/tokenrequest/AuthorizationCodeFlow.java
+++ b/core/src/main/java/com/predic8/membrane/core/interceptor/oauth2/request/tokenrequest/AuthorizationCodeFlow.java
@@ -72,11 +72,11 @@ protected Response processWithParameters() throws Exception {
}
scope = getScope(session);
- token = authServer.getTokenGenerator().getToken(username, client.getClientId(), client.getClientSecret());
+ token = authServer.getTokenGenerator().getToken(username, client.getClientId(), client.getClientSecret(), null);
expiration = authServer.getTokenGenerator().getExpiration();
authServer.getSessionFinder().addSessionForToken(token,session);
- refreshToken = authServer.getRefreshTokenGenerator().getToken(username, client.getClientId(), client.getClientSecret());
+ refreshToken = authServer.getRefreshTokenGenerator().getToken(username, client.getClientId(), client.getClientSecret(), null);
authServer.getSessionFinder().addSessionForRefreshToken(refreshToken, session);
if (OAuth2Util.isOpenIdScope(scope)) {
idToken = createSignedIdToken(session, username, client);
diff --git a/core/src/main/java/com/predic8/membrane/core/interceptor/oauth2/request/tokenrequest/CredentialsFlow.java b/core/src/main/java/com/predic8/membrane/core/interceptor/oauth2/request/tokenrequest/CredentialsFlow.java
index 708b03195f..e7bdaa3dc4 100644
--- a/core/src/main/java/com/predic8/membrane/core/interceptor/oauth2/request/tokenrequest/CredentialsFlow.java
+++ b/core/src/main/java/com/predic8/membrane/core/interceptor/oauth2/request/tokenrequest/CredentialsFlow.java
@@ -77,7 +77,7 @@ protected Response processWithParameters() throws Exception {
authServer.getSessionFinder().addSessionForToken(token,session);
if (authServer.isIssueNonSpecRefreshTokens()) {
- refreshToken = authServer.getRefreshTokenGenerator().getToken(client.getClientId(), client.getClientId(), client.getClientSecret());
+ refreshToken = authServer.getRefreshTokenGenerator().getToken(client.getClientId(), client.getClientId(), client.getClientSecret(), null);
authServer.getSessionFinder().addSessionForRefreshToken(refreshToken, session);
}
diff --git a/core/src/main/java/com/predic8/membrane/core/interceptor/oauth2/request/tokenrequest/PasswordFlow.java b/core/src/main/java/com/predic8/membrane/core/interceptor/oauth2/request/tokenrequest/PasswordFlow.java
index 73a62b633c..9c2f7a9346 100644
--- a/core/src/main/java/com/predic8/membrane/core/interceptor/oauth2/request/tokenrequest/PasswordFlow.java
+++ b/core/src/main/java/com/predic8/membrane/core/interceptor/oauth2/request/tokenrequest/PasswordFlow.java
@@ -53,9 +53,9 @@ protected Response processWithParameters() throws Exception {
return OAuth2Util.createParameterizedJsonErrorResponse(exc,jsonGen,"error","access_denied");
scope = getScope();
- token = createTokenForVerifiedUserAndClient();
+ token = createTokenForVerifiedUserAndClient(userParams);
expiration = authServer.getTokenGenerator().getExpiration();
- refreshToken = authServer.getRefreshTokenGenerator().getToken(getUsername(), getClientId(), getClientSecret());
+ refreshToken = authServer.getRefreshTokenGenerator().getToken(getUsername(), getClientId(), getClientSecret(), claimsMapForRefresh(userParams));
SessionManager.Session session = createSessionForAuthorizedUserWithParams();
synchronized(session) {
@@ -78,7 +78,7 @@ protected Response processWithParameters() throws Exception {
return OAuth2Util.createParameterizedJsonErrorResponse(exc, jsonGen, "error", "invalid_grant_type");
}
- refreshToken = authServer.getRefreshTokenGenerator().getToken(client.getClientId(), client.getClientId(), client.getClientSecret());
+ refreshToken = authServer.getRefreshTokenGenerator().getToken(client.getClientId(), client.getClientId(), client.getClientSecret(), claimsMapForRefresh(userParams));
authServer.getSessionFinder().addSessionForRefreshToken(refreshToken, session);
if (authServer.isIssueNonSpecIdTokens() && OAuth2Util.isOpenIdScope(scope)) {
diff --git a/core/src/main/java/com/predic8/membrane/core/interceptor/oauth2/request/tokenrequest/RefreshTokenFlow.java b/core/src/main/java/com/predic8/membrane/core/interceptor/oauth2/request/tokenrequest/RefreshTokenFlow.java
index c866f707ed..768fdcac4e 100644
--- a/core/src/main/java/com/predic8/membrane/core/interceptor/oauth2/request/tokenrequest/RefreshTokenFlow.java
+++ b/core/src/main/java/com/predic8/membrane/core/interceptor/oauth2/request/tokenrequest/RefreshTokenFlow.java
@@ -24,6 +24,7 @@
import com.predic8.membrane.core.interceptor.oauth2.tokengenerators.JwtGenerator;
import java.util.ArrayList;
+import java.util.Map;
import java.util.NoSuchElementException;
import org.jose4j.lang.JoseException;
@@ -49,8 +50,10 @@ protected Response processWithParameters() throws Exception {
return OAuth2Util.createParameterizedJsonErrorResponse(exc,jsonGen,"error","unauthorized_client");
String username;
+ Map additionalClaims;
try {
username = authServer.getRefreshTokenGenerator().getUsername(getRefreshToken());
+ additionalClaims = authServer.getRefreshTokenGenerator().getAdditionalClaims(getRefreshToken());
}catch(NoSuchElementException ex){
return OAuth2Util.createParameterizedJsonErrorResponse(exc, jsonGen,"error", "invalid_request");
}
@@ -81,9 +84,9 @@ protected Response processWithParameters() throws Exception {
}
scope = getScope();
- token = authServer.getTokenGenerator().getToken(getUsername(),getClientId(),getClientSecret());
+ token = authServer.getTokenGenerator().getToken(getUsername(),getClientId(),getClientSecret(), claimsMapFromRefresh(additionalClaims));
expiration = authServer.getTokenGenerator().getExpiration();
- refreshToken = authServer.getRefreshTokenGenerator().getToken(getUsername(), getClientId(), getClientSecret());
+ refreshToken = authServer.getRefreshTokenGenerator().getToken(getUsername(), getClientId(), getClientSecret(), additionalClaims);
SessionManager.Session session = authServer.getSessionFinder().getSessionForRefreshToken(getRefreshToken());
synchronized(session) {
diff --git a/core/src/main/java/com/predic8/membrane/core/interceptor/oauth2/tokengenerators/BearerJwtTokenGenerator.java b/core/src/main/java/com/predic8/membrane/core/interceptor/oauth2/tokengenerators/BearerJwtTokenGenerator.java
index 5c983fa2e5..9e7dbb635e 100644
--- a/core/src/main/java/com/predic8/membrane/core/interceptor/oauth2/tokengenerators/BearerJwtTokenGenerator.java
+++ b/core/src/main/java/com/predic8/membrane/core/interceptor/oauth2/tokengenerators/BearerJwtTokenGenerator.java
@@ -15,7 +15,6 @@
import com.predic8.membrane.annot.MCAttribute;
import com.predic8.membrane.annot.MCChildElement;
import com.predic8.membrane.annot.MCElement;
-import com.predic8.membrane.annot.MCTextContent;
import com.predic8.membrane.core.Router;
import com.predic8.membrane.core.config.security.Blob;
import com.predic8.membrane.core.interceptor.session.JwtSessionManager;
@@ -38,6 +37,9 @@
import java.security.SecureRandom;
import java.util.Map;
import java.util.NoSuchElementException;
+import java.util.stream.Collectors;
+
+import static java.util.stream.Collectors.toUnmodifiableMap;
@MCElement(name = "bearerJwtToken")
public class BearerJwtTokenGenerator implements TokenGenerator {
@@ -76,12 +78,14 @@ public String getTokenType() {
}
@Override
- public String getToken(String username, String clientId, String clientSecret) {
+ public String getToken(String username, String clientId, String clientSecret, Map additionalClaims) {
JwtClaims claims = new JwtClaims();
claims.setSubject(username);
claims.setClaim("clientId", clientId);
if (expiration != 0)
claims.setExpirationTimeMinutesInTheFuture(expiration / 60.0f);
+ if (additionalClaims != null)
+ additionalClaims.forEach(claims::setClaim);
JsonWebSignature jws = new JsonWebSignature();
jws.setPayload(claims.toJson());
jws.setKey(rsaJsonWebKey.getRsaPrivateKey());
@@ -111,6 +115,21 @@ public String getUsername(String token) throws NoSuchElementException {
}
}
+ @Override
+ public Map getAdditionalClaims(String token) throws NoSuchElementException {
+ try {
+ return verify(token).getClaimsMap().entrySet().stream()
+ .filter(e -> !isNormalClaim(e.getKey()))
+ .collect(toUnmodifiableMap(Map.Entry::getKey, Map.Entry::getValue));
+ } catch (InvalidJwtException e) {
+ throw new NoSuchElementException(e);
+ }
+ }
+
+ private boolean isNormalClaim(String key) {
+ return "sub".equals(key) || "clientId".equals(key) || "exp".equals(key);
+ }
+
@Override
public String getClientId(String token) throws NoSuchElementException {
try {
diff --git a/core/src/main/java/com/predic8/membrane/core/interceptor/oauth2/tokengenerators/BearerTokenGenerator.java b/core/src/main/java/com/predic8/membrane/core/interceptor/oauth2/tokengenerators/BearerTokenGenerator.java
index ede0ac5f4a..5f74038020 100644
--- a/core/src/main/java/com/predic8/membrane/core/interceptor/oauth2/tokengenerators/BearerTokenGenerator.java
+++ b/core/src/main/java/com/predic8/membrane/core/interceptor/oauth2/tokengenerators/BearerTokenGenerator.java
@@ -18,6 +18,7 @@
import java.math.BigInteger;
import java.security.SecureRandom;
+import java.util.Map;
import java.util.NoSuchElementException;
import java.util.concurrent.ConcurrentHashMap;
@@ -74,7 +75,7 @@ public String getTokenType() {
}
@Override
- public String getToken(String username, String clientId, String clientSecret) {
+ public String getToken(String username, String clientId, String clientSecret, Map additionalClaims) {
String token = new BigInteger(130, random).toString(32);
tokenToUser.put(token, new User(username, clientId, clientSecret));
return token;
@@ -89,6 +90,11 @@ public String getUsername(String token) throws NoSuchElementException {
}
}
+ @Override
+ public Map getAdditionalClaims(String token) throws NoSuchElementException {
+ return Map.of();
+ }
+
@Override
public String getClientId(String token) throws NoSuchElementException {
try {
diff --git a/core/src/main/java/com/predic8/membrane/core/interceptor/oauth2/tokengenerators/TokenGenerator.java b/core/src/main/java/com/predic8/membrane/core/interceptor/oauth2/tokengenerators/TokenGenerator.java
index d41da3b196..e95cfdcfd2 100644
--- a/core/src/main/java/com/predic8/membrane/core/interceptor/oauth2/tokengenerators/TokenGenerator.java
+++ b/core/src/main/java/com/predic8/membrane/core/interceptor/oauth2/tokengenerators/TokenGenerator.java
@@ -15,6 +15,7 @@
import com.predic8.membrane.core.Router;
+import java.util.Map;
import java.util.NoSuchElementException;
public interface TokenGenerator {
@@ -28,7 +29,7 @@ public interface TokenGenerator {
/**
* @return a new token for the specified user and client.
*/
- String getToken(String username, String clientId, String clientSecret);
+ String getToken(String username, String clientId, String clientSecret, Map additionalClaims);
/**
* Checks the token for validity. Returns the username the token was generated for.
@@ -38,6 +39,14 @@ public interface TokenGenerator {
*/
String getUsername(String token) throws NoSuchElementException;
+ /**
+ * Checks the token for validity. Returns the additional claims the token was generated for.
+ * @param token The token.
+ * @return The additional claims.
+ * @throws NoSuchElementException if the token is not valid.
+ */
+ Map getAdditionalClaims(String token) throws NoSuchElementException;
+
/**
* Checks the token for validity. Returns the clientId the token was generated for.
* @param token The token.
diff --git a/core/src/main/java/com/predic8/membrane/core/interceptor/soap/SampleSoapServiceInterceptor.java b/core/src/main/java/com/predic8/membrane/core/interceptor/soap/SampleSoapServiceInterceptor.java
index 07fafbe9e7..4540f1b66a 100644
--- a/core/src/main/java/com/predic8/membrane/core/interceptor/soap/SampleSoapServiceInterceptor.java
+++ b/core/src/main/java/com/predic8/membrane/core/interceptor/soap/SampleSoapServiceInterceptor.java
@@ -28,8 +28,10 @@
import javax.xml.stream.events.StartElement;
import javax.xml.stream.events.XMLEvent;
import javax.xml.transform.TransformerException;
+import java.io.FileNotFoundException;
import java.io.InputStream;
import java.io.StringWriter;
+import java.net.URISyntaxException;
import java.util.HashMap;
import java.util.Objects;
import java.util.regex.Pattern;
@@ -86,7 +88,7 @@ private static Response createMethodNotAllowedSOAPFault() throws Exception {
return ok(getSoapFault("Method Not Allowed", "405", "Use POST to access the service.")).contentType(APPLICATION_XML).build();
}
- private Response createWSDLResponse(Exchange exc) throws XMLStreamException {
+ private Response createWSDLResponse(Exchange exc) throws XMLStreamException, FileNotFoundException {
return ok().header(CONTENT_TYPE, TEXT_XML_UTF8)
.body(setWsdlServer(
getResourceAsStream(this,"/wsdl/city.wsdl"),exc)
diff --git a/core/src/main/java/com/predic8/membrane/core/openapi/serviceproxy/APIProxy.java b/core/src/main/java/com/predic8/membrane/core/openapi/serviceproxy/APIProxy.java
index 93d5e75b05..cc070c337c 100644
--- a/core/src/main/java/com/predic8/membrane/core/openapi/serviceproxy/APIProxy.java
+++ b/core/src/main/java/com/predic8/membrane/core/openapi/serviceproxy/APIProxy.java
@@ -82,7 +82,7 @@ public void init() throws Exception {
initOpenAPI();
}
- private void initOpenAPI() throws IOException, ClassNotFoundException {
+ private void initOpenAPI() throws IOException, ClassNotFoundException, URISyntaxException {
if (specs.isEmpty())
return;
diff --git a/core/src/main/java/com/predic8/membrane/core/openapi/serviceproxy/OpenAPIPublisher.java b/core/src/main/java/com/predic8/membrane/core/openapi/serviceproxy/OpenAPIPublisher.java
index 3da6b50d74..62316e7c17 100644
--- a/core/src/main/java/com/predic8/membrane/core/openapi/serviceproxy/OpenAPIPublisher.java
+++ b/core/src/main/java/com/predic8/membrane/core/openapi/serviceproxy/OpenAPIPublisher.java
@@ -33,6 +33,7 @@
import java.net.URISyntaxException;
import java.util.HashMap;
import java.util.Map;
+import java.util.Objects;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
@@ -60,7 +61,7 @@ public class OpenAPIPublisher {
protected Map apis;
- public OpenAPIPublisher(Map apis) throws IOException, ClassNotFoundException {
+ public OpenAPIPublisher(Map apis) throws IOException, ClassNotFoundException, URISyntaxException {
this.apis = apis;
swaggerUiHtmlTemplate = createTemplate("/openapi/swagger-ui.html");
apiOverviewHtmlTemplate = createTemplate("/openapi/overview.html");
@@ -142,8 +143,8 @@ private Outcome returnOpenApiAsYaml(Exchange exc, OpenAPIRecord rec, Router rout
return RETURN;
}
- private Template createTemplate(String filePath) throws ClassNotFoundException, IOException {
- return new StreamingTemplateEngine().createTemplate(new InputStreamReader(getResourceAsStream(this, filePath)));
+ private Template createTemplate(String filePath) throws ClassNotFoundException, IOException, URISyntaxException {
+ return new StreamingTemplateEngine().createTemplate(new InputStreamReader(Objects.requireNonNull(getResourceAsStream(this, filePath))));
}
private String renderOverviewTemplate(Router router) {
diff --git a/core/src/main/java/com/predic8/membrane/core/openapi/serviceproxy/OpenAPIPublisherInterceptor.java b/core/src/main/java/com/predic8/membrane/core/openapi/serviceproxy/OpenAPIPublisherInterceptor.java
index 58c305ef1a..5b2ef4f5d9 100644
--- a/core/src/main/java/com/predic8/membrane/core/openapi/serviceproxy/OpenAPIPublisherInterceptor.java
+++ b/core/src/main/java/com/predic8/membrane/core/openapi/serviceproxy/OpenAPIPublisherInterceptor.java
@@ -60,7 +60,7 @@ public class OpenAPIPublisherInterceptor extends AbstractInterceptor {
private final Template swaggerUiHtmlTemplate;
private final Template apiOverviewHtmlTemplate;
- public OpenAPIPublisherInterceptor(Map apis) throws IOException, ClassNotFoundException {
+ public OpenAPIPublisherInterceptor(Map apis) throws IOException, ClassNotFoundException, URISyntaxException {
name = "OpenAPI Publisher";
this.apis = apis;
swaggerUiHtmlTemplate = createTemplate("/openapi/swagger-ui.html");
@@ -69,7 +69,7 @@ public OpenAPIPublisherInterceptor(Map apis) throws IOExc
}
private Template createTemplate(String filePath) throws ClassNotFoundException, IOException {
- return new StreamingTemplateEngine().createTemplate(new InputStreamReader(getResourceAsStream(this, filePath)));
+ return new StreamingTemplateEngine().createTemplate(new InputStreamReader(Objects.requireNonNull(getResourceAsStream(this, filePath))));
}
@Override
diff --git a/core/src/main/java/com/predic8/membrane/core/openapi/util/Utils.java b/core/src/main/java/com/predic8/membrane/core/openapi/util/Utils.java
index fee77de0d7..8d461bd09b 100644
--- a/core/src/main/java/com/predic8/membrane/core/openapi/util/Utils.java
+++ b/core/src/main/java/com/predic8/membrane/core/openapi/util/Utils.java
@@ -16,28 +16,37 @@
package com.predic8.membrane.core.openapi.util;
-import com.predic8.membrane.core.exchange.*;
-import com.predic8.membrane.core.http.*;
+import com.predic8.membrane.core.exchange.Exchange;
+import com.predic8.membrane.core.http.HeaderField;
import com.predic8.membrane.core.openapi.model.Body;
import com.predic8.membrane.core.openapi.model.Request;
import com.predic8.membrane.core.openapi.model.Response;
-import com.predic8.membrane.core.openapi.validators.*;
-import com.predic8.membrane.core.security.*;
-import jakarta.mail.internet.*;
+import com.predic8.membrane.core.openapi.validators.ValidationErrors;
+import com.predic8.membrane.core.security.SecurityScheme;
+import jakarta.mail.internet.ParseException;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
import java.io.*;
-import java.time.*;
-import java.time.format.*;
-import java.util.*;
-import java.util.regex.*;
-
-import static com.predic8.membrane.core.exchange.Exchange.*;
-import static java.nio.charset.StandardCharsets.*;
-import static java.util.regex.Pattern.*;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.net.URL;
+import java.time.LocalDate;
+import java.time.format.DateTimeFormatter;
+import java.time.format.ResolverStyle;
+import java.util.Collection;
+import java.util.List;
+import java.util.Map;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import static com.predic8.membrane.core.exchange.Exchange.SECURITY_SCHEMES;
+import static java.nio.charset.StandardCharsets.UTF_8;
+import static java.util.regex.Pattern.compile;
public class Utils {
- private Utils() {}
+ private static final Logger LOG = LoggerFactory.getLogger(Utils.class);
//noinspection
static final Pattern componentSchemaPattern = compile("#/components/\\w+/(.*)");
@@ -58,6 +67,8 @@ private Utils() {}
static final DateTimeFormatter dateFormat = DateTimeFormatter.ofPattern("uuuu-MM-dd").withResolverStyle(ResolverStyle.STRICT);
static final DateTimeFormatter dateTimeFormat = DateTimeFormatter.ISO_DATE_TIME;
+ private Utils() {}
+
public static String getComponentLocalNameFromRef(String ref) {
try {
Matcher matcher = componentSchemaPattern.matcher(ref);
@@ -199,8 +210,28 @@ public static String normalizeForId(String s) {
return s.replaceAll("\\W+","-").toLowerCase();
}
- public static InputStream getResourceAsStream(Object obj, String fileName) {
- return obj.getClass().getResourceAsStream(fileName);
+ /**
+ * Safe alternative of Class.getResourceAsStream() that can handle spaces in the base path.
+ * @param obj Object reference of caller. Usually set to `this` of the caller.
+ * @param location Location of the resource. E.g. /foo
+ * @return InputStream of resource.
+ * @throws FileNotFoundException when resource not found.
+ */
+ public static InputStream getResourceAsStream(Object obj, String location) throws FileNotFoundException {
+ try {
+ URL url = obj.getClass().getResource(location);
+ if (url == null) {
+ LOG.warn("Resource {} not found", location);
+ throw new FileNotFoundException(location);
+ }
+
+ // Uses wrapping in URI and FileInputStream because of:
+ // https://stackoverflow.com/questions/3263560/sysloader-getresource-problem-in-java
+ return new FileInputStream(new URI(url.toString()).getPath());
+ } catch (URISyntaxException e) {
+ LOG.error(e.getMessage());
+ throw new RuntimeException(e);
+ }
}
public static byte[] createErrorMessage(String msg) {
diff --git a/core/src/main/resources/test/foo.bar b/core/src/main/resources/test/foo.bar
new file mode 100644
index 0000000000..3f95386662
--- /dev/null
+++ b/core/src/main/resources/test/foo.bar
@@ -0,0 +1 @@
+baz
\ No newline at end of file
diff --git a/core/src/test/java/com/predic8/membrane/core/interceptor/oauth2/OAuth2Test.java b/core/src/test/java/com/predic8/membrane/core/interceptor/oauth2/OAuth2Test.java
index 51121f8a34..f2e5418f2c 100644
--- a/core/src/test/java/com/predic8/membrane/core/interceptor/oauth2/OAuth2Test.java
+++ b/core/src/test/java/com/predic8/membrane/core/interceptor/oauth2/OAuth2Test.java
@@ -41,8 +41,7 @@
import java.util.List;
import java.util.regex.Pattern;
-import static org.junit.jupiter.api.Assertions.assertEquals;
-import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.*;
class OAuth2Test {
@@ -101,11 +100,32 @@ static void shutdown() {
@Test
void testJwtAuthentication() throws Exception {
- String json = getTokenRequestResponse();
+ String json = getTokenRequestResponse(createTokenRequestParameters());
JSONObject jsonObject = new JSONObject(json);
assertEquals("Bearer", jsonObject.getString("token_type"));
assertNotNull(jsonObject.getString("access_token"));
- assertEquals("Ok", sendRequestToTarget(parseTokenRequestResponse(json)));
+ assertEquals("Ok", sendRequestToTarget(parseTokenRequestResponse("access_token", json)));
+ }
+
+ @Test
+ void testJwtAuthenticationAfterRefresh() throws Exception {
+ String json = getTokenRequestResponse(createTokenRequestParameters());
+ JSONObject jsonObject = new JSONObject(json);
+
+ json = getTokenRequestResponse(createTokenRequestParametersAfterRefresh(jsonObject.getString("refresh_token")));
+ jsonObject = new JSONObject(json);
+ assertEquals("Bearer", jsonObject.getString("token_type"));
+ assertNotNull(jsonObject.getString("access_token"));
+ assertEquals("Ok", sendRequestToTarget(parseTokenRequestResponse("access_token", json)));
+ }
+
+ @Test
+ void testJwtAuthenticationFailingWithRefreshInsteadOfAccessToken() throws Exception {
+ String json = getTokenRequestResponse(createTokenRequestParameters());
+ JSONObject jsonObject = new JSONObject(json);
+ assertEquals("Bearer", jsonObject.getString("token_type"));
+ assertNotNull(jsonObject.getString("refresh_token"));
+ assertEquals("Bad Request", sendRequestToTarget(parseTokenRequestResponse("refresh_token", json)));
}
private static OAuth2AuthorizationServerInterceptor createOAuth2AuthServerInterceptor() {
@@ -114,10 +134,14 @@ private static OAuth2AuthorizationServerInterceptor createOAuth2AuthServerInterc
oAuth2AuthSI.setTokenGenerator(new BearerJwtTokenGenerator() {{
setExpiration(60);
}});
- oAuth2AuthSI.setRefreshTokenGenerator(new BearerTokenGenerator());
+ oAuth2AuthSI.setRefreshTokenGenerator(new BearerJwtTokenGenerator() {{
+ setExpiration(60);
+ }});
oAuth2AuthSI.setUserDataProvider(new StaticUserDataProvider() {{
- setUsers(List.of(new StaticUserDataProvider.User("john", "password")));
+ User u = new User("john", "password");
+ u.getAttributes().put("aud", "demo1");
+ setUsers(List.of(u));
}});
oAuth2AuthSI.setClientList(new StaticClientList() {{
@@ -139,6 +163,7 @@ private static OAuth2AuthorizationServerInterceptor createOAuth2AuthServerInterc
private static JwtAuthInterceptor createJwtAuthInterceptor() {
return new JwtAuthInterceptor() {{
+ setExpectedAud("demo1");
setJwks(new Jwks() {{
setJwksUris("http://localhost:2000/oauth2/certs");
}});
@@ -154,9 +179,9 @@ private static String sendRequestToTarget(String authorizationHeaderValue) throw
return connection.getResponseMessage();
}
- private static String parseTokenRequestResponse(String tokenRequestResponse) {
+ private static String parseTokenRequestResponse(String key, String tokenRequestResponse) {
- String temp = tokenRequestResponse.replaceFirst(Pattern.quote("{\"access_token\":\""),"");
+ String temp = tokenRequestResponse.replaceFirst(Pattern.quote("{\""+key+"\":\""),"");
String token = temp.split(Pattern.quote("\""))[0];
temp = temp.replaceFirst(Pattern.quote(token + "\",\"token_type\":\""),"");
@@ -165,17 +190,21 @@ private static String parseTokenRequestResponse(String tokenRequestResponse) {
return tokenType + " " + token;
}
- private static String getTokenRequestResponse() throws IOException {
+ private static String getTokenRequestResponse(String data) throws IOException {
HttpURLConnection connection = (HttpURLConnection) new URL("http://localhost:2000/oauth2/token").openConnection();
connection.setRequestMethod("POST");
- sendPostData(connection, createTokenRequestParameters());
+ sendPostData(connection, data);
return readResponse(connection);
}
private static String createTokenRequestParameters() {
- return "grant_type=client_credentials&client_id=" + clientId + "&client_secret=" + clientSecret;
+ return "grant_type=password&client_id=" + clientId + "&client_secret=" + clientSecret + "&username=john&password=password";
+ }
+
+ private static String createTokenRequestParametersAfterRefresh(String refreshToken) {
+ return "grant_type=refresh_token&client_id=" + clientId + "&client_secret=" + clientSecret + "&refresh_token=" + refreshToken;
}
private static String readResponse(HttpURLConnection connection) throws IOException {
diff --git a/core/src/test/java/com/predic8/membrane/core/openapi/serviceproxy/XMembraneExtensionSecurityTest.java b/core/src/test/java/com/predic8/membrane/core/openapi/serviceproxy/XMembraneExtensionSecurityTest.java
index 7680d3efdd..48ee9e013f 100644
--- a/core/src/test/java/com/predic8/membrane/core/openapi/serviceproxy/XMembraneExtensionSecurityTest.java
+++ b/core/src/test/java/com/predic8/membrane/core/openapi/serviceproxy/XMembraneExtensionSecurityTest.java
@@ -20,6 +20,7 @@
import org.junit.jupiter.api.*;
import java.io.*;
+import java.net.URISyntaxException;
import java.util.*;
import static com.predic8.membrane.core.openapi.serviceproxy.APIProxy.*;
@@ -31,7 +32,7 @@ public class XMembraneExtensionSecurityTest {
OpenAPIPublisherInterceptor interceptor;
@BeforeEach
- void setUp() throws IOException, ClassNotFoundException {
+ void setUp() throws IOException, ClassNotFoundException, URISyntaxException {
Router router = new Router();
router.setBaseLocation("");
OpenAPIRecordFactory factory = new OpenAPIRecordFactory(router);
@@ -40,7 +41,6 @@ void setUp() throws IOException, ClassNotFoundException {
Map records = factory.create(Collections.singletonList(spec));
interceptor = new OpenAPIPublisherInterceptor(records);
-
}
@Test
diff --git a/core/src/test/java/com/predic8/membrane/core/openapi/util/UtilsTest.java b/core/src/test/java/com/predic8/membrane/core/openapi/util/UtilsTest.java
index cde6fadbcf..0f5e17b646 100644
--- a/core/src/test/java/com/predic8/membrane/core/openapi/util/UtilsTest.java
+++ b/core/src/test/java/com/predic8/membrane/core/openapi/util/UtilsTest.java
@@ -24,10 +24,12 @@
import java.io.*;
import java.net.*;
+import java.nio.charset.StandardCharsets;
import java.util.*;
import static com.predic8.membrane.core.http.MimeType.*;
import static com.predic8.membrane.core.openapi.util.Utils.*;
+import static java.util.Objects.requireNonNull;
import static org.junit.jupiter.api.Assertions.*;
class UtilsTest {
@@ -248,4 +250,16 @@ void getOpenapiValidatorResponseWithNoContentType() {
});
}
+ @Test
+ void getResourceAsStreamValidResource() throws IOException {
+ assertEquals("baz",
+ new String(requireNonNull(
+ getResourceAsStream(this, "/test/foo.bar")).readAllBytes(), StandardCharsets.UTF_8)
+ );
+ }
+
+ @Test
+ void getResourceAsStreamInvalidResource() {
+ assertThrows(FileNotFoundException.class, () -> getResourceAsStream(this, "/doesnot.exist"));
+ }
}
\ No newline at end of file
diff --git a/distribution/examples/embedding-java/pom.xml b/distribution/examples/embedding-java/pom.xml
index e3b0c13404..549fafd873 100644
--- a/distribution/examples/embedding-java/pom.xml
+++ b/distribution/examples/embedding-java/pom.xml
@@ -24,7 +24,7 @@
ch.qos.logback
logback-classic
- 1.5.7
+ 1.5.8
org.membrane-soa