Spring LDAP/Active Directory with SQL - active-directory

I am trying to enable both JDBA and Active Directory Authentication , i made great progress but currently i am stuck as userDetailsService is trying to compare the password in LdapUserDetails which does not exist . When checking the log i see it is able to query the user and authenticate and get the roles correctly.
I know i should use bindService or so , but i couldn't find till now how to do that.
Below is what i did .
in WebSecurityConfigurerAdapter
#Autowired
public void configureGlobal(UserDetailsService userDetailsService,UserLdapRepositoryUserDetailsService userLdapRepositoryUserDetailsService,AuthenticationManagerBuilder auth) throws Exception {
auth
.userDetailsService(userDetailsService).
and()
.userDetailsService(userLdapRepositoryUserDetailsService);
}
For LDAP Configuration
#Bean
public BaseLdapPathContextSource contextSource() {
DefaultSpringSecurityContextSource contextSource = new DefaultSpringSecurityContextSource("ldap://XXXXX:389/dc=XXXXXX,dc=co");
//contextSource.setUserDn("CN=Ali Shahbour,OU=Users,DC=XXXXXXX,DC=co");
contextSource.setUserDn("XXXXXX");
contextSource.setPassword("XXXXXX");
return contextSource;
}
#Bean
#Autowired
public LdapUserSearch userSearch(BaseLdapPathContextSource contextSource) {
FilterBasedLdapUserSearch userSearch = new FilterBasedLdapUserSearch("", "(uid={0})", contextSource);
return userSearch;
}
#Bean
#Autowired
public LdapAuthoritiesPopulator authoritiesPopulator(BaseLdapPathContextSource contextSource) {
DefaultLdapAuthoritiesPopulator authoritiesPopulator = new DefaultLdapAuthoritiesPopulator(contextSource, "OU=CDRMonitor");
authoritiesPopulator.setGroupSearchFilter("(member={0})");
//authoritiesPopulator.setRolePrefix("ROLE");
authoritiesPopulator.setSearchSubtree(true);
//authoritiesPopulator.setConvertToUpperCase(true);
return authoritiesPopulator;
}
As for the LdapUserDetailsService
#Service("userLdapRepositoryUserDetailsService")
public class UserLdapRepositoryUserDetailsService extends LdapUserDetailsService {
private final UserRepository userRepository;
#Autowired
public UserLdapRepositoryUserDetailsService(LdapUserSearch userSearch,
LdapAuthoritiesPopulator authoritiesPopulator,UserRepository userRepository) {
super(userSearch, authoritiesPopulator);
this.userRepository = userRepository;
}
#Override
public UserDetails loadUserByUsername(String username)
throws UsernameNotFoundException {
UserDetails userDetails = super.loadUserByUsername(username);
//User user = userRepository.findByEmail(username);
User user = new User();
return new LdapUserRepositoryUserDetails(user, userDetails);
}
#Override
public void setUserDetailsMapper(UserDetailsContextMapper userDetailsMapper) {
super.setUserDetailsMapper(userDetailsMapper);
}
private final static class LdapUserRepositoryUserDetails extends User implements LdapUserDetails {
private final LdapUserDetailsImpl ldapUserDetailsImpl;
private LdapUserRepositoryUserDetails(User user,UserDetails userDetails) {
super(user);
ldapUserDetailsImpl = (LdapUserDetailsImpl) userDetails;
}
private static final long serialVersionUID = 5639683223516504866L;
#Override
public Collection<? extends GrantedAuthority> getAuthorities() {
return ldapUserDetailsImpl.getAuthorities();
}
#Override
public String getUsername() {
// TODO Auto-generated method stub
return ldapUserDetailsImpl.getUsername();
}
#Override
public boolean isAccountNonExpired() {
return ldapUserDetailsImpl.isAccountNonExpired();
}
#Override
public boolean isAccountNonLocked() {
return ldapUserDetailsImpl.isAccountNonLocked();
}
#Override
public boolean isCredentialsNonExpired() {
return ldapUserDetailsImpl.isCredentialsNonExpired();
}
#Override
public boolean isEnabled() {
return ldapUserDetailsImpl.isEnabled();
}
#Override
public String getDn() {
return ldapUserDetailsImpl.getDn();
}
}
}

