How to use InetOrgPersonContextMapper class - spring

I'm authenticated and authorise to Active Directory by Spring Security.
But can not retrive LDAP attributes, for example MAIL.
I trying use InetOrgPersonContextMapper for it...
#Bean
public InetOrgPersonContextMapper inetOrgPersonContextMapper(){
InetOrgPersonContextMapper contextMapper = new InetOrgPersonContextMapper();
return contextMapper;
}
#Bean
public LdapAuthenticationProvider ldapAuthenticationProvider(){
LdapAuthenticationProvider ldapAuthenticationProvider = new LdapAuthenticationProvider(ldapAuthenticator(),ldapAuthoritiesPopulator());
ldapAuthenticationProvider.setUserDetailsContextMapper(inetOrgPersonContextMapper());
return ldapAuthenticationProvider;
}
but when i trying retrive attributes in controller to i get ClassCastExeption
Authentication auth = SecurityContextHolder.getContext().getAuthentication();
InetOrgPerson person = (InetOrgPerson)auth.getPrincipal();
Please tell me correct way for reitrive attributes.

I guess it's no better way, but it's working.
If anybody know how can do it better, please tell me.
#Bean
public UserDetailsContextMapper userDetailsContextMapper(){
return new LdapUserDetailsMapper(){
#Override
public UserDetails mapUserFromContext(DirContextOperations ctx, String username, Collection<? extends GrantedAuthority> authorities) {
InetOrgPersonContextMapper personContextMapper = new InetOrgPersonContextMapper();
UserDetails cm = personContextMapper.mapUserFromContext(ctx,username,authorities);
String MAIL = ((InetOrgPerson)(personContextMapper.mapUserFromContext(ctx,username,authorities))).getMail();
String FullName = ((InetOrgPerson)(personContextMapper.mapUserFromContext(ctx,username,authorities))).getDisplayName();
System.out.println("MAIL: " + MAIL + " Full Name: " + FullName);
return cm;
}
};
}

Related

Oauth-2 authentication extracting the user details expects CustomPrincipal object

I got this code to implement authentication in a Spring boot project. Everything is working fine with the exception of extracting the user who is to be authenticated. I have searched and could not find could not find the solution somewhere else.
public class CustomUserAuthenticationConverter implements UserAuthenticationConverter {
private final String EMAIL = "email";
private Collection<? extends GrantedAuthority> defaultAuthorities;
public void setDefaultAuthorities(String[] defaultAuthorities) {
this.defaultAuthorities = AuthorityUtils
.commaSeparatedStringToAuthorityList(StringUtils.arrayToCommaDelimitedString(defaultAuthorities));
}
#Override
public Map<String, ?> convertUserAuthentication(Authentication userAuthentication) {
Map<String, Object> response = new LinkedHashMap<String, Object>();
response.put(USERNAME, userAuthentication.getName());
if (userAuthentication.getAuthorities() != null && !userAuthentication.getAuthorities().isEmpty())
response.put(AUTHORITIES, AuthorityUtils.authorityListToSet(userAuthentication.getAuthorities()));
return response;
}
#Override
public Authentication extractAuthentication(Map<String, ?> map) {
if (map.containsKey(USERNAME))
return new UsernamePasswordAuthenticationToken(
new CustomPrincipal(map.get(USERNAME).toString(), map.get(EMAIL).toString()), "N/A",
getAuthorities(map));
return null;
}
private Collection<? extends GrantedAuthority> getAuthorities(Map<String, ?> map) {
if (!map.containsKey(AUTHORITIES))
return defaultAuthorities;
Object authorities = map.get(AUTHORITIES);
if (authorities instanceof String)
return AuthorityUtils.commaSeparatedStringToAuthorityList((String) authorities);
if (authorities instanceof Collection)
return AuthorityUtils.commaSeparatedStringToAuthorityList(
StringUtils.collectionToCommaDelimitedString((Collection<?>) authorities));
throw new IllegalArgumentException("Authorities must be either a String or a Collection");
}
}
The other parts are working correctly except for this method snippet
#Override
public Authentication extractAuthentication(Map<String, ?> map) {
if (map.containsKey(USERNAME))
return new UsernamePasswordAuthenticationToken(
new CustomPrincipal(map.get(USERNAME).toString(), map.get(EMAIL).toString()), "N/A",
getAuthorities(map));
return null;
}
I understand that it's expecting a CustomPrincipal object where this Strings are passed as USERNAME, EMAIL. What do I do to pass in the right parameters?
Thanks everyone. The.problem was that the Lombok library to create constructors failed, so the simple answer is to create them manually.
It works perfectly. The CustomPrincipal class needs to have a constructor that accepts 2 arguments which was implicitly defined but not injected when needed in the class instantiation in another class

How to restrict access to the Spring MVC controller

