Java Spring hash AND validate passwords - java

I'm pulling my hair out over this. I have a simple User entity like this
#Entity
public class User {
private static final PasswordEncoder pwEncoder = new BCryptPasswordEncoder();
#Id
#GeneratedValue
private long id;
#NotNull(message = "FIELD_IS_NULL")
#NotEmpty(message = "FIELD_IS_REQUIRED")
#Length(min = 3, message = "FIELD_MUST_HAVE_AT_LEAST_3_CHARACTERS")
private String username;
#NotNull(message = "FIELD_IS_NULL")
#NotEmpty(message = "FIELD_IS_REQUIRED")
#Length(min = 6, message = "FIELD_MUST_HAVE_AT_LEAST_6_CHARACTERS")
#Pattern(regexp = "^(?=.*\\d)(?=.*[a-z])(?=.*[A-Z]).{6,128}$", message="PW_MIN_6_MAX_128_1_UPPER_1_LOWER_1_NUMERIC")
private String password;
public User(String username, String password){
this.username = username;
this.password = pwEncoder.encode(password);
}
/* getters and setters */
}
This works fine except that the password hashing happens before any validation, which means that the hashed password is validated instead of the unhashed.
I'm using a PagingAndSortingRepository for all my repository needs and I'd really like to avoid implementing a controller just for this case.
I'm feeling like I'm missing something really big there...

If you using this constructor
public User(String username, String password){
this.username = username;
this.password = pwEncoder.encode(password);
}
you'll have encoded password instead of original value
you can make #PrePersist method like this:
#PrePersist
public void prePersist(){
password = pwEncoder.encode(password);
}

Related

How to i18n form errors with spring

