ClassCastException while searching for LDAP user - spring-security

After migrating to Spring security 4.2.2 (from 3.1), I am hitting the following error in LDAP user search:
java.lang.ClassCastException: com.sun.jndi.ldap.LdapCtx cannot be cast to org.springframework.ldap.core.DirContextAdapter
at org.springframework.security.ldap.SpringSecurityLdapTemplate.searchForSingleEntryInternal(SpringSecurityLdapTemplate.java:345)
at org.springframework.security.ldap.SpringSecurityLdapTemplate$3.executeWithContext(SpringSecurityLdapTemplate.java:318)
at org.springframework.ldap.core.LdapTemplate.executeWithContext(LdapTemplate.java:817)
at org.springframework.ldap.core.LdapTemplate.executeReadOnly(LdapTemplate.java:803)
at org.springframework.security.ldap.SpringSecurityLdapTemplate.searchForSingleEntry(SpringSecurityLdapTemplate.java:316)
at org.springframework.security.ldap.search.FilterBasedLdapUserSearch.searchForUser(FilterBasedLdapUserSearch.java:127)
...
...
I would appreciate any help in resolving the issue. I am using OpenLDAP server for the test if it makes any difference.
Thanks,
Raghu

Old question, but I had the same issue and manage to solve it somehow, so I thought I should share.
I'm using Spring Security 4.2.3 and I had something similar:
public AdvisorResponse getAdvisorInfo(final String uid) {
return cdsLdapTemplate.searchForObject(
createCriteria(uid), this::mapAdvisorResponse
);
}
private AdvisorResponse mapAdvisorResponse(final Object ctx) {
final DirContextAdapter context = (DirContextAdapter) ctx;
final AdvisorResponse advisor = new AdvisorResponse();
advisor.setUid(context.getStringAttribute("uid"));
return advisor;
}
private ContainerCriteria createCriteria(final String uid) {
return query()
.base("ou=people")
.countLimit(1)
.searchScope(SUBTREE)
.timeLimit(TIMEOUT)
.where("uid").is(uid);
}
It used to work flawlessly until I had to run it in an #Async thread, then I started to have the same class cast exception.
As suggested here the error seems to be due to a different classloader used in the thread. My solution was to force the correct classloader into the execution thread:
public AdvisorResponse getAdvisorInfo(final String uid) {
Thread.currentThread().setContextClassLoader(this.getClass().getClassLoader());
return cdsLdapTemplate.searchForObject(
createCriteria(uid), this::mapAdvisorResponse
);
}

Related

Custom repository implementation for Neo4j doesn't work