LDAP and SQL in an authentication context are in general not used together,
because LDAP BIND authentication sends the password to the LDAP server instead of retrieving a hash value of the password.
The intended procedure of LDAP Authentication is as follows:
Usually a spring security UsernamePassword filter is used to pick up the credentials and is active by default. e.g. if you us a login form, when submitting the form, this filter picks up the credentials.
Next an LDAP Authentication Provider performs a login (LDAPBindAuthenticator) against the LDAP server (LDAP ContextSource) to verify the credentials.
If the login is successful, the LDAP Authentication Provider searches the LDAP for a user entry. This can be customised by providing a 'usersearch' spring bean.
If the user entry is found, the LDAP Authority mapper will map attributes of the user entry to groups/roles in spring security. By default this are all OU attributes
Lastly a new authentication object is made with the username and retrieved groups from the LDAP.
How to integrate LDAP Authentication using Spring XML is explained here.
http://docs.spring.io/spring-security/site/docs/3.1.x/reference/ldap.html

Related

Single Spring boot for multiple RDBMS databases

I have a requirement that I need to connect a small API to an Oracle DB for few GET ops. Now in dev since there is only 1 instance, it's quite easy to provide. But when it comes to production, the DBs are located in 5 different countries and hence are 5 different instances (All of them are exactly the same except for the data). My idea is to run them via application.properties with active profile and then connect them to application-{active-profile}.properties. Something like below:
application.properties:
spring.profiles.active=DB1
spring.application.name=demo
application-DB1.properties:
spring.jpa.hibernate.ddl-auto=none
spring.datasource.url=jdbc:oracle:thin:#//host1:port/SID
spring.datasource.username=username
spring.datasource.password=password
Configuration class:
#Configuration
#ConfigurationProperties("spring.datasource")
public class DBConfiguration {
private String url;
private String username;
private String password;
public String getUrl() {
return url;
}
public void setUrl(String url) {
this.url = url;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
#Profile("dev")
#Bean
public String devDBConnection(){
System.out.println("URL:"+getUrl());
System.out.println("username:"+getUsername());
System.out.println("password:"+getPassword());
return "DEV DB connection";
}
#Profile("uat")
#Bean
public String uatDBConnection() {
System.out.println("URL:" + getUrl());
System.out.println("username:" + getUsername());
System.out.println("password:" + getPassword());
return "UAT DB connection";
}
}
So when I have to enable the API for another DB, say DB-3 , all I have to do is change the active.profile=DB3 and create a new file application-DB3.properties.
Does this seem a standard practice or is there an way to do this more efficiently ?
A good practice is to pass such configuration as environment variables or system properties.
So you don't need to change the code/configuration when these variables change.

How do I implement Github OAuth2 login with Spring Boot using only REST api?

I am trying to implement Signup with Github in a sample project, where front-end code is made up of ReactJS and Backend API is made up of Spring Boot.
I am not using Server Template Engines such as ThymeLeaf or Mustache, but only creating RESTful APIs.
Below is my Spring Security Configuration.
#RequiredArgsConstructor
#EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
private final GithubOAuth2UserService githubOAuth2UserService;
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.httpBasic().disable()
.csrf().disable()
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.authorizeRequests()
.antMatchers("/v1/health-check")
.permitAll()
.and()
.logout()
.logoutSuccessUrl("/")
.and()
.oauth2Login()
.userInfoEndpoint()
.userService(githubOAuth2UserService);
}
}
Below is my code for GithubOAuth2UserService
#RequiredArgsConstructor
#Service
public class GithubOAuth2UserService implements OAuth2UserService<OAuth2UserRequest, OAuth2User> {
private final UserRepository userRepository;
#Override
public OAuth2User loadUser(OAuth2UserRequest userRequest) throws OAuth2AuthenticationException {
OAuth2UserService<OAuth2UserRequest, OAuth2User> delegate = new DefaultOAuth2UserService();
OAuth2User oAuth2User = delegate.loadUser(userRequest);
String registrationId = userRequest.getClientRegistration().getRegistrationId();
System.out.println(registrationId);
String userNameAttributeName = userRequest.getClientRegistration()
.getProviderDetails().getUserInfoEndpoint()
.getUserNameAttributeName();
System.out.println(userNameAttributeName);
OAuthAttributes attributes = OAuthAttributes.ofGithub(userNameAttributeName, oAuth2User.getAttributes());
User user = saveUser(attributes);
return new DefaultOAuth2User(
Collections.singleton(new SimpleGrantedAuthority(user.getRole().name())),
attributes.getAttributes(),
attributes.getNameAttributeKey()
);
}
#Transactional
protected User saveUser(OAuthAttributes attributes) {
System.out.println("SAVE USER!");
if(userRepository.findByEmail(attributes.getEmail()).isPresent()) {
throw new UserEmailConflictException();
} else {
User user = attributes.toEntity();
return userRepository.save(user);
}
}
}
Finally, OAuthAttributes is as below.
#Getter
public class OAuthAttributes {
private Map<String, Object> attributes;
private String nameAttributeKey;
private String name;
private String email;
#Builder
public OAuthAttributes(Map<String, Object> attributes, String nameAttributeKey, String name, String email) {
this.attributes = attributes;
this.nameAttributeKey = nameAttributeKey;
this.name = name;
this.email = email;
}
public static OAuthAttributes ofGithub(String userNameAttributeName, Map<String, Object> attributes) {
return OAuthAttributes.builder()
.name((String) attributes.get("name"))
.email((String) attributes.get("email"))
.attributes(attributes)
.nameAttributeKey(userNameAttributeName)
.build();
}
public User toEntity() {
return User.builder()
.name(name)
.email(email)
.build();
}
}
Via PostMan, I can see the API result as below, but my ReactJS code doesn't show anything, and just refreshes the page.
On ReactJS, it calls this API like below.
// OnClick function
const result = await axios.get("http://localhost:8080/oauth2/authorization/github");
console.log(result);
UPDATE
I am getting CORS policy violation, and I configured this using a configuration class below. Is this related to SecurityConfig class ?
#Configuration
#EnableWebMvc
public class CORSConfig implements WebMvcConfigurer {
#Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**").allowedOrigins("*").allowedMethods("*");
}
}
Thank you so much in advance. If any code needs to be shown, I will happily edit this post.

