I was following the samples of Spring LDAP project and was trying to convert xml
configuration to Java Configuration
.
I am trying to do CRUD operations on the LDAP server.
I was able to figure out the following,
This is the xml configuration for the application, which I am hoping to convert into Java Config.
<context:property-placeholder location="classpath:/ldap.properties"
system-properties-mode="OVERRIDE" />
<context:annotation-config />
<ldap:context-source id="contextSource" password="${sample.ldap.password}"
url="${sample.ldap.url}" username="${sample.ldap.userDn}" base="${sample.ldap.base}" />
<ldap:ldap-template id="ldapTemplate"
context-source-ref="contextSource" />
<!-- This will scan the org.springframework.ldap.samples.useradmin.domain
package for interfaces extending CrudRepository (in our case, LdapRepository),
automatically creating repository beans based on these interfaces. -->
<ldap:repositories base-package="com.cazysystems.appstore.model" />
<!-- This one will never be referenced directly, but the ldap:repositories
tag will make sure it will be 'wired in', because the GroupRepo interface
extends from an interface that GroupRepoImpl imlements. -->
<bean class="com.cazysystems.appstore.model.impli.GroupRepoImpl" />
<bean class="com.cazysystems.appstore.model.impli.DepartmentRepoImpl" />
<bean class="com.cazysystems.appstore.service.UserService">
<property name="directoryType" value="${sample.ldap.directory.type}" />
</bean>
So I have the following classes that are supposed to do the migration,
@Configuration
@EnableLdapRepositories("com.cazysystems.appstore.model")
public class LdapConfiguration {
@Autowired
Environment env;
@Bean
public LdapContextSource contextSource() {
LdapContextSource contextSource = new LdapContextSource();
contextSource.setUrl(env.getRequiredProperty("sample.ldap.url"));
contextSource.setBase(env.getRequiredProperty("sample.ldap.base"));
contextSource.setUserDn(env.getRequiredProperty("sample.ldap.userDn"));
contextSource.setPassword(env
.getRequiredProperty("sample.ldap.password"));
return contextSource;
}
@Bean
public LdapTemplate ldapTemplate() {
return new LdapTemplate(contextSource());
}
}
and the
@Configuration
public class AuthenticationConfiguration extends
GlobalAuthenticationConfigurerAdapter {
@Autowired
Environment env;
@Override
public void init(AuthenticationManagerBuilder auth) throws Exception {
auth.ldapAuthentication()
// .userDetailsContextMapper(userDetailsContextMapper())
.userDnPatterns(
env.getRequiredProperty("ldap.user_dn_patterns"))
.groupSearchBase(
env.getRequiredProperty("ldap.group_search_base"))
.contextSource().ldif("classpath:setup_data.ldif")
.url(env.getRequiredProperty("sample.ldap.url"))
.managerDn("sample.ldap.userDn")
.managerPassword("sample.ldap.password").port(10389);
}
}
But when I run the application,
I get the following error,
Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'groupRepo': Invocation of init method failed; nested exception is java.lang.IllegalArgumentException: Not an managed type: class com.cazysystems.appstore.model.Group
But under the com.cazysystems.appstore.model
package,
I have,
public interface GroupRepo extends LdapRepository<Group>, GroupRepoExtension {
public final static String USER_GROUP = "ROLE_USER";
Group findByName(String groupName);
@Query("(member={0})")
Collection<Group> findByMember(Name member);
}
and
@Entry(objectClasses = { "groupOfNames", "top" }, base = "ou=Groups")
public final class Group {
@Id
private Name id;
@Attribute(name = "cn")
@DnAttribute(value = "cn", index = 1)
private String name;
@Attribute(name = "description")
private String description;
@Attribute(name = "member")
private Set<Name> members = new HashSet<Name>();
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
public Set<Name> getMembers() {
return members;
}
public void addMember(Name newMember) {
members.add(newMember);
}
public void removeMember(Name member) {
members.remove(member);
}
public Name getId() {
return id;
}
public void setId(Name id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
But this annotation is supposed to do the trick, But it is not working,
@EnableLdapRepositories("com.cazysystems.appstore.model")
I am using the following dependencies in my pom.
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-ldap</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.ldap</groupId>
<artifactId>spring-ldap-core</artifactId>
<version>2.0.3.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.ldap</groupId>
<artifactId>spring-ldap-core-tiger</artifactId>
<version>2.0.3.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-commons</artifactId>
</dependency>
I am not sure what I am doing wrong, because the documentation on this are very hard to find. Please help if you know this stuff.
EDIT:
Here is my Controller class,
@Controller
public class GroupController {
@Autowired
private GroupRepo groupRepo;
@Autowired
private UserService userService;
@RequestMapping(value = "/groups", method = GET)
public String listGroups(ModelMap map) {
map.put("groups", groupRepo.getAllGroupNames());
return "listGroups";
}
@RequestMapping(value = "/newGroup", method = GET)
public String initNewGroup() {
return "newGroup";
}
@RequestMapping(value = "/groups", method = POST)
public String newGroup(Group group) {
groupRepo.create(group);
return "redirect:groups/" + group.getName();
}
@RequestMapping(value = "/groups/{name}", method = GET)
public String editGroup(@PathVariable String name, ModelMap map) {
Group foundGroup = groupRepo.findByName(name);
map.put("group", foundGroup);
final Set<User> groupMembers = userService.findAllMembers(foundGroup.getMembers());
map.put("members", groupMembers);
Iterable<User> otherUsers = Iterables.filter(userService.findAll(), new Predicate<User>() {
@Override
public boolean apply(User user) {
return !groupMembers.contains(user);
}
});
map.put("nonMembers", Lists.newLinkedList(otherUsers));
return "editGroup";
}
@RequestMapping(value = "/groups/{name}/members", method = POST)
public String addUserToGroup(@PathVariable String name, @RequestParam String userId) {
Group group = groupRepo.findByName(name);
group.addMember(userService.toAbsoluteDn(LdapUtils.newLdapName(userId)));
groupRepo.save(group);
return "redirect:/groups/" + name;
}
@RequestMapping(value = "/groups/{name}/members", method = DELETE)
public String removeUserFromGroup(@PathVariable String name, @RequestParam String userId) {
Group group = groupRepo.findByName(name);
group.removeMember(userService.toAbsoluteDn(LdapUtils.newLdapName(userId)));
groupRepo.save(group);
return "redirect:/groups/" + name;
}
}
EDIT:
The following is the complete stack trace,
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'groupController': Injection of autowired dependencies failed; nested exception is org.springframework.beans.factory.BeanCreationException: Could not autowire field: private com.cazysystems.appstore.domain.GroupRepo com.eazysystems.appstore.controller.GroupController.groupRepo; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'groupRepo': Invocation of init method failed; nested exception is java.lang.IllegalArgumentException: Not an managed type: class com.cazysystems.appstore.domain.Group
at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.postProcessPropertyValues(AutowiredAnnotationBeanPostProcessor.java:334)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.populateBean(AbstractAutowireCapableBeanFactory.java:1210)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:537)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:476)
at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:303)
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:230)
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:299)
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:194)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:755)
at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:757)
at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:480)
at org.springframework.boot.context.embedded.EmbeddedWebApplicationContext.refresh(EmbeddedWebApplicationContext.java:118)
at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:686)
at org.springframework.boot.SpringApplication.run(SpringApplication.java:320)
at org.springframework.boot.SpringApplication.run(SpringApplication.java:957)
at org.springframework.boot.SpringApplication.run(SpringApplication.java:946)
at com.eazysystems.appstore.Application.main(Application.java:20)
Caused by: org.springframework.beans.factory.BeanCreationException: Could not autowire field: private com.cazysystems.appstore.domain.GroupRepo com.eazysystems.appstore.controller.GroupController.groupRepo; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'groupRepo': Invocation of init method failed; nested exception is java.lang.IllegalArgumentException: Not an managed type: class com.eazysystems.appstore.domain.Group
at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject(AutowiredAnnotationBeanPostProcessor.java:561)
at org.springframework.beans.factory.annotation.InjectionMetadata.inject(InjectionMetadata.java:88)
at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.postProcessPropertyValues(AutowiredAnnotationBeanPostProcessor.java:331)
... 16 common frames omitted
Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'groupRepo': Invocation of init method failed; nested exception is java.lang.IllegalArgumentException: Not an managed type: class com.eazysystems.appstore.domain.Group
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1574)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:539)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:476)
at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:303)
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:230)
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:299)
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:194)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.findAutowireCandidates(DefaultListableBeanFactory.java:1120)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1044)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:942)
at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject(AutowiredAnnotationBeanPostProcessor.java:533)
... 18 common frames omitted
Caused by: java.lang.IllegalArgumentException: Not an managed type: class com.cazysystems.appstore.domain.Group
at org.hibernate.jpa.internal.metamodel.MetamodelImpl.managedType(MetamodelImpl.java:219)
at org.springframework.data.jpa.repository.support.JpaMetamodelEntityInformation.<init>(JpaMetamodelEntityInformation.java:68)
at org.springframework.data.jpa.repository.support.JpaEntityInformationSupport.getMetadata(JpaEntityInformationSupport.java:67)
at org.springframework.data.jpa.repository.support.JpaRepositoryFactory.getEntityInformation(JpaRepositoryFactory.java:145)
at org.springframework.data.jpa.repository.support.JpaRepositoryFactory.getTargetRepository(JpaRepositoryFactory.java:89)
at org.springframework.data.jpa.repository.support.JpaRepositoryFactory.getTargetRepository(JpaRepositoryFactory.java:69)
at org.springframework.data.repository.core.support.RepositoryFactorySupport.getRepository(RepositoryFactorySupport.java:173)
at org.springframework.data.repository.core.support.RepositoryFactoryBeanSupport.initAndReturn(RepositoryFactoryBeanSupport.java:239)
at org.springframework.data.repository.core.support.RepositoryFactoryBeanSupport.afterPropertiesSet(RepositoryFactoryBeanSupport.java:225)
at org.springframework.data.jpa.repository.support.JpaRepositoryFactoryBean.afterPropertiesSet(JpaRepositoryFactoryBean.java:92)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.invokeInitMethods(AbstractAutowireCapableBeanFactory.java:1633)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1570)
... 28 common frames omitted
My UserService class as following,
@Component
public class UserService implements BaseLdapNameAware {
@Autowired
private UserRepo userRepo;
@Autowired
private GroupRepo groupRepo;
private LdapName baseLdapPath;
@Autowired
@Value("${sample.ldap.directory.type}")
private DirectoryType directoryType;
/*
* @Autowired public UserService(UserRepo userRepo, GroupRepo groupRepo) {
* this.userRepo = userRepo; this.groupRepo = groupRepo; }
*/
public Group getUserGroup() {
return groupRepo.findByName(GroupRepo.USER_GROUP);
}
public void setDirectoryType(DirectoryType directoryType) {
this.directoryType = directoryType;
}
@Override
public void setBaseLdapPath(LdapName baseLdapPath) {
this.baseLdapPath = baseLdapPath;
}
public Iterable<User> findAll() {
return userRepo.findAll();
}
public User findUser(String userId) {
return userRepo.findOne(LdapUtils.newLdapName(userId));
}
public User createUser(User user) {
User savedUser = userRepo.save(user);
Group userGroup = getUserGroup();
// The DN the member attribute must be absolute
userGroup.addMember(toAbsoluteDn(savedUser.getId()));
groupRepo.save(userGroup);
return savedUser;
}
public LdapName toAbsoluteDn(Name relativeName) {
return LdapNameBuilder.newInstance(baseLdapPath).add(relativeName)
.build();
}
/**
* This method expects absolute DNs of group members. In order to find the
* actual users the DNs need to have the base LDAP path removed.
*
* @param absoluteIds
* @return
*/
public Set<User> findAllMembers(Iterable<Name> absoluteIds) {
return Sets.newLinkedHashSet(userRepo
.findAll(toRelativeIds(absoluteIds)));
}
public Iterable<Name> toRelativeIds(Iterable<Name> absoluteIds) {
return Iterables.transform(absoluteIds, new Function<Name, Name>() {
@Override
public Name apply(Name input) {
return LdapUtils.removeFirst(input, baseLdapPath);
}
});
}
public User updateUser(String userId, User user) {
LdapName originalId = LdapUtils.newLdapName(userId);
User existingUser = userRepo.findOne(originalId);
existingUser.setFirstName(user.getFirstName());
existingUser.setLastName(user.getLastName());
existingUser.setFullName(user.getFullName());
existingUser.setEmail(user.getEmail());
existingUser.setPhone(user.getPhone());
existingUser.setTitle(user.getTitle());
existingUser.setDepartment(user.getDepartment());
existingUser.setUnit(user.getUnit());
if (directoryType == DirectoryType.AD) {
return updateUserAd(originalId, existingUser);
} else {
return updateUserStandard(originalId, existingUser);
}
}
/**
* Update the user and - if its id changed - update all group references to
* the user.
*
* @param originalId
* the original id of the user.
* @param existingUser
* the user, populated with new data
*
* @return the updated entry
*/
private User updateUserStandard(LdapName originalId, User existingUser) {
User savedUser = userRepo.save(existingUser);
if (!originalId.equals(savedUser.getId())) {
// The user has moved - we need to update group references.
LdapName oldMemberDn = toAbsoluteDn(originalId);
LdapName newMemberDn = toAbsoluteDn(savedUser.getId());
Collection<Group> groups = groupRepo.findByMember(oldMemberDn);
updateGroupReferences(groups, oldMemberDn, newMemberDn);
}
return savedUser;
}
/**
* Special behaviour in AD forces us to get the group membership before the
* user is updated, because AD clears group membership for removed entries,
* which means that once the user is update we've lost track of which groups
* the user was originally member of, preventing us to update the membership
* references so that they point to the new DN of the user.
*
* This is slightly less efficient, since we need to get the group
* membership for all updates even though the user may not have been moved.
* Using our knowledge of which attributes are part of the distinguished
* name we can do this more efficiently if we are implementing specifically
* for Active Directory - this approach is just to highlight this quite
* significant difference.
*
* @param originalId
* the original id of the user.
* @param existingUser
* the user, populated with new data
*
* @return the updated entry
*/
private User updateUserAd(LdapName originalId, User existingUser) {
LdapName oldMemberDn = toAbsoluteDn(originalId);
Collection<Group> groups = groupRepo.findByMember(oldMemberDn);
User savedUser = userRepo.save(existingUser);
LdapName newMemberDn = toAbsoluteDn(savedUser.getId());
if (!originalId.equals(savedUser.getId())) {
// The user has moved - we need to update group references.
updateGroupReferences(groups, oldMemberDn, newMemberDn);
}
return savedUser;
}
private void updateGroupReferences(Collection<Group> groups,
Name originalId, Name newId) {
for (Group group : groups) {
group.removeMember(originalId);
group.addMember(newId);
groupRepo.save(group);
}
}
public List<User> searchByNameName(String lastName) {
return userRepo.findByFullNameContains(lastName);
}
}
During the trouble shooting,
I noticed that, the application fails only when I used both JPA and LDAP together. When I removed all the JPA dependencies and implementations, the application works as expected.
The issue seems to be caused by the conflict between JPA and LDAP dependencies. But I am not sure how to resolve it.
You are not stating what you want to achive, create a user, read user, seems to me you are mixing stuff from spring-data with ldap. Ill paste my configuration maybe it helps, if it does i can show you how im adding users with this configurations, again im not sure what you want to achieve, hope it helps
@Configuration
public class LdapManagerConfiguration {
@Autowired
Environment ldapProperties;
@Bean
public LdapContextSource contextSourceTarget() {
LdapContextSource ldapContextSource = new LdapContextSource();
ldapContextSource.setUrl(ldapProperties.getProperty("auth.ldap.url"));
ldapContextSource.setBase(ldapProperties.getProperty("auth.ldap.base"));
ldapContextSource.setUserDn(ldapProperties
.getProperty("auth.ldap.userdn"));
ldapContextSource.setPassword(ldapProperties
.getProperty("auth.ldap.password"));
return ldapContextSource;
}
@Bean
public LdapTemplate ldapTemplate() {
return new LdapTemplate(contextSourceTarget());
}
@Bean
public InetOrgPersonContextMapper inetOrgPersonContextMapper() {
return new InetOrgPersonContextMapper();
}
@Bean
public DefaultLdapUsernameToDnMapper defaultLdapUsernameToDnMapper() {
return new DefaultLdapUsernameToDnMapper(
ldapProperties.getProperty("auth.ldap.groupbase"),
ldapProperties.getProperty("auth.ldap.attributename.username"));// "uid"
}
@Bean
public LdapUserDetailsManager ldapUserDetailManager() {
LdapUserDetailsManager userManager = new LdapUserDetailsManager(
contextSourceTarget());
userManager.setGroupSearchBase(ldapProperties
.getProperty("auth.ldap.groupbase"));
userManager.setUserDetailsMapper(inetOrgPersonContextMapper());
userManager.setUsernameMapper(defaultLdapUsernameToDnMapper());
userManager.setGroupRoleAttributeName(ldapProperties
.getProperty("auth.ldap.attributename.grouprole"));
userManager.setGroupMemberAttributeName(ldapProperties
.getProperty("auth.ldap.attributename.groupmemeber"));
return userManager;
}
}
to add a user then
Logger logger = LoggerFactory.getLogger(LdapUserManagerImpl.class);
@Autowired
LdapTemplate ldapTemplate;
@Autowired
Environment ldapProperties;
final String passwordAttribute = "userPassword";
@Override
public boolean createUser(final String user,
final String defaultPasswordPolicy)
throws UserAlreadyExistsAsInactive {
boolean created = false;
String inactivePeople = ldapProperties.getProperty(
"auth.ldap.inactive.groupbase").replace("ou=", "");
String activePeople = ldapProperties.getProperty("auth.ldap.groupbase")
.replace("ou=", "");
if (existsUser(user, inactivePeople)) {
logger.error("User " + user
+ " already exists in ldap but it's incative");
throw new UserAlreadyExistsAsInactive(user);
}
if (existsUser(user, activePeople)) {
logger.error("User " + user
+ " already exists in ldap and is Active");
return false;
}
try {
ldapTemplate.bind(buildDn(user, inactivePeople), null,
createUserDetails(user, defaultPasswordPolicy));
created = true;
} catch (Exception e) {
logger.error(
"Unexpected exception when trying to create user in LdapException",
e);
return created;
}
return created;
}
So That Will Be the create
This would be de read
public UserI findUser(String userName, String group) {
try {
String dn = buildDN(userName, group);
User user = ldapTemplate.lookup(dn, new UserAttributesMapper());
return user;
} catch (NameNotFoundException e) {
return new UserNullObject();
}
}
This the update
@Override
public void changeUserGroup(String userName, String oldGroup,
String newGroup) throws ReLocoException {
UserI user = findUser(userName, oldGroup);
if (!user.isEmpty()) {
try {
Object password = getLdapPassword(userName, oldGroup);
ldapTemplate.unbind(buildDN(userName, oldGroup));
String defaultPasswordPolicy = ldapProperties
.getProperty("auth.ldap.default.userpollicy");
ldapTemplate.bind(buildDN(userName, newGroup), null,
createUserDetails(userName, defaultPasswordPolicy));
applyPasswordToUser(userName, password, newGroup);
} catch (Exception e) {
logger.error("Error on change user from incative to active people group "
+ e);
throw new ConfirmAccountValidationCodeException(userName);
}
} else {
throw new UserNotFoundException(
"The username provided for the given group '" + oldGroup
+ "' does not exist");
}
}