This is similar to what is discussed at Unable to use two Neo4j Instances with Spring boot/Spring data neo4j but I don't have two databases. I have downloaded the spring-data neo4j sample java application from the git repo and want to execute a dynamic query instead of executing a static query via repository interface.
I am facing an issue of null transaction manager.
Here's my interface :
public interface SearchRepositoryCustom {
Iterable<Movie> searchByCriteria();
}
Here's my custom repo impl:
#Repository
#Transactional
public class SearchRepositoryImpl implements SearchRepositoryCustom {
#Autowired
private SessionFactory sessionFactory;
#Override
public Iterable<Movie> searchByCriteria() {
String query = "MATCH (m:Movie)<-[r:ACTED_IN]-(a:Person) RETURN m,r,a LIMIT 10";
return sessionFactory.openSession().query(Movie.class, query, Collections.emptyMap());
}
}
Here's my configuration :
#Configuration
#EnableTransactionManagement
#EnableNeo4jRepositories(basePackages = "movies.spring.data.neo4j.repositories")
public class Neo4jPersistenceConfig {
#Bean
#ConfigurationProperties("spring.data.neo4j")
public Neo4jProperties neo4jProperties() {
return new Neo4jProperties();
}
#Bean
public org.neo4j.ogm.config.Configuration userConfiguration() {
return neo4jProperties().createConfiguration();
}
#Bean
public SessionFactory getSessionFactory() {
return new SessionFactory(userConfiguration(), "movies.spring.data.neo4j.domain");
}
#Bean
public Neo4jTransactionManager transactionManager() {
return new Neo4jTransactionManager(getSessionFactory());
}
}
Since I have only one TransactionManager and One SessionFactory (as I have only one Neo4j instance) I don't need to name the beans separately.
I am seeing the following exception :
org.neo4j.ogm.exception.core.TransactionManagerException: Transaction is not current for this thread
at org.neo4j.ogm.session.transaction.DefaultTransactionManager.rollback(DefaultTransactionManager.java:86) ~[neo4j-ogm-core-3.1.0.jar:3.1.0]
at org.neo4j.ogm.transaction.AbstractTransaction.rollback(AbstractTransaction.java:65) ~[neo4j-ogm-api-3.1.0.jar:3.1.0]
at org.neo4j.ogm.drivers.bolt.transaction.BoltTransaction.rollback(BoltTransaction.java:61) ~[neo4j-ogm-bolt-driver-3.1.0.jar:3.1.0]
at org.neo4j.ogm.transaction.AbstractTransaction.close(AbstractTransaction.java:144) ~[neo4j-ogm-api-3.1.0.jar:3.1.0]
at org.springframework.data.neo4j.transaction.Neo4jTransactionManager.doCleanupAfterCompletion(Neo4jTransactionManager.java:379) ~[spring-data-neo4j-5.0.5.RELEASE.jar:5.0.5.RELEASE]
at org.springframework.transaction.support.AbstractPlatformTransactionManager.cleanupAfterCompletion(AbstractPlatformTransactionManager.java:1007) ~[spring-tx-5.0.4.RELEASE.jar:5.0.4.RELEASE]
at org.springframework.transaction.support.AbstractPlatformTransactionManager.processCommit(AbstractPlatformTransactionManager.java:793) ~[spring-tx-5.0.4.RELEASE.jar:5.0.4.RELEASE]
at org.springframework.transaction.support.AbstractPlatformTransactionManager.commit(AbstractPlatformTransactionManager.java:714) ~[spring-tx-5.0.4.RELEASE.jar:5.0.4.RELEASE]
at org.springframework.transaction.interceptor.TransactionAspectSupport.commitTransactionAfterReturning(TransactionAspectSupport.java:532) ~[spring-tx-5.0.4.RELEASE.jar:5.0.4.RELEASE]
at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:304) ~[spring-tx-5.0.4.RELEASE.jar:5.0.4.RELEASE]
at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:98) ~[spring-tx-5.0.4.RELEASE.jar:5.0.4.RELEASE]
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:185) ~[spring-aop-5.0.4.RELEASE.jar:5.0.4.RELEASE]
at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:689) ~[spring-aop-5.0.4.RELEASE.jar:5.0.4.RELEASE]
at movies.spring.data.neo4j.repositories.SearchRepositoryImpl$$EnhancerBySpringCGLIB$$d2631bcd.searchByCriteria(<generated>) ~[classes/:na]
at movies.spring.data.neo4j.controller.MovieController.advGlobal(MovieController.java:54) ~[classes/:na]
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:1.8.0_171]
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[na:1.8.0_171]
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:1.8.0_171]
at java.lang.reflect.Method.invoke(Method.java:498) ~[na:1.8.0_171]
Even if I actually go ahead and declare the name of the beans and mark the method transactional by specifying the name of the transactionManager, I still get the same error consistently.
Java version : 1.8
neo4j version : 3.4.6
What am I missing?
Gerrit is right. I'd like to add the two options we have here. We provide an injectable Session that is bound to the current thread and is integrated with Springs transactions. Just auto wire that instead of the SessionFactory and you're good to go with your solution. Please note that I'm using constructor injection as recommended throughout all Spring projects:
#Repository
#Transactional
class SearchRepositoryImpl implements SearchRepositoryCustom {
private final Session session;
public SearchRepositoryImpl(Session session) {
this.session = session;
}
#Override
public Iterable<ThingEntity> searchByCriteria() {
String query = "MATCH (t:ThingEntity) RETURN t LIMIT 10";
return session.query(ThingEntity.class, query, Map.of());
}
}
I have used another domain to create a concise example project, but the idea stays the same.
For a simple use case like that I fully agree with Gerrit and would use the #Query annotation on a declarative Spring Data Neo4j repository like this:
interface ThingRepository extends Neo4jRepository<ThingEntity, Long> {
#Query("MATCH (t:ThingEntity) RETURN t LIMIT 10")
public Iterable<ThingEntity> searchByCriteria();
}
The usage is the same, as demonstrated here:
#Component
class ExampleUsage implements CommandLineRunner {
private final ThingRepository thingRepository;
private final SearchRepositoryCustom searchRepositoryCustom;
public ExampleUsage(ThingRepository thingRepository, SearchRepositoryCustom searchRepositoryCustom) {
this.thingRepository = thingRepository;
this.searchRepositoryCustom = searchRepositoryCustom;
}
#Override
public void run(String... args) {
this.thingRepository.save(new ThingEntity(1));
this.thingRepository.save(new ThingEntity(2));
var things = this.searchRepositoryCustom.searchByCriteria();
things.forEach(System.out::println);
things = this.thingRepository.searchByCriteria();
things.forEach(System.out::println);
}
}
You'll find the complete application as a gist: Use Spring Data Neo4js injectable OGM Session. I have used Java 10 instead of 8 as we approaching EOL for Java 8, but that doesn't change the repository implementations. Apart from that, tested with Spring Boot 2.0.4, Spring Data Kay and OGM 3.1.0.
Edit: In regard to the comment: The injectable session is a proxy. The field itself is final, but the proxy opens sessions as needed and then delegates to it.
You are mixing up Neo4j-OGM‘s SessionFactory/Session and the #Transactional support of Spring (Data Neo4j). The latter will create a new transaction of which the OGM code is not aware and tries to create a fresh transaction.
If you use Spring Data Neo4j you can also define the query within your entity repository with a #Query annotated method.
The other solution would be to remove the #Transactional annotation in your service layer and create it manually if you plan to execute multiple operations (Not needed for one because OGM will create a transaction implicit if it does not exist).

