Spring boot ldap security - authentication

Hello I have a problem creating simple login with Ldap. I have downloaded getting started project from spring.io website: Getting started LDAP.
It is working perfectly with ldif file but I want to replace it with running ldap server. I have tried it for days with no progress. I get best results with this piece of code (replaced in WebSecurityConfig of getting started project)
#Configuration
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests().anyRequest().authenticated().and().httpBasic();
}
#Override
protected void configure(AuthenticationManagerBuilder authManagerBuilder) throws Exception {
authManagerBuilder.authenticationProvider(activeDirectoryLdapAuthenticationProvider()).userDetailsService(userDetailsService());
}
#Bean
public AuthenticationManager authenticationManager() {
return new ProviderManager(Arrays.asList(activeDirectoryLdapAuthenticationProvider()));
}
#Bean
public AuthenticationProvider activeDirectoryLdapAuthenticationProvider() {
ActiveDirectoryLdapAuthenticationProvider provider = new ActiveDirectoryLdapAuthenticationProvider(null, "ldap://ip:port/", "ou=GROUP,dc=domain,dc=com");
provider.setConvertSubErrorCodesToExceptions(true);
provider.setUseAuthenticationRequestCredentials(true);
return provider;
}
}
If i try to login with good username and password in format "username" "password" console output: ActiveDirectoryLdapAuthenticationProvider : Active Directory authentication failed: Supplied password was invalid
If I use "username#domain.com" and good password, page just reloads with no output to console.
If I use random username and password console: Active Directory authentication failed: Supplied password was invalid
Can someone help?

As suggested in comment I have turned on logging and found out that the problem is same with "username#domain.com" too.
Problem was in aciveDirectoryLdapAuthenticationProvider() there were 3 problems in it.
I have removed OU group from rootDn and added domain so we can use only username to log in.
ActiveDirectoryLdapAuthenticationProvider provider = new ActiveDirectoryLdapAuthenticationProvider("domain.com", "ldap://ip:port/", "dc=domain,dc=com");
changed searchfilter of provider
provider.setSearchFilter("(&(objectClass=user)(sAMAccountName={0}))");
and finally I had to change ActiveDirectoryLdapAuthProvider searchForUser method because it was matching "username#domain.com" with sAMAccountName istead of "username". This:
return SpringSecurityLdapTemplate.searchForSingleEntryInternal(context,
searchControls, searchRoot, searchFilter,
new Object[] { bindPrincipal });
replaced with this:
return SpringSecurityLdapTemplate.searchForSingleEntryInternal(context,
searchControls, searchRoot, searchFilter,
new Object[] { username });
Complete aciveDirectoryLdapAuthenticationProvider:
#Bean
public AuthenticationProvider activeDirectoryLdapAuthenticationProvider() {
ActiveDirectoryLdapAuthenticationProvider provider = new ActiveDirectoryLdapAuthenticationProvider("domain.com", "ldap://ip:port/", "dc=domain,dc=com");
provider.setSearchFilter("(&(objectClass=user)(sAMAccountName={0}))");
provider.setConvertSubErrorCodesToExceptions(true);
provider.setUseAuthenticationRequestCredentials(true);
return provider;
}
Can someone provide better solution for the second/third problem? maybe better searchfilter? I dont have any field in ldap that matches "username#domain.com" format of bindPrincipal that is using ActiveDirectoryLdapAuthProvider.

Related

Is it ok to add access_token authorities to the OAuth2LoginAuthenticationToken?