I am writing a web service with an authorization and registration form. There are two types of users: regular and administrator. There is a controller that sends to the admin page at a given URL:
#Controller
public class ViewPageController {
#RequestMapping(value = "/admin", method = RequestMethod.GET)
public String sendAdminPage(){
return "AdminPage";
}
}
But ordinary users can also access this page. It is necessary that only those who logged in as an administrator get to the admin page. There are options for how this can be organized? Maybe save the logged in user in the session? (Preferably without Spring Security)
the easy way define a Aspect and A annotation.some code like this
#Inherited
#Target({ElementType.TYPE, ElementType.METHOD})
#Retention(RetentionPolicy.RUNTIME)
public #interface Authorize {
//
String[] value() default {};
}
AuthorizationAspect.java
#Slf4j
#Aspect
#Component
#RequiredArgsConstructor
public class AuthorizationAspect {
private final AuthorizationService authorizationService;
private final CacheUtil cacheUtil;
private static final String PRE = "AUTH";
#Before("#annotation(com.jin.learn.config.security.Authorize)")
public void checkPermission(JoinPoint joinPoint) {
HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
Long accountId = JWTUtil.getUserIdFromRequest(request);
Set<String> authorization = cacheUtil.getAllSet(PRE + accountId);
if(authorization==null){
authorization = authorizationService.findByAccountId(accountId);
cacheUtil.save(PRE + accountId, authorization);
}
Authorize authorize = ((MethodSignature) joinPoint.getSignature()).getMethod().getAnnotation(Authorize.class);
String[] needAuthorization = authorize.value();
if (needAuthorization.length == 0) return;
if (authorization!=null && !authorization.isEmpty()) {
if (!authorization.containsAll(Arrays.asList(needAuthorization))){
throw new SystemException(ExceptionCode.NO_PERMISSION);
}
} else {
throw new SystemException(ExceptionCode.NO_PERMISSION);
}
}
}
use like this
#Authorize(value="needRight")
#RequestMapping(value = "/admin", method = RequestMethod.GET)
public String sendAdminPage(){
return "AdminPage";
}
besides,there are some security framework shiro and spring-security

Spring Rest Api Design For Following a User in Twitter Clone Appliction?

I Want to Design a Demo Twitter Clone Application where user can follow any other user . however i am doubting my rest api design . please suggest me am i right .
Can I pass followerId in url rather than passing it as requestbody as we already know followerId in Advance and server does not create followerId here ?
and if better option could be there like put/patch or any rest api design ?
Please suggest me better design if possible
Here JwtUser is Authenticated User
public class FollowerDto {
private Long followerId;
private boolean following;
public FollowerDto() {
}
public FollowerDto(Long followerId, boolean following) {
this.followerId = followerId;
this.following = following;
}
public boolean getFollowing() {
return following;
}
public void setFollowing(boolean following) {
this.following = following;
}
public Long getFollowerId() {
return followerId;
}
public void setFollowerId(Long followerId) {
this.followerId = followerId;
}
}
#PostMapping("/follower")
#ResponseStatus(HttpStatus.CREATED)
public StatusDto addFollower(#RequestBody #Valid final FollowerDto
followerDto, #CurrentUser final JwtUser user, final
HttpServletResponse response) {
RestPreconditions.checkRequestElementNotNull(followerDto);
RestPreconditions.checkArgumentCondition(followerDto.getFollowing());
return userService.addFollower(user, followerDto.getFollowerId(),
response);
}
// Service Layer
#Override
public StatusDto addFollower(final JwtUser jwtUser, final Long followerId, final HttpServletResponse response) {
final User follower = userRepository.findById(followerId).orElse(null);
ServicePreconditions.checkEntityExists(follower, "Follower does not exist with id " + followerId);
final User currentUser = userRepository.findByEmail(jwtUser.getEmail());
if (currentUser != null) {
ServicePreconditions.checkOKArgument(!currentUser.equals(follower));
final Set<User> existingFollowers = currentUser.getFollowers();
if (existingFollowers != null) {
existingFollowers.add(follower);
} else {
currentUser.setFollowers(Sets.<User>newHashSet(follower));
}
userRepository.save(currentUser);
final URI uri = ServletUriComponentsBuilder.fromCurrentRequestUri().path("/{idOfNewResource}").buildAndExpand(follower.getId()).toUri();
response.setHeader(HttpHeaders.LOCATION, uri.toASCIIString());
return new StatusDto("Follower Added Successfully to user having email " + jwtUser.getEmail());
}
return new StatusDto("Follower is not Added to user with email " + jwtUser.getEmail());
}

Update User's first name and last name in principal