spring-amqp unable to use custom MessagingMessageListenerAdapter in SimpleMessageListenerContainerFactory

In spring-amqp 2.0.3.RELEASE module it's no possible to use custom MessagingMessageListenerAdapter in SimpleMessageListenerContainerFactory.
Even thought registration own bean we stuck at highest one, where last object instance just hard created thought "new MethodRabbitListenerEndpoint" at org.springframework.amqp.rabbit.annotation.RabbitListenerAnnotationBeanPostProcessor#processAmqpListener.
Maybe spring developers could add some producer registration to SimpleMessageListenerContainerFactory like "smlcf.setMessageListenerAdapterCreator"
I think what you are asking can be done via a RabbitListenerAnnotationBeanPostProcessor extension:
#Bean(name = RabbitListenerConfigUtils.RABBIT_LISTENER_ANNOTATION_PROCESSOR_BEAN_NAME)
#Role(BeanDefinition.ROLE_INFRASTRUCTURE)
static RabbitListenerAnnotationBeanPostProcessor myRabbitListenerAnnotationBeanPostProcessor() {
return new RabbitListenerAnnotationBeanPostProcessor() {
#Override
protected void processListener(MethodRabbitListenerEndpoint endpoint, RabbitListener rabbitListener, Object bean,
Object adminTarget, String beanName) {
super.processListener(new MyMethodRabbitListenerEndpoint(), rabbitListener, proxy, adminTarget, beanName);
}
};
}
But what you are saying about retry for the reply really makes sense and we definitely should consider to let to inject a RabbitTemplate into the RabbitListenerContainerFactory.
Feel free to raise a JIRA on the matter.
I have created a story for adding retry functionality for ReplyTo https://jira.spring.io/browse/AMQP-825

Spring OAuth2 - There is no client authentication. Try adding an appropriate authentication filter

