-
Suggestion
-
Resolution: Unresolved
-
None
-
29
-
12
-
Group creation with duplicate name is now prohibited in Azure AD
Issue Summary
Crowd can't sync when there are duplicate groups in AzureAD, as per mentioned in the following Documentation: Configuring Azure Active Directory - Atlassian Documentation
In Azure AD, you can have multiple groups with the same name (displayName), but it's not supported in Crowd and results in a failing synchronization. Make sure you change your Azure AD group names to unique ones.
Crowd needs to use the DN to distinguish between the groups that appear to be duplicated. Azure AD allows duplicate group names - Morgan Simonsen's Blog
Active Directory is based on LDAP and in the LDAP naming scheme an object may have the same Relative Distinguished Name (RDN), as long as the Distinguished Name (DN) is unique. In this case the RDN is the leaf name and the DN is the fully qualified name. So you can have two users named John Doe as long as they do not both reside within the same Organizational Unit (OU), or location, in the directory.
Environment
AzureAD & Crowd 3.1.2
Expected Results
synchronization succeded
Actual Results
Failure to finish sync.
The below exception is thrown in the atlassian-crowd.log file:
2018-11-15 11:04:47,308 Caesium-2-1 ERROR [hibernate.batch.hibernate5.Hibernate5BatchProcessor] Could not process class com.atlassian.crowd.model.group.InternalGroup: com.atlassian.crowd.model.group.InternalGroup@53977e56[id=2050678,name=groupOne,type=GROUP,active=true,description=description,lowerName=groupone,createdDate=Thu Nov 15 11:04:46 UTC 2018,updatedDate=Thu Nov 15 11:04:46 UTC 2018,directoryId=557057,externalId=5c432810-6d7e-497e-af41-195301e20c59] javax.persistence.PersistenceException: org.hibernate.exception.ConstraintViolationException: could not execute batch at org.hibernate.internal.ExceptionConverterImpl.convert(ExceptionConverterImpl.java:147) at org.hibernate.internal.ExceptionConverterImpl.convert(ExceptionConverterImpl.java:155) at org.hibernate.internal.ExceptionConverterImpl.convert(ExceptionConverterImpl.java:162) at org.hibernate.internal.SessionImpl.doFlush(SessionImpl.java:1441) at org.hibernate.internal.SessionImpl.flush(SessionImpl.java:1421) at com.atlassian.crowd.util.persistence.hibernate.batch.hibernate5.Hibernate5BatchProcessor.flushSession(Hibernate5BatchProcessor.java:51) at com.atlassian.crowd.util.persistence.hibernate.batch.hibernate5.AbstractHibernateBatchProcessor.commitTransaction(AbstractHibernateBatchProcessor.java:55) at com.atlassian.crowd.util.persistence.hibernate.batch.hibernate5.AbstractHibernateBatchProcessor.afterProcessIndividual(AbstractHibernateBatchProcessor.java:22) at com.atlassian.crowd.util.persistence.hibernate.batch.AbstractBatchProcessor.processIndividual(AbstractBatchProcessor.java:173) at com.atlassian.crowd.util.persistence.hibernate.batch.AbstractBatchProcessor.processBatch(AbstractBatchProcessor.java:155) at com.atlassian.crowd.util.persistence.hibernate.batch.AbstractBatchProcessor.execute(AbstractBatchProcessor.java:125) at com.atlassian.crowd.dao.group.GroupDAOHibernate.addAll(GroupDAOHibernate.java:77) at com.atlassian.crowd.directory.CachingDirectory.addAllGroups(CachingDirectory.java:122) at com.atlassian.crowd.directory.DbCachingRemoteChangeOperations.addGroups(DbCachingRemoteChangeOperations.java:482) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.lang.reflect.Method.invoke(Method.java:498) at org.springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(AopUtils.java:333) at org.springframework.aop.framework.ReflectiveMethodInvocation.invokeJoinpoint(ReflectiveMethodInvocation.java:190) at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:157) at org.springframework.transaction.interceptor.TransactionInterceptor$1.proceedWithInvocation(TransactionInterceptor.java:99) at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:282) at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:96) at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179) at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:213) at com.sun.proxy.$Proxy500.addGroups(Unknown Source) at com.atlassian.crowd.directory.DirectoryCacheImplUsingChangeOperations.addOrUpdateCachedGroups(DirectoryCacheImplUsingChangeOperations.java:84) at com.atlassian.crowd.directory.cache.DeltaQueryCacheRefresher.synchroniseAllGroups(DeltaQueryCacheRefresher.java:326) at com.atlassian.crowd.directory.cache.DeltaQueryCacheRefresher.synchroniseAll(DeltaQueryCacheRefresher.java:91) at com.atlassian.crowd.directory.DbCachingRemoteDirectory.synchroniseCache(DbCachingRemoteDirectory.java:968) at com.atlassian.crowd.manager.directory.DirectorySynchroniserImpl.synchronise(DirectorySynchroniserImpl.java:71) at sun.reflect.GeneratedMethodAccessor850.invoke(Unknown Source) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.lang.reflect.Method.invoke(Method.java:498) at org.springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(AopUtils.java:333) at org.springframework.aop.framework.ReflectiveMethodInvocation.invokeJoinpoint(ReflectiveMethodInvocation.java:190) at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:157) at org.springframework.transaction.interceptor.TransactionInterceptor$1.proceedWithInvocation(TransactionInterceptor.java:99) at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:282) at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:96) at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179) at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:213) at com.sun.proxy.$Proxy83.synchronise(Unknown Source) at com.atlassian.crowd.directory.DbCachingDirectoryPoller.pollChanges(DbCachingDirectoryPoller.java:45) at com.atlassian.crowd.manager.directory.monitor.poller.DirectoryPollerJobRunner.runJob(DirectoryPollerJobRunner.java:85) at com.atlassian.scheduler.core.JobLauncher.runJob(JobLauncher.java:153) at com.atlassian.scheduler.core.JobLauncher.launchAndBuildResponse(JobLauncher.java:118) at com.atlassian.scheduler.core.JobLauncher.launch(JobLauncher.java:97) at com.atlassian.scheduler.caesium.impl.CaesiumSchedulerService.launchJob(CaesiumSchedulerService.java:443) at com.atlassian.scheduler.caesium.impl.CaesiumSchedulerService.executeClusteredJob(CaesiumSchedulerService.java:438) at com.atlassian.scheduler.caesium.impl.CaesiumSchedulerService.executeClusteredJobWithRecoveryGuard(CaesiumSchedulerService.java:462) at com.atlassian.scheduler.caesium.impl.CaesiumSchedulerService.executeQueuedJob(CaesiumSchedulerService.java:390) at com.atlassian.scheduler.caesium.impl.CaesiumSchedulerService$1.consume(CaesiumSchedulerService.java:285) at com.atlassian.scheduler.caesium.impl.CaesiumSchedulerService$1.consume(CaesiumSchedulerService.java:282) at com.atlassian.scheduler.caesium.impl.SchedulerQueueWorker.executeJob(SchedulerQueueWorker.java:65) at com.atlassian.scheduler.caesium.impl.SchedulerQueueWorker.executeNextJob(SchedulerQueueWorker.java:59) at com.atlassian.scheduler.caesium.impl.SchedulerQueueWorker.run(SchedulerQueueWorker.java:34) at java.lang.Thread.run(Thread.java:745) Caused by: org.hibernate.exception.ConstraintViolationException: could not execute batch at org.hibernate.exception.internal.SQLStateConversionDelegate.convert(SQLStateConversionDelegate.java:112) at org.hibernate.exception.internal.StandardSQLExceptionConverter.convert(StandardSQLExceptionConverter.java:42) at org.hibernate.engine.jdbc.spi.SqlExceptionHelper.convert(SqlExceptionHelper.java:111) at org.hibernate.engine.jdbc.batch.internal.BatchingBatch.performExecution(BatchingBatch.java:121) at org.hibernate.engine.jdbc.batch.internal.BatchingBatch.doExecuteBatch(BatchingBatch.java:97) at org.hibernate.engine.jdbc.batch.internal.AbstractBatchImpl.execute(AbstractBatchImpl.java:147) at org.hibernate.engine.jdbc.internal.JdbcCoordinatorImpl.executeBatch(JdbcCoordinatorImpl.java:206) at org.hibernate.engine.spi.ActionQueue.executeActions(ActionQueue.java:618) at org.hibernate.engine.spi.ActionQueue.executeActions(ActionQueue.java:463) at org.hibernate.event.internal.AbstractFlushingEventListener.performExecutions(AbstractFlushingEventListener.java:337) at org.hibernate.event.internal.DefaultFlushEventListener.onFlush(DefaultFlushEventListener.java:39) at org.hibernate.internal.SessionImpl.doFlush(SessionImpl.java:1435) ... 55 more Caused by: java.sql.BatchUpdateException: Batch entry 0 insert into cwd_group (group_name, lower_group_name, active, is_local, created_date, updated_date, description, group_type, directory_id, external_id, id) values ('groupOne', 'groupone', 'T', 'F', '2018-11-15 11:04:46.993000+00', '2018-11-15 11:04:46.993000+00', 'description', 'GROUP', 557057, '5c432810-6d7e-497e-af41-195301e20c59', 2050678) was aborted: ERROR: duplicate key value violates unique constraint "uk_group_name_dir_id" Detail: Key (lower_group_name, directory_id)=(groupone, 557057) already exists. Location: File: nbtinsert.c, Routine: _bt_check_unique, Line: 433 Server SQLState: 23505 Call getNextException to see other errors in the batch. at org.postgresql.jdbc.BatchResultHandler.handleError(BatchResultHandler.java:148) at org.postgresql.core.v3.QueryExecutorImpl.processResults(QueryExecutorImpl.java:2190) at org.postgresql.core.v3.QueryExecutorImpl.execute(QueryExecutorImpl.java:472) at org.postgresql.jdbc.PgStatement.executeBatch(PgStatement.java:791) at org.postgresql.jdbc.PgPreparedStatement.executeBatch(PgPreparedStatement.java:1547) at com.mchange.v2.c3p0.impl.NewProxyPreparedStatement.executeBatch(NewProxyPreparedStatement.java:1135) at org.hibernate.engine.jdbc.batch.internal.BatchingBatch.performExecution(BatchingBatch.java:111) ... 63 more
Workaround
- Customers have to purchase additional Add-Ons/App to sync AzureAD, specifically Office 365 Directory Connector for Crowd (ODCC) Cleito, to get AzureAD to sync properly.
- Ensure that there are no duplicated groups in Azure AD side
- Find the duplicated groups from the error logs (From the above example, it's groupOne) and rename it to something unique
Suggestion:
Duplicated Azure AD groups should be imported with a suffix generated from another property of the AD object.