I have a simple spring boot application with two services - ui and resource.
I trying to configure oauth2+oidc authentication using uaa server.
When I login in the ui service, spring security creates authentication result (in OidcAuthorizationCodeAuthenticationProvider) using id_token and it doesn't contain any scopes except openid. When the authentication result is created it contains only one authority - ROLE_USER so a can't use authorization on the client side.
Is is ok to override OidcUserService and add to the user's authorities scopes from the access_token to check access on the client side?
#Override
public OidcUser loadUser(OidcUserRequest userRequest) throws OAuth2AuthenticationException {
OidcUser user = super.loadUser(userRequest);
Collection<? extends GrantedAuthority> authorities = buildAuthorities(
user,
userRequest.getAccessToken().getScopes()
);
return new DefaultOidcUser(
authorities,
userRequest.getIdToken(),
user.getUserInfo()
);
}
Security configuration:
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.mvcMatchers("/protected/**").hasAuthority("SCOPE_protected")
.anyRequest().authenticated()
.and()
.oauth2Login()
.userInfoEndpoint().oidcUserService(oidcUserService())
.and()
...
It works but I'm not sure it's a good idea.
It is the approach as outlined in the Spring Security documentation, so the approach is fine.
The only thing is that when I have implemented it, I didn't add all the scopes to the authorities set - I pulled out the specific claim that had the role information - a custom groups claim that I configured in the identity provider's authorization server.
I include some example code for how to do this with Spring Webflux as most examples show how to do it with Spring MVC as per your code.
note: I'm very inexperienced with using reactor!
public class CustomClaimsOidcReactiveOAuth2UserService implements ReactiveOAuth2UserService<OidcUserRequest, OidcUser> {
private final OidcReactiveOAuth2UserService service = new OidcReactiveOAuth2UserService();
public Mono<OidcUser> loadUser(OidcUserRequest userRequest) throws OAuth2AuthenticationException {
log.debug("inside CustomClaimsOidcReactiveOAuth2UserService..");
Mono<OidcUser> mOidcUser = service.loadUser(userRequest);
return mOidcUser
.log()
.cast(DefaultOidcUser.class)
.map(DefaultOidcUser::getClaims)
.flatMapIterable(Map::entrySet)
.filter(entry -> entry.getKey().equals("groups"))
.flatMapIterable(roleEntry -> (JSONArray) roleEntry.getValue())
.map(roleString -> {
log.debug("roleString={}", roleString);
return new OidcUserAuthority((String) roleString, userRequest.getIdToken(), null);
})
.collect(Collectors.toSet())
.map(authorities -> {
log.debug("authorities={}", authorities);
return new DefaultOidcUser(authorities, userRequest.getIdToken());
});
}
}
...
#Bean
ReactiveOAuth2UserService<OidcUserRequest, OidcUser> userService() {
return new CustomClaimsOidcReactiveOAuth2UserService();
}

How to get past the Authentication Required Spring-boot Security

I have put in the password which is "root" and it keeps popping back up. How can I suppress this or get rid of it. I am using spring boot and spring security.
application.properties
spring.datasource.driverClassName=com.mysql.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost:3306/springbootpractice
spring.datasource.username=root
spring.jpa.database = MYSQL
spring.jpa.show-sql = true
# Hibernate
hibernate.dialect: org.hibernate.dialect.MySQL5Dialect
hibernate.show_sql: true
hibernate.hbm2ddl.auto: update
entitymanager.packagesToScan: /
I am using intellij 14 if that matters.
----Update 1-----
#Configuration
#EnableWebMvcSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/", "/index").permitAll()
.anyRequest().authenticated()
.and()
.formLogin()
.loginPage("/index")
.permitAll()
.and()
.logout()
.permitAll();
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/", "/index").permitAll()
.anyRequest().authenticated()
.and()
.formLogin()
.loginPage("/index")
.permitAll()
.and()
.logout()
.permitAll();
}
#Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
auth
.inMemoryAuthentication()
.withUser("user").password("password").roles("USER");
}
}
This class has to be in a parent package of all other packages:
WebSecurityConfig.
Also in application.properties set:
security.basic.enabled=false
ACV's answer is probably the easiest way to turn off the authentication completely by adding security.basic.enabled=false to the application.properties file which is usually located under src/main/resources folder.
or you just type in the password :)
1. use default password
When you run your spring application, there is usually a whole bunch of logging printed, which people usually don't read. The password is actually generated and printed to the screen at the startup. and the username is simply user. If you are testing using a browser and it probably only need you enter it once and caches it, so once for all, you should be securely logged in without authenticating every time.
(however, every time you restart your app, it will generate a new password)
2. customize your password
Add the following properties to your application.properties if you want to customize your username and password:
security.user.name=myuser
security.user.password=mypassword
And here is how it looks like with your own username and password
Reference:
Spring Boot Features - Security
Monitoring and Management over HTTP
You can bypass this spring boot security mechanism. See an example below for this:
#SpringBootApplication
#EnableAutoConfiguration(exclude = {SecurityAutoConfiguration.class})
public class SampleSpringBootApplication {
public static void main(String[] args) {
SpringApplication.run(SampleSpringBootApplication.class, args);
}
}
When Spring Security is in the classpath, Spring Boot by default secures all your pages with Basic authentication. That's why you are being asked for userid and password.
You will need to configure the security. To do so, commonly people would extend a WebSecurityConfigurerAdapter, like this:
#Configuration
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/", "/home").permitAll()
.anyRequest().authenticated()
.and()
...
Refer this Spring Security guide for more details.
Here was the issues
(1) .loginPage("/index") was saying my login page was at index, however I just wanted to use spring's default login page.
(2) had to to move the security package inside the demo package (the main package). Thanks to #Sanjay for suggesting that. I tried to use #ComponantScan but it could not get it to work.