We have an application which is using spring-security-oauth2:1.0. I was trying to change it to a newer version, spring-security-oauth2:2.0.7.RELEASE. Some classes were removed, some package structure is changed, I managed to sort out all those things and I was able to start the server without any issue. But I am facing a strange issue here.
With OAuth2 - 1.0 version, when the user logs in we used to do a GET request on /oauth/token, For example :
http://localhost:8080/echo/oauth/token?grant_type=password&client_id=ws&client_secret=secret&scope=read,write&username=john#abc.com&password=password123
and It used to work just fine.
When I try the same thing, First of all I am not able to make a GET request because of the logic in TokenEndPoint.java
private Set<HttpMethod> allowedRequestMethods = new HashSet<HttpMethod>(Arrays.asList(HttpMethod.POST));
#RequestMapping(value = "/oauth/token", method=RequestMethod.GET)
public ResponseEntity<OAuth2AccessToken> getAccessToken(Principal principal, #RequestParam
Map<String, String> parameters) throws HttpRequestMethodNotSupportedException {
if (!allowedRequestMethods.contains(HttpMethod.GET)) {
throw new HttpRequestMethodNotSupportedException("GET");
}
return postAccessToken(principal, parameters);
}
I have tried to make a POST request same as above URL, but I get InsufficientAuthenticationException with the error message
There is no client authentication. Try adding an appropriate authentication filter
This is because of the following POST request controller in TokenEndpoint.java. When I debug, I see that principal is null.
#RequestMapping(value = "/oauth/token", method=RequestMethod.POST)
public ResponseEntity<OAuth2AccessToken> postAccessToken(Principal principal, #RequestParam
Map<String, String> parameters) throws HttpRequestMethodNotSupportedException {
//principal is null here
if (!(principal instanceof Authentication)) {
throw new InsufficientAuthenticationException(
"There is no client authentication. Try adding an appropriate authentication filter.");
}
.............
}
I have an authentication filter and it worked well when I used version 1.0. This is the relevant prats of my config:
<authentication-manager xmlns="http://www.springframework.org/schema/security">
<authentication-provider user-service-ref="userDetailsService"/>
</authentication-manager>
<bean id="userDetailsService" class="com.hcl.nc.service.UserDetailsService">
<constructor-arg><ref bean="sessionFactory" /></constructor-arg>
</bean>
I always thought that the request will be authenticated by authentication-provider and goes to token-endpoint but that does not seem to be the correct flow. After debugging the application with version 2.0.7, now I really doubt my understanding about the flow.
Could somebody please explain why it worked in previous version and why it's not working now?
Do I have do to something different to get a OAuth token??
NOTE: I have already checked these questions : here, here, here. But I was not able to find the correct solution.
I don't know the previous version, but I know a bit about 2.0.7.
I suspect your problem is that your TokenEndpoint security tries to authenticate your clients against your user service.
The TokenEndpoint is protected by a BasicAuthenticationFilter. By default this filter would use an AuthenticationManager instance, which itself holds an AuthenticationProvider, which itself depends on an instance of UserDetailsService.
The trick is that this particular instance of UserDetailsService must be client based, not user based : that's why there is a ClientDetailsUserDetailsService, which adapts ClientDetailsService to UserDetailsService.
Normally all this stuff is already done by default when you use the framework's configuration classes AuthorizationServerConfigurerAdapter, #EnableAuthorizationServer, etc..
I had the same problem and my application.yml had this line:
servlet:
path: /auth
so the token address was: /auth/oauth/token
I remove the path from application.yml so the token path became:
/oauth/token
And everything works fine.
I hope this help
One of the problems of the following error, can be that authentication was not performed. I have encountered this problem with older implementation of Spring.
verify that:
TokenEndpoint -> postAccessToken method. Check if Principal is not null. If it is null it means that Basic Authroziation was not performed.
One of the solution to add filter was to use:
#Configuration
public class FilterChainInitializer extends AbstractSecurityWebApplicationInitializer {
}
More information about AbstractSecurityWebApplicationInitializer can be found in Spring docs
The problem can be because of opening all requests. You should remove it.
#Override
public void configure(WebSecurity web) throws Exception {
web
.ignoring()
.antMatchers("/**");
}
in my case, i found this config:
security.allowFormAuthenticationForClients(); // here
then post this
http://localhost:8081/sso/oauth/token?client_id=unity-client&client_secret=unity&grant_type=authorization_code&code=Yk4Sum&redirect_uri=http://localhost:8082/sso-demo/passport/login
its works for me, try it
#Configuration
#EnableAuthorizationServer
public class Oauth2Config extends AuthorizationServerConfigurerAdapter {
private static final Logger log = LoggerFactory.getLogger(Oauth2Config.class);
#Override
public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {
security.allowFormAuthenticationForClients(); // here
}
#Override
public void configure(final ClientDetailsServiceConfigurer clients) throws Exception { // #formatter:off
clients.inMemory()
.withClient("unity-client")
.secret("unity")
.authorizedGrantTypes("authorization_code", "password", "client_credentials", "implicit", "refresh_token")
.scopes("foo", "read", "write")
.accessTokenValiditySeconds(3600) // 1 hour
.refreshTokenValiditySeconds(2592000) // 30 days
;
} // #formatter:on
#Override
public void configure(final AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
}
}
I am following this tutorial - Practical Guide to Building an API Back End with Spring Boot'. See https://www.infoq.com/minibooks/spring-boot-building-api-backend , But with the latest SpringBoot Version(2.7)
and I run into this problem:
org.springframework.security.authentication.InsufficientAuthenticationException: There is no client authentication. Try adding an appropriate authentication filter. at org.springframework.security.oauth2.provider.endpoint.TokenEndpoint.postAccessToken(TokenEndpoint.java:91) ~[spring-security-oauth2-2.3.5.RELEASE.jar:na]
My solution/fix was to annotate WebSecurityGlobalConfig with #EnableWebSecurity because in the original course this annotation was missing.
So adding this annotaiton has fixed the error for me.

