here's my code for the controller:
I have put my object in a map in the "doLogin" method below and I am trying to access it in my "logout" function but I am getting null value when I am trying to fetch value of my session attribute using "map.get(key)"
#Controller
#SessionAttributes(value={"session1"})
public class CredentialsController {
#Autowired
private Authentication authenticationDao;
#Autowired
private User userDao;
#RequestMapping(value="/start",method=RequestMethod.GET) //Default Method
public String doStart(#ModelAttribute CredentialsBean credentialsBean)
{
return "login";
}
#RequestMapping(value="/login",method=RequestMethod.GET) //Default Method
public String doLogin(#ModelAttribute CredentialsBean credentialsBean,Map<String,Object> map)
{
String result="";
if(credentialsBean!=null){
if(authenticationDao.authenticate(credentialsBean)){
String userType=authenticationDao.authorize(credentialsBean.getUserID());
if(userType.equalsIgnoreCase("A")){
CredentialsBean cBean= authenticationDao.changeLoginStatus(credentialsBean, 1);
map.put("session1",cBean); ----->Here I am putting the object inside a map .
result= "admin";
//map.put("username",credentialsBean.getProfileBean().getFirstName());
}
else{
CredentialsBean cBean=authenticationDao.changeLoginStatus(credentialsBean, 1);
map.put("session1",cBean.getUserID());
//System.out.println(cBean.getUserID());
result= "customer";
//map.put("username",credentialsBean.getProfileBean().getFirstName());
}
}
else{
result="ERROR";
}
}
return result;
}
#RequestMapping(value="/logout",method=RequestMethod.GET) //Default Method
public String doLogout(Map<String,Object > map)
{
CredentialsBean credentialsBean=(CredentialsBean)map.get("session1");
//System.out.println(userID);
System.out.println(credentialsBean.getUserID());
if(credentialsBean!=null){
if(userDao.logout(credentialsBean.getUserID())){
return "logout";
}
else{
return "error1";
}
}
else{
return "error";
}
}
}
Here is the way I would do it:
in your doLogin method you should add HttpSession session:
#RequestMapping(value="/login",method=RequestMethod.GET) //Default Method
public String doLogin(#ModelAttribute CredentialsBean credentialsBean, HttpSession session)
{
String result="";
if(credentialsBean!=null){
if(authenticationDao.authenticate(credentialsBean)){
String userType=authenticationDao.authorize(credentialsBean.getUserID());
if(userType.equalsIgnoreCase("A")){
CredentialsBean cBean= authenticationDao.changeLoginStatus(credentialsBean, 1);
// add object to session
session.setAttribute("session1",cBean);
result= "admin";
//map.put("username",credentialsBean.getProfileBean().getFirstName());
}
else{
CredentialsBean cBean=authenticationDao.changeLoginStatus(credentialsBean, 1);
session.setAttribute("session1",cBean);
result= "customer";
}
}
else{
result="ERROR";
}
}
return result;
}
Note, that you should add to session objects of the same type in order to safely retrieve it later (because now you added different objects cBean and cBean.getUserID() for the same key session1)
Then in your logout:
#RequestMapping(value="/logout",method=RequestMethod.GET) //Default Method
public String doLogout(HttpSession session)
{
CredentialsBean credentialsBean=(CredentialsBean)session.getAttribute("session1");
.....
}
But anyway, since you're implementing login\logout here I encourage you to learn more about Spring Security.
Related
I know that in Java a method can return only one return type... But if there is any possiblity to this, kindly let me know. From the below method I am trying to return a list if condition satisfies else i am trying to return an error message.
Here is my code:
#RequestMapping(value = "/getcompanies", method = RequestMethod.POST)
public List<CompanyMaster> getCompanies(#RequestBody UserDetails user) {
String OrgLoginId = user.getOrgLoginId();
String password = user.getuPassword();
String checkLoginId = null;
String uPassword = null;
String encPassword = null;
String loginId = null;
String checkAuthorized = null;
// String loginId=userService.getLoginId(OrgLoginId);
List<Object[]> CheckIdPassword = userService.checkLoginId(OrgLoginId);
List<Object[]> results = CheckIdPassword;
for (Object[] obj : results) {
checkLoginId = obj[0].toString();
if (null == obj[1]) {
uPassword = "";
} else {
uPassword = obj[1].toString();
}
loginId = obj[2].toString();
}
checkAuthorized = loginId.substring(0, 3);
if (null != password) {
MD5 md5 = new MD5();
encPassword = md5.getPassword(password);
}
if (checkLoginId == null) {
return "Incorrect loginId..Please enter valid loginId";
} else if (encPassword.equals(uPassword)) {
if (checkAuthorized.equals("STE")) {
List<CompanyMaster> companyList = userService.getCompanyList(OrgLoginId);
return companyList;
} else {
return "You are not Authorized";
}
} else {
return "Incorrect Password";
}
Yes its possible, create a custom Exception say 'MyAppException' and throw that exception with the error message you want.
Write your logic in a try{}catch block and throw the exception in catch so that the response has the error message
public List<CompanyMaster> getCompanies(#RequestBody UserDetails user) throws MyAppppException
{
try
{
//your logic which throws error
return companyList;
}
catch( final MyAppException we )
{
throw new MyAppException("User not found", HttpStatus.NOT_FOUND);
}
}
Refer this link
https://www.codejava.net/java-core/exception/how-to-create-custom-exceptions-in-java
You can achieve this by creating a new presenter Class which contains List and status of type String and change the return type of getCompanies method to presenter class like
public CompaniesPresenter getCompanies()
And your CompaniesPresenter class should look like
public class CompaniesPresenter {
private List<CompanyMaster> companyMaster;
private string status;
//default constructor
public CompaniesPresenter(){
}
//parameterized constructor to return only string in exception case
public CompaniesPresenter(Stirng status){
this.status = status;
}
//parametirized constructor to return success case
public CompaniesPresenter(List<CompanyMaster> companyMaster, Stirng status){
this.companyMaster = companyMaster;
this.status = status;
}
//getters and setters
}
This is how your updated method lokks like
#RequestMapping(value = "/getcompanies", method = RequestMethod.POST)
public CompaniesPresenter getCompanies(#RequestBody UserDetails user) {
String OrgLoginId = user.getOrgLoginId();
String password = user.getuPassword();
String checkLoginId = null;
String uPassword = null;
String encPassword = null;
String loginId = null;
String checkAuthorized = null;
// String loginId=userService.getLoginId(OrgLoginId);
List<Object[]> CheckIdPassword = userService.checkLoginId(OrgLoginId);
List<Object[]> results = CheckIdPassword;
for (Object[] obj : results) {
checkLoginId = obj[0].toString();
if (null == obj[1]) {
uPassword = "";
} else {
uPassword = obj[1].toString();
}
loginId = obj[2].toString();
}
checkAuthorized = loginId.substring(0, 3);
if (null != password) {
MD5 md5 = new MD5();
encPassword = md5.getPassword(password);
}
if (checkLoginId == null) {
return new CompaniesPresenter("Incorrect loginId..Please enter valid loginId");
} else if (encPassword.equals(uPassword)) {
if (checkAuthorized.equals("STE")) {
List<CompanyMaster> companyList = userService.getCompanyList(OrgLoginId);
return new CompaniesPresenter(companyList,"success");
} else {
return new CompaniesPresenter("You are not Authorized");
}
} else {
return new CompaniesPresenter("Incorrect Password");
}
This is not tested please make sure for any compilation errors
vavr's Either class would be a good choice.
The usage of custom exception is most reasonable solution. However, creating custom exception for just one case is not ideal always.
Another solution is to return empty List from your method, check if the List is empty in your servlet (or wherever you are invoking this method from), and show error message there.
It seems like you want to return multiple error messages for different cases. In this case, custom exception is recommended solution. If you don't like custom exceptions, you can return List<Object> and populate error message as the first element in the list. In the place where this List is obtained, check if the first element is instanceOf String or CompanyMaster. Based on what it is, you can perform your operations. This is a weird but possible solution (only if you don't like custom exceptions).
You need to understand the problem first. You are mixing two things here, first authorization, does the user has correct privileges to get company details, second giving the company details itself. Let's understand the first problem when a user tries to access "/getcompanies" endpoint will you let him in if does not have access, in REST world your security model should take care of it. I would use spring security to achieve this. My recommendation would be to explore on "interceptor" and solve the problem of invalid user. This will make your other problem easy as your "/getcompanies" endpoint can focus only on getting the details and return it (SRP).
I am introducing Spring Security in an existing application. Currently db has MD5 encoded passwords which we want to migrate to bcrypt. Since we have a large number of users initially we would like to support both MD5 and bcrypt together. We have thought off having a table which will store how many users are migrated to bcrypt once we have every one migrated we will stop supporting MD5.
So I thought of extending the BCryptPasswordEncoder class of SpringSecurity and do the things inside matches method. So I have below class,
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
public class CustomPasswordEncoder extends BCryptPasswordEncoder {
#Override
public boolean matches(CharSequence rawPassword, String encodedPassword) {
if (rawPassword == null || encodedPassword == null) {
return false;
}
if (!super.matches(rawPassword, encodedPassword)) { // This is not BCrypt password try OLD password encoding instead
boolean isOldPasswordMatched = rawPassword.equals(SHA1.getSHA1Hash(encodedPassword));
if(isOldPasswordMatched){
migrateToBCrypt(userName /* error here*/, encode(rawPassword));
}
return isOldPasswordMatched;
}
return true;
}
private boolean migrateToBCrypt(String userName, String newBcryptPassword){
//update password in database
//Insert to migrated table
return true;
}
}
However my problem is I don't get username inside this function to do the migration, How can I get username inside matches() of password encoder ? Am I doing something wrong here ? What could be the best approach in this situation ?
The proposed logic is just my idea, you can modify it as per your needs.
public class UserService extends BCryptPasswordEncoder{
public Response login(#RequestBody User user){
User existingUser = UserDao.getInstance().getUserByUsername( user.getUsername() );
//Assuming all the users have `PasswordType` column as "MD5" in user table
if( existingUser.getPasswordType().equals("MD5") ){
// Your MD5 verification method, return boolean
if( verifyMD5(user.getPassword, existingUser.getPassword()) ){
migrateToBCrypt(existingUser, user);
return Response.status(200).entity("Successfully Logged in").build();
}else{
return Response.status(400).entity("Invalid Credentials").build();
}
}else if( existingUser.getPasswordType().equals("BCrypt") ){
if( matches(user.getPassword(), existingUser.getPassword()) ){
return Response.status(200).entity("Successfully Logged in").build();
}else{
return Response.status(400).entity("Invalid Credentials").build();
}
}
}
private void migrateToBcrypt(User existingUser, User user){
existingUser.setPassword( encode(user.getPassword()) );
existingUser.setPasswordType( "Bcrypt" );
UserDao.getInstance().updateUser( existingUser );
}
}
Or if you don't want to introduce another column on table,
public class UserService extends BCryptPasswordEncoder{
public Response login(#RequestBody User user){
User existingUser = UserDao.getInstance().getUserByUsername( user.getUsername() );
if( !existingUser.getPassword().startsWith("$") ){
// Your MD5 verification method, return boolean
if( verifyMD5(user.getPassword, existingUser.getPassword()) ){
migrateToBCrypt(existingUser, user);
return Response.status(200).entity("Successfully Logged in").build();
}else{
return Response.status(400).entity("Invalid Credentials").build();
}
}else {
if( matches(user.getPassword(), existingUser.getPassword()) ){
return Response.status(200).entity("Successfully Logged in").build();
}else{
return Response.status(400).entity("Invalid Credentials").build();
}
}
}
private void migrateToBcrypt(User existingUser, User user){
existingUser.setPassword( encode(user.getPassword()) );
UserDao.getInstance().updateUser( existingUser );
}
}
I'd like to test a controller method named authenticate(), which has very simple logic: validating email and password from request and returning the result as JSON.
public class Users extends Controller {
static Form<User> userForm = Form.form(User.class);
public static Result login() {
return ok(views.html.users.login.render(userForm));
}
public static Result authenticate() {
Form<User> filledForm = userForm.bindFromRequest();
if (filledForm.hasErrors()) {
return badRequest(views.html.users.login.render(filledForm));
} else {
ObjectNode result = Json.newObject();
User u = filledForm.get();
if (User.isAuthValid(u.email, u.password))
result.put("status", "OK");
else
result.put("status", "Authentication failed");
return ok(result);
}
}
}
Following is the test code for authenticate():
#Test
public void callAuthenticate() {
Map<String, String> formData = Maps.newHashMap();
formData.put("email", "aaa#bbb.com");
formData.put("password", "password");
Result result = callAction(controllers.routes.ref.Users.authenticate(),
fakeRequest().withFormUrlEncodedBody(formData));
assertThat(status(result)).isEqualTo(Http.Status.OK);
}
But I got an error with following stacktrace:
javax.validation.ValidationException: HV000041: Call to TraversableResolver.isReachable() threw an exception.
at org.hibernate.validator.internal.engine.ValidatorImpl.isValidationRequired(ValidatorImpl.java:1230)
at org.hibernate.validator.internal.engine.ValidatorImpl.validateConstraint(ValidatorImpl.java:438)
at org.hibernate.validator.internal.engine.ValidatorImpl.validateConstraintsForDefaultGroup(ValidatorImpl.java:387)
at org.hibernate.validator.internal.engine.ValidatorImpl.validateConstraintsForCurrentGroup(ValidatorImpl.java:351)
at org.hibernate.validator.internal.engine.ValidatorImpl.validateInContext(ValidatorImpl.java:303)
at org.hibernate.validator.internal.engine.ValidatorImpl.validate(ValidatorImpl.java:133)
at org.springframework.validation.beanvalidation.SpringValidatorAdapter.validate(SpringValidatorAdapter.java:194)
at play.data.Form.bind(Form.java:327)
at play.data.Form.bindFromRequest(Form.java:215)
at controllers.Users.authenticate(Users.java:50)
at controllers.ref.ReverseUsers$$anonfun$authenticate$1.apply(routes_reverseRouting.scala:477)
at controllers.ref.ReverseUsers$$anonfun$authenticate$1.apply(routes_reverseRouting.scala:477)
at play.core.Router$HandlerInvoker$$anon$6$$anon$2.invocation(Router.scala:164)
at play.core.j.JavaAction$$anon$1.call(JavaAction.scala:31)
at play.core.j.JavaAction$$anon$2.apply(JavaAction.scala:74)
at play.core.j.JavaAction$$anon$2.apply(JavaAction.scala:73)
at play.libs.F$Promise$PromiseActor.onReceive(F.java:420)
at akka.actor.UntypedActor$$anonfun$receive$1.applyOrElse(UntypedActor.scala:159)
at akka.actor.ActorCell.receiveMessage(ActorCell.scala:425)
at akka.actor.ActorCell.invoke(ActorCell.scala:386)
at akka.dispatch.Mailbox.processMailbox(Mailbox.scala:230)
at akka.dispatch.Mailbox.run(Mailbox.scala:212)
at akka.dispatch.ForkJoinExecutorConfigurator$MailboxExecutionTask.exec(AbstractDispatcher.scala:502)
at scala.concurrent.forkjoin.ForkJoinTask.doExec(ForkJoinTask.java:262)
at scala.concurrent.forkjoin.ForkJoinPool$WorkQueue.runTask(ForkJoinPool.java:975)
at scala.concurrent.forkjoin.ForkJoinPool.runWorker(ForkJoinPool.java:1478)
at scala.concurrent.forkjoin.ForkJoinWorkerThread.run(ForkJoinWorkerThread.java:104)
Caused by: org.springframework.beans.InvalidPropertyException: Invalid property 'email' of bean class [models.User]: No property 'email' found
at org.springframework.beans.BeanWrapperImpl.getPropertyDescriptor(BeanWrapperImpl.java:337)
at play.db.ebean.Model._idAccessors(Model.java:47)
at play.db.ebean.Model._getId(Model.java:67)
at play.db.ebean.Model.hashCode(Model.java:208)
at org.hibernate.validator.internal.engine.resolver.SingleThreadCachedTraversableResolver$TraversableHolder.buildHashCode(SingleThreadCachedTraversableResolver.java:153)
at org.hibernate.validator.internal.engine.resolver.SingleThreadCachedTraversableResolver$TraversableHolder.<init>(SingleThreadCachedTraversableResolver.java:114)
at org.hibernate.validator.internal.engine.resolver.SingleThreadCachedTraversableResolver$TraversableHolder.<init>(SingleThreadCachedTraversableResolver.java:96)
at org.hibernate.validator.internal.engine.resolver.SingleThreadCachedTraversableResolver.isReachable(SingleThreadCachedTraversableResolver.java:41)
at org.hibernate.validator.internal.engine.ValidatorImpl.isValidationRequired(ValidatorImpl.java:1221)
... 26 more
When testing on the browser, it worked as expected. But only JUnit test fails with InvalidPropertyException. What's wrong with my test code?
FYI, here's the model User:
#Entity
public class User extends Model {
#Id
#Required
#NonEmpty
public String email;
public String nickname;
#Required
public String password;
public String salt;
public static Finder<String, User> find = new Finder<String, User>(
String.class, User.class);
public static User findByEmail(String email) {
return find.where().eq("email", email).findUnique();
}
public static boolean isAuthValid(String email, String password) {
User user = findByEmail(email);
if (user == null)
return false;
return user.isValidPassword(password);
}
public boolean isValidPassword(String password) {
return this.password.equals(DigestUtils.md5Hex(password + this.salt));
}
}
Thanks for any advices/corrections.
You need to run the test inside a "Fake" application, so the binding can work. So, you're Test will look like this:
#Test
public void callAuthenticate() {
running(fakeApplication(), new Runnable() {
public void run() {
Map<String, String> formData = Maps.newHashMap();
formData.put("email", "aaa#bbb.com");
formData.put("password", "password");
Result result = callAction(controllers.routes.ref.Users.authenticate(),
fakeRequest().withFormUrlEncodedBody(formData));
assertThat(status(result)).isEqualTo(Http.Status.OK);
}
}
}
I would like to support more than one user in my realm.
How can this be done?
The following methods should be override as follows:
#Override
protected String getPassword(String username)
{
if(username.equals(_firstUser))
return _firstUserPassword;
else if(username.equals(_secondUser))
return _secondUserPassword;
//etc with your other users
}
#Override
protected Principal getPrincipal(String username)
{
if(username.equals(_firstUser))
{
return new GenericPrincipal(username, _firstUserPassword, _roles);
}
else if(username.equals(_secondUser)
{
return new GenericPrincipal(username, _secondUserPassword, _roles);
}
//etc with your other users
}
i have an enum in my data-class as follows
public enum ProcStat
{
NOT_READY ((byte)-1, "Not Ready For Processing"),
READY_FOR_PROCESSING ((byte)0, "Ready For Processing"),
BEING_PROCESSED ((byte)1, "Being Processed"),
PROCESSED_SUCCESSFULLY ((byte)2, "Processed Successfully"),
MSG_SUPPRESSED ((byte)98, "Msg suppressed before processing"),
PROCESSED_ERROR ((byte)99, "Processed With Error");
private final Byte statByte;
private final String statusDesc;
ProcStat(Byte statByte, String statusDesc)
{
this.statByte = statByte;
this.statusDesc = statusDesc;
}
#Override
public String toString()
{
return statusDesc;
}
protected static ProcStat getProcStat(Byte procStat)
{
if (READY_FOR_PROCESSING.statByte.equals(procStat))
{
return READY_FOR_PROCESSING;
}
else if (BEING_PROCESSED.statByte.equals(procStat))
{
return BEING_PROCESSED;
}
else if (PROCESSED_SUCCESSFULLY.statByte.equals(procStat))
{
return PROCESSED_SUCCESSFULLY;
}
else if (MSG_SUPPRESSED.statByte.equals(procStat))
{
return MSG_SUPPRESSED;
}
else if (PROCESSED_ERROR.statByte.equals(procStat))
{
return PROCESSED_ERROR;
}
else
{
return NOT_READY;
}
}
public Byte getStatByte()
{
return this.statByte;
}
};
The Proc_Stat refers to a Number field in a DataBase table and i need to show a column on the page where it shows the corresponding String of the numeric proc_stat for each row.
This is how i render any other field of the same data-class on the xhtml page:
id="dataTable" name="dataTable" var="data"
How do i give output value for enum type?
do i give like this : value="#{data.procstat.toString()}" ??
I think you need to create method in one of your managed bean that does the same as getProcStat method. Something like:
public String getProcStat(Byte byte) {
return ProcState.getProcState(byte).toString();
}
In xhtml:
value="#{yourBean.getProcState(data)}"