EJB: how to check user is authenticated

Use logged throuth the JSP form:
#ManagedBean
#SessionScoped
public class LoginView {
private String username; //+getter +setter
private String password; //+getter +setter
public String submit()
{
try {
HttpServletRequest request = (HttpServletRequest) FacesContext
.getCurrentInstance()
.getExternalContext()
.getRequest();
request.login(username, password);
} catch (ServletException e) {
FacesContext.getCurrentInstance().addMessage("login-form:username",
new FacesMessage(FacesMessage.SEVERITY_ERROR, "Validation Error", "Incorrect login or password"));
return "/login";
}
return "/index?faces-redirect=true";
}
}
How to check user logged in thorug EJB?
Below example whan I need:
#Stateless
public class SessionServiceBean {
#Resource
SessionContext sessionContext;
#EJB
UserService userService;
#Produces
#Named
#LoggedIn
public User getLoggedUser() {
if (/* check user is logged */) {
return userService.getByName(sessionContext.getCallerPrincipal().getName());
}
}
}
I foun only that the not logged use has name "anonymous", but it not better way, I think.
You are using a #Stateless EJB. In a Stateless EJB, you are working with user sessions. This is probably not right and the bean should be #Stateful.
I see no point in using EJBs in your example. Using pure CDI bean annotated with #SessionScoped would be sufficient.
You can store you user's session information directly in a session scoped bean. There is not need to use HttpServletRequest. For example:
#Named
#SessionScoped
public class UserSession implements Serializable {
private User activeUser;
public void logIn(User user) {
this.activeUser = user;
}
public void logOut() {
activeUser = null;
}
public boolean isLoggedIn() {
return activeUser != null;
}
public User getActiveUser() {
return activeUser;
}
}

Spring Security OAUTH2 getting token with username/password

