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