Apache cxf java client + ntlm authentication and multi user support

I am using apache cxf java client to connect my WS. I am also using NTLM for authentication.
Now problem I am facing due to credential caching. First time i tried user which does not have privileges to access WS method. when I changed the user , it is still using same user to access WS method.
I am running in tomcat, so cannot kill my JVM .. tried all possible combination on httpClientPolicy.
Any help will be appreciated.
This is NTLM specific problem. sun.net.www.protocol.https.HttpsURLConnectionImpl is getting serverAuthorization via java.net.Authenticator. requestPasswordAuthentication(). This authorization info is maintained in sun.net.www.protocol.http.AuthCacheValue.cache.
So if we override sun.net.www.protocol.http.AuthCacheValue means we can fix this issue.
AuthCacheValue.setAuthCache(new AuthCache()
{
#Override
public void remove(String arg0, AuthCacheValue arg1) { }
#Override
public void put(String arg0, AuthCacheValue arg1) { }
#Override
public AuthCacheValue get(String arg0, String arg1)
{
return null;
}
});
Reference :
http://web.archiveorange.com/archive/v/ACbGtycfTs2dqbRNpy6d
http://tigrou.nl/2011/06/11/cached-credentials-in-http-basic-authentication/
I googled and tried a lot of solutions to this problem.. apparently the simplest code that worked is as below using the JCIFS library
//Set the jcifs properties
jcifs.Config.setProperty("jcifs.smb.client.domain", "domainname");
jcifs.Config.setProperty("jcifs.netbios.wins", "xxx.xxx.xxx.xxx");
jcifs.Config.setProperty("jcifs.smb.client.soTimeout", "300000"); // 5 minutes
jcifs.Config.setProperty("jcifs.netbios.cachePolicy", "1200"); // 20 minutes
jcifs.Config.setProperty("jcifs.smb.client.username", "username");
jcifs.Config.setProperty("jcifs.smb.client.password", "password");
//Register the jcifs URL handler to enable NTLM
jcifs.Config.registerSmbURLHandler();
Apparently CXF 3.0 doesnt have a valid way of configuring the HTTP Client (4.3.x) with NTCredentials instance. Please refer to bug https://issues.apache.org/jira/browse/CXF-5671
By the way, if you have a simple message which needs to be transmitted, just use HTTP Client (I worked using 4.3.4.. not sure of the earlier versions) with NTCredentials Instance. That too did the magic for me.. The sample is as below:
final NTCredentials ntCredentials = new NTCredentials("username", "Passworrd","destination", "domain");
CredentialsProvider credsProvider = new BasicCredentialsProvider();
credsProvider.setCredentials(AuthScope.ANY, ntCredentials);
CloseableHttpClient httpclient = HttpClientBuilder.create()
.setDefaultCredentialsProvider(credsProvider)
.build();

can not find entity using seam managed persistence context