So I can't make these default messages change to the ones I want. In all the JSP files they work great, but I can't make them work inside a form.
Here is my UserController:
#RequestMapping(path = "/user/profile", method = RequestMethod.POST)
public ModelAndView userProfilePost(#ModelAttribute("userForm") #Valid UserForm userForm, BindingResult result) {
if(result.hasErrors()){
return userProfileGet(userForm);
}
User user = us.findByEmail(SecurityContextHolder.getContext().getAuthentication().getName());
user = userHelper.update(userForm, us ,user);
userForm = new UserForm();
userForm.setName(user.getName());
userForm.setEmail(user.getEmail());
return userProfileGet(userForm);
}
Then we have my WebConfig:
#Bean
public LocalValidatorFactoryBean localValidatorFactoryBean() {
LocalValidatorFactoryBean bean = new LocalValidatorFactoryBean();
bean.setValidationMessageSource(messageSource());
return bean;
}
#Bean
public MessageSource messageSource()
{
final ReloadableResourceBundleMessageSource messageSource = new ReloadableResourceBundleMessageSource();
messageSource.setBasenames("classpath:languages_i18n/messages", "classpath:languages_i18n/validation");
messageSource.setDefaultEncoding(StandardCharsets.UTF_8.displayName());
messageSource.setCacheSeconds(5);
return messageSource;
}
My UserForm:
public class UserForm {
#Email()
private String email;
#Size(max=100 , min=3)
private String password;
#Size(max=100 , min =3)
private String name;
public String getEmail() { return email; }
public void setEmail(String email) { this.email = email; }
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public String getName() { return name; }
public void setName(String name) { this.name = name; }
}
Here I tried also doing (which didn't work of course haha):
#Email(message = "{Email.UserForm.email}")
private String email;
#Size(max=100 , min=3, message = "{Size.UserForm.password}")
private String password;
#Size(max=100 , min =3, message = "{Size.UserForm.name}")
private String name;
Here is my validation_en.properties:
Email.UserForm.email = Your e-mail format is not valid
Size.UserForm.password = Your password must have between {min} and {max} characters
Size.UserForm.name = Your name must have between {min} and {max} characters
I know that when I do it right, the lines in the .properties file should get colored but they are still grey, so clearly I am not reaching them correctly.
Any comment will be well received, thank you in advance.
Apparently the solution was that the first letter of the form should be lower case, like this (I do not know why):
Size.userForm.password = Your password must have between {2} and {1} characters
Size.userForm.name = Your name must have between {2} and {1} characters
After that we have to add what Tung Phan said that was also correct because parameters are object array, so {min} and {max} were wrong.
Hope this is useful for anyone in the future.
Parameters are an Object array.
The {0} is field name, other fields are in alphabatical order, max and then min.
So it should be:
Size.UserForm.password = Your password must have between {2} and {1} characters
Size.UserForm.name = Your name must have between {2} and {1} characters

Vaadin ThreadLocal for User Management

I'm currently developing a web application in Java which will be accessed by multiple users at the same time and as such need to store userdata in order to tailor the application to their individual requirements (such as what company they are apart of etc).
There are 2 classes that i use to manage this. User, MainSystem detailed below:
User
#Entity
public class User implements Serializable{
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String firstName;
private String lastName;
private String username;
private String password;
private String type;
private String company;
private String DOB;
private String email;
private int PayrollId;
public User(String firstName, String lastName, String username, String password, String type, String company, String DOB, String email) {
this.firstName = firstName;
this. lastName = lastName;
this.username = username;
this.password = password;
this.type = type;
this.company = company;
this.DOB = DOB;
this.email = email;
}
MainSystem:
public class MainSystem {
public UserController userController;
private UserRepository userRepository;
private static ThreadLocal<User> loggedInUser = new ThreadLocal<>();
public DbController dbController;
public MainSystem(){
userController = new UserController(userRepository);
loggedInUser.set(new User());
}
public Boolean Login(String username, String password) {
if(userController.checkUser(username,password)){
User aUser = userController.getUser(username);
setLoggedInUser(userController.getUser(username));
VaadinSession.getCurrent().setAttribute("username",loggedInUser.get().getUsername());
System.out.println("Logged in User: "+loggedInUser.get().getUsername());
return true;
}
else {
return false;
}
}
public static void setLoggedInUser(User user){
loggedInUser.set(user);
}
public static User getLoggedInUser() {
return loggedInUser.get();
}
Ideally what i'd like to do is access the ThreadLocal variable from another class, for instance the ViewProfile.View:
public class EditProfileView extends VerticalLayout implements View {
MainSystem main = new MainSystem();
NavigatorUI aUI = new NavigatorUI();
User aUser = main.getLoggedInUser();
TextField username = new TextField("Username");
TextField Id = new TextField("Id");
TextField email = new TextField("Email");
TextField firstName = new TextField("First name");
TextField lastName = new TextField("Last name");
TextField type = new TextField("Type");
PasswordField oldPassword = new PasswordField("Current Password");
PasswordField changePassword1 = new PasswordField("New Password");
PasswordField changePassword2 = new PasswordField("Confirm Password");
private UserController userController;
private UserRepository userRepository;
public EditProfileView() {
setDefaultComponentAlignment(Alignment.MIDDLE_CENTER);
userController = new UserController(userRepository);
setStyleName("backgroundImage");
setMargin(true);
setSizeFull();
addComponent(aUI.getHeader());
FormLayout content = new FormLayout(generateInfo());
Panel aPanel = new Panel("Edit User",content);
aPanel.setWidthUndefined();
content.setMargin(true);
aPanel.setStyleName(ValoTheme.PANEL_WELL);
addComponent(aPanel);
}
#Override
public void enter(ViewChangeListener.ViewChangeEvent event) {
try {
aUser = main.getLoggedInUser();
System.out.println( aUser.getUsername());
Id.setValue(aUser.getId().toString());
username.setValue(aUser.getUsername());
firstName.setValue(aUser.getFirstName());
lastName.setValue(aUser.getLastName());
type.setValue(aUser.getType());
email.setValue(aUser.getEmail());
aUI.setUserMenu();
aUI.refreshPayroll();}
catch (Exception e){
System.out.println(e.getMessage());
}
}}
However, i'm finding that it is presenting me with a "null" value? I fear i may have missunderstood how ThreadLocal works. but essentially what i'm trying to achieve is to Store an instance relevant variable of the User.Class in MainSystem for other classes to use?
Any help would be appreciated.
My Solution:
My solution to this was to store the User.class in a VaadinSession Attribute like so:
public Boolean Login(String username, String password) {
if(userController.checkUser(username,password)){
User aUser = userController.getUser(username);
VaadinSession.getCurrent().setAttribute("user",aUser);
VaadinSession.getCurrent().setAttribute("username",loggedInUser.get().getUsername());
System.out.println("Logged in User: "+loggedInUser.get().getUsername());
return true;
}
else {
return false;
}
}
Then in other classes if i want to use that attribute i retrieved it like so:
#Override
public void enter(ViewChangeListener.ViewChangeEvent event) {
try {
aUser = (User)VaadinSession.getCurrent().getAttribute("user");
}
catch (Exception e){
System.out.println(e.getMessage());
}
}
The problem is that there's no guarantee that MainSystem.Login() and EditProfileView.enter() will happen on the same thread. Every user action is processed as a separate HTTP request that the servlet container will run on any available thread.
For this kind of functionality, I would instead recommend storing the user information in the HTTP session. If you don't have any custom servlets or such, you could instead have a an field that contains the user object in your own custom UI class. Vaadin takes care of making UI.getCurrent() always return the right value in all code that is run through Vaadin.
If you instead also are integrating with other servlet functionality, you could store the user in the HttpSession instead. Generic servlet code can find the session through the getSession() method in e.g. servlet requests and response. In code run by Vaadin, you can use VaadinSession().getCurrent().getSession() to get a WrappedSession instance that is based on to the same HttpSession data.
There is also another approach. Instead it could be possible to make #SessionScoped user service, and either #Inject or #Autowire that depending whether you are using CDI or Spring. When doing this way, it will be the bean manager that takes care of binding correct entity with your view.

javax validation for login(email or phone)

class SingIn {
private String login;
private String password;
public SingIn(String login, String password) {
this.login = login;
this.password = password;
}
public String getLogin() {
return login;
}
public String getPassword() {
return password;
}
}
Login property could be an email(pattern ".+#.+") or a phone number(pattern "\+\d+").
Is it possible somehow with javax annotation to validate the login property?
Yes, it's possible by composing 2 #Pattern annotations with logical OR:
#ConstraintComposition(OR)
#Pattern(regexp = ".+#.+")
#Pattern(regexp = "\+\d+")
#ReportAsSingleViolation
#Target({ METHOD, FIELD })
#Retention(RUNTIME)
#Constraint(validatedBy = { })
public #interface EmailOrPhone {
See also the topic Boolean composition of constraints in the Hibernate Validator documentation.

Spring Data & mongodb Converter error: java.lang.StackOverflowError

I am using Spring data connect to mongodb. Please see my problems below:
Now, I have two data models (User and Draft):
#Document
public class User implements Serializable {
#Id
private String id;
private String showName;
private String password;
//..... (other attributes)
#DBRef
private List<Draft> createdDraft = new ArrayList<Draft>();
//constructors
public User() {
super();
}
public User(String id, String showName, String password, //....other attributes
List<Draft> createdDraft) {
super();
this.id = id;
this.showName = showName;
this.password = password;
//....
}
//getters and setters
}
and
#Document
public class Draft {
#Id
private String id;
private String title;
private Date createTime;
private Date lastEditTime;
#DBRef
private User lastEditor;
#DBRef
private User mainAuthor;
#DBRef
private List<User> coAuthors = new ArrayList<User>();
private String externalURL;
//constructors..
public Draft() {
super();
}
public Draft(String id, String title, Date createTime, Date lastEditTime,
User lastEditor, User mainAuthor, String externalURL) {
super();
this.id = id;
this.title = title;
this.createTime = createTime;
this.lastEditTime = lastEditTime;
this.lastEditor = lastEditor;
this.mainAuthor = mainAuthor;
this.externalURL = externalURL;
}
//getters and setters...
}
In my project, I have created the user successfully
then, I would like to add a draft to the existing user.
public String CreateNewDraft(User mainAuthor)
{
Draft draft = new Draft();
draft.setMainAuthor(mainAuthor);
Date now = new Date(System.currentTimeMillis());
draft.setCreateTime(now);
mainAuthor.getCreatedDraft().add(draft);
//insert the draft --> Successful (1)
mongoOps.insert(draft);
//update the user --> Successful (2)
mongoOps.save(mainAuthor);
//find the last inserted draft. --> Errors.
Draft d = mongoOps.findOne(query(where("createTime").is(now) ), Draft.class);
return d.getId()
}
In (1), I have found a new "draft" document created in mongoDB, which has _id = it has 52a1591597d738f7b397be96.
In (2), I have found the existing user (mainAuhtor) document has one entry added in the createdDraft field like
[ { "$ref" : "draft" , "$id" : { "$oid" : "52a1591597d738f7b397be96"}}]
Exception & log:
processing failed; nested exception is java.lang.StackOverflowError
org.springframework.web.servlet.DispatcherServlet.
doDispatch(DispatcherServlet.java:972)
org.springframework.web.servlet.DispatcherServlet.
doService(DispatcherServlet.java:852)
org.springframework.web.servlet.FrameworkServlet.p
rocessRequest(FrameworkServlet.java:882)
org.springframework.web.servlet.FrameworkServlet.d
oGet(FrameworkServlet.java:778)
javax.servlet.http.HttpServlet.service(HttpServlet .java:621)
javax.servlet.http.HttpServlet.service(HttpServlet .java:728)
org.apache.tomcat.websocket.server.WsFilter.doFilt
er(WsFilter.java:51)
root cause
java.lang.StackOverflowError
java.net.SocketInputStream.socketRead0(Native Method)
java.net.SocketInputStream.read(Unknown Source)
java.net.SocketInputStream.read(Unknown Source)
java.io.BufferedInputStream.fill(Unknown Source)
java.io.BufferedInputStream.read1(Unknown Source)
java.io.BufferedInputStream.read(Unknown Source)
org.bson.io.Bits.readFully(Bits.java:46)
org.bson.io.Bits.readFully(Bits.java:33)
org.bson.io.Bits.readFully(Bits.java:28)
com.mongodb.Response.<init>(Response.java:40)
com.mongodb.DBPort.go(DBPort.java:124)
com.mongodb.DBPort.call(DBPort.java:74)
com.mongodb.DBTCPConnector.innerCall(DBTCPConnecto r.java:286)
com.mongodb.DBTCPConnector.call(DBTCPConnector.jav a:257)
com.mongodb.DBApiLayer$MyCollection.__find(DBApiLa yer.java:310)
com.mongodb.DBApiLayer$MyCollection.__find(DBApiLa yer.java:295)
com.mongodb.DBCollection.findOne(DBCollection.java :346)
com.mongodb.DBCollection.findOne(DBCollection.java :331)
com.mongodb.DBRefBase.fetch(DBRefBase.java:53)
org.springframework.data.mongodb.core.convert.Mapp
ingMongoConverter.readValue(MappingMongoConverter. java:1046)
org.springframework.data.mongodb.core.convert.Mapp
ingMongoConverter.access$100(MappingMongoConverter .java:77)
org.springframework.data.mongodb.core.convert.Mapp
ingMongoConverter$MongoDbPropertyValueProvider.get
PropertyValue(MappingMongoConverter.java:999)
org.springframework.data.mongodb.core.convert.Mapp
ingMongoConverter.getValueInternal(MappingMongoCon verter.java:755)
org.springframework.data.mongodb.core.convert.Mapp
ingMongoConverter$2.doWithAssociation(MappingMongo Converter.java:265)
org.springframework.data.mapping.model.BasicPersis
tentEntity.doWithAssociations(BasicPersistentEntit y.java:269)
org.springframework.data.mongodb.core.convert.Mapp
ingMongoConverter.read(MappingMongoConverter.java: 262)
org.springframework.data.mongodb.core.convert.Mapp
ingMongoConverter.read(MappingMongoConverter.java: 223)
org.springframework.data.mongodb.core.convert.Mapp
ingMongoConverter.readCollectionOrArray(MappingMon
goConverter.java:788)
org.springframework.data.mongodb.core.convert.Mapp
ingMongoConverter.readValue(MappingMongoConverter. java:1048)
org.springframework.data.mongodb.core.convert.Mapp
ingMongoConverter.access$100(MappingMongoConverter .java:77)
org.springframework.data.mongodb.core.convert.Mapp
ingMongoConverter$MongoDbPropertyValueProvider.get
PropertyValue(MappingMon```
Can someone help me to take a look? Thanks so much!
This is a bug (or expected behaviour?) of spring data mongodb (I get this in 1.3.x version, haven't tried 1.4.x).
The problem is that User has reference to Draft and Draft to the same user instance so the converter gets into infinite loop.
#Document
public class User implements Serializable {
...
#DBRef
private List<Draft> createdDraft = new ArrayList<Draft>();
and
#Document
public class Draft {
...
#DBRef
private User lastEditor;
#DBRef
private User mainAuthor;
#DBRef
private List<User> coAuthors = new ArrayList<User>();
You should probably use simple id references, not DBRef (it is even suggested here http://docs.mongodb.org/manual/reference/database-references/ as suitable for most use cases)
If you find yourself using DBRef a lot you should consider using different kind of database, e.g. a graph database.
you should map your received entity (document) from the mongo database..
use the Springs Converter interface
for example:
public class ProfileReadConverter implements Converter<DBObject, Profile> {
#Override
public Profile convert(DBObject source) {
#SuppressWarnings("unchecked")
Profile p = new Profile((ObjectId) source.get("_id"), (boolean) source.get("active"), (String) source.get("name"),
(String) source.get("shortName"), (List<Person>) source.get("person"));
return p;
}
}
Profile.java
#Document(collection = "profile")
public class Profile {
#Id
private ObjectId id;
private boolean active;
#Indexed(unique = true)
#Field("ProfileName")
private String name;
private String shortName;
#DBRef
private List<Person> person = new ArrayList<Person>();
public Profile() {
}
#PersistenceConstructor
public Profile(ObjectId id, boolean active, String name, String shortName, List<Person> person,) {
this.id = id;
this.active = active;
this.name = name;
this.shortName = shortName;
this.person = person;
}
//getter and setter
Person.java
#Document(collection = "person")
public class Person extends Ressource {
#Indexed
private String firstname;
private String lastname;
#Field("email")
#Indexed(unique = true)
private String eMailAddress;
private String login;
#DBRef
private List<Profile> profiles = new ArrayList<Profile>();
public Person(ObjectId id, String firstname, String lastname, String eMailAddress, String login) {
this.setId(id);
this.firstname = firstname;
this.lastname = lastname;
this.eMailAddress = eMailAddress;
this.login = login;
}
#PersistenceConstructor
public Person(ObjectId id, String firstname, String lastname, String eMailAddress, String login,
List<Profile> profiles) {
this.firstname = firstname;
this.lastname = lastname;
this.eMailAddress = eMailAddress;
this.login = login;
this.profiles = profiles;
}
main or test class
...
Profile profileFind = mongoOps.findOne(new Query(where("shortName").is("SE")), Profile.class, "profile");

Simple object persist in Spring + hibernate

I suppose it is not standard way of doing that so any tips will be helpful, here is my code:
#RequestMapping("/register")
public String register(Map<String, Object> map, #ModelAttribute("user") MyUser user) {
if(user.getLogin() == ""){
map.put("user", new MyUser());
}
else{
map.put("user", user);
map.put("result", userService.addMyUser(user));
}
return "register";
}
what cause following error:
org.hibernate.AssertionFailure: null id in org.mypackage.MyUser entry
(don't flush the Session after an exception occurs)
Here is MyUser class:
#Entity
#Table(name="MyUser")
public class MyUser{
#Id
#Column(name="idMyUser")
#GeneratedValue
private Integer id;
#Column(name="login")
private String login;
#Column(name="password")
private String password;
public String getLogin() {
return login;
}
public void setLogin(String login) {
this.login = login;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
}
Try changing the strategy and/or generator for the #GeneratedValue, see here and here for details (for example, you could try #GeneratedValue(strategy = GenerationType.IDENTITY). You could also check if your database table is set to generate the primary key values. The exception seems to indicate that the primary key -field is left unset by the current strategy and/or generator.

Categories

Resources