Spring Ldap get roles/authorities from user - java

i am trying to get the roles from a user in our ldap system.
First of all, my ldap user entry and role entry:
#Data
#Entry(objectClasses = {"inetOrgPerson", "top"}, base = "ou=people"
public class LdapUserEntry {
#Id
private Name id;
#DnAttribute(value = "uid")
private String username;
#Attribute(name = "cn")
private String cn;
#Attribute(name = "userPassword")
private String password;
#DnAttribute(value = "ou")
#Transient
private String group;
}
Role Entry class:
#Data
#Entry(objectClasses = {"groupOfUniqueNames", "top"}, base = "ourBase")
public class LdapRoleEntry {
#Id
private Name dn;
#DnAttribute("cn")
private String name;
#Attribute(name = "uniqueMember")
#Transient
private List<String> members;
}
For our authorization and authentication, i need to get the roles from the ldap, before the user gets logged.
EDIT: My Repos looks like:
public interface LdapUserRepository extends LdapRepository<LdapUserEntry> {
LdapUserEntry findByUsername(String username);
}
public interface LdapRoleRepository extends LdapRepository<LdapRoleEntry> {
}
Thank you!

Related

Using Spring Data JPA method name, how to find by foreign keys?

I have entities like below:
public class UserName {
#Id
private Long id;
private String name;
#OneToMany(mappedBy = "userName")
private User;
}
public class UserAge {
#Id
private Long id;
private int age;
#OneToMany(mappedBy = "userAge")
private User;
}
public class User {
#Id
private Long id;
#ManyToOne
private UserName userName;
#ManyToOne
private UserAge userAge;
}
When I search users whose name is the one with UserName.id = 100,
how can I do that?
I found an almost same question here, however it didn't work like below
public interface UserRepository extends JpaRepository<User, Long>{
Long countByUserNameId(Long id);
}

Get logged in user ID springboot and thymeleaf