I'm trying to change the authentication approach in my seam application. I currently use a login form to authenticate. In the future, I'd like to delegate the authentication to another layer that will rewrite every request with a specific HTTP header containing the username of authenticated user.
I'm facing a weird problem: when using login page to authenticate, I'm able to extract the user through the entityManager. But when I query the entityManager using the information off the header, I'm unable to find the user. The entityManager behave like the user does not exist.
I already tried two approaches:
Creating a fake login page which triggers the authentication process
Creating a servlet which gets the request and starts the
authentication process
Both times, the entityManager fails to return me any user.
I read a lot about how seam manages the persistence context, but I didn't find a single explanation which make this issue clear. Do you have any ideas? suggestions? or even guesses?
the code which uses the entityManager is the following:
#Name("userService")
#AutoCreate
public class UserService {
#Logger
private Log logger;
#In
private EntityManager entityManager;
public User getUser(String email) {
try {
return entityManager
.createQuery("SELECT u FROM User u where u.email=:email",
User.class).setParameter("email", email.trim())
.getSingleResult();
} catch (NoResultException e) {
return null;
}
}
}
The configuration for persistence context is:
<persistence:managed-persistence-context startup="false" scope="stateless"
auto-create="true" name="entityManager" persistence-unit-jndi-name="java:/EntityManagerFactory" />
I created an empty fake login page which executes a page action (authentication) in which i get the request user header as the following:
#Name("applicationAuthenticator")
public class ApplicationAuthenticator {
#Logger
private Log log;
#In
private Identity identity;
#In
private Credentials credentials;
#In(required=true)
private UserService userService;
#Begin
public void login() throws LoginException {
HttpServletRequest request = (HttpServletRequest) FacesContext.getCurrentInstance().getExternalContext().getRequest();
String userName=request.getHeader("user");
identity.unAuthenticate();
credentials.setUsername(userName);
credentials.setPassword("fake");
identity.acceptExternallyAuthenticatedPrincipal(new SimplePrincipal(credentials.getUsername()));
User user=userService.getUserByEmail(credentials.getUsername());
identity.authenticate();
identity.quietLogin();
}
}
Thx in advance :-)
Thx #DaveB for your reply, the code which uses the entityManager is the following:
public User getUser(String email) {
try {
return entityManager
.createQuery("SELECT u FROM User u where u.email=:email",
User.class).setParameter("email", email.trim())
.getSingleResult();
} catch (NoResultException e) {
return null;
}
}
The configuration for persistence context is:
<persistence:managed-persistence-context startup="false" scope="stateless"
auto-create="true" name="entityManager" persistence-unit-jndi-name="java:/EntityManagerFactory" />
I created an empty fake login page which executes a page action (authentication) in which i get the request user header as the following:
HttpServletRequest request = (HttpServletRequest) FacesContext
.getCurrentInstance().getExternalContext().getRequest();
String userName = request.getHeader("user");

Work with OData secured service

I want to generate entity classes and Service class of OData secured service.
In OData Java extension page it is written that I need to use org.restlet.ext.odata.Generator class that should get uri and output directory parameters.
But if my OData service is secured the generator instance is not able to generate service classes without username and password of the service.
I did not find any way to pass username and password to generator class.
I get 401 HTTP response code.
Please help.
In the org.restlet.ext.odata.Generator class, in the method main,
The following code would clear the credential details set in the setCredentials() method.
Service service = new Service(dataServiceUri);
if(service.getMetadata() == null)
{
errorMessage = "Cannot retrieve the metadata.";
}
Kindly provide a solution for this issue as I am currently unable to generate the classes for my rest service as the service is secured with an user password.
I tried the following code to generate the code for my secured service uri:
import org.restlet.ext.odata.Generator;
import org.restlet.ext.odata.Service;
import org.restlet.data.ChallengeResponse;
import org.restlet.data.ChallengeScheme;
public class ODataRestletGenerator extends Service {
public ODataRestletGenerator(String serviceUri) {
super(serviceUri);
}
public static final String APPLICATION_URI = "http://ldcigkd.xxx.yyy.corp:50033/xxx/opu/sdata/IWCNT/CUSTOMER/";
public static void main(String[] args) {
// Add the client authentication to the call
ChallengeScheme scheme = ChallengeScheme.HTTP_BASIC;
ChallengeResponse credentials = new ChallengeResponse(scheme, "user", "pwd");
new ODataRestletGenerator(APPLICATION_URI).setauth(credentials);
String[] arguments = { APPLICATION_URI, "/customer/src" };
Generator.main(arguments);
}
private void setauth(ChallengeResponse credentials) {
super.setCredentials(credentials);
}
}
In the org.restlet.ext.odata.Service subclass that is generated by OData extension, you can call setCredentials() and pass an instance of ChallengeResponse including scheme (BASIC?), login (identifier) and password (secret).