I have a spring boot application and I am trying to request token from azure using the following code:
public String getTokenFromAzure() {
String token = null;
ConfidentialClientApplication application = getApplication();
final String claims = JsonSerializer.convertToJson(new Employee("public"));
final com.microsoft.aad.msal4j.ClaimsRequest claims1 = CustomClaimRequest.formatAsClaimsRequest(claims);
ClaimsRequest claims2 = new ClaimsRequest();
claims2.requestClaimInIdToken(claims, null);
MyClaims claims3 = new MyClaims();
claims3.requestClaimInAccessToken(claims,new RequestedClaimAdditionalInfo(true,"value", Arrays.asList("employeeid","dummy")));
if (application == null) {
log.error("application is not instantiated");
} else {
ClientCredentialParameters parameters = ClientCredentialParameters.builder(Collections.singleton(clientId + "/.default")).claims(claims3).build();
IAuthenticationResult auth = application.acquireToken(parameters).join();
if (auth == null) {
log.info("auth still == null");
} else {
log.info("idToken: " + auth.idToken());
log.info("accessToken: " + auth.accessToken());
token = isEmpty(auth.idToken()) ? auth.accessToken() : auth.idToken();
}
}
return token;
}
private ConfidentialClientApplication getApplication() {
if (application == null) {
try {
application = ConfidentialClientApplication.builder(clientId, ClientCredentialFactory.createFromSecret(clientSecret)).authority("https://login.microsoftonline.com/" + tenantId + "/").build();
} catch (MalformedURLException e) {
log.error("unable to instantiate application for tenant " + tenantId + " with client " + clientId + " with configuration", e);
}
}
return application;
}
static class MyClaims extends ClaimsRequest {
#Override
protected void requestClaimInAccessToken(String claim, RequestedClaimAdditionalInfo requestedClaimAdditionalInfo) {
super.requestClaimInAccessToken(claim, requestedClaimAdditionalInfo);
}
}
I have tried with claims1, claims2 and with claims3. I am getting a functional access token but the claims are not set.
These are the dependencies that I am using:
<dependency>
<groupId>com.microsoft.azure</groupId>
<artifactId>applicationinsights-spring-boot-starter</artifactId>
<version>2.6.1</version>
</dependency>
<dependency>
<groupId>com.microsoft.azure</groupId>
<artifactId>applicationinsights-logging-logback</artifactId>
<version>2.6.1</version>
</dependency>
<dependency>
<groupId>com.microsoft.azure</groupId>
<artifactId>azure-active-directory-spring-boot-starter</artifactId>
<version>2.3.2</version>
</dependency>
<dependency>
<groupId>com.microsoft.graph</groupId>
<artifactId>microsoft-graph</artifactId>
<version>2.5.0</version>
</dependency>
<dependency>
<groupId>com.microsoft.graph</groupId>
<artifactId>microsoft-graph-auth</artifactId>
<version>0.2.0</version>
</dependency>
<dependency>
<groupId>com.microsoft.azure</groupId>
<artifactId>azure-storage</artifactId>
<version>4.2.0</version>
</dependency>
<dependency>
<groupId>com.azure</groupId>
<artifactId>azure-identity</artifactId>
<version>1.3.5</version>
<scope>test</scope>
</dependency>
<!-- https://mvnrepository.com/artifact/com.microsoft.azure/msal4j -->
<dependency>
<groupId>com.microsoft.azure</groupId>
<artifactId>msal4j</artifactId>
<version>1.11.0</version>
</dependency>
Does anyone knows the correct way to add the claims into the jwt token?
You can add custom key-value pairs to the JWT's body as custom claims. It may be a user's department at work, a user's role or privilege, or whatever else you need to add to JWT. For instance, I am including two custom claims for the user's role and department at work in the code sample below.
String token = Jwts.builder()
.setSubject(subject)
.setExpiration(expDate)
.claim("Role", "Admin")
.claim("Department", "Product development")
.signWith(SignatureAlgorithm.HS512, secret )
.compact();
In the above code example, Role and Department are two custom claims that I have added. You can expand JWT's body of claims as necessary. Just keep in mind not to include sensitive data such as a user password or token secret. You may examine and decode JWT assertions.
Use the following bit of code to read the custom Claims from the JWT token's body:
Claims claims = Jwts.parser()
.setSigningKey(tokenSecret)
.parseClaimsJws(jwt).getBody();
// Reading Reserved Claims
System.out.println("Subject: " + claims.getSubject());
System.out.println("Expiration: " + claims.getExpiration());
// Reading Custom Claims
System.out.println("Role: " + claims.get("Role"));
System.out.println("Department: " + claims.get("Department"));
Remember that JWT is a Base64 encoded string and can be easily decoded. Therefore, you should not put into Claims any user details that are sensitive. Even though the information in Claims cannot be altered, this information can be viewed by the Base64-decoding JWT token.
Related
Getting below error while trying to create an s3 bucket of aws using java API :
Error : Exception in thread "main" java.lang.NoClassDefFoundError: software/amazon/awssdk/protocols/query/internal/unmarshall/AwsXmlErrorUnmarshaller at software.amazon.awssdk.protocols.xml.internal.unmarshall.AwsXmlErrorTransformer.(AwsXmlErrorTransformer.java:40) at software.amazon.awssdk.protocols.xml.internal.unmarshall.AwsXmlErrorTransformer.(AwsXmlErrorTransformer.java:34) at software.amazon.awssdk.protocols.xml.internal.unmarshall.AwsXmlErrorTransformer$Builder.build(AwsXmlErrorTransformer.java:113) at software.amazon.awssdk.protocols.xml.AwsXmlProtocolFactory.createErrorTransformer(AwsXmlProtocolFactory.java:135) at software.amazon.awssdk.protocols.xml.AwsS3ProtocolFactory.createErrorCouldBeInBodyResponseHandler(AwsS3ProtocolFactory.java:80) at software.amazon.awssdk.services.s3.DefaultS3Client.createBucket(DefaultS3Client.java:1144) at com.act.niti.main(niti.java:33) Caused by: java.lang.ClassNotFoundException: software.amazon.awssdk.protocols.query.internal.unmarshall.AwsXmlErrorUnmarshaller at java.net.URLClassLoader.findClass(URLClassLoader.java:382) at java.lang.ClassLoader.loadClass(ClassLoader.java:418) at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:355) at java.lang.ClassLoader.loadClass(ClassLoader.java:351) ..
Code :
try {
Region region = Region.US_EAST_2;
S3Client s3 = S3Client.builder()
.region(region)
.build();
S3Waiter s3Waiter = s3.waiter();
CreateBucketRequest bucketRequest = CreateBucketRequest.builder()
.bucket("abc")
.build();
s3.createBucket(bucketRequest); //creating s3 bucket
System.out.println("bucket........abc");
HeadBucketRequest bucketRequestWait = HeadBucketRequest.builder()
.bucket("abc")
.build();
// Wait until the bucket is created and print out the response
WaiterResponse<HeadBucketResponse> waiterResponse =
s3Waiter.waitUntilBucketExists(bucketRequestWait);
waiterResponse.matched().response().ifPresent(System.out::println);
System.out.println("abc" +" is ready");
} catch (S3Exception e) {
System.err.println(e.awsErrorDetails().errorMessage());
System.exit(1);
}
Note : Using java 8
POM Xml :
<dependencies>
<dependency>
<groupId>com.amazonaws</groupId>
<artifactId>aws-java-sdk</artifactId>
<version>1.11.570</version>
</dependency>
<dependency>
<groupId>software.amazon.awssdk</groupId>
<artifactId>s3</artifactId>
<version>2.17.269</version>
</dependency>
<dependency>
<groupId>software.amazon.awssdk</groupId>
<artifactId>ec2</artifactId>
<version>2.5.10</version>
</dependency>
<!-- https://mvnrepository.com/artifact/com.fasterxml.jackson.core/jackson-databind -->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.9.10</version>
</dependency>
<!-- https://mvnrepository.com/artifact/com.amazonaws/aws-java-sdk-gamelift -->
<dependency>
<groupId>com.amazonaws</groupId>
<artifactId>aws-java-sdk-gamelift</artifactId>
<version>1.11.647</version>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-api</artifactId>
<version>5.8.2</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-engine</artifactId>
<version>5.8.2</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.junit.platform</groupId>
<artifactId>junit-platform-commons</artifactId>
<version>1.8.2</version>
</dependency>
<dependency>
<groupId>org.junit.platform</groupId>
<artifactId>junit-platform-launcher</artifactId>
<version>1.8.2</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>software.amazon.awssdk</groupId>
<artifactId>s3-transfer-manager</artifactId>
<version>2.17.103-PREVIEW</version>
</dependency>
<dependencies>
Looks like you are mixing up V1 and V2 in your POM file. You have
<dependency>
<groupId>com.amazonaws</groupId>
<artifactId>aws-java-sdk</artifactId>
<version>1.11.570</version>
</dependency>
There is no need for this Dep when using AWS SDK for V2. S3Client is a V2 Service Client. In fact -- your errors are most likely related to mxing up SDK versions in your POM file.
The POM file that you should use can be found in the AWS Github repo here:
https://github.com/awsdocs/aws-doc-sdk-examples/tree/main/javav2/example_code/s3
If you are not familiar with AWS SDK for Java v2, refer to the DEV Guide:
https://docs.aws.amazon.com/sdk-for-java/latest/developer-guide/home.html
The V2 Java code that works when you use the proper POM file is here:
package com.example.s3;
// snippet-start:[s3.java2.create_bucket_waiters.import]
import software.amazon.awssdk.auth.credentials.ProfileCredentialsProvider;
import software.amazon.awssdk.core.waiters.WaiterResponse;
import software.amazon.awssdk.regions.Region;
import software.amazon.awssdk.services.s3.S3Client;
import software.amazon.awssdk.services.s3.model.CreateBucketRequest;
import software.amazon.awssdk.services.s3.model.HeadBucketRequest;
import software.amazon.awssdk.services.s3.model.HeadBucketResponse;
import software.amazon.awssdk.services.s3.model.S3Exception;
import software.amazon.awssdk.services.s3.waiters.S3Waiter;
import java.net.URISyntaxException;
// snippet-end:[s3.java2.create_bucket_waiters.import]
/**
* Before running this Java V2 code example, set up your development environment, including your credentials.
*
* For more information, see the following documentation topic:
*
* https://docs.aws.amazon.com/sdk-for-java/latest/developer-guide/get-started.html
*/
public class CreateBucket {
public static void main(String[] args) throws URISyntaxException {
final String usage = "\n" +
"Usage:\n" +
" <bucketName> \n\n" +
"Where:\n" +
" bucketName - The name of the bucket to create. The bucket name must be unique, or an error occurs.\n\n" ;
if (args.length != 1) {
System.out.println(usage);
System.exit(1);
}
String bucketName = args[0];
System.out.format("Creating a bucket named %s\n", bucketName);
ProfileCredentialsProvider credentialsProvider = ProfileCredentialsProvider.create();
Region region = Region.US_EAST_1;
S3Client s3 = S3Client.builder()
.region(region)
.credentialsProvider(credentialsProvider)
.build();
createBucket (s3, bucketName);
s3.close();
}
// snippet-start:[s3.java2.create_bucket_waiters.main]
public static void createBucket( S3Client s3Client, String bucketName) {
try {
S3Waiter s3Waiter = s3Client.waiter();
CreateBucketRequest bucketRequest = CreateBucketRequest.builder()
.bucket(bucketName)
.build();
s3Client.createBucket(bucketRequest);
HeadBucketRequest bucketRequestWait = HeadBucketRequest.builder()
.bucket(bucketName)
.build();
// Wait until the bucket is created and print out the response.
WaiterResponse<HeadBucketResponse> waiterResponse = s3Waiter.waitUntilBucketExists(bucketRequestWait);
waiterResponse.matched().response().ifPresent(System.out::println);
System.out.println(bucketName +" is ready");
} catch (S3Exception e) {
System.err.println(e.awsErrorDetails().errorMessage());
System.exit(1);
}
}
// snippet-end:[s3.java2.create_bucket_waiters.main]
}
I'm trying to run a simple example with JOOQ, but It hasn't been possible. I'm using IntelliJ as IDE and making sure that I'm using the correct JDK.
The solution here does not apply since I'm using the JDK 18 and the JOOQ version of 3.17.2.
This is the code snippet:
public static void connectToDb() {
String userName = "root";
String password = "";
String url = "jdbc:mysql://localhost:3306/javaspark-db";
try (Connection conn = DriverManager.getConnection(url, userName, password)) {
DSLContext dslContext = DSL.using(conn, SQLDialect.MYSQL);
Author AUTHOR = Author.AUTHOR;
// Getting error just here at select()
Result<Record> result = dslContext.select().from(AUTHOR).fetch();
for (Record r : result) {
Integer id = r.getValue(AUTHOR.ID);
String firstName = r.getValue(AUTHOR.FIRST_NAME);
String lastName = r.getValue(AUTHOR.LAST_NAME);
System.out.println("ID: " + id + " first name: " + firstName + " last name: " + lastName);
}
}
// For the sake of this tutorial, let's keep exception handling simple
catch (Exception e) {
e.printStackTrace();
}
}
}
The error:
/Users/xxxx/xxxx/todo-spark-app/src/main/java/app/Application.java:26:56
java: cannot access java.util.concurrent.Flow
class file for java.util.concurrent.Flow not found
The maven dependencies:
<dependency>
<groupId>org.jooq</groupId>
<artifactId>jooq</artifactId>
<version>3.17.2</version>
</dependency>
<dependency>
<groupId>org.jooq</groupId>
<artifactId>jooq-meta</artifactId>
<version>3.17.2</version>
</dependency>
<dependency>
<groupId>org.jooq</groupId>
<artifactId>jooq-codegen</artifactId>
<version>3.17.2</version>
</dependency>
Well after all it seems that I forgot to change the maven.compiler.source and target in my pom.xml file.
So in my pom.xml file I changed this:
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
</properties>
To be like this:
<properties>
<maven.compiler.source>18</maven.compiler.source>
<maven.compiler.target>18</maven.compiler.target>
</properties>
And that got my app running. Silly mistake indeed.
I'm trying to use imports from SES and SQS at the same time but the combination causes an error to be thrown with the .withBody method. I'm guessing it's to do with the dependencies but they are at the latest version.
Error:(116,54) java:incompatible types:com.amazonaws.services.simpleemail.model.Body cannot be converted to java.lang.String
import com.amazonaws.services.sqs.AmazonSQS;
import com.amazonaws.services.sqs.AmazonSQSClientBuilder;
import com.amazonaws.services.sqs.model.Message;
import com.amazonaws.services.sqs.model.ReceiveMessageRequest;
import com.amazonaws.services.simpleemail.AmazonSimpleEmailService;
import com.amazonaws.services.simpleemail.AmazonSimpleEmailServiceClientBuilder;
import com.amazonaws.services.simpleemail.model.*;
public void email(S3Event event, Person person, Boolean error) {
ObjectMapper mapper = new ObjectMapper();
String emailText = null;
if (error) {
emailText = "Error! No image in file!";
} else {
try {
emailText = mapper.writeValueAsString(person);
} catch (JsonProcessingException e) {
e.printStackTrace();
}
}
String key = event.getRecords().get(0).getS3().getObject().getKey();
AmazonSimpleEmailService client =
AmazonSimpleEmailServiceClientBuilder.standard().withRegion(Regions.EU_WEST_1).build();
Body body = new Body().withText(new Content().withData(emailText));
SendEmailRequest request = new SendEmailRequest().withDestination(
new Destination().withToAddresses(person.getEmail()))
.withMessage(new Message()
.withBody(new Body().withHtml(new
Content().withCharset("UTF8").withData(emailText)))
.withSubject(new Content()
.withCharset("UTF-8").withData("Message from passport service.")))
.withSource(person.getEmail());
client.sendEmail(request);
}
public void getBaseCodeFromSQS() {
AmazonSQS sqs = AmazonSQSClientBuilder.defaultClient();
try {
ReceiveMessageRequest receiveMessageRequest = new ReceiveMessageRequest("https://sqs.eu-
west-1.amazonaws.com/416031944655/TISFEXP-PSS-2-QUEUE");
List<Message> messages = sqs.receiveMessage(receiveMessageRequest).getMessages();
for (Message message : messages) {
LOGGER.info("MessageId: " + message.getMessageId());
LOGGER.info("ReceiptHandle: " + message.getReceiptHandle());
LOGGER.info("MD5OfBody: " + message.getMD5OfBody());
LOGGER.info("Body: " + message.getBody());
for (final Map.Entry<String, String> entry : message.getAttributes().entrySet())
{
LOGGER.info("Attribute - Name: " + entry.getKey());
LOGGER.info("Attribute - Value: " + entry.getValue());
}
}
} catch (Exception e) {
LOGGER.error(e);
}
}
<dependency>
<groupId>com.amazonaws</groupId>
<artifactId>aws-java-sdk-sqs</artifactId>
<version>1.11.634</version>
</dependency>
<dependency>
<groupId>com.amazonaws</groupId>
<artifactId>amazon-sqs-java-messaging-lib</artifactId>
<version>1.0.8</version>
<type>jar</type>
</dependency>
<dependency>
<groupId>com.amazonaws</groupId>
<artifactId>aws-java-sdk-ses</artifactId>
<version>1.11.634</version>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>com.amazonaws</groupId>
<artifactId>aws-java-sdk-bom</artifactId>
<version>1.11.634</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
There is a Message class defined in both SES and SQS packages. You are using the Message class defined in the SQS package. You should use the one defined in SES package instead.
https://docs.aws.amazon.com/AWSJavaSDK/latest/javadoc/com/amazonaws/services/sqs/model/Message.html
https://docs.aws.amazon.com/AWSJavaSDK/latest/javadoc/com/amazonaws/services/simpleemail/model/Message.html
SendEmailRequest request = new SendEmailRequest().withDestination(
new Destination().withToAddresses(person.getEmail()))
.withMessage(new com.amazonaws.services.simpleemail.model.Message()
.withBody(new Body().withHtml(new
Content().withCharset("UTF8").withData(emailText)))
.withSubject(new Content()
.withCharset("UTF-8").withData("Message from passport service.")))
.withSource(person.getEmail());
I have been writing some simple unit testing routines for a simple spring web application. When I add #JsonIgnore annotation on a getter method of a resource, the resulting json object does not include the corresponding json element. So when my unit test routine tries to test if this is null (which is the expected behavior for my case, I don't want the password to be available in json object), test routine runs into an exception:
java.lang.AssertionError: No value for JSON path: $.password, exception: No results for path: $['password']
This is the unit test method I wrote, testing the 'password' field with is(nullValue()) method:
#Test
public void getUserThatExists() throws Exception {
User user = new User();
user.setId(1L);
user.setUsername("zobayer");
user.setPassword("123456");
when(userService.getUserById(1L)).thenReturn(user);
mockMvc.perform(get("/users/1"))
.andExpect(jsonPath("$.username", is(user.getUsername())))
.andExpect(jsonPath("$.password", is(nullValue())))
.andExpect(jsonPath("$.links[*].href", hasItem(endsWith("/users/1"))))
.andExpect(status().isOk())
.andDo(print());
}
I have also tried it with jsonPath().exists() which gets a similar exception stating that the path doesn't exist. I am sharing some more code snippets so that the whole situation becomes more readable.
The controller method I am testing looks something like this:
#RequestMapping(value="/users/{userId}", method= RequestMethod.GET)
public ResponseEntity<UserResource> getUser(#PathVariable Long userId) {
logger.info("Request arrived for getUser() with params {}", userId);
User user = userService.getUserById(userId);
if(user != null) {
UserResource userResource = new UserResourceAsm().toResource(user);
return new ResponseEntity<>(userResource, HttpStatus.OK);
} else {
return new ResponseEntity<>(HttpStatus.NOT_FOUND);
}
}
I am using spring hateos resource assembler for converting entity to resource objects and this is my resource class:
public class UserResource extends ResourceSupport {
private Long userId;
private String username;
private String password;
public Long getUserId() {
return userId;
}
public void setUserId(Long userId) {
this.userId = userId;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
#JsonIgnore
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
}
I understand why this is getting an exception, also in a way, the test is successful that it could not find the password field. But what I want to do is, run this test to ensure that the field is not present, or if present, it contains null value. How can I achieve this?
There is a similar post in stack overflow:
Hamcrest with MockMvc: check that key exists but value may be null
In my case, the field may be non existent as well.
For the record, these are the versions of test packages I am using:
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
<version>2.6.1</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-annotations</artifactId>
<version>2.6.1</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.6.1</version>
</dependency>
<dependency>
<groupId>com.jayway.jsonpath</groupId>
<artifactId>json-path</artifactId>
<version>2.0.0</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.jayway.jsonpath</groupId>
<artifactId>json-path-assert</artifactId>
<version>2.0.0</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-all</artifactId>
<version>1.10.19</version>
<scope>test</scope>
</dependency>
[EDIT]
To be more precise, say, you have to write a test for an entity where you know some of the fields need to be null or empty or should not even exists, and you don't actually go through the code to see if there is a JsonIgnore added on top of the property. And you want your tests to pass, how can I do this.
Please feel free to tell me that this is not practical at all, but still would be nice to know.
[EDIT]
The above test succeeds with the following older json-path dependencies:
<dependency>
<groupId>com.jayway.jsonpath</groupId>
<artifactId>json-path</artifactId>
<version>0.9.1</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.jayway.jsonpath</groupId>
<artifactId>json-path-assert</artifactId>
<version>0.9.1</version>
<scope>test</scope>
</dependency>
[EDIT] Found a quickfix that works with latest version of jayway.jasonpath after reading the documentation of spring's json path matcher.
.andExpect(jsonPath("$.password").doesNotExist())
I had the same problem with the newer version. It looks to me that the doesNotExist() function will verify that the key is not in the result:
.andExpect(jsonPath("$.password").doesNotExist())
There is a difference between the property that is present, but having null value, and the property not being present at all.
If the test should fail only when there is a non-null value, use:
.andExpect(jsonPath("password").doesNotExist())
If the test should fail as soon as the property is present, even with a null value, use:
.andExpect(jsonPath("password").doesNotHaveJsonPath())
#JsonIgnore is behaving as expected, not producing the password in the json output, so how could you expect to test something that you are explicitly excluding from the output?
The line:
.andExpect(jsonPath("$.property", is("some value")));
or even a test that the property is null:
.andExpect(jsonPath("$.property").value(IsNull.nullValue()));
correspond to a json like:
{
...
"property": "some value",
...
}
where the important part is the left side, that is the existence of "property":
Instead, #JsonIgnore is not producing the porperty in the output at all, so you can't expect it not in the test nor in the production output.
If you don't want the property in the output, it's fine, but you can't expect it in test.
If you want it empty in output (both in prod and test) you want to create a static Mapper method in the middle that is not passing the value of the property to the json object:
Mapper.mapPersonToRest(User user) {//exclude the password}
and then your method would be:
#RequestMapping(value="/users/{userId}", method= RequestMethod.GET)
public ResponseEntity<UserResource> getUser(#PathVariable Long userId) {
logger.info("Request arrived for getUser() with params {}", userId);
User user = Mapper.mapPersonToRest(userService.getUserById(userId));
if(user != null) {
UserResource userResource = new UserResourceAsm().toResource(user);
return new ResponseEntity<>(userResource, HttpStatus.OK);
} else {
return new ResponseEntity<>(HttpStatus.NOT_FOUND);
}
}
At this point, if your expectations are for Mapper.mapPersonToRest to return a user with a null password, you can write a normal Unit test on this method.
P.S. Of course the password is crypted on the DB, right? ;)
doesNotHaveJsonPath for checking that it is not in json body
I wanted to reuse the same code I use for testing for the parameter being supplied, and for it missing, and this is what I came up with
#Test
void testEditionFoundInRequest() throws JsonProcessingException {
testEditionWithValue("myEdition");
}
#Test
void testEditionNotFoundInRequest() {
try {
testEditionWithValue(null);
throw new RuntimeException("Shouldn't pass");
} catch (AssertionError | JsonProcessingException e) {
var msg = e.getMessage();
assertTrue(msg.contains("No value at JSON path"));
}
}
void testEditionWithValue(String edition) {
var HOST ="fakeHost";
var restTemplate = new RestTemplate();
var myRestClientUsingRestTemplate = new MyRestClientUsingRestTemplate(HOST, restTemplate);
MockRestServiceServer mockServer;
ObjectMapper objectMapper = new ObjectMapper();
String id = "userId";
var mockResponse = "{}";
var request = new MyRequest.Builder(id).edition(null).build();
mockServer = MockRestServiceServer.bindTo(restTemplate).bufferContent().build();
mockServer
.expect(method(POST))
// THIS IS THE LINE I'd like to say "NOT" found
.andExpect(jsonPath("$.edition").value(edition))
.andRespond(withSuccess(mockResponse, APPLICATION_JSON));
var response = myRestClientUsingRestTemplate.makeRestCall(request);
} catch (AssertionError | JsonProcessingException e) {
var msg = e.getMessage();
assertTrue(msg.contains("No value at JSON path"));
}
Hey, there is something wrong with the third alternative, because the loop in JcrServiceFactory is searching for properties starting with jcr.* (others are not passed along), but right after in RepositoryFactoryImpl (Jackrabbit impl) it is searched for "org.apache.jackrabbit.repository.home" in the collection of properties that was passed along... that doesn't make sense. even if org.apache.jackrabbit.repository.home is there, it doesn't start with PREFIX_JCR_CONFIG so it is not put into jcrConfig collection that goes to RepositoryFactoryImpl.getRepository()
It would make sense if Map<String, String> map = null; because there is if (parameters == null) condition in RepositoryFactoryImpl, but this does not
It happens in the init method
JcrServiceFactory.java
private TypeManager typeManager;
private Map<String, String> jcrConfig;
private String mountPath;
private JcrRepository jcrRepository;
#Override
public void init(Map<String, String> parameters) {
typeManager = new TypeManager();
readConfiguration(parameters);
jcrRepository = new JcrRepository(acquireJcrRepository(jcrConfig), mountPath, typeManager);
}
Caused by: org.apache.chemistry.opencmis.commons.exceptions.CmisConnectionException: No JCR repository factory for configured parameters
at org.apache.chemistry.opencmis.jcr.JcrServiceFactory.acquireJcrRepository(JcrServiceFactory.java:95)
at org.apache.chemistry.opencmis.jcr.JcrServiceFactory.init(JcrServiceFactory.java:61)
at org.apache.chemistry.opencmis.client.bindings.spi.local.CmisLocalSpi.getSpiInstance(CmisLocalSpi.java:94)
... 34 more
private void readConfiguration(Map<String, String> parameters) {
Map<String, String> map = new HashMap<String, String>();
List<String> keys = new ArrayList<String>(parameters.keySet());
Collections.sort(keys);
/* the loop is searching for properties starting with jcr.* */
for (String key : keys) {
if (key.startsWith(PREFIX_JCR_CONFIG)) {
String jcrKey = key.substring(PREFIX_JCR_CONFIG.length());
String jcrValue = replaceSystemProperties(parameters.get(key));
map.put(jcrKey, jcrValue);
}
else if (MOUNT_PATH_CONFIG.equals(key)) {
mountPath = parameters.get(key);
log.debug("Configuration: " + MOUNT_PATH_CONFIG + '=' + mountPath);
}
else {
log.warn("Configuration: unrecognized key: " + key);
}
}
jcrConfig = Collections.unmodifiableMap(map);
log.debug("Configuration: jcr=" + jcrConfig);
}
But here the parameter Map is empty {} and it returns null; because it is searching for RepositoryFactoryImpl.REPOSITORY_HOME, which is org.apache.jackrabbit.repository.home
RepositoryFactoryImpl.java
/* parameters = jcrConfig */
public Repository getRepository(Map parameters) throws RepositoryException {
if (parameters == null) {
return getRepository(null, Collections.emptyMap());
} else if (parameters.containsKey(REPOSITORY_HOME)) {
String home = parameters.get(REPOSITORY_HOME).toString();
return getRepository(home, parameters);
} else if (parameters.containsKey(JcrUtils.REPOSITORY_URI)) {
Object parameter = parameters.get(JcrUtils.REPOSITORY_URI);
try {
URI uri = new URI(parameter.toString().trim());
String scheme = uri.getScheme();
if (("file".equalsIgnoreCase(scheme)
|| "jcr-jackrabbit".equalsIgnoreCase(scheme))
&& uri.getAuthority() == null) {
File file = new File(uri.getPath());
if (file.isFile()) {
return null; // Not a (possibly missing) directory
} else {
return getRepository(file.getPath(), parameters);
}
} else {
return null; // not a file: or jcr-jackrabbit: URI
}
} catch (URISyntaxException e) {
return null; // not a valid URI
}
} else {
return null; // unknown or insufficient parameters
}
}
<dependencies>
<dependency>
<groupId>javax.jcr</groupId>
<artifactId>jcr</artifactId>
<version>2.0</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.apache.jackrabbit</groupId>
<artifactId>jackrabbit-core</artifactId>
<version>2.2.4</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.apache.jackrabbit</groupId>
<artifactId>jackrabbit-api</artifactId>
<version>2.2.4</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<version>1.5.11</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.testng</groupId>
<artifactId>testng</artifactId>
<version>5.14</version>
<type>jar</type>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.apache.chemistry.opencmis</groupId>
<artifactId>chemistry-opencmis-server-jcr</artifactId>
<version>0.3.0-incubating-SNAPSHOT</version>
<classifier>classes</classifier>
</dependency>
<dependency>
<groupId>org.apache.chemistry.opencmis</groupId>
<artifactId>chemistry-opencmis-client-bindings</artifactId>
<version>0.3.0-incubating-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.apache.chemistry.opencmis</groupId>
<artifactId>chemistry-opencmis-client-api</artifactId>
<version>0.3.0-incubating-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.apache.chemistry.opencmis</groupId>
<artifactId>chemistry-opencmis-client-impl</artifactId>
<version>0.3.0-incubating-SNAPSHOT</version>
</dependency>
The answer is right in the loop I complained about :-)
String jcrKey = key.substring(PREFIX_JCR_CONFIG.length());
It's a substring, so it cuts jcr. of and the rest goes on...
parameters.put("jcr.org.apache.jackrabbit.repository.home", repositoryHome);
It's a tricky and one kinda needs to figure out all this from debugging.
you need to configure your 'repository.properties' at 'WEB-INF/classes' with below entry.
jcr.org.apache.jackrabbit.repository.home={user.home}\jcr-repository (your repository location).
Cheers.