I'm using Springboot and I want to create a profile.html page where the User can visualise and edit its informations.
Now, there are 2 tables on my db :
Credentials (login phase)
User (provides user informations
Credentials class :
#Entity
public class Credentials {
public static final String DEFAULT_ROLE = "DEFAULT";
public static final String ADMIN_ROLE = "ADMIN";
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
#Column(nullable = false, unique = true)
#NotEmpty
// #Size(min = 5, max = 250)
private String username;
#Column(nullable = false)
#NotEmpty
// #Size(min = 8, max = 20)
private String password;
#Column(nullable = false)
private String role = "DEFAULT";
#OneToOne(cascade = CascadeType.ALL,fetch = FetchType.EAGER)
private User user;
public Credentials() {
}
public Credentials(String email, String newPassword,long newId) {
this.username=email;
this.password=newPassword;
this.id=newId;
this.user= new User();
}
//some getters and setters
}
And User class :
#Getter
#Setter
#Entity
#Table(name = "users")
public class User {
public User() {}
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
#Column
private String name ="";
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
In profile page I want to show all User informations (in this case just its name) so I was thinking to get the current user inside ProfileController, add it to Model class and show all informations needed using html.
The problem is that I don't know how to get the current user.
I think the best way to do this is through Id inside Credentials class because it is the foreign key of User table but I don't know how to get this id. Maybe Springboot Authentication class can help?
I was thinking to retrieve, don't know how, current Credentials class with Authentication and use its id to get the linked User (fetchType.EAGER).
This is pseudocode for ProfileController page :
#Controller
public class ProfileController {
#Autowired
UserService us;
#RequestMapping(value="/profile",method = RequestMethod.GET)
public String showProfilePage(Model model) {
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
String currentUserName = authentication.getName();
Credentials c = getCredentialsFromService() //How I can do this?Is it the right way?
User currentUser = c.getUser();
model.addAttribute("user", currentUser ) ;
return "profile";
}
}

java.lang.StackOverflowError: null [Spring Boot, Hibernate]

I have two classes User.java and Address.java and there is a one-to-one bi-directional mapping between them.
But when I try to get the address using the User class I get an "java.lang.StackOverflowError: null" exception.
The same thing happens when I try to get the User from the Address class.
User.java
#Entity
#Table(name = "user")
public class User {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private long id;
private String name;
private String email;
private String phone;
private String password;
private String imageUrl;
#OneToOne(cascade = CascadeType.ALL)
#JoinColumn(name = "address")
private Address address;
Address.java
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private long id;
#OneToOne(cascade = CascadeType.ALL, mappedBy = "address")
private User user;
private String country;
private String state;
private String city;
private String street;
private String pincode;
MainController.java
#Controller
public class MainController {
#Autowired
private UserDao userDao;
#Autowired
private AddressDao addressDao;
#RequestMapping("/test")
#ResponseBody
public String test() {
User user = new User();
user.setName("name");
user.setEmail("email");
user.setPhone("phone");
user.setPassword("password");
user.setImageUrl("imageUrl");
Address address = new Address();
address.setCountry("country");
address.setState("state");
address.setCity("city");
address.setStreet("street");
address.setPincode("123456");
user.setAddress(address);
userDao.save(user);
return "working";
}
#RequestMapping("/fetch")
#ResponseBody
public String fetch() {
User user = userDao.getById((long) 1);
System.out.println(user.getAddress());
return "working";
}
}
I am using the test() function to put data in the database and it is working fine.
database image
But when I call the fetch() function I am getting the following error
java.lang.StackOverflowError: null
at org.hibernate.proxy.pojo.BasicLazyInitializer.invoke(BasicLazyInitializer.java:58) ~[hibernate-core-5.6.5.Final.jar:5.6.5.Final]
at org.hibernate.proxy.pojo.bytebuddy.ByteBuddyInterceptor.intercept(ByteBuddyInterceptor.java:43) ~[hibernate-core-5.6.5.Final.jar:5.6.5.Final]
at
Updated MainController.java
package com.demo.controller;
import java.util.Optional;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import com.demo.dao.AddressDao;
import com.demo.dao.UserDao;
import com.demo.entity.Address;
import com.demo.entity.User;
#Controller
public class MainController {
#Autowired
private UserDao userDao;
#Autowired
private AddressDao addressDao;
#RequestMapping("/test")
#ResponseBody
public String test() {
User user = new User();
user.setName("name");
user.setEmail("email");
user.setPhone("phone");
user.setPassword("password");
user.setImageUrl("imageUrl");
userDao.save(user);
Address address = new Address();
address.setCountry("country");
address.setState("state");
address.setCity("city");
address.setStreet("street");
address.setPincode("123456");
addressDao.save(address);
user.setAddress(address);
userDao.save(user);
return "working";
}
#RequestMapping("/fetch")
#ResponseBody
public String fetch() {
Optional<User> op = userDao.findById((long) 1);
User user = op.get();
// working
System.out.println(user.getName() + " " + user.getEmail() + " " + user.getPhone());
// java.lang.StackOverflowError:null
System.out.println(user.getAddress());
return "working";
}
}
TLDR: you aren't actually saving anything anywhere, but it's easy to fix. Here's my code and my explanation:
MainController.java:
#RestController
public class MainController {
private final UserRepository userRepository;
private final AddressRepository addressRepository;
public MainController(UserRepository userRepository, AddressRepository addressRepository){
this.userRepository = userRepository;
this.addressRepository = addressRepository;
}
#GetMapping("/test")
public String test() {
User user = new User();
user.setName("name");
user.setEmail("email");
user.setPhone("phone");
user.setPassword("password");
user.setImageUrl("imageUrl");
user = userRepository.save(user);
System.out.println("saved user");
Address address = new Address();
address.setCountry("country");
address.setState("state");
address.setCity("city");
address.setStreet("street");
address.setPincode("123456");
address = addressRepository.save(address);
System.out.println("saved address");
user.setAddress(address);
userRepository.save(user);
System.out.println("set user's address");
return "working";
}
#GetMapping("/fetch")
public String fetch() {
Optional<User> optionalUser = userRepository.findById((long) 1);
if(optionalUser.isPresent()){
User user = optionalUser.get();
System.out.println(user.getAddress());
boolean addressExists = addressRepository.existsById((long) 1);
System.out.println(addressExists);
System.out.println(user.getAddress().getCountry());
return "working";
}
System.out.println("Error: user with id 1 not found!");
return "failing";
}
}
User.java:
#Entity
#Table(name = "user")
public class User {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private long id;
private String name;
private String email;
private String phone;
private String password;
private String imageUrl;
#OneToOne(cascade = CascadeType.ALL)
#JoinColumn(name = "address_id", referencedColumnName = "id")
private Address address;
//getters and setters omitted for brevity
}
Address.java:
#Entity
#Table(name = "address")
public class Address {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private long id;
#OneToOne(mappedBy = "address")
private User user;
private String country;
private String state;
private String city;
private String street;
private String pincode;
//getters and setters omitted for brevity
}
AddressRepository.java:
public interface AddressRepository extends CrudRepository<Address, Long> {
}
UserRepository.java:
public interface UserRepository extends CrudRepository<User, Long> {
}
UserDAO.java:
public class UserDAO {
private final String name;
private final String email;
private final String phone;
private final String imageUrl;
public UserDAO(User user) {
name = user.getName();
email = user.getEmail();
phone = user.getPhone();
imageUrl = user.getImageUrl();
}
public String getName() {
return name;
}
public String getEmail() {
return email;
}
public String getPhone() {
return phone;
}
public String getImageUrl() {
return imageUrl;
}
}
A DAO has no connection to the database, it's intent is what the acronym stands for, simply to transfer data, and that's it. When you make a repository, you can stick your objects there by saving them in the repository. Notice that by extending the CrudRepository with correct generics, you don't even need to implement the methods yourself. The save method actually saves the POJO, and returns the saved version, which is why I did user = userRepository.save(user), which may seem counterintuitive at first, but it simply helps ensure that everything is as you expect. If you then want to send the UserDAO object as a response, you can create it using the user object that is returned from the database, maybe something like:
UserDAO dao = new UserDAO(userRepository.save(user));
Please take notice of what is happening inside the test method in MainController. First, we create the POJO User object and set its fields. Then we have to save it to the repository, it is only persisted after you call save method of the repository. Please note that the user object is saved again once it is updated with the address.
This is a very crude way to do things, it is best to create a service layer and do this there with the #Transactional annotation, which would mean that everything is rolled back in case something goes wrong inside a method annotated as #Transactional.
Also, using CascadeType.ALL may be not what you want, please refer to this answer.
Inside fetch method, I ensure that the user indeed exists, which is not guaranteed. To avoid 500 errors, it's important to have a fallback mechanism for when something doesn't work.
As a final side note, you shouldn't be storing raw passwords like that, you should at least use hashing with salt and pepper, or use one of the many available libraries for implementing such functionality (although it can be quite fun getting down and dirty with the code itself). You should also consider how much information you are revealing when something does go wrong, as you don't want to give away too much information which could be used to deanonimise a specific user, or even learn more about your code and the system architecture.

#OneToOne mapping isnt working in my Java Spring project

So I am tidying up my small Spring project and I noticed for some reason the #OneToOne annotation is not doing its job for me which in turn causes issues in another model.
github link : https://github.com/eamonmckelvey/sports-app
Basically, I have a User model class, a team model class and a player model class.
I want only one user to be able to create one team, and one team to have many players.
However, I am able to add as many teams to my user as I want which is wrong.
All the answers provided require me to add a no arg constructor and a constructor for my users class, but when I do this I get an error in my registration from class.
Please help.
1. User Model
#Entity
#Data
#NoArgsConstructor(access= AccessLevel.PRIVATE, force=true)
#RequiredArgsConstructor
public class User implements UserDetails {
#OneToOne(cascade = CascadeType.ALL,mappedBy = "user")
private Team team;
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy= GenerationType.AUTO)
private Long id;
private final String username;
private final String password;
//private final String fullname;
#Override
public Collection<? extends GrantedAuthority> getAuthorities() {
return Arrays.asList(new SimpleGrantedAuthority("ROLE_USER"));
}
#Override
public boolean isAccountNonExpired() {
return true;
}
#Override
public boolean isAccountNonLocked() {
return true;
}
#Override
public boolean isCredentialsNonExpired() {
return true;
}
#Override
public boolean isEnabled() {
return true;
}
}
2. Team Model
#Data
#Entity
#Table(name="User_Team")
public class Team implements Serializable {
#OneToOne(fetch= FetchType.LAZY)
private User user;
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy= GenerationType.AUTO)
private Long id;
//#NotBlank(message="Team Name is required")
private String teamName;
//#NotBlank(message="Location is required")
private String location;
//#NotBlank(message="Nickname required")
private String nickName;
private String yearEstablished;
public Sport sport;
private Divison divison;
3. Team Controller
#Slf4j
#Controller
#SessionAttributes("Team")
public class TeamController {
private TeamRepository teamRepository;
public TeamController(TeamRepository teamRepository) {
this.teamRepository = teamRepository;
}
#Autowired
TeamRepository service;
#GetMapping("/team")
public String displayTeam(Model model) {
model.addAttribute("team", service.findAll());
return "/team";
}
#GetMapping("/addTeam")
public String showSignUpForm(User user) {
return "addTeam";
}
#PostMapping("/addTeam")
public String processOrder(#Valid Team team, BindingResult result, SessionStatus
sessionStatus,
#AuthenticationPrincipal User user, Model model) {
if (result.hasErrors()) {
return "addTeam";
}
team.setUser(user);
service.save(team);
model.addAttribute("team", service.findAll());
return "team";
}
4. Registeration Form
#Data
public class RegistrationForm {
private String username;
private String password;
//private String fullname;
public User toUser(PasswordEncoder passwordEncoder) {
return new User(
username, passwordEncoder.encode(password));
}
}
5. Registration Controller
#Controller
#RequestMapping("/register")
public class RegistrationController {
private UserRepository userRepo;
private PasswordEncoder passwordEncoder;
public RegistrationController( UserRepository userRepo,
PasswordEncoder passwordEncoder){
this.userRepo = userRepo;
this.passwordEncoder = passwordEncoder;
}
#GetMapping
public String registerForm(){
return "registration";
}
#PostMapping
public String processRegistration(RegistrationForm form){
userRepo.save(form.toUser(passwordEncoder));
return "redirect:/login";
}
6. user details class
#Service
public class UserRepositoryUserDetailsService implements
UserDetailsService {
private UserRepository userRepo;
#Autowired
public UserRepositoryUserDetailsService(UserRepository userRepo) {
this.userRepo = userRepo;
}
#Override
public UserDetails loadUserByUsername(String username) throws
UsernameNotFoundException {
User user = userRepo.findByUsername(username);
if (user != null) {
return user;
}
throw new UsernameNotFoundException(
"User '" + username + "' not found");
}
1) One user can have one team. That means OneToOne relation between user and team. You need not to annotate both user and team with #OneToOne.Remove #OneToOne annotation from team model.
Changes required are:
User model:
#Entity
class User{
#Id
private String id;
#OneToOne(fetch = FetchType.LAZY,cascade = CascadeType.ALL)
#JoinColumn(name = "team_id")
private Team team;
//other fields
}
Team Model:
#Entity
class Team{
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private int id;
private String teamName;
//other field
}
2) For one team to have many players requires #OneToMany
So, I copied your code and did some changes. After the following changes your code works fine.
1) Drop final keyword from below fields in user class(initalizing them doesn't seems to be a great idea).
private final String username;
private final String password;
2) User and Team should not have same serialization version.
private static final long serialVersionUID = 1L;
3) After doing above corrections. Your code will give you the actual error "nested exception is javax.persistence.PersistenceException"
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'entityManagerFactory' defined in class path resource [org/springframework/boot/autoconfigure/orm/jpa/HibernateJpaAutoConfiguration.class]: Invocation of init method failed; nested exception is javax.persistence.PersistenceException: [PersistenceUnit: default] Unable to build Hibernate SessionFactory
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1628)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:555)
..............
..........
Caused by: org.postgresql.util.PSQLException: ERROR: syntax error at or near "user"
To avoid it do the following changes in your model :
Put #Table(name="users") in user model.
Following are the models:
#Entity
#Table(name="users")
public class User {
#Id
#GeneratedValue(strategy= GenerationType.AUTO)
private Long id;
private String username;
#OneToOne(mappedBy = "user")
private Team team;
public User() {
}
}
Team Model
#Table(name="teams")
public class Team {
#Id
private Long id;
#OneToOne
#MapsId
// or you can use
// #OneToOne
// #JoinColumn(name="user_id")
private User user;
private String teamName;
public Team() {
}
}
Follow the above code. It works fine for me. Test Controller to check:
#RequestMapping(value = "/test", method = RequestMethod.GET)
public ResponseEntity<?> test() {
User user = userRepository.findById(2l);
Team team = user.getTeam();
return new ResponseEntity(team, HttpStatus.OK);
}
}
I hope this will help you out.
There are several issues with your code:
#JoinColumn on the child side is missing. It's not even on the parent side. In the User entity you declare #OneToOne(cascade = CascadeType.ALL,mappedBy = "user"), but it is not mapped in the child class.
FetchType.LAZY does not give you much in terms of performance in one-to-one, since hibernate needs to check the database for existence of an object to know whether return a proxy or null.
You're saving a child entity in your TeamController: service.save(team);, but there is no cascading from Team to User.
Try the following mapping:
public class User implements UserDetails {
#OneToOne(cascade = CascadeType.ALL, mappedBy = "user")
private Team team;
// other fields
}
public class Team implements Serializable {
#OneToOne(cascade=CascadeType.ALL)
#JoinColumn(name = "user_id")
private User user;
// other fields
}
And keeping both sides synchronized. Instead of:
team.setUser(user);
service.save(team);
Try the following code in your TeamController (you will have to autowire UserRepository):
team = service.save(team);
team.setUser(user);
user.setTeam(team);
userRepository.save(user);
Hey so i found a fix here for my code.
1. Team Controller
#GetMapping("/addTeam")
public String showSignUpForm(SessionStatus sessionStatus,
#AuthenticationPrincipal User user, Model model)
{
//if the user has already the team we should not let them add another
// one
//this is due to having one to one relationship
long userHasTeamCount = service.countAllByUser(user);
if (userHasTeamCount > 0) {
return "redirect:team";
}
return "addTeam";
}
2. Team model
#OneToOne(fetch = FetchType.EAGER)
#JoinColumn(name = "user_id", referencedColumnName = "id")
private User user;
3. User Model
Removed the OneToOne here as its not needed
4. Team repo
#Repository
public interface TeamRepository extends JpaRepository<Team, Long> {
Team findAllById(Long id);
long countAllByUser(final User user);
}

Form Validation in Rest Webservice

I started building my first REST webservice in Java using Spring and JPA.
Now I'm trying to create sign-up service. I have no problem with sending a request containing all Entity fields what looks:
#AllArgsConstructor
#NoArgsConstructor
#Data
#Builder
#Entity
#Table(name = "users")
public class User implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Integer id;
#Enumerated(EnumType.STRING)
private Gender gender;
#Column(name = "email")
private String email;
#Column(name = "login")
private String login;
#Column(name = "password")
private String password;
#Column(name = "registration_date")
#CreatedDate
private LocalDateTime registrationDate;
#OneToMany(mappedBy = "bookOwner", cascade = CascadeType.ALL, fetch = FetchType.EAGER)
private List<Book> bookList = new ArrayList<>();
}
But what to do in situation I want my registration form having only login, password and email fields and filling the rest user details would be optional - after confirmation of registration?
I consider using ModelMapper and creating separate classes for every form, but is there any better approach?
I solved problem by my own using mentioned ModelMapper. I paste my code. Can be useful if someone's interested. Didn't make tests, but my DB looks fine and no exceptions are thrown.
public class DTOMapper {
private static final ModelMapper MAPPER = new ModelMapper();
private DTOMapper(){}
public static <S, T> T map(S source, Class<T> targetClass){
return MAPPER.map(source, targetClass);
}
}
#Service
#Transactional
public class SignUpService {
private final UserRepository userRepository;
#Autowired
public SignUpService(UserRepository userRepository) {
this.userRepository = userRepository;
}
public User registerUser(SignUpForm form){
if(userRepository.findByLogin(form.getLogin())!=null){
throw new LoginAlreadyUsedException(form.getLogin());
}
if(userRepository.findByEmail(form.getEmail())!=null){
throw new EmailAlreadyUsedException(form.getEmail());
}
User user = DTOMapper.map(form, User.class);
User saved = userRepository.save(user);
return DTOMapper.map(saved, User.class);
}
}
#AllArgsConstructor
#NoArgsConstructor
#Data
#Builder
public class SignUpForm implements Serializable {
private static final long serialVersionUID = 1L;
#NotEmpty
#Size(min = 5)
private String login;
#NotEmpty
#Size(min = 7)
private String password;
//todo email validation
#NotEmpty
private String email;
}
#RestController
public class SignUpController {
private static final Logger log = LoggerFactory.getLogger(SignUpController.class);
#Autowired
private SignUpService signUpService;
#PostMapping(value = "/signup")
public ResponseEntity<?> addUser(#RequestBody #Valid SignUpForm form, BindingResult errors){
if(errors.hasErrors()){
throw new InvalidRequestException(errors);
}
signUpService.registerUser(form);
return new ResponseEntity<>(form, HttpStatus.CREATED);
}
}

Categories