I'm using smallrye.jwt as authorization tool. My quarkus app does not generate jwt tokens, but verifies them having secret key.
Problem is that incoming tokens have sub claim of non-string type, but parser expects java.lang.String (I receive 45 instead of "45"). I don't have access to token generation, so I need it to work with what I have. Apparently there's no way to make it work with microprofile. How can I achieve it?
The error I get (I replaced a few values with ...):
Caused by: org.jose4j.jwt.consumer.InvalidJwtException: JWT (claims->{"iss":"...","iat":...,"exp":...,"nbf":...,"jti":"...","sub":45,"prv":"...","pid": ...}) rejected due to invalid claims or other invalid content. Additional details: [[18] The value of the 'sub' claim is not the expected type (1517 - Cannot cast java.lang.Long to java.lang.String)]
My application.properties:
smallrye.jwt.verify.key-format=JWK
smallrye.jwt.verify.key.location=JWTSecret.jwk
smallrye.jwt.verify.algorithm=HS256
Looks like it is coming directly from Jose4j, sub is a standard JWT claim, https://www.rfc-editor.org/rfc/rfc7519#section-4.1.2, and looks like Jose4J is expecting it to be a string as per the RFC7519 text
In the end I had to provide custom factory implementation of JWTCallerPrincipalFactory, where I manually parsed json and replaced "sub" claim with the same claim of String type.
import javax.annotation.Priority
import javax.enterprise.context.ApplicationScoped
import javax.enterprise.inject.Alternative
import io.smallrye.jwt.auth.principal.*
import org.jose4j.jwt.JwtClaims
import org.jose4j.jwt.consumer.InvalidJwtException
import java.nio.charset.StandardCharsets
import java.util.*
import com.your.company.SignatureValidator // this is your custom validator for your algorithm
#ApplicationScoped
#Alternative
#Priority(1)
class MyJWTCallerPrincipalFactory : JWTCallerPrincipalFactory() {
#Inject
#field:Default
lateinit var signatureValidator: SignatureValidator
override fun parse(token: String, authContextInfo: JWTAuthContextInfo): JWTCallerPrincipal {
return try {
val isSignatureValid = signatureValidator.validate(token)
if (!isSignatureValid) {
throw ForbiddenException("Invalid Token Signature")
}
val json = String(Base64.getUrlDecoder().decode(token.split("\\.".toRegex()).dropLastWhile { it.isEmpty() }
.toTypedArray()[1]), StandardCharsets.UTF_8)
val jwtClaims = JwtClaims.parse(json)
val subClaimValue = jwtClaims.getClaimValue("sub")
jwtClaims.setClaim("sub", subClaimValue.toString())
DefaultJWTCallerPrincipal(jwtClaims)
} catch (ex: Exception) {
throw ParseException(ex.message)
}
}
}
Related
I need to convert an object of Map<String,String> with keys like "some_att_name" to class object fields like someAttName.
I couldn't find an easy way to do this.
MapStruct does support this type of mapping (From Map to object) since v1.5.0.Beta1 as stated here.
What I want should look something like this (similar to how JSON converters work):
#Mapper
public interface MapToObjectMapper {
MapToObjectMapper INSTANCE = Mappers.getMapper(MapToObjectMapper.class);
#Mapping(strategy = SnakeCaseToCamelCaseStrategy.class)
MyObject toMyObject(Map<String,String> map);
}
You'll have to translate the keys by yourself, but that's not that hard. Here is how I do it:
import org.mapstruct.Mapper;
import org.mapstruct.factory.Mappers;
import java.util.Collections;
import java.util.Map;
import java.util.regex.Pattern;
import static java.util.stream.Collectors.collectingAndThen;
import static java.util.stream.Collectors.toMap;
#Mapper
public interface MapToObjectMapper {
MapToObjectMapper INSTANCE = Mappers.getMapper(MapToObjectMapper.class);
private static String snakeToCamel(String snakeCaseString) {
// You can use Guava, Apache Commons, write it yourself or just use this one
// Credits to https://stackoverflow.com/a/67605103/4494577
return Pattern.compile("_([a-z])")
.matcher(snakeCaseString)
.replaceAll(m -> m.group(1).toUpperCase());
}
MyObject toMyObject(Map<String, String> map);
default MyObject toMyObjectFromSnakeCaseMap(Map<String, String> snakeKeyPropertyMap) {
return toMyObject(snakeKeyPropertyMap.entrySet().stream()
.collect(collectingAndThen(
toMap(s -> snakeToCamel(s.getKey()), Map.Entry::getValue),
Collections::unmodifiableMap)));
}
}
Full example: https://github.com/jannis-baratheon/stackoverflow--mapstruct-snake-case-map-mapping/
Looking at the docs I can't see a 1 step mapping. I think referring to From snake_case to camelCase in Java for converting your Map's keys to camelCase and then going for a mapper without #Mapping annotations might be your best chance.
I'm using Citrus 2.7.8 with Cucumber 2.4.0. I'm making a soap call and want to get the response and do some advanced parsing on it to validate a graphql response has matching values. (I understand how to do validations when it's something that just has one element, but I need something able to handle when there could be one or many elements returned (for example, 1 vehicle or 4 vehicles)). To make my validation very dynamic and able to handle many different 'quotes', I want to store the response to a Citrus variable and then make it available to java to read in the file and do the advanced parsing and validation.
The TestContext injection doesn't appear to currently work with cucumber (see https://github.com/citrusframework/citrus/issues/657) so I'm using the workaround here:
How to inject TestContext using TestRunner and cucumber to manually create the context. Without this I get a nullpointerexception on anything with the context.
I am able to use Citrus's message function to grab the soap response which is awesome. My echo statements in the console show that it successfully put the right value into the citrus variable. But I'm having problems making that available to java so that I can then open it up and parse through it.
I've scaled down my step definition file to just the pertinent code. My couple attempts are listed below along with the problems I encountered in their results.
Does anyone have any ideas on how I can successfully workaround the context issues and make my response available to java?
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import com.consol.citrus.Citrus;
import com.consol.citrus.annotations.CitrusFramework;
import com.consol.citrus.annotations.CitrusResource;
import com.consol.citrus.config.CitrusSpringConfig;
import com.consol.citrus.context.TestContext;
import com.consol.citrus.dsl.junit.JUnit4CitrusTestRunner;
import com.consol.citrus.dsl.runner.TestRunner;
import com.consol.citrus.ws.client.WebServiceClient;
import cucumber.api.java.en.When;
#ContextConfiguration(classes = CitrusSpringConfig.class)
public class CitrusSteps extends JUnit4CitrusTestRunner {
#CitrusFramework
private Citrus citrus;
#CitrusResource
private TestRunner runner;
#CitrusResource
private TestContext context;
#Autowired
private WebServiceClient getQuote;
#When("^I call getQuote with id \"([^\"]*)\"$")
public void i_call_getquote_with_id(String quoteId) throws Throwable {
context = citrus.createTestContext();
String soappayload = "my payload (taken out for privacy purposes)";
runner.soap(action -> action.client(getQuote)
.send()
.soapAction("getQuote")
.payload(soappayload));
runner.soap(action -> action.client(getQuote)
.receive()
.name("getQuoteResponseStoredMessage"));
//this bombs out on the context line with this: "com.consol.citrus.exceptions.CitrusRuntimeException: Unknown variable 'messageStoreGetQuoteResponse1'"
runner.variable("messageStoreGetQuoteResponse1", "citrus:message(getQuoteResponseStoredMessage.payload())");
runner.echo("First try: ${messageStoreGetQuoteResponse1}");
String firstTry = context.getVariable("messageStoreGetQuoteResponse1");
log.info("First Try java variable: " + firstTry);
//this bombs out on the context line with this: "com.consol.citrus.exceptions.CitrusRuntimeException: Unknown variable 'messageStoreGetQuoteResponse2'"
runner.createVariable("messageStoreGetQuoteResponse2", "citrus:message(getQuoteResponseStoredMessage.payload())");
runner.echo("Second try: ${messageStoreGetQuoteResponse2}");
String secondTry = context.getVariable("messageStoreGetQuoteResponse2");
log.info("Second Try java variable: " + secondTry);
//This stores the literal as the value - it doesn't store the message so it appears I can't use citrus functions within the context
context.setVariable("messageStoreGetQuoteResponse3", "citrus:message(getQuoteResponseStoredMessage.payload())");
String thirdTry = context.getVariable("messageStoreGetQuoteResponse3");
log.info("Third Try java variable: " + thirdTry);
}
}
A smart co-worker figured out a workaround for the injection not working w/ cucumber.
I replaced these two lines:
#CitrusResource
private TestContext context;
with these lines instead:
TestContext testContext;
public TestContext getTestContext() {
if (testContext == null) {
runner.run(new AbstractTestAction() {
#Override
public void doExecute(TestContext context) {
testContext = context;
}
});
}
return testContext;
}
Then within my step where I want the context, I can use the above method. In my case I wanted my message response, so I was able to use this and confirm that the response is now in my java variable:
String responseXML = getTestContext().getMessageStore().getMessage("getQuoteResponseStoredMessage").getPayload(String.class);
log.info("Show response XML: " + responseXML);
Request Body has one parameter such as int data. Where as if i pass extra parameter which is not there in the class it does not throw an error.
Why its able to consume the data which is not part of request body.
By default spring will recognize only the declared request params remaining parameters will be ignored. If you want to restrict it. you can do like this
Add HttpServletRequest request in the method parameter
String params = request.getQueryString();
in the method body validate the params.
But In my opinion a more pragmatic approach may be to ignore the invalid params.
Having used RESTful APIs from numerous vendors over the years, let me give you a "users" perspective.
A lot of times documentation is simply bad or out of date. Maybe a parameter name changed, maybe you enforce exact casing on the property names, maybe you have used the wrong font in your documentation .
So by default , REST API doesn't check for the extra attributes not presents in request body; otherwise checks whether the supplied attributes is present or not.
if you want not to deserialise the property which is not present; you can add validations as follows :
import java.io.Serializable;
import javax.validation.constraints.NotBlank;
public class DemoDto implements Serializable{
private static final long serialVersionUID = 1L;
private int id;
#NotBlank(message = "name can't be empty.")
private String name;
//getters & setters
}
Controller :
import javax.validation.Valid;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;
#RestController
public class TestController {
#PostMapping("/test")
public ResponseEntity<DemoDto> test(#RequestBody #Valid DemoDto demoDto){
return ResponseEntity.ok(demoDto);
}
}
In my Scalatra routes, I often use halt() to fail fast:
val user: User = userRepository.getUserById(params("userId"))
.getOrElse {
logger.warn(s"Unknown user: $userId")
halt(404, s"Unknown user: $userId")
}
As shown in the example, I also want to log a warning in those cases. But I'd like to avoid the code duplication between the halt() and the logger. It would be a lot cleaner to simply do:
val user: User = userRepository.getUserById(params("userId"))
.getOrElse(halt(404, s"Unknown user: $userId"))
What would be the best way of logging all "HaltExceptions" in a cross-cutting manner ?
I've considered:
1) Overriding the halt() method in my route:
override def halt[T](status: Integer, body: T, headers: Map[String, String])(implicit evidence$1: Manifest[T]): Nothing = {
logger.warn(s"Halting with status $status and message: $body")
super.halt(status, body, headers)
}
Aside from the weird method signature, I don't really like this approach, because I could be calling the real halt() by mistake instead of the overridden method, for example if I'm halting outside the route. In this case, no warning would be logged.
2) Use trap() to log all error responses:
trap(400 to 600) {
logger.warn(s"Error returned with status $status and body ${extractBodyInSomeWay()}")
}
But I'm not sure it's the best approach, especially since it adds 201 routes to the _statusRoutes Map (one mapping for each integer in the range...). I also don't know how to extract the body here ?
3) Enable some kind of response logging in Jetty for specific status codes ?
What would be the best approach to do this? Am I even approaching this correctly?
The easiest solution is doing it in a servlet filter like below:
package org.scalatra.example
import javax.servlet._
import javax.servlet.http.HttpServletResponse
class LoggingFilter extends Filter {
override def init(filterConfig: FilterConfig): Unit = ()
override def destroy(): Unit = ()
override def doFilter(request: ServletRequest, response: ServletResponse, chain: FilterChain): Unit = {
chain.doFilter(request, response)
val status = response.asInstanceOf[HttpServletResponse].getStatus
if (status >= 400 && status <= 600) {
// Do logging here!
}
}
}
Register this filter in your Bootstrap class (or it's possible even in web.xml):
package org.scalatra.example
import org.scalatra._
import javax.servlet.ServletContext
class ScalatraBootstrap extends LifeCycle {
override def init(context: ServletContext): Unit = {
context.addFilter("loggingFilter", new LoggingFilter())
context.getFilterRegistration("loggingFilter")
.addMappingForUrlPatterns(EnumSet.allOf(classOf[DispatcherType]), true, "/*")
// mount your servlets or filters
...
}
}
In my opinion, Scalatra should provide a way to trap halting easier essentially. In fact, there is a method named renderHaltException in ScalatraBase, it looks to be possible to add logging by overriding this method at a glance:
https://github.com/scalatra/scalatra/blob/cec3f75e3484f2233274b1af900f078eb15c35b1/core/src/main/scala/org/scalatra/ScalatraBase.scala#L512
However we can't do it actually because HaltException is package private and it can be accessed inside of org.scalatra package only. I wonder HaltException should be public.
I am working on a REST based interface where people get a json file. The client needs to access the file from another Domain. I use jsonp which works so far. My problem is the rendering in Grails. At the moment I use the 'as JSON' to marshalling the object:
render "${params.jsoncallback}(${user as JSON})"
The Json file getting to the client inclused all attributes, incluing the id and class, which I do not want to have in there. In case it is not jsonp, I do it this way, which works great:
render(contentType:'text/json'){
userName user.userName
userImage user.userImage
:
:
}
So how do I get the id and class attributes out of the json when rendering "user as JSON"? Any idea?
best regards,
Klaas
You can get rid of the class and id properties in the JSON result by creating a custom ObjectMarshaller.
// CustomDomainMarshaller.groovy in src/groovy:
import grails.converters.JSON;
import org.codehaus.groovy.grails.web.converters.ConverterUtil;
import org.codehaus.groovy.grails.web.converters.exceptions.ConverterException;
import org.codehaus.groovy.grails.web.converters.marshaller.ObjectMarshaller;
import org.codehaus.groovy.grails.web.json.JSONWriter;
import org.springframework.beans.BeanUtils;
public class CustomDomainMarshaller implements ObjectMarshaller<JSON> {
static EXCLUDED = ['metaClass','class','id','version']
public boolean supports(Object object) {
return ConverterUtil.isDomainClass(object.getClass());
}
public void marshalObject(Object o, JSON json) throws ConverterException {
JSONWriter writer = json.getWriter();
try {
writer.object();
def properties = BeanUtils.getPropertyDescriptors(o.getClass());
for (property in properties) {
String name = property.getName();
if(!EXCLUDED.contains(name)) {
def readMethod = property.getReadMethod();
if (readMethod != null) {
def value = readMethod.invoke(o, (Object[]) null);
writer.key(name);
json.convertAnother(value);
}
}
}
writer.endObject();
} catch (Exception e) {
throw new ConverterException("Exception in CustomDomainMarshaller", e);
}
}
}
You'll need to register in you grails-app/conf/BootStrap.groovy:
class BootStrap {
def init = { servletContext ->
grails.converters.JSON.registerObjectMarshaller(new CustomDomainMarshaller())
}
def destroy = {}
}
This should work in Grails >= 1.1
thanks for the 'quick' reply!
Man, it looks so easy in the end and took so long to figure out.
I got it working doing a map out of the values I needed and rendered them 'as json' like this:
def userProfile = user.get(randomUser)
def jsonData = [
username: userProfile.userName,
userimage: userProfile.userImage,
userstreet: userProfile.userStreet,
:
:
] as JSON
println jsonData
voila, there was the json I needed :)
It doesn't seem to me as if the JSON auto marshaller supports this.
You could use FlexJSON which allows you to exlude certain properties and wrap it into a custom Codec.
Also see here.