I am updating user's information like first name and last name and I am getting first name and last name in all the pages for welcome message.
I have two controllers one for ajax request mapping and the other for normal request mapping.
Normal request mapping controller have this method. In this controller all page navigation is present and some request mapping which are not ajax calls
private String getPrincipalDisplay() {
GreenBusUser user = null;
String userName = "";
Object principal = SecurityContextHolder.getContext().getAuthentication().getPrincipal();
if (principal instanceof UserDetails) {
user = (GreenBusUser) principal;
userName = user.getFirstName() + " " + user.getLastName();
} else {
userName = "";
}
return userName;
}
This is how I am getting the username on every page by return string of this function I am adding it in ModelMap object.
When I update user's information I am doing in ajax request mapping.
#RequestMapping(value = "/restify/updateUserData", method = RequestMethod.PUT, headers = "Accept=application/json")
public ServiceResponse forgotPassword(#RequestBody Object user)
{
//logger.debug("getting response");
return setDataPut("http://localhost:7020/forgotPassword",user);
}
user is an Object type which has json data. Now how do I retrieve data from object and update my first name and last name in principal.
This is my GreenBusUser class
public class GreenBusUser implements UserDetails
{
private static final long serialVersionUID = 1L;
private String username;
private String password;
private Collection<? extends GrantedAuthority> grantedAuthorities;
private String firstName;
private String lastName;
public GreenBusUser(String username,String password,Collection<? extends GrantedAuthority> authorities,String firstName, String lastName)
{
this.username = username;
this.password = password;
this.grantedAuthorities = authorities;
this.firstName=firstName;
this.lastName=lastName;
this.grantedAuthorities.stream().forEach(System.out::println);
}
public Collection<? extends GrantedAuthority> getAuthorities()
{
return grantedAuthorities;
}
public String getFirstName() {
return firstName;
}
public String getLastName() {
return lastName;
}
public String getPassword()
{
return password;
}
public String getUsername()
{
return username;
}
public boolean isAccountNonExpired()
{
return true;
}
public boolean isAccountNonLocked()
{
return true;
}
public boolean isCredentialsNonExpired()
{
return true;
}
public boolean isEnabled()
{
return true;
}
}
UPDATE:::::
I have updated your code and applied some part of your answer into mine but still I ran into a problem
#RequestMapping(value="/updateUser",method=RequestMethod.GET)
public String updateUser(ModelMap model) {
UserInfo user = getUserObject();
GreenBusUser newGreenBususer = null;
List<User> list = new ArrayList<User>();
list = FetchDataService.fetchDataUser("http://localhost:8060/GetuserbyUserName?username=" + getPrincipal(), user.getUsername(), user.getPassword());
logger.debug("new user list ----->>>"+list.size());
User newuser=(User)list.get(0);
UsernamePasswordAuthenticationToken auth = new UsernamePasswordAuthenticationToken(
SecurityContextHolder.getContext().getAuthentication().getPrincipal(), SecurityContextHolder.getContext().getAuthentication().getCredentials());
Object principal = SecurityContextHolder.getContext().getAuthentication().getPrincipal();
newGreenBususer=(GreenBusUser)principal;
logger.debug("newGreenBususerDetails---->>>"+newGreenBususer.toString());
newGreenBususer.setFirstName(newuser.getFirstName());
newGreenBususer.setLastName(newuser.getLastName());
if(newGreenBususer.getFirstName()!=null) {
logger.debug("got my first name");
}
if(newGreenBususer.getLastName()!=null) {
logger.debug("got my last name");
}
auth.setDetails(newGreenBususer);
SecurityContext context = SecurityContextHolder.getContext();
context.setAuthentication(auth);
SecurityContextHolder.setContext(context);
model.addAttribute("user", getPrincipalDisplay());
model.addAttribute("userData", list);
model.addAttribute("check", true);
return "GreenBus_updateProfile_User";
}
At first it sets the firstname and lastname to GreenBusUser and then there is setDetails method when I reload the page it says No user found when I am calling getUserObject() method at the top of this method.
private X2CUser getUserObject() {
X2CUser userName = null;
Object principal = SecurityContextHolder.getContext().getAuthentication().getPrincipal();
if (principal instanceof UserDetails) {
userName = ((X2CUser) principal);
} else {
logger.info("No user found");
}
return userName;
}
If you are updating the password, then it will be good to logout the user and tell him to relogin.
Try this code .. It might help you.
UsernamePasswordAuthenticationToken authReq = new UsernamePasswordAuthenticationToken(user, pass);
Authentication auth = authManager.authenticate(authReq);
SecurityContext sc = SecurityContextHolder.getContext();
securityContext.setAuthentication(auth);
I have finally resolved my problem though I have later added some code in my question part in UPDATE section.
Object principal = SecurityContextHolder.getContext().getAuthentication().getPrincipal();
newGreenBususer=(GreenBusUser)principal;
newGreenBususer.setFirstName(newuser.getFirstName());
newGreenBususer.setLastName(newuser.getLastName());
Yes that's all need to be done.
This part--->>
auth.setDetails(newGreenBususer);
SecurityContext context = SecurityContextHolder.getContext();
context.setAuthentication(auth);
SecurityContextHolder.setContext(context);
set new context making security pointing to null when I reload still not clear because I am setting the details before reload so its like I get new context but I have set the new user details.
Though I have finally resolved my problem but if anyone could shed some light why it was happening then I will accept his/her answer.
Thanks alot for your support. Keep Learning!

Spring Security LDAP get User Given Name

I am using Spring security 3.2.4 with Windows AD LDAP.
I am able to successfully authenticate and LdapUserDetailsImpl is populated.
From LdapUserDetailsImpl I can get the username, authorities, but how to get the employee name (not the login user name)
LdapUserDetailsImpl contains following properties and values
Username = 40000 ,
Enabled = true,
AccountNonExpired = true,
Dn: cn=employee name,ou=IT_FM,ou=XXX_USERS,dc=XXXX,dc=CO,dc=IN;
How do it get the employee name, Do I need to extend some class and write my own mapping or
may be simply get Dn from the principal and split the string to get the employee name.
You can just get the Dn from Principal and extract the username (cn)
LdapUserDetailsImpl ldapDetails = (LdapUserDetailsImpl) SecurityContextHolder
.getContext().getAuthentication().getPrincipal();
String dn = ldapDetails.getDn();
int beginIndex = dn.indexOf("cn=") + 3;
int endIndex = dn.indexOf(",");
String username = dn.substring(beginIndex, endIndex);
#Mukun almost has this. The only thing is, instead of:
String dn = ldapUserDetailsImpl.getDn();
int beginIndex = dn.indexOf("cn=") + 3;
int endIndex = dn.indexOf(",");
myUserDetails.setEmployeeName(dn.substring(beginIndex, endIndex));
I would have:
String name = ctx.getObjectAttribute("cn").toString()
myUserDetails.setEmployeeName(name)
This lets LDAP integration handle all the horrible stuff for you and loses the danger of chopping up strings yourself.
You might also consider
myUserDetails.setFirstName(ctx.getObjectAttribute("givenName").toString())
myUserDetails.setLastName(ctx.getObjectAttribute("sn").toString())
These things should work for both MS AD, "normal" LDAP and possible Novell too.
So the full answer would be:
#Service
public class MyUserDetailsContextMapper extends LdapUserDetailsMapper implements UserDetailsContextMapper {
#Override
public UserDetails mapUserFromContext(DirContextOperations ctx, String username, Collection<? extends GrantedAuthority> authorities) {
LdapUserDetailsImpl ldapUserDetailsImpl = (LdapUserDetailsImpl) super.mapUserFromContext(ctx, username, authorities);
MyUserDetails myUserDetails = new MyUserDetails();
myUserDetails.setAccountNonExpired(ldapUserDetailsImpl.isAccountNonExpired());
myUserDetails.setAccountNonLocked(ldapUserDetailsImpl.isAccountNonLocked());
myUserDetails.setCredentialsNonExpired(ldapUserDetailsImpl.isCredentialsNonExpired());
myUserDetails.setEnabled(ldapUserDetailsImpl.isEnabled());
myUserDetails.setUsername(ldapUserDetailsImpl.getUsername());
myUserDetails.setAuthorities(ldapUserDetailsImpl.getAuthorities());
myUserDetails.setEmployeeName(ctx.getObjectAttribute("cn").toString());
return myUserDetails;
}
}
My Custom Mapper. Is this correct way of doing ?
#Service
public class MyUserDetailsContextMapper extends LdapUserDetailsMapper implements UserDetailsContextMapper {
#Override
public UserDetails mapUserFromContext(DirContextOperations ctx, String username, Collection<? extends GrantedAuthority> authorities) {
LdapUserDetailsImpl ldapUserDetailsImpl = (LdapUserDetailsImpl) super.mapUserFromContext(ctx, username, authorities);
MyUserDetails myUserDetails = new MyUserDetails();
myUserDetails.setAccountNonExpired(ldapUserDetailsImpl.isAccountNonExpired());
myUserDetails.setAccountNonLocked(ldapUserDetailsImpl.isAccountNonLocked());
myUserDetails.setCredentialsNonExpired(ldapUserDetailsImpl.isCredentialsNonExpired());
myUserDetails.setEnabled(ldapUserDetailsImpl.isEnabled());
myUserDetails.setUsername(ldapUserDetailsImpl.getUsername());
myUserDetails.setAuthorities(ldapUserDetailsImpl.getAuthorities());
String dn = ldapUserDetailsImpl.getDn();
int beginIndex = dn.indexOf("cn=") + 3;
int endIndex = dn.indexOf(",");
myUserDetails.setEmployeeName(dn.substring(beginIndex, endIndex));
return myUserDetails;
}
}

Resources