NullReferenceException in DotNetOpenAuth

I tracked down what appears to be a defect causing a NullReferenceException in my ASP.NET MVC app. Did I break this, or should this break DotNetOpenAuth for the majority of installations?
I get this:
[NullReferenceException: Object reference not set to an instance of an object.]
DotNetOpenAuth.AspNet.OpenAuthSecurityManager.GetUsername(HttpContextBase context) +27
DotNetOpenAuth.AspNet.OpenAuthSecurityManager.RequestAuthentication(String returnUrl) +341
Controllers.LoginController.Index() +226
Here's the Index() method. Note it currently returns User in production and OK in development. Those are obviously temporary. The commented code is from the last URL I referenced below, where I reduced the amount of code between me and the problem. The stack trace was still similar, though.
public virtual ActionResult Index()
{
if (HttpContext == null) return Content("HttpContext");
if (HttpContext.User == null) return Content("User");
if (HttpContext.User.Identity == null) return Content("Identity");
return Content("OK");
new OpenAuthSecurityManager(HttpContext, s_fbClient, OAuthDataProvider.Instance)
.RequestAuthentication(Url.Action(Actions.Callback()));
// OAuthWebSecurity
// .RequestAuthentication("facebook", Url.Action(Actions.Callback()));
return null;
}
The exception arises because HttpContext.User is null. Here's the DotNetOpenAuth.AspNet library source for that failing method.
private static string GetUsername(HttpContextBase context) {
string username = null;
if (context.User.Identity.IsAuthenticated) {
username = context.User.Identity.Name;
}
return username ?? string.Empty;
}
User has apparently has been nullable ever since IIS Integrated Mode was available. This explains why I don't see it in development, as I'm running IIS Express with defaults, but it doesn't explain why I can't find any information about the defect. Integrated mode was released in 2007, and DotNetOpenAuth is still maintained. Microsoft docs say this about the setting:
Classic Mode: Use this mode only when the applications in the
application pool cannot run in Integrated mode.
I must be missing something, because it seems like everyone should have this issue.
Have I NuGet'ed a non-maintained library somehow? It seems odd, since it shows it was just updated a week ago. But when I follow the documentation link from NuGet, the source code I arrive at doesn't seem to even have an AspNet namespace, where my exception arose.
EDIT: The only related package I use is currently DotNetOpenAuth.AspNet (and it has 8 dependencies), last published less than a week ago. I've tried other packages also. I don't need SimpleAuth or any WebMatrix jazz. In the process of resolving this, I tried switching libraries as described http://techblog.dorogin.com/2013/06/Microsoft.AspNet.WebPages.OAuth.html
EDIT: Logged a defect related to this, but it seems this library may not be maintained. https://github.com/DotNetOpenAuth/DotNetOpenAuth/issues/317#issuecomment-29580565
EDIT: Pending stack trace, it may be the same defect as MVC 5 Owin Facebook Auth results in Null Reference Exception
This is indeed a defect in the DotNetOpenAuth.AspNet code. Unfortunately that DotNetOpenAuth library is no longer maintained. Modifying the source with:
private static string GetUsername(HttpContextBase context) {
string username = null;
if (context.User != null && context.User.Identity.IsAuthenticated) {
username = context.User.Identity.Name;
}
return username ?? string.Empty;
}
Of course does the trick.