my problem is very "simple". I don't know how to setup my OAUTH2 auth server to accept username/password and returns me token.
If I use:
curl curl:password#localhost:8081/oauth/token\?grant_type=client_credentials
it returns me the token but the problem is that it registers user "curl" in the DB so... not so good...
If I use:
http://www.example.com:8081/oauth/authorize?client_id=web&response_type=token
it prompts username and password dialog, I enter them and then it asks me I "Do you authorize 'web' to access your protected resources?
scope.read: Approve Deny"
Can I combine those two and just create simple request which will return me the token? I want to use it for angularjs frontend using RESTful WS in Spring Boot and Jersey.
Should I use this scheme
https://github.com/spring-projects/spring-security-oauth/blob/master/spring-security-oauth2/src/test/resources/schema.sql
use this config -> clients.jdbc(dataSource);
How to setup one user for that scheme? just basic login with username and password.
OauthConfiguration
#Configuration
#EnableAuthorizationServer
public class OAuthConfiguration extends AuthorizationServerConfigurerAdapter
{
#Autowired
private DataSource dataSource;
#Bean
public TokenStore tokenStore()
{
return new JdbcTokenStore(dataSource);
}
#Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception
{
endpoints.tokenStore(tokenStore());
}
#Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception
{
// #formatter:off
clients.inMemory()
.withClient("curl")
.authorities("USER")
.resourceIds("reee")
.scopes("read", "write")
.authorizedGrantTypes("client_credentials")
.secret("password")
.and()
.withClient("web")
.redirectUris("http://github.com/techdev-solutions/")
.resourceIds("reee")
.scopes("read")
.authorizedGrantTypes("implicit");
// #formatter:on
}
}
SecurityConfiguration
#Configuration
#EnableWebSecurity
public class SecurityConfiguration extends WebSecurityConfigurerAdapter
{
#Autowired
private DataSource dataSource;
#Override
protected void configure(HttpSecurity http) throws Exception
{
http.authorizeRequests().antMatchers("/**").authenticated()
.and().httpBasic().realmName("OAuth Server");
}
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception
{
PasswordEncoder encoder = new BCryptPasswordEncoder();
auth.userDetailsService(userDetailsService()).passwordEncoder(encoder);
auth.jdbcAuthentication().dataSource(dataSource);
}
#Bean
public UserDetailsService userDetailsService()
{
return new CustomUserDetailsService(dataSource);
}
}
i think we have similar issues as i have opened a q & a what are the java* configuration for oauth2 to return token after authentication
you can avoid the username and password box by adding username and password in the header of the request http://localhost:8081/oauth/authorize?client_id=web&response_type=token and in your client configuration for web add clients.inMemory().autoApprove(true) however server will respond with the redirect url. My problem is that I don't want it to redirect I just want the token in the response..

After Facebook Login (javascript sdk) - How do i create a user session in Spring-Security?

I am using:
- Facebook-JavaScript-SDK (for login)
- Angular-JS (front end)
- Spring-Security (server security layer)
- Spring MVC (server MVC)
User's can log-into my web app by
(A) either using Facebook login (implemented in javascript FB SDK) OR
(B) via normal username/password form.
In case of (B)
i do a Form Post with fields:
j_username
j_password
to /j_spring_security_check (Spring Security)
This works fine.
But in case of (A), after the user has logged in via FB (in front-end),
What do i need to do to have Spring Security create the Session for the user?
All i have is facebook user's email with me at this point, and Server is unaware of this login in the front-end.
After successful login with Face Book,you should make AJAX request to your back-end API and make database entry about the Face book authentication for the user.
1)You need to implement UserDetailsService from Spring-Security
#Component
public class CustomUserDetailsService implements UserDetailsService {
#Override
public UserDetails loadUserByUsername(String userEmail) throws UsernameNotFoundException {
// check for Facebook authentication(DB Entry)
// fetch user details and roles from db using e-mail
SimpleGrantedAuthority authority = new SimpleGrantedAuthority(roles);
UserDetails usd = new User(userId, userId, grantedAuths);
return usd;
}
}
2) Implement custom authentication provider
#Component("customAuthenticationProvider")
public class CustomAuthenticationProvider implements AuthenticationProvider {
#Autowired
private CustomUserDetailsService customUserDetailsService;
#Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
String userId = authentication.getName();
String password = authentication.getCredentials().toString();
UserDetails userFromDB = customUserDetailsService.loadUserByUsername(userId);
if (userFromDB != null) {
List<GrantedAuthority> grantedAuths = new ArrayList<GrantedAuthority>();
for (GrantedAuthority auth : userFromDB.getAuthorities()) {
grantedAuths.add(auth);
}
if (grantedAuths.size() != 0) {
System.out.println("user:" + userId + " got auth from custom auth provider");
return new UsernamePasswordAuthenticationToken(userFromDB, password, grantedAuths);
} else {
throw new DisabledException("No roles assigned");
}
}
throw new DisabledException("User or password incorrect");
}
#Override
public boolean supports(Class<?> authentication) {
return authentication.equals(UsernamePasswordAuthenticationToken.class);
}
}
3) Login controller
#Controller
public class LoginController {
#Autowired
private CustomAuthenticationProvider customAuthenticationProvider;
#Autowired
private CustomUserDetailsService customUserDetailsService;
#RequestMapping(value = "/login", )
public ResponseEntity<Object> fbLogin(#RequestParam String userEmail, HttpServletRequest request) {
UserDetails userDetails=customUserDetailsService.loadUserByUsername(userid);
UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken(userDetails, userid);
token.setDetails(new WebAuthenticationDetails(request));
Authentication authentication = this.customAuthenticationProvider.authenticate(token);
}
}
4) Spring Security Config
#Configuration
#EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
private CustomAuthenticationProvider authProvider;
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.authenticationProvider(authProvider);
}
}

Resources