Need to create default user profile when my Web Application [Spring MVC] starts:
--using postConstruct
-- Create default user on start up
-- do not create same user in next run
-- Default user like admin with some other information like First Name, Last Name,DOB etc. and it should be stored in database.
Of-course I have to follow proper mvc pattern like controller, model, service, repository, spring-configuration,spring-security configuration.
Please do not post code in spring-boot or JSON.
Any help will be highly appreciated and thanks in advance.
The question have simple solution after lot of research I have concluded that using #PostConstruct anything can be create at initializing phase of the application.
Hare is What I have found:
#Component
public class DbInit {
#Autowired
private UserRepository userRepository;
#PostConstruct
private void postConstruct() {
User admin = new User("admin", "admin password");
User normalUser = new User("user", "user password");
userRepository.save(admin, normalUser);
}
}
A reference from : https://www.baeldung.com/spring-postconstruct-predestroy
Thanks to this site a grate place to learn.
Create a class call SecurityUtil like below.
import org.springframework.security.authentication.encoding.Md5PasswordEncoder;
import java.util.HashSet;
import java.util.Set;
public class SecurityUtil {
public static User getFirstTimeUser() {
User defaultUser = new User();
defaultUser.setActive(true);
defaultUser.setUserId("admin");
defaultUser.setFirstName("First Time");
defaultUser.setLastName("Admin");
defaultUser.setId(0L);
defaultUser.setPassword(new Md5PasswordEncoder().encodePassword("admin", Constants.SALT));
//Set<Role> roles = new HashSet<Role>();
Role role = new Role();
role.setId(0L);
role.setRoleName("ROLE_ADMIN");
//roles.add(role);
defaultUser.setRole(role);
return defaultUser;
}
Then call this method from Userservice class which is implemented from Spring's UserDetails.
#Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
UserFilter filter = new UserFilter();
filter.setUserId(username);
List<User> users = userDAO.find(filter);
if (users != null && users.size() == 1) {
return users.get(0);
} else {
if (userDAO.count() == 0) {
return getFirstTimeUser();
} else {
throw new UsernameNotFoundException("Username Not Found Exception");
}
}
}
Related
I try to test my Spring Repository by #DataJpaTest.
I want to found exactly this same user by email, but I get another set for roles.
Firstly If I start my project then I setup test user and roles to him.
#Component
#RequiredArgsConstructor
public class SetupDataLoader implements ApplicationListener<ContextRefreshedEvent> {
private final UserRepository userRepository;
private final RoleRepository roleRepository;
private final UserService userService;
boolean alreadySetup = false;
#Override
#Transactional
public void onApplicationEvent(ContextRefreshedEvent event) {
if (alreadySetup)
return;
createRoleIfNotFound("ROLE_USER");
createRoleIfNotFound("ROLE_ADMIN");
if (userRepository.findByUsername("test123") == null) {
UserRegistrationDto user = new UserRegistrationDto();
user.setUsername("test123");
user.setPassword("Test12345");
user.setEmail("test#test.com");
userService.save(user);
}
alreadySetup = true;
}
#Transactional
Role createRoleIfNotFound(String name) {
Role role = roleRepository.findByName(name);
if (role == null) {
role = roleRepository.save(new Role(name));
}
return role;
}
Piece of code from UserService where user will be saved.
public void save(UserRegistrationDto userRegistrationDto) {
userValidator.userRegistrationValidator(userRegistrationDto);
User user = new User();
user.setUsername(userRegistrationDto.getUsername());
user.setPassword(passwordEncoder.encode(userRegistrationDto.getPassword()));
user.setEmail(userRegistrationDto.getEmail());
user.setEnabled(true);
Set<Role> roles = new HashSet<>();
roles.add(roleRepository.findByName("ROLE_USER"));
user.setRoles(roles);
userRepository.save(user);
}
So next step is my repository test. I want to find user by email.
#DataJpaTest
#AutoConfigureTestDatabase(replace = AutoConfigureTestDatabase.Replace.NONE)
class UserRepositoryTest {
#Autowired
private UserRepository testUserRepository;
#Autowired
private RoleRepository testRoleRepository;
#Test
void shouldFindUserByEmail() {
//given
Set<Role> roles = new HashSet<>();
roles.add(testRoleRepository.findByName("ROLE_USER"));
User user = new User();
user.setId(1L);
user.setUsername("test123");
user.setPassword("$2a$10$Dk0CTgpWyqckt/UmSviLmOOYeqWpf4lFeuZcoLvW33LdZqRAYI2lW");
user.setEmail("test#test.com");
user.setRoles(roles);
user.setEnabled(true);
//when
User userWithMail = testUserRepository.findByEmail("test#test.com");
//then
assertThat(userWithMail).isEqualTo(user);
}
Here Is debug from this method
I got two different roles one as HashSet from test method and second PersistentSet from database, so these objects are different. Why I got PersistentSet? Is it because of database did anything? Can someone explain whats this PersistentSet exist? One more clue, roles are many to many with user model.
Is good idea to compare objects in tests or for example username/email is enough?
assertThat(userWithMail).isEqualTo(user);
Did you override equals() method in User? Otherwise Java defaults to comparing a reference - in which case two separate objects will always be different (new User().equals(new User()) would return false).
If you don't want to override equals() yourself, in your test you could look for a method that does field by field comparison - AssertJ has for instance isEqualToComparingFieldByField(), which should return true.
It should return true by the way, because Sets define equals() method in a way that ensures any Set implementation with the exact same content will be equal to one another. So it won't matter that Hibernate uses a wrapper implementation, and that PersistentSet will be equal to your HashSet if all the elements match. Well, that's if the elements do match - which might require overriding equals() and hashCode() of Role entity.
I'm using MSAL on the front end (PKCE) and azure-active-directory-spring-boot-starter on the server to provide an entity which represents the logged in user and their claims.
I've built a class which wraps Microsoft's UserPrincipal to provide easy access to well-known claims:
import com.microsoft.azure.spring.autoconfigure.aad.UserPrincipal;
public class MyCustomUser {
private UserPrincipal userPrincipal;
public MyCustomUser(UserPrincipal userPrincipal) {
this.userPrincipal = userPrincipal;
}
public String getEmployeeId() {
return String.valueOf(this.userPrincipal.getClaim("emplid"));
}
}
I make this available via a helper class
#Component
public class MyAppSecurityContext {
public MyCustomUser getUser() {
UserPrincipal principal = (UserPrincipal) SecurityContextHolder.getContext().getAuthentication().getPrincipal();
return new MyCustomUser(principal);
}
}
and then use it in my service layer:
#Service
public class AnnouncementServiceImpl implements AnnouncementService {
#Autowired
private MyAppSecurityContext securityContext;
#Override
public List<Announcement> saveAnnouncement(Announcement announcement) {
MyCustomUser currentUser = this.securityContext.getUser();
String empid = currentUser.getEmployeeId();
return this.announcementRepository.saveAnnouncement(empid, announcement);
}
}
This works, but it feels wrong. I'd prefer to have MyCustomUser extend UserPrincipal and have getPrincipal() return my custom type (without effectively re-implementing my own UserPrincipal) instead of providing a facade in front of a member object. The problem is that UserPrincipal's constructor expects JWT concerns, which suggests that this isn't the correct approach. Is there another, more appropriate way to model the user for a Spring security project which relies on client-side claims only?
#Josh.
In azure-active-directory-spring-boot-starter, UserPrincipal is used in AADAuthenticationFilter and AADAppRoleStatelessAuthenticationFilter. Now both of the 2 filters are deprecated.
Could you please use the latest version of azure-spring-boot-starter-active-diectory? Which is 3.7.0, and it works for spring-boot:2.5.0. Since you used UserPrincipal, then you application is a resource server.
Here is the docs: https://github.com/Azure/azure-sdk-for-java/tree/azure-spring-boot-starter-active-directory_3.7.0/sdk/spring/azure-spring-boot-starter-active-directory#accessing-a-resource-server
We have some samples.
You current application is similar to:
https://github.com/Azure-Samples/azure-spring-boot-samples/tree/azure-spring-boot_3.6/aad/azure-spring-boot-sample-active-directory-resource-server-by-filter-stateless
https://github.com/Azure-Samples/azure-spring-boot-samples/tree/azure-spring-boot_3.6/aad/azure-spring-boot-sample-active-directory-resource-server-by-filter
But the 2 usage is deprecated, I suggest you to learn the new way:https://github.com/Azure-Samples/azure-spring-boot-samples/tree/azure-spring-boot_3.6/aad/azure-spring-boot-sample-active-directory-resource-server
I have service:
#Slf4j
#Service
public class CashierServiceDefault implements CashierService {
private final UserRepository userRepository;
#Autowired
public CashierServiceDefault(UserRepository userRepository) {
this.userRepository = userRepository;
}
#Override
#Transactional
public CashierDto login(CashierDto cashier) {
User dbUser = userRepository.findOneByLoginAndPassword(cashier.getLogin(), cashier.getPassword());
validateCashier(cashier.getLogin(), dbUser);
User userWithToken = createAuthToken(dbUser);
return domainUserToCashierDto(userWithToken, cashier);
}
private void validateCashier(String login, User dbUser) {
if (dbUser == null) {
log.error("Cashier: {} not found", login);
throw new AuthException(AuthException.ErrorCode.USER_NOT_FOUND_EXCEPTION);
}
UserRole userRole = UserRole.valueOf(dbUser.getUserRole().getCode());
if (userRole != UserRole.CASHIER) {
log.error("User: {} has role: {}. expected: CASHIER ", login, userRole.toString());
throw new AuthException(AuthException.ErrorCode.USER_ROLE_NOT_PERMISSION_EXCEPTION);
}
}
private User createAuthToken(User user) {
user.setAuthToken(TokenGenerator.nextToken());
user.setAuthTokenCreatedDate(new Date());
return userRepository.save(user);
}
private CashierDto domainUserToCashierDto(User user, CashierDto cashier) {
//mapping user's fields to CashierDto,s fields
return cashier;
}
I want create Test for this service. I tried this:
#RunWith(SpringRunner.class)
public class CashierServiceDefaultTest {
#MockBean
private UserRepository userRepository;
private CashierService cashierService;
#Before
public void setUp() throws Exception {
cashierService = new CashierServiceDefault(userRepository);
}
#Test
public void login() {
CashierDto cashierDto = new CashierDto();
cashierDto.setLogin("Alex");
cashierDto.setPassword("123");
User user = new User();
user.setLogin("Alex");
user.setPassword("123");
//and other test values
when(userRepository.findOneByLoginAndPassword(cashierDto.getLogin(), cashierDto.getPassword())).thenReturn(user);
CashierDto found = cashierService.login(cashierDto);
assertThat(found.getAuthToken()).isEqualTo("123");
}
And I have questions:
1. How can I tests private methods in my service? Do I need to test them? If so, how?
2. How should I test the public login method? I made a stub for repository methods:
when(userRepository.findOneByLoginAndPassword(cashierDto.getLogin(), cashierDto.getPassword())).thenReturn(user);
But should I do stubs for internal service methods?(validateCashier, createAuthToken, domainUserToCashierDto). If so, how?
UnitTests do not test code, they verify public observable behavior which is return values and communication with dependencies.
private methods are implementation details which you test indirectly (as stated by JWo)
The reason is that you later may change your implementation details (refactor them) whithout breaking any of your existing UnitTests.
I would not test them directly. Since you implemented them as a help for some other methods, you can test those. By testing all public methods you will test the private ones, too. Don't forget to add inputs and outputs for the private methods, when testing the public ones.
Another way is to put test methods into the same package as the production code. Then you have to set your private methods to package or protected.
I've the following model:
public class Users {
public static PlayJongo jongo = Play.current().injector().instanceOf(PlayJongo.class);
public static MongoCollection users() {
return jongo.getCollection("DB.users");
}
..
..
public static Users authenticate(String email, String password) {
Users user = users().findOne("{email: #, removed: false}", email).as(Users.class);
if (user != null) {
if (HomeController.checkPassword(password, user.password)) {
return user;
}
}
return null;
}
..
I use that in my controllers as:
public Result authenticate() {
DynamicForm requestData = Form.form().bindFromRequest();
String email = requestData.get("email").trim();
String password = requestData.get("password").trim();
Users user = Users.authenticate(email, password);
if (user == null) {
flash("danger", "Incorrect email or password.");
return redirect(routes.HomeController.login());
}
session("email", user.getEmail());
session("role", user.getRole());
session("fullname", user.getLastname() + " " + user.getFirstname());
session("id", user.getId().toString());
return redirect(routes.HomeController.index());
}
I tried a lot of combination to use injection with play-jongo without result. E.g.
#Inject
public PlayJongo jongo;
public MongoCollection users() {
return jongo.getCollection("DocBox.users");
}
I enter in a loop of static/non-static referenced context errors. If I remove all static declaration, I'm unable to call Users.method. If I try to inject Users to a controller
public class HomeController extends Controller {
#Inject
public Users users;
.
.
and try to call a Users method:
Users user = users.authenticate(email, password);
I receive a org.jongo.marshall.MarshallingException.
My brain is definitively goes overheating, someone can explain me how to use Injection with play-jongo?
I solve the problem. Now I've a UsersRepository that contains the methods that operate on the mongo collection (authenticate, addUser, et al.). And a Users object that only contains the actual data fields (firstname, lastname, email, etc.).
After that I can inject UsersRepository into my controller and use that one instance everywhere.
Thanks to Greg Methvin, Tech Lead - Play Framework
I was just reading this article:
http://www.tutorialized.com/view/tutorial/Spring-MVC-Application-Architecture/11986
which I find great. It explains the layer architecture nicely and I was glad that the architecture I'm working with pretty much is what he describes.
But there's one thing, that I don't seem to get:
First: what exactly is business logic and what is it not? In the article he says (and he's not the only one), that business logic should go in the domain model. So an Account class should have an activate() method that knows how to activate an Account. In my understanding this would involve some persistence work probably. But the domain model should not have a dependency of DAOs. Only the service layer should know about DAOs.
So, is business logic just what a domain entity can do with itself? Like the activate()method would set the active property to true, plus set the dateActivated property to new Date() and then it's the service's task to first call account.activate()and second dao.saveAccount(account)? And what needs external dependencies goes to a service? That's what I did until now mostly.
public AccountServiceImpl implements AccountService
{
private AccountDAO dao;
private MailSender mailSender;
public void activateAccount(Account account)
{
account.setActive(true);
account.setDateActivated(new Date());
dao.saveAccount(account);
sendActivationEmail(account);
}
private void sendActivationEmail(Account account)
{
...
}
}
This is in contrast to what he says, I think, no?
What I also don't get is the example on how to have Spring wire domain objects like Account. Which would be needed should Account send its e-mail on its own.
Given this code:
import org.springframework.mail.MailSender;
import org.springframework.mail.SimpleMailMessage;
public class Account {
private String email;
private MailSender mailSender;
private boolean active = false;
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public void setMailSender(MailSender mailSender) {
this.mailSender = mailSender;
}
public void activate() {
if (active) {
throw new IllegalStateException("Already active");
}
active = true;
sendActivationEmail();
}
private void sendActivationEmail() {
SimpleMailMessage msg = new SimpleMailMessage();
msg.setTo(email);
msg.setSubject("Congrats!");
msg.setText("You're the best.");
mailSender.send(msg);
}
}
If I use Hibernate, I could use the DependencyInjectionInterceptorFactoryBean in order to wire mailSender. If I use JDBC instead, I'd really write the follwing cumbersome code? Plus, also when I create a new instance for Account in a MVC controller, for let's say populating it to a model??
BeanFactory beanFactory = new XmlBeanFactory(
new ClassPathResource("chapter3.xml"));
Account account = new Account();
account.setEmail("email#example.com");
((AutowireCapableBeanFactory)beanFactory).applyBeanPropertyValues(
account, "accountPrototype");
account.activate();
This is not reliable and very cumbersome, no? I'd have to ask myself where that object has been created, whenever I see an instance of Account. Plus, if I would go with this approach: I have not a single appContext.xml I could pass, but several, one for persistence, one for the service config. How would I do that? Plus, that would create a completely new context every time such an instance is created or am I missing something?
Is there no better solution to that?
Any help is greatly appreciated.
I think send activation email action is not a part of a business-layer here, your domain logic here is the account activation action, that piece of logic should live in the DomainObject with name Account ( activate() method ). The send activation email action is the part of infrastructure or application layers.
Service is the object that handles account activation request and connects business-layer and others. Service takes the given account, activates them and performs send activation email action of MailSenderService or something like this.
Short sample:
public AccountServiceImpl implements AccountService
{
private AccountDAO dao;
private MailSenderService mailSender;
public void activateAccount(AccountID accountID)
{
Account account = dao.findAccount(accountID);
....
account.activate();
dao.updateAccount(account);
....
mailSender.sendActivationEmail(account);
}
}
The next step that I can suggest is a complete separation of business layer
and a layer of infrastructure. This can be obtained by introducing the
business event. Service no longer has to perform an action to send an
email, it creates event notifying other layers about
account activation.
In the Spring we have two tools to work with events, ApplicationEventPublisher and ApplicationListener.
Short example, service that publish domain events:
public AccountActivationEvent extends ApplicationEvent {
private Account account;
AccountActivationEvent(Account account) {
this.account = account;
}
public Account getActivatedAccount() {
return account;
}
}
public AccountServiceImpl implements AccountService, ApplicationEventPublisherAware
{
private AccountDAO dao;
private ApplicationEventPublisher epublisher;
public void setApplicationEventPublisher(ApplicationEventPublisher epublisher) {
this.epublisher = epublisher;
}
public void activateAccount(AccountID accountID)
{
Account account = dao.findAccount(accountID);
....
account.activate();
dao.updateAccount(account);
....
epublisher.publishEvent(new AccountActivationEvent(account));
}
}
And domain event listener, on the infrastructure layer:
public class SendAccountActivationEmailEventListener
implements ApplicationListener<AccountActivationEvent> {
private MailSenderService mailSender;
....
public final void onApplicationEvent(final AccountActivationEvent event) {
Account account = event.getActivatedAccount():
.... perform mail ...
mailSender.sendEmail(email);
}
}
Now you can add another activation types, logging, other infrastructure stuff support without change and pollute your domain(business)-layer.
Ah, you can learn more about spring events in the documentation.