multiple exceptions being thrown in intelliJ on junit testing - java

So I am debugging this application that is returning null on emails in the Login method. When using Postman to check data. I am using IntelliJ. My userbean works, but when I try to use the repository it blows up with several exceptions.
public UserServiceBean login(UserServiceBean userServiceBean) {
//set the flag to false
userServiceBean.setSuccess(false);
User user =
userRepository
.findByUsernameAndPassword(
userServiceBean.getUsername(),
userServiceBean.getPassword());
if(null != user) {
BeanUtils.copyProperties(user, userServiceBean);
userServiceBean.setSuccess(true);
}
return userServiceBean;
I am first time ever trying to write Junit testing for this code, I am completely lost on why this isn't working. We are using Java8, mongoDB and Springboot...
import static org.junit.jupiter.api.Assertions.assertEquals;
import edu.team3.cocktailapp.beans.UserServiceBean;
import edu.team3.cocktailapp.models.User;
import edu.team3.cocktailapp.repositories.UserRepository;
import edu.team3.cocktailapp.services.UserService;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
I think my repository isn't working right?? Or maybe its coded wrong?? Most of the errors were with MongoDB regarding the repository-
Errors thrown
#SpringBootTest
class CocktailappApplicationTests {
#Autowired
UserRepository repository;
#Test
void testUserService() {
//testing login
UserService service = new UserService();
UserServiceBean bean = new UserServiceBean();
bean.setEmail("bean#testing");
bean.setUsername("Frank");
bean.setPassword("password");
assertEquals(bean.getUsername(), "Frank");
assertEquals(bean.getEmail(), "bean#testing");
assertEquals(bean.getPassword(), "password");
//testing repository
repository.save(
new User(
bean.getUsername(),
bean.getEmail(),
bean.getPassword()
)
);
UserServiceBean login = service.login(bean);
Any kind of help would be greatly appreciated-

Related

issue with #Aspectj logging if #EnableTransactionManagement added in sprig boot project

I tried to use #Aspect for logging all requests and responses the code is running fine and successful with a simple spring boot project with a common configuration and not have any init methods or DB call.
The below code is not working that includes init configuration and DB call for required data used #EnableTransactionManagement its getting Error while use #Aspect while running my application
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationListener;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.event.ContextRefreshedEvent;
import org.springframework.transaction.annotation.EnableTransactionManagement;
#Configuration
#EnableTransactionManagement
public class MyContextClass implements ApplicationListener<ContextRefreshedEvent>{
#Autowire
private MyServiceRepository myServiceRepo;
#Override
public void onApplicationEvent(ContextRefreshedEvent event) {
// Added Db call
CommonUtils.setYears(myServiceRepo.getTotalYears());
}
}
#Aspect logging code
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import org.springframework.util.StopWatch;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.*;
#Aspect
#Component
public class LoggingAspect {
private final Logger log = LoggerFactory.getLogger(this.getClass());
#Autowired
private HttpServletRequest request;
#Autowired
private HttpServletResponse response;
#Pointcut("within(#org.springframework.stereotype.Repository *)"
+ " || within(#org.springframework.stereotype.Service *)"
+ " || within(#org.springframework.web.bind.annotation.RestController *)")
public void springBeanPointcut() {
// Method is empty as this is just a Pointcut, the implementations are in the
// advices.
log.info("springBeanPointcut");
}
/**
* Pointcut that matches all Spring beans in the application's main packages.
*/
#Pointcut("within(com.test.mypackage..*)")
public void applicationPackagePointcut() {
// Method is empty as this is just a Pointcut, the implementations are in the
// advices.
log.info("applicationPackagePointcut");
}
#Around("applicationPackagePointcut() && springBeanPointcut()")
public Object logAround(ProceedingJoinPoint joinPoint) throws Throwable {
MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
String className = methodSignature.getDeclaringType().getSimpleName();
String methodName = methodSignature.getName();
String methodParameterNames = methodSignature.getMethod().toString();
if (request ! = null) {
log.info(request.getRequestURL());
log.info(request.getMethod());
}
if (response != null) {
log.info(response.getRequestURL());
log.info(response.getStatus());
}
final StopWatch stopWatch = new StopWatch();
stopWatch.start();
Object result = joinPoint.proceed();
stopWatch.stop();
}
this code build success. but when I run the application below error display the error in setYears
2021-05-07 20:14:58 [restartedMain] ERROR o.s.boot.SpringApplication-[SpringApplication.java:856]-Application run failed
java.lang.IllegalStateException: No thread-bound request found: Are you referring to request attributes outside of an actual web request, or processing a request outside of the originally receiving thread? If you are actually operating within a web request and still receive this message, your code is probably running outside of DispatcherServlet: In this case, use RequestContextListener or RequestContextFilter to expose the current request.
at org.springframework.web.context.request.RequestContextHolder.currentRequestAttributes(RequestContextHolder.java:131)
at org.springframework.web.context.support.WebApplicationContextUtils.currentRequestAttributes(WebApplicationContextUtils.java:313)
at org.springframework.web.context.support.WebApplicationContextUtils.access$400(WebApplicationContextUtils.java:66)
at org.springframework.web.context.support.WebApplicationContextUtils$RequestObjectFactory.getObject(WebApplicationContextUtils.java:329)
at org.springframework.web.context.support.WebApplicationContextUtils$RequestObjectFactory.getObject(WebApplicationContextUtils.java:324)
at org.springframework.beans.factory.support.AutowireUtils$ObjectFactoryDelegatingInvocationHandler.invoke(AutowireUtils.java:292)
at com.sun.proxy.$Proxy120.getRequestURL(Unknown Source)
at com.opl.ans.config.utils.LoggingAspect.logAround(LoggingAspect.java:137)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at org.springframework.aop.aspectj.AbstractAspectJAdvice.invokeAdviceMethodWithGivenArgs(AbstractAspectJAdvice.java:634)
at org.springframework.aop.aspectj.AbstractAspectJAdvice.invokeAdviceMethod(AbstractAspectJAdvice.java:624)
at org.springframework.aop.aspectj.AspectJAroundAdvice.invoke(AspectJAroundAdvice.java:72)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186)
at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.proceed(CglibAopProxy.java:749)
at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:691)
Is it the right way to do it? Are there any suggestions? Please help.
Based on the error stack the issue is that the request instance is invalid. I would guess that the request instance autowired to the Aspect could be stale or rather not assosicated with the current thread. This means that request instance is not null and the check within logAround() method gives unexepected results.
RequestContextHolder.getRequestAttributes() would return null if no request attributes are currently bound to the thread. Modifying the
if (request ! = null) { ..} check with if (RequestContextHolder.getRequestAttributes() ! = null) {..} should fix the issue.
Also spring boot provides out of the box solutions to what you are currently attempting through AOP . Do check them out as well.
HTTP Tracing
Spring MVC Metrics

Spring Boot console application causes JUnit test case to wait

I have Spring Boot console application which accepts user input. In the same application, there are service to perform some logic.
On running the service's JUnit test case directly, I am observing a behavior where the console application stays in the state of accepting user input, and thus not entering the JUnit tests.
Usually there is no issue becasue for a normal Spring Boot application, there is nothing to run on start up. But for this console application setup, it awaits user input.
May I know if there is any way that can make the test cases run while not triggering the console application? Thank you very much.
Observation: The console application is triggered on JUnit test case run, test case is not entered
Main application
import java.io.BufferedReader;
import java.io.InputStreamReader;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
#SpringBootApplication
public class ToyRobotApplication implements CommandLineRunner {
public static void main(String[] args) {
SpringApplication.run(ToyRobotApplication.class, args);
}
#Override
public void run(String... args) throws Exception {
// Instruct user to perform input
System.out.println("Welcome to toy robot application!");
System.out.println("Below are the possible operations:");
System.out.println("PLACE <x-coordinate> <y-coordinate> <facing>");
System.out.println("MOVE");
System.out.println("LEFT");
System.out.println("RIGHT");
System.out.println("REPORT");
while (true) {
System.out.println("Please enter your command:");
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
String usrInput = br.readLine();
//...
}
}
}
Test case
import static org.junit.jupiter.api.Assertions.assertEquals;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import com.somecompany.model.Facing;
import com.somecompany.model.Location;
import com.somecompany.model.Robot;
import com.somecompany.service.ToyRobotService;
#SpringBootTest
public class ToyRobotReportTest {
#Autowired
private Robot robot;
#Autowired
private ToyRobotService toyRobotService;
#Test
public void shouldBeAbleToReportLocation() {
Location location = new Location();
location.setXCor("1");
location.setYCor("2");
location.setFacing(Facing.NORTH);
robot.setLocation(location);
// Actual result
String result = toyRobotService.report();
// Assertion
assertEquals("1,2,NORTH", result);
}
}
Note: Other sources omitted
I got the setup working after some research. To get this to work, the JUnit test class should not be annotated with the usual #SpringBootTest annotation.
When the class is annotated with #SpringBootTest, it will start up the application's context (i.e. start the application). In most usual test scenarios, this should be fine if there is no waiting of user input.
In our case, we want the Spring Boot annotations (e.g. #Autowired) to be recognized, but do not want the application itself to be started up.
We can use the following in JUnit:
#ExtendWith(SpringExtension.class)
#ContextConfiguration(classes = ToyRobotApplication.class, initializers = ConfigDataApplicationContextInitializer.class)
public class ToyRobotPlaceTest {
...
}
This will regonize spring annotations, but will not start the application context.

Spock testing for SpringBoot RestController and mocking of exceptions

I am new to groovy way of testing RestController for spring boot application. I have a Controller class :
#RestController
#RequestMapping(value = "/onboarding/v1")
public class OnboardingController {
private static final Logger LOG = LoggerFactory.getLogger(OnboardingServiceImpl.class);
#Autowired
private OnboardingService onboardingService;
#RequestMapping(
value = "/service-models",
method = RequestMethod.POST,
consumes = { "multipart/form-data" },
produces = { "application/json" }
)
public ResponseEntity createServiceModel(
#RequestParam("name") final String name,
#RequestParam("file") final MultipartFile file
){
try {
final ServiceModelRequestData serviceModelRequestData =
new ServiceModelRequestData(name, file);
final ServiceModelDetail createdServiceModel =
onboardingService.createServiceModel(serviceModelRequestData);
return new ResponseEntity<>(createdServiceModel, HttpStatus.OK);
}
catch (MalformedContentException ex) {
LOG.error("Malformed Content:", ex);
return new ResponseEntity<>(errorMessage(ex), HttpStatus.BAD_REQUEST);
}
catch (ServiceModelNameAlreadyExistsException ex) {
LOG.error("Service Model Name Already Exists:", ex);
return new ResponseEntity<>(errorMessage(ex), HttpStatus.CONFLICT);
}
catch (ServiceUnavailableException ex) {
LOG.error("Service Unavailable currently:" + ex);
return new ResponseEntity<>(errorMessage(ex), HttpStatus.SERVICE_UNAVAILABLE);
}
}
//...
}
I am unable to find how to spock test the above class along with writing the spock test cases for exceptions and checking the desired response?I want to create a test case which throws an error when the method is called and returns the responseentity which I want to check contains the given Jason data along with the desired Http Status. Sample spock test for the code snippet would be highly appreciated.
EDITED:
The test Class is as below(along with comments of what I was trying to do):
package com.service.onboarding.web.controller
import spock.lang.Specification
import com.service.onboarding.business.OnboardingServiceImpl
import com.service.onboarding.business.api.OnboardingService
import com.service.onboarding.domain.exception.MalformedContentException
import com.service.onboarding.domain.exception.ServiceModelNameAlreadyExistsException
import com.service.onboarding.domain.exception.ServiceUnavailableException
import com.service.onboarding.domain.resource.Greeting
import groovy.json.internal.Exceptions
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest
import org.springframework.test.web.servlet.MockMvc
import org.springframework.web.multipart.MultipartFile
import org.springframework.boot.test.mock.mockito.MockBean
import org.springframework.http.ResponseEntity
import spock.lang.Unroll
import static org.mockito.BDDMockito.given
/*#WebMvcTest
public class OnboardingControllerSpec extends Specification {
#MockBean
private OnboardingService onboardingService;
#Autowired
private MockMvc mockMvc;
def "controller should return expected JSON content and OK response"() {
given: 'hello world service responds with greeting'
def name = "Emily"
given(onboardingService.getPersonalGreeting("${name}")).willReturn(new Greeting(1, "Hi, ${name}"));
when: 'hello world service is called with name provided'
def response = mockMvc.perform(get("/onboarding/v1?name=${name}"))
then: 'expected JSON returned and response code is OK'
response
.andExpect(status().isOk())
.andExpect(content().json("{'id': 1, 'content': 'Hi, ${name}'}"))
}
}*/
public class OnboardingControllerSpec extends Specification{
OnboardingServiceImpl service =new OnboardingServiceImpl()
OnboardingController controller
ResponseEntity response
#Unroll
def "HTTP response #statusCode when creating service model"() {
given:
if (exception) {
service = Stub() {
createServiceModel(_) >> { throw exception }
}
}
controller= new OnboardingController(onboardingService: service)
when:
response=controller.createServiceModel("test", Mock(MultipartFile))
then:
response.statusCode == statusCode
where:
exception | statusCode
null | OK
new MalformedContentException() | BAD_REQUEST
new ServiceModelNameAlreadyExistsException() | CONFLICT
new ServiceUnavailableException() | SERVICE_UNAVAILABLE
}
}
My service class is as follows:
package com.service.onboarding.business;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.atomic.AtomicLong;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.service.onboarding.business.api.OnboardingService;
import com.service.onboarding.business.servicemanagement.ServiceModelRepository;
import com.service.onboarding.domain.exception.MalformedContentException;
import com.service.onboarding.domain.exception.ServiceModelDoesNotExistException;
import com.service.onboarding.domain.exception.ServiceModelInUseException;
import com.service.onboarding.domain.exception.ServiceModelNameAlreadyExistsException;
import com.onboarding.domain.exception.ServiceUnavailableException;
import com.service.onboarding.domain.requestdata.ServiceModelPaginationFilter;
import com.service.onboarding.domain.requestdata.ServiceModelRequestData;
import com.service.onboarding.domain.resource.Greeting;
import com.service.onboarding.domain.resource.ServiceModel;
import com.service.onboarding.domain.resource.ServiceModelDetail;
import com.service.onboarding.domain.responsedata.PaginatedServiceResponseData;
/*
* Sample service to demonstrate what the API would use to get things done
*/
#Service
public class OnboardingServiceImpl implements OnboardingService {
private final AtomicLong counter = new AtomicLong();
private static final String TEMPLATE = "Hello, %s!";
private static final Logger LOG = LoggerFactory.getLogger(OnboardingServiceImpl.class);
#Autowired
private ServiceModelRepository serviceModelRepository;
public OnboardingServiceImpl() {
}
#Override
public Greeting getPersonalGreeting(final String name) {
return new Greeting(counter.incrementAndGet(),
String.format(TEMPLATE, name));
}
#Override
public ServiceModelDetail createServiceModel(final ServiceModelRequestData serviceModelRequestData) throws MalformedContentException, ServiceModelNameAlreadyExistsException, ServiceUnavailableException {
return serviceModelRepository.create(serviceModelRequestData);
}
Your test should look like this:
package de.scrum_master.stackoverflow
import org.springframework.http.ResponseEntity
import org.springframework.web.multipart.MultipartFile
import spock.lang.Specification
import spock.lang.Unroll
import static org.springframework.http.HttpStatus.*
class OnboardingControllerTest extends Specification {
OnboardingService service = new OnboardingService()
OnboardingController controller
ResponseEntity response
#Unroll
def "HTTP response #statusCode when creating service model"() {
given:
if (exception) {
service = Stub() {
createServiceModel(_) >> { throw exception }
}
}
controller = new OnboardingController(onboardingService: service)
when:
response = controller.createServiceModel("test", Mock(MultipartFile))
then:
response.statusCode == statusCode
where:
exception | statusCode
null | OK
new MalformedContentException() | BAD_REQUEST
new ServiceModelNameAlreadyExistsException() | CONFLICT
new ServiceUnavailableException() | SERVICE_UNAVAILABLE
}
}
The console log (I switched to Java logging when replicating your use case) would be:
Mär 10, 2018 12:51:50 PM de.scrum_master.stackoverflow.OnboardingController createServiceModel
INFORMATION: Malformed Content: de.scrum_master.stackoverflow.MalformedContentException
Mär 10, 2018 12:51:50 PM de.scrum_master.stackoverflow.OnboardingController createServiceModel
INFORMATION: Service Model Name Already Exists: de.scrum_master.stackoverflow.ServiceModelNameAlreadyExistsException
Mär 10, 2018 12:51:50 PM de.scrum_master.stackoverflow.OnboardingController createServiceModel
INFORMATION: Service Unavailable currently: de.scrum_master.stackoverflow.ServiceUnavailableException
In my IDE (IntelliJ IDEA) the test result looks like this:
P.S.:
I know you are new to SO. So this was your free shot. Next time please provide an MCVE, i.e. a minimal, compilable and executable example, not just a code snippet without package name, imports and test class. You want help with Spock, so please write a Spock specification (test class), show it to us and explain your problem with it. That way we do not need to recreate your situation from scratch but can just concentrate on fixing your test and solving your problem.
You were lucky I just felt a bit bored, waiting for a guest to arrive, otherwise I would not have done that. Saying "I have nothing, please do everything for me" only shows that you are lazy. If you want help, make it easy for others to help and show some respect towards their time budget. They are doing it for free!

NoSuchMethod when calling AWS Lambda using the AWS Sdk

I've created & deployed one simple GET API in API Gateway and here is the ARN and there is no authentication whatsoever on this function, I can simply call it on my browser
arn:aws:lambda:ap-southeast-1:XXXXXXXXXXXXXX:function:La
and the public url that can be browsed using the browser is:
https://xxxxxxxxx.execute-api.ap-southeast-1.amazonaws.com/v1/lambda/geta
and I'm using Spring boot project and the below code to invoke the API (Following this Doc)
The interface as the lambda service
package com.xxxxxxx.services.interfaces;
import com.amazonaws.services.lambda.invoke.LambdaFunction;
public interface ILambdaGetBalance {
#LambdaFunction(functionName="La")
String getA();
}
The service using that interface to call the lambda function
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import com.xxxxxxxx.services.interfaces.ILambdaGetBalance;
import com.amazonaws.auth.AWSCredentials;
import com.amazonaws.auth.AWSStaticCredentialsProvider;
import com.amazonaws.auth.BasicAWSCredentials;
import com.amazonaws.services.lambda.AWSLambda;
import com.amazonaws.services.lambda.AWSLambdaClientBuilder;
import com.amazonaws.services.lambda.invoke.LambdaInvokerFactory;
#Service
public class LambdaService {
#Value("${aws.access-key}")
private String accessKey;
#Value("${aws.secret-key}")
private String secretKey;
#Value("${aws.lambda.region-name}") // this is ap-southeast-1
private String regionName;
public void test() {
AWSCredentials credentials = new BasicAWSCredentials(accessKey,
secretKey);
AWSLambda client = AWSLambdaClientBuilder.standard()
.withCredentials(new AWSStaticCredentialsProvider(credentials))
.withRegion(regionName)
.build();
final ILambdaGetBalance getBalance = LambdaInvokerFactory.builder()
.lambdaClient(client)
.build(ILambdaGetBalance.class);
getBalance.getA();
}
}
after calling the getA function the system will through the following exception:
java.lang.NoSuchMethodError: com.amazonaws.services.lambda.AWSLambdaClient.beforeClientExecution(Lcom/amazonaws/AmazonWebServiceRequest;)Lcom/amazonaws/AmazonWebServiceRequest;
Any idea why is this happening? What am I missing?
Looks like your aws-java-sdk-lambda and aws-java-sdk-core modules may have incompatible versions. How are you resolving the dependencies for your project? The beforeClientExecution method was added to the AmazonWebServiceClient base class in version 1.11.106 of aws-java-sdk-core - see here: https://github.com/aws/aws-sdk-java/blame/master/aws-java-sdk-core/src/main/java/com/amazonaws/AmazonWebServiceClient.java#L590

dropwizard hibernate use a resource from within the server

I'm new to dropwizard and i'm trying to create an Authenticator which gets credentials from the user, then it uses the rest api getUser method which i implemented in my UserResouce class to get the user with the username that is in the credentials from the db users table. However in my autheticator class i having troubles in figuring out how to use the user resource functions to get the user.
I was trying to do something like that:
public List<com.amitbaz.tss.db.User> getUsersFromDB(String username){
SessionFactory sessionFactory = TradingSystemServerApplication.hibernateBundle.getSessionFactory();
UserDAO userDAO = new UserDAO(sessionFactory);
List<com.amitbaz.tss.db.User> user = userDAO.getUser(username);
logger.debug(user.toString());
return user;
}
inside the autheticator and call it from the authenticte function but it says there is no session bound...
EDIT:
Ok so after much thinking i got to this:
I'm dropwizard authenticator and authorizer implementions with BasicCredentials and.
Autheticator (don't mind the VALID_USER thing..):
package com.amitbaz.tss.auth;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import javax.persistence.NamedQuery;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.amitbaz.tss.TradingSystemServerApplication;
import com.amitbaz.tss.db.UserDAO;
import com.amitbaz.tss.db.UserResource;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import io.dropwizard.auth.AuthenticationException;
import io.dropwizard.auth.Authenticator;
import io.dropwizard.auth.basic.BasicCredentials;
public class TradingSystemServerAuthenticator implements Authenticator<BasicCredentials, User> {
private Logger logger = LoggerFactory.getLogger(TradingSystemServerAuthenticator.class);
private static final Map<String, Set<String>> VALID_USERS = ImmutableMap.of(
"guest", ImmutableSet.of(),
"amit", ImmutableSet.of("admin"),
"stav", ImmutableSet.of("broker")
);
private UserDAO userDAO;
public TradingSystemServerAuthenticator(UserDAO userDAO) {
// TODO Auto-generated constructor stub
this.userDAO = userDAO;
}
#Override
public Optional<User> authenticate(BasicCredentials credentials) throws AuthenticationException {
// TODO Auto-generated method stub
List<com.amitbaz.tss.db.User> user = userDAO.getUser(credentials.getUsername());
logger.debug(user.toString());
if("amit".equals(credentials.getPassword())){
return Optional.of(new User(credentials.getUsername(), VALID_USERS.get(credentials.getUsername())));
}
return Optional.empty();
}
}
Authorizer:
package com.amitbaz.tss.auth;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.amitbaz.tss.db.UserDAO;
import io.dropwizard.auth.Authorizer;
public class TradingSystemServerAuthorizer implements Authorizer<User>{
private Logger logger = LoggerFactory.getLogger(TradingSystemServerAuthorizer.class);
private UserDAO userDAO;
public TradingSystemServerAuthorizer(UserDAO userDAO) {
super();
this.userDAO = userDAO;
}
#Override
public boolean authorize(User user, String role) {
// TODO Auto-generated method stub
logger.debug(userDAO.getUser(user.getName()).toString());
return user.getName().equals("amit") && user.getRole().contains(new String("admin"));
}
}
Now, In my Application class I do this:
package com.amitbaz.tss;
import org.glassfish.jersey.server.filter.RolesAllowedDynamicFeature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.amitbaz.tss.auth.TradingSystemServerAuthenticator;
import com.amitbaz.tss.auth.TradingSystemServerAuthorizer;
import com.amitbaz.tss.auth.User;
import com.amitbaz.tss.db.Broker;
import com.amitbaz.tss.db.BrokerDAO;
import com.amitbaz.tss.db.BrokerResource;
import com.amitbaz.tss.db.Contact;
import com.amitbaz.tss.db.ContactDAO;
import com.amitbaz.tss.db.ContactResource;
import com.amitbaz.tss.db.Product;
import com.amitbaz.tss.db.ProductDAO;
import com.amitbaz.tss.db.ProductResource;
import com.amitbaz.tss.db.Test;
import com.amitbaz.tss.db.TestDAO;
import com.amitbaz.tss.db.TestResource;
import com.amitbaz.tss.db.Transaction;
import com.amitbaz.tss.db.TransactionDAO;
import com.amitbaz.tss.db.TransactionResource;
import com.amitbaz.tss.db.UserDAO;
import com.amitbaz.tss.db.UserResource;
import com.amitbaz.tss.db.UserRole;
import com.amitbaz.tss.db.UserRoleDAO;
import com.amitbaz.tss.db.UserRoleResource;
import com.amitbaz.tss.db.Website;
import com.amitbaz.tss.db.WebsiteDAO;
import com.amitbaz.tss.db.WebsiteResource;
import io.dropwizard.Application;
import io.dropwizard.auth.AuthDynamicFeature;
import io.dropwizard.auth.AuthValueFactoryProvider;
import io.dropwizard.auth.basic.BasicCredentialAuthFilter;
import io.dropwizard.db.DataSourceFactory;
import io.dropwizard.hibernate.HibernateBundle;
import io.dropwizard.setup.Bootstrap;
import io.dropwizard.setup.Environment;
import javassist.tools.web.Webserver;
public class TradingSystemServerApplication extends Application<TradingSystemServerConfiguration>{
public static void main(String[] args) throws Exception{
new TradingSystemServerApplication().run(args);
}
public final static HibernateBundle<TradingSystemServerConfiguration> hibernateBundle
= new HibernateBundle<TradingSystemServerConfiguration>(
Test.class,Broker.class, com.amitbaz.tss.db.User.class, UserRole.class
,Product.class, Transaction.class, Website.class, Contact.class
) {
#Override
public DataSourceFactory getDataSourceFactory(
TradingSystemServerConfiguration configuration
) {
return configuration.getDataSourceFactory();
}
};
final Logger logger = LoggerFactory.getLogger(TradingSystemServerApplication.class);
#Override
public void initialize(
final Bootstrap<TradingSystemServerConfiguration> bootstrap) {
bootstrap.addBundle(hibernateBundle);
}
#Override
public void run(TradingSystemServerConfiguration config, Environment env) throws Exception {
final UserDAO userDAO = new UserDAO(hibernateBundle.getSessionFactory());
final UserRoleDAO userRoleDAO = new
env.jersey().register(new UserResource(userDAO));
/...
BasicCredentialAuthFilter.Builder<User>()
.setAuthenticator(new TradingSystemServerAuthenticator(userDAO))
.setAuthorizer(new TradingSystemServerAuthorizer(userDAO))
.setRealm("Authetication Required")
.buildAuthFilter()));
env.jersey().register(RolesAllowedDynamicFeature.class);
env.jersey().register(new AuthValueFactoryProvider.Binder<>(User.class));
}
}
And I have the annotation #RolesAllowed("role_name") on one of the rest api methods which with im trying to test the auth.
Now when i try to test this and i make a request to that rest api method, I
get the error No session currently bound to execution context where i do userDAO.getUser(...) in the authanticator and in the authorizer
EDIT 2:
UserDAO implementation:
package com.amitbaz.tss.db;
import java.util.List;
import org.hibernate.SessionFactory;
import io.dropwizard.hibernate.AbstractDAO;
public class UserDAO extends AbstractDAO<User>{
public UserDAO(SessionFactory sessionFactory) {
super(sessionFactory);
// TODO Auto-generated constructor stub
}
public List<User> getUser(String username){
return list(namedQuery("com.amitbaz.tss.db.user.getUser")
.setParameter("username", username));
}
}
EDIT 3:
Added #UnitOfWork to authenticate and authorize methods.
registered them as follow ( Notice the changes in hibernateBundle and run method):
package com.amitbaz.tss;
import javax.servlet.ServletRegistration;
import org.atmosphere.cpr.ApplicationConfig;
import org.atmosphere.cpr.AtmosphereServlet;
import org.glassfish.jersey.server.filter.RolesAllowedDynamicFeature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.amitbaz.tss.auth.TradingSystemServerAuthenticator;
import com.amitbaz.tss.auth.TradingSystemServerAuthorizer;
import com.amitbaz.tss.auth.User;
import com.amitbaz.tss.db.Broker;
import com.amitbaz.tss.db.BrokerDAO;
import com.amitbaz.tss.db.BrokerResource;
import com.amitbaz.tss.db.Contact;
import com.amitbaz.tss.db.ContactDAO;
import com.amitbaz.tss.db.ContactResource;
import com.amitbaz.tss.db.Product;
import com.amitbaz.tss.db.ProductDAO;
import com.amitbaz.tss.db.ProductResource;
import com.amitbaz.tss.db.Test;
import com.amitbaz.tss.db.TestDAO;
import com.amitbaz.tss.db.TestResource;
import com.amitbaz.tss.db.Transaction;
import com.amitbaz.tss.db.TransactionDAO;
import com.amitbaz.tss.db.TransactionResource;
import com.amitbaz.tss.db.UserDAO;
import com.amitbaz.tss.db.UserResource;
import com.amitbaz.tss.db.UserRole;
import com.amitbaz.tss.db.UserRoleDAO;
import com.amitbaz.tss.db.UserRoleResource;
import com.amitbaz.tss.db.Website;
import com.amitbaz.tss.db.WebsiteDAO;
import com.amitbaz.tss.db.WebsiteResource;
import io.dropwizard.Application;
import io.dropwizard.auth.AuthDynamicFeature;
import io.dropwizard.auth.AuthValueFactoryProvider;
import io.dropwizard.auth.basic.BasicCredentialAuthFilter;
import io.dropwizard.db.DataSourceFactory;
import io.dropwizard.hibernate.HibernateBundle;
import io.dropwizard.setup.Bootstrap;
import io.dropwizard.setup.Environment;
import javassist.tools.web.Webserver;
public class TradingSystemServerApplication extends Application<TradingSystemServerConfiguration>{
public static void main(String[] args) throws Exception{
new TradingSystemServerApplication().run(args);
}
public final static HibernateBundle<TradingSystemServerConfiguration> hibernateBundle
= new HibernateBundle<TradingSystemServerConfiguration>(
Test.class,Broker.class, com.amitbaz.tss.db.User.class, UserRole.class
,Product.class, Transaction.class, Website.class, Contact.class
,TradingSystemServerAuthenticator.class, TradingSystemServerAuthorizer.class
) {
#Override
public DataSourceFactory getDataSourceFactory(
TradingSystemServerConfiguration configuration
) {
return configuration.getDataSourceFactory();
}
};
final Logger logger = LoggerFactory.getLogger(TradingSystemServerApplication.class);
#Override
public void initialize(
final Bootstrap<TradingSystemServerConfiguration> bootstrap) {
bootstrap.addBundle(hibernateBundle);
}
#Override
public void run(TradingSystemServerConfiguration config, Environment env) throws Exception {
final UserDAO userDAO = new UserDAO(hibernateBundle.getSessionFactory());
final UserRoleDAO userRoleDAO = new UserRoleDAO(hibernateBundle.getSessionFactory());
final TradingSystemServerAuthorizer authorizer = new TradingSystemServerAuthorizer(userDAO);
final TradingSystemServerAuthenticator authenticator = new TradingSystemServerAuthenticator(userDAO);
env.jersey().register(new UserResource(userDAO));
env.jersey().register(new UserRoleResource(userRoleDAO));
env.jersey().register(authorizer);
env.jersey().register(authenticator);
env.jersey().register(new AuthDynamicFeature(new BasicCredentialAuthFilter.Builder<User>()
.setAuthenticator(authenticator)
.setAuthorizer(authorizer)
.setRealm("Authetication Required")
.buildAuthFilter()));
env.jersey().register(RolesAllowedDynamicFeature.class);
env.jersey().register(new AuthValueFactoryProvider.Binder<>(User.class));
}
Your approach looks like a design issue. The issue I see is that you are trying to integrate via rest with a service that is already accessible for you within your application. That adds a lot of overhead and complicates things.
Fortunately, DW already has a fully integrated Authorization and Authentication system just waiting for you to plug in. You can read more about it here: http://www.dropwizard.io/1.0.2/docs/manual/auth.html
The essential thing to note here is that you should split the service used by your resource from your resource. In your case for example the UserDao, or you could split it into a UserService and UserResource, where the UserService provides access to your database layer. Up to you really.
Here is how you would implement this with DW integrated auth and how you would register this as well.
In my example I am skipping the Hibernate aspect of this as it isn't too relevant. you can read about it here: http://www.dropwizard.io/1.0.2/docs/manual/hibernate.html
Here's my code:
public class AuthenticatorTest extends io.dropwizard.Application<Configuration> {
#Override
public void run(Configuration configuration, Environment environment) throws Exception {
// register resource
environment.jersey().register(MyHelloResource.class);
// create the dao + dependencies
UserDao dao = new UserDao(null);
// register new authenticator
environment.jersey().register(new AuthDynamicFeature(new BasicCredentialAuthFilter.Builder<Principal>()
.setAuthenticator(new UserAuth(dao)).setRealm("SUPER SECRET STUFF").buildAuthFilter()));
// enables authentication via filter
environment.jersey().register(RolesAllowedDynamicFeature.class);
}
public static void main(String[] args) throws Exception {
new AuthenticatorTest().run("server", "/home/artur/dev/repo/sandbox/src/main/resources/config/test.yaml");
}
#Path("test")
#Produces(MediaType.APPLICATION_JSON)
public static class MyHelloResource {
#GET
#Path("asd")
#PermitAll
public String test(String x) {
return "Hello";
}
}
public static class UserAuth implements Authenticator<BasicCredentials, Principal> {
private UserDao dao;
public UserAuth(UserDao dao) {
this.dao = dao;
}
#Override
public Optional<Principal> authenticate(BasicCredentials credentials) throws AuthenticationException {
String user = dao.getUser();
return Optional.of(new Principal() {
#Override
public String getName() {
return user;
}
});
}
}
public static class UserDao {
private SessionFactory s;
public UserDao(final SessionFactory s) {
this.s = s;
}
public String getUser() {
return "pandaadb";
}
}
}
And this is the breakdown of what we are doing.
First, as per docs, you would register your HibernateBundle within the bootstrap method as shown (in docs). This gives you access to the SessionFactory you require for your authentication.
Your resource method will be annotated with a java security annotation. i am using PermitAll because I am disregarding roles.
In the run method, you then create your DAO, register your resource and use the DW builder to add the required Filter and the Authenticator. This one specifically is for BasicCredentials, however there is nothing stopping you from doing any kind of filter for this. DW already supports things like Ldap (in a different dependency), Basic Auth and so on.
Now, since you create your beans in the run method, and you added your Hibernate bundle in the bootstrap method, you have access to the SessionFactory and can instantiate the DAO accordingly. No need to have to pass it around.
You also don't have to do any rest-request to access your user (though there is nothing stopping you adding that resource anyway, in case you need external access to it.)
So, to sum up, the important parts are:
Add A security annotation to your resource (e.g. PermitAll to allow all roles)
Add an authenticator implementation (in my case UserAuth) that uses your DAO
Instantiate it in the run method provided by dropwizard and register it with the jersey environment.
Note, this requires your user to implement the javax.security.Principal interface. this is not a bad idea in general as a lot of security Frameworks make use of this.
This, also, gives you more options with regards to DW.
You can add an Authorization implementation and a filter, and you'll be able to inject the User object into any resource method by adding an #Auth annotated object (see docs).
Finally, the test of the standalone app from above:
artur#pandaadb:~$ curl "localhost:9085/api/test/asd" -v
* Trying 127.0.0.1...
* Connected to localhost (127.0.0.1) port 9085 (#0)
> GET /api/test/asd HTTP/1.1
> Host: localhost:9085
> User-Agent: curl/7.47.0
> Accept: */*
>
< HTTP/1.1 401 Unauthorized
< Date: Mon, 17 Oct 2016 10:45:51 GMT
< WWW-Authenticate: Basic realm="SUPER SECRET STUFF"
< Content-Type: text/plain
< Content-Length: 49
<
* Connection #0 to host localhost left intact
Credentials are required to access this resource.
artur#pandaadb:~$ curl "localhost:9085/api/test/asd" -H "Authorization: Basic dXNlcjpwYXNz" -v
* Trying 127.0.0.1...
* Connected to localhost (127.0.0.1) port 9085 (#0)
> GET /api/test/asd HTTP/1.1
> Host: localhost:9085
> User-Agent: curl/7.47.0
> Accept: */*
> Authorization: Basic dXNlcjpwYXNz
>
< HTTP/1.1 200 OK
< Date: Mon, 17 Oct 2016 10:46:11 GMT
< Content-Type: application/json
< Vary: Accept-Encoding
< Content-Length: 5
<
* Connection #0 to host localhost left intact
Helloartur
I hope this helps you with your issue.
Of course you don't need to use the DW method for Authentication. However I would recommend going down that road as you will have more support and a lot of things out of the box.
However, the one thing you should rethink (if you don't use DW) is to not do a curl request to your Filter. Instead, instantiate it in the run method, and pass that instance to your Filter.
Note also, if you register your DAO with DW (as seen in hibernate docs), you will be able to use #Inject to inject your DAO into the Filter class that needs to use it.
Right, I think that's all the info you need :)
Let me know if you have any problems,
Artur
Edit:
I am doing an edit because I wrote a lot above and don't want to go over it.
I set up Hibernate to test this. The reason you are seeing issues is because the UnitOfWork is bound to the request scope. However, the resource annotations is matched AFTER the filter is invoked (since you need to do auth before executing the method). This is why you don't have a session.
This is the solution.
In your run method, register a proxy for your auth implementation:
UnitOfWorkAwareProxyFactory fac = new UnitOfWorkAwareProxyFactory(hibernate);
UserAuth proxy = fac.create(UserAuth.class, UserDao.class, dao);
environment.jersey().register(new AuthDynamicFeature(new BasicCredentialAuthFilter.Builder<Principal>()
.setAuthenticator(proxy).setRealm("SUPER SECRET STUFF").buildAuthFilter()));
This creates a proxy around the UserAuth class so that it is aware of the UnitOfWork annotation.
In your UserAuth class (or mine rather) you do:
public static class UserAuth implements Authenticator<BasicCredentials, Principal> {
private UserDao dao;
public UserAuth(UserDao dao) {
this.dao = dao;
}
#Override
#UnitOfWork
public Optional<Principal> authenticate(BasicCredentials credentials) throws AuthenticationException {
String user = dao.getUser();
return Optional.of(new Principal() {
#Override
public String getName() {
return user;
}
});
}
}
Note the UnitOfWork annotation on the authenticate. This now opens a new session for you. Please make sure to read up on UnitOfWork as it may have tricky side effects (or not) depending on how you use it.
Finally, this allowed my dao to talk to the database on an existing session.
Regards,
Artur
I finally, after much debugginf, find the proper way to open a session and execute a query.
I added this lines in Authenticator's authenticate mehod:
Session session = userDAO.getSessionFactory().openSession();
Transaction transaction = session.getTransaction();
Query userquery = session.createQuery("select u from User u where u.username = :username").setParameter("username", credentials.getUsername());
List<com.amitbaz.tss.db.User> u = userquery.list();
session.close();
And the same in Authorizater and it now works :)

Categories

Resources