I am hashing my user passwords while saving the entities to the database.
While doing that, I'm unable to update the username, without hashing the password again.
I wrote the test for it, which fails.
#Test
#DisplayName("Update")
public void testForUpdate() {
final User user = new User("UserForUpdate", "UpdatedUser123");
this.userService.save(user);
User found = this.userService.findOneByUsernameAndPassword(user.getUsername(), user.getPassword());
assertTrue(found.getId() != null, "Found real user");
this.userService.save(found);
final User asserted = this.userService.findOneByUsernameAndPassword(found.getUsername(), user.getPassword());
assertTrue(asserted != null, "Updated user found");
assertTrue(user.getId() == asserted.getId(), "User ID is persisted");
}
The save method from the UserService looks like this:
#Override
public User save(User newUser) {
newUser.setPassword(passwordEncoder.encode(newUser.getPassword()));
return repository.save(newUser);
}
Am I doing something wrong saving it like this?
How should I proceed, to implement the CRUD Update correctly?
Thanks for any pointers
Do not modify the password when you store the user. Instead encode the password when you modify it.
public class UserSevice {
...
public changePassword(User user, String plainPassword) {
user.setPassword(passwordEncoder.encode(plainPassword));
}
}
Related
I have User class like this :
#Data
#Entity
public class User {
#Id #GeneratedValue Long userID;
String eMail;
String passwordHash;
}
And I have data like this :
[{"userID":1,"passwordHash":"asdasd","email":"admin#admin.com"},
{"userID":2,"passwordHash":"12345","email":"admin1asdasd#admin.com"}]
I have two method , one - to get single user :
// Single item
#GetMapping("/user/{id}")
User one(#PathVariable Long id) {
return repository.findById(id)
.orElseThrow(() -> new UserNotFoundException(id));
}
Other method to retrieve all user :
// Aggregate root
#GetMapping("/user")
List<User> all() {
return repository.findAll();
}
Now how can I match password ? What will be the efficient way ?
You may want to consider this kind of an aproach: in general, you should save hashed password in the database and check passwords using hashed values. Bcrypt is a good option for hashing and it can be easily integrated with Spring.
As explained in the link above you can define a password encoder service:
#Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
and you can use it like this:
#Autowired
private PasswordEncoder passwordEncoder;
//...
User user = new User();
user.setFirstName(accountDto.getFirstName());
user.setLastName(accountDto.getLastName());
user.setPassword(passwordEncoder.encode(accountDto.getPassword()));
user.setEmail(accountDto.getEmail());
user.setRole(new Role(Integer.valueOf(1), user));
repository.save(user);
where accountDto contains the password in clear-text.
Now you can expose a dedicated login method that compares hashed values, something along these lines:
void login(String username, char[] password) throws Exception {
User user = userRepository.findByUsername(username);
if (user != null) {
String encodedPassword = user.getPassword();
if(passwordEncoder.matches(String.valueOf(password), encodedPassword)) {
return;
}
}
throw new Exception("User cannot be authenticated");
}
I am working with jhipster. I need to create a new table for auditing my database changes and link it with the default jhi_persistenet_audit_event table generated by the Jhipster. How I can get the current logged user record from the jhi_persistenet_audit_event table to link that id to my new table?
Solution 1: Principal principal
#RequestMapping(value = {"/", ""})
public String start(Principal principal, Model model) {
String currentUser = principal.getName();
return currentUser;
}
Solution 2: Authentication authentication
#RequestMapping(value = {"/", ""})
public String currentUserName(Authentication authentication) {
return authentication.getName();
}
Solution 3: SecurityContextHolder
Object principal = SecurityContextHolder.getContext().getAuthentication().getPrincipal();
if (principal instanceof UserDetails) {
String username = ((UserDetails)principal).getUsername();
} else {
String username = principal.toString();
}
Details 1 Details 2
I'm working on a legacy application with MySQL DB.
Here's the relevant table:
RESERVATION
============
id: int
creation_date: tinyblob
...other stuff...
When the entity is created, the creation_date is calculated using Java's LocalDate.now().
When I select the data from the table (in MySQL Workbench), I see only "BLOB" in the creation_date column. When I click "Open value in editor", I get something like:
’ sr
java.time.Ser]º"H² xpw àx
When I double click the field, nothing happens, it won't let me enter the editing mode.
Is there any way to directly edit the date stored like this? I could create a Java function that will insert some dates other than LocalDate.now(), but it would be easier to do this manually in the Workbench (or some other application). Since this is a legacy software, I'm not able to alter the column type.
Tnx.
Here is the solution. This guy helped me: https://stackoverflow.com/a/8504540/1177067
Create a new class which extends AttributeConverter:
import java.time.LocalDateTime;
import javax.persistence.AttributeConverter;
import javax.persistence.Converter;
#Converter(autoApply = true)
public class LocalDateTimeAttributeConverter implements AttributeConverter<LocalDateTime, Object> {
#Override
public Object convertToDatabaseColumn(LocalDateTime locDateTime) {
return locDateTime == null ? null : Timestamp.valueOf(locDateTime);
}
#Override
public LocalDateTime convertToEntityAttribute(Object sqlTimestamp) {
if (sqlTimestamp != null) {
try {
byte[] dbBytes = (byte[]) sqlTimestamp;
ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(dbBytes));
LocalDateTime covertedDateFromBytes = (LocalDateTime) ois.readObject();
ois.close();
return covertedDateFromBytes;
} catch (IOException ioe) {
ioe.printStackTrace();
} catch (ClassNotFoundException cnfe) {
cnfe.printStackTrace();
}
}
return null;
}
}
Be aware that:
#Override
public Object convertToDatabaseColumn(LocalDateTime locDateTime) {
return locDateTime == null ? null : Timestamp.valueOf(locDateTime);
}
is storing the date in a new way (as a Timestamp) so decide if you might want to go with the tinyblob further or use a new approach with Timestamp.
I'm trying to do the Mockito for a method called generateToken() by using MockitoJUnitRunner.class. The source which I have tried to do as follows.
#RunWith(MockitoJUnitRunner.class)
public class LoginServiceTest {
#Mock
private UserRepository userRepository;
#Mock
private JwtTokenGenerator jwtTokenGenerator;
#InjectMocks
private LoginServiceImpl loginServiceImpl = new LoginServiceImpl();
private JwtUserDto user;
private String jwtSecret;
private String username;
private String password;
/**
* Initialize test data before test cases execution
*/
#Before
public void init() {
user = new JwtUserDto();
user.setId(1L);
user.setUsername("kray1");
user.setRole("Admin");
}
#Test
public void testLogin() {
try {
Mockito.when(jwtTokenGenerator.generateToken(user, jwtSecret)).thenReturn("myToken");
String actual = loginServiceImpl.login(username, password);
assertNotNull(actual);
} catch (Exception e) {
e.printStackTrace();
}
}
For that generateToken() method, I have to pass user object and a string. I'm declaring the user object in Init() method. When I try to execute this, the value return from the login method is null. But when I try to pass the user object as null then it will work as expected. So the problem should be with the user object.
Is there anything, like Mockito is blocking this kind of object with added properties or related thing? Please help to find a way to pass this user object with Mockito.
The LoginServiceImpl class as follows.
public class LoginServiceImpl implements LoginInterface {
#Autowired
private UserRepository userRepository;
#Autowired
private JwtTokenGenerator jwtTokenGenerator;
/*
* (non-Javadoc)
*/
public String login(String userName, String password) {
if (userName != null && password != null && !userName.isEmpty() && !password.isEmpty()) {
List<UserAuthenticationInfo> authInfo = userRepository.findUserRolesByUsernamePassword(userName, password);
if (authInfo != null && !authInfo.isEmpty()) {
JwtUserDto user = new JwtUserDto();
user.setId((long) authInfo.get(0).getUserId());
user.setUsername(userName);
user.setRole(authInfo.get(0).getUserRole());
return jwtTokenGenerator.generateToken(user, jwtSecret);
}
}
return null;
}
}
Do you have equals/hashcode on User class?
What is the result if you setup mock using
Mockito.when(jwtTokenGenerator.generateToken(any(User.class),any(String.class))
.thenReturn("myToken");
explanation:
When setting expectation as
Mockito.when(jwtTokenGenerator.generateToken(user, jwtSecret)).then...
You instruct your mock to act only for given user object. equals method is used for that. So, if your User is missing equals method, then reference equality is used. Two User objects (each crated with separate new User() call will not be equal.
For non-matching parameters in Mockito.when your mock (thenReturn) is not applied. Default value (null) is returned from mock.
Therefore I prefer to setup mocks not for specific arguments and then use Mockito.verify to check if expected interactions with mock took place. That way your tests are more expressive. Actually most of my object have equals/hashode not because of business reasons (I do not put them in collections) but only for testing and comparing using assertEquals.
Side note:
do not catch (Exception e) { e.printStackTrace(); } in test. It is much easier just to declare test method to throw Exception. End result is same (stacktrace printed) but with less code.
You are probably creating a new JwtUserDto() in your production code or getting the user instance from another mock. If you haven't overwritten the equals() method in your JwtUserDto class your 'test' user won't equal the 'production' user.
Make sure that the production and test user are the same instance or that they .equals each other.
I am trying to encrypt password which are stored in my mysql database, the connection to the databse work well but i don't understand how to encrypt the password i tried something i saw on a tutorial but it doesn"t work here is my domain class
I am using the 2.4.4 version of grails
package jweb
class Clients {
transient springSecurityService
int id
String name
String password
static constraints = {
id()
name()
password()
}
def beforeInsert(){
encodePassword()
}
def encodePassword(){
password = springSecurityService.encodePassword(password,null)
}
}
Edit
I found a way to do it, but i think that you are right Burt, i should certainly use sprinSecurity but i am a beginner in grails and i didn't succeed to make it work so at this time i have this
package jweb
import java.security.MessageDigest
class Clients {
int id
String name
String password
String cart_id
boolean admin
static constraints = {
id()
name()
password()
cart_id nullable:true
admin()
}
def beforeInsert(){
encodePassword()
}
def beforeUpdate(){
encodePassword()
}
def encodePassword(){
MessageDigest md5Digest;
byte[] digest;
md5Digest = MessageDigest.getInstance("SHA-512");
md5Digest.reset();
md5Digest.update(password.getBytes());
digest = md5Digest.digest();
password = new BigInteger(1,digest).toString(16)
}
}