Spring Security ACL: NotFoundException from JDBCMutableAclService.createAcl

I've been working on this task for too long to abandon the idea of using Spring Security to achieve it, but I wish that the community will provide with some support that will help reduce the regret that I have for choosing Spring Security. Enough ranting and now let's get to the point.
I'm trying to create an ACL by using JDBCMutableAclService.createAcl as follows:
public void addPermission(IWFArtifact securedObject, Sid recipient, Permission permission,
Class clazz) {
ObjectIdentity oid = new ObjectIdentityImpl(clazz.getCanonicalName(), securedObject.getId());
this.addPermission(oid, recipient, permission);
}
#Override
#Transactional(propagation = Propagation.REQUIRED, isolation = Isolation.READ_UNCOMMITTED, readOnly = false)
public void addPermission(ObjectIdentity oid, Sid recipient, Permission permission) {
SpringSecurityUtils.assureThreadLocalAuthSet();
MutableAcl acl;
try {
acl = this.mutableAclService.createAcl(oid);
} catch (AlreadyExistsException e) {
acl = (MutableAcl) this.mutableAclService.readAclById(oid);
}
// try {
// acl = (MutableAcl) this.mutableAclService.readAclById(oid);
// } catch (NotFoundException nfe) {
// acl = this.mutableAclService.createAcl(oid);
// }
acl.insertAce(acl.getEntries().length, permission, recipient, true);
this.mutableAclService.updateAcl(acl);
}
The call throws a NotFoundException from the line:
// Retrieve the ACL via superclass (ensures cache registration, proper retrieval etc)
Acl acl = readAclById(objectIdentity);
I believe this is caused by something related to Transactional, and that's why I have tested with many TransactionDefinition attributes. I have also doubted the annotation and tried with declarative transaction definition, but still with no luck.
One important point is that I have used the statement used to insert the oid in the database earlier in the method directly on the database and it worked, and also threw a unique constraint exception at me when it tried to insert it in the method.
I'm using Spring Security 2.0.8 and IceFaces 1.8 (which doesn't support spring 3.0 but definetely supprorts 2.0.x, specially when I keep caling SpringSecurityUtils.assureThreadLocalAuthSet()). My AppServer is Tomcat 6.0, and my DB Server is MySQL 6.0
I wish to get back a reply soon because I need to get this task off my way
Your code isn't formatted very well to understand on which line error is thrown.
Anyway a NotFoundException has to catched and create a new acl :
try {
acl = (MutableAcl) mutableAclService.readAclById(oi);
}
catch (NotFoundException e) {
acl = mutableAclService.createAcl(oi);
}

Resources