Uploaded image for project: 'Crowd Data Center'
  1. Crowd Data Center
  2. CWD-2478

LDAP Delegate: Synchronize with AD to disable deleted users

    • Our product teams collect and evaluate feedback from a number of different sources. To learn more about how we use customer feedback in the planning process, check out our new feature policy.

       

      Atlassian Update - 21 February 2018

      Hi everyone,

      Thanks for all your votes and comments on this suggestion. Your voice means a lot to us.

      We're excited to announce that we've shipped an experimental plugin (Atlassian Labs) for Crowd that is able to do delegated directory user pruning.

      This plugin allows to optimize your license usage by periodically cleaning inactive users from your delegated directories. After configuring pruning for a delegated directory the plugin will periodically check if the directory contains any users who have been deactivated or removed from the remote directory.

      By default such users will be deactivated in Crowd. It is also possible to enable "hard delete" mode, in which users who have been deleted in the remote directory will also be deleted in Crowd.

      This plugin can be downloaded from Atlassian Marketplace here.
       

      Thanks,

      Atlassian Crowd Team

        

      When a user is being deleted in the AD, it will not be able to login to Crowd's Applications anymore.
      But as the user still exisits in the LDAP Delegate, it will count against the license limits (of Crowd, Confluence, JIRA, whatever).
      Support (CWDSUP-4973) told me to deactivate the user in Crowd manually in such cases.
      This leads to a double maintenance (delete in AD, deactivate in Crowd). In addition the AD admins might not be Crowd admins.

      Crowd could regularly (once per day?) check if the users in their Delegate Directories are still present/active in the AD and then deactivate them if they are not.

        1. image-2017-10-17-15-55-45-636.png
          160 kB
        2. image-2017-10-17-16-09-03-898.png
          45 kB
        3. image-2017-10-24-11-51-30-128.png
          65 kB
        4. image-2017-10-24-11-53-04-065.png
          68 kB
        5. image-2017-10-24-11-55-35-379.png
          74 kB
        6. image-2018-04-02-23-49-21-541.png
          image-2018-04-02-23-49-21-541.png
          193 kB
        7. image-2018-04-02-23-51-01-740.png
          image-2018-04-02-23-51-01-740.png
          233 kB
        8. image-2018-04-03-00-13-56-512.png
          image-2018-04-03-00-13-56-512.png
          241 kB
        9. screenshot-1.png
          screenshot-1.png
          241 kB

            [CWD-2478] LDAP Delegate: Synchronize with AD to disable deleted users

            Hi ttbell,

            I am glad that you've get the plugin working!

            Indeed this could be an enhancement in the plugin itself and probably will be fixed in future releases of the plugin. We appreciate your feedback!

             

            Best Regards,

            Marcin Kempa

            Marcin Kempa added a comment - Hi ttbell , I am glad that you've get the plugin working! Indeed this could be an enhancement in the plugin itself and probably will be fixed in future releases of the plugin. We appreciate your feedback!   Best Regards, Marcin Kempa

            Tom Bell added a comment -

            Hi Marcin,

            That worked!!  The plugin ran again at the expected time without any errors and marked the missing LDAP users as inactive.

            I ran the following query against another delegate directory it appears that (in our Crowd instance at least) userids with null "external_id" values have never logged in.  Seems like these are good candidates for removal or update the null field with something else as a placeholder but want to verify that's true first.

            mysql> SELECT cwd_user.user_name, from_unixtime(cwd_user_attribute.attribute_value/1000) FROM cwd_user, cwd_user_attribute WHERE cwd_user_attribute.user_id = cwd_user.id AND cwd_user_attribute.attribute_name = 'lastAuthenticated' and cwd_user.external_id is null and cwd_user.directory_id=32770;
            Empty set (0.00 sec) 

            We can clean up the mess on our Crowd instance but it may be a useful enhancement if the pruning plugin would ignore or mark userids with null external_id entries as inactive within the delegate directory. Just a thought.

            Thanks for your help!

            Tom

            Tom Bell added a comment - Hi Marcin, That worked!!  The plugin ran again at the expected time without any errors and marked the missing LDAP users as inactive. I ran the following query against another delegate directory it appears that (in our Crowd instance at least) userids with null "external_id" values have never logged in.  Seems like these are good candidates for removal or update the null field with something else as a placeholder but want to verify that's true first. mysql> SELECT cwd_user.user_name, from_unixtime(cwd_user_attribute.attribute_value/1000) FROM cwd_user, cwd_user_attribute WHERE cwd_user_attribute.user_id = cwd_user.id AND cwd_user_attribute.attribute_name = 'lastAuthenticated' and cwd_user.external_id is null and cwd_user.directory_id=32770; Empty set (0.00 sec) We can clean up the mess on our Crowd instance but it may be a useful enhancement if the pruning plugin would ignore or mark userids with null external_id entries as inactive within the delegate directory. Just a thought. Thanks for your help! Tom

            Tom Bell added a comment -

            Yes, that is certainly very possible.  I do recall my team performing some crowd user migration testing a year or two ago but don't remember the details.  So, it's also possible that we will need to perform a similar cleanup or update on our production host.  That'll need further investigation.

            Tom Bell added a comment - Yes, that is certainly very possible.  I do recall my team performing some crowd user migration testing a year or two ago but don't remember the details.  So, it's also possible that we will need to perform a similar cleanup or update on our production host.  That'll need further investigation.

            Regarding the users without the External Id, that you've listed from you delegated directory, I suspect that those could have beed initially added to the Internal Directory (the internalTestUser may suggest that) and later on this directory was changed to a delegated one. Did you by any chance followed this KB article some time in the past?

            Marcin Kempa added a comment - Regarding the users without the External Id, that you've listed from you delegated directory, I suspect that those could have beed initially added to the Internal Directory (the internalTestUser may suggest that) and later on this directory was changed to a delegated one. Did you by any chance followed this KB article some time in the past?

            Tom Bell added a comment -

            Yes, we're testing on a stage system so will clean up those accounts. Nothing posted so far is related to any IP but thanks for the caution.  I'll let you know the results.  Thanks!

            Tom Bell added a comment - Yes, we're testing on a stage system so will clean up those accounts. Nothing posted so far is related to any IP but thanks for the caution.  I'll let you know the results.  Thanks!

            Sorry ttbell my bad, I just assumed that you have only one directory. Limiting the query is a good idea

            If you are running those tests on some sort of staging environment (not a production one) and you are able to clean up those i believe you can try that for a test. I wonder if those users were at some point in the remote directory?

             

            Please note that this communication is public and everyone can see those comments so please do not post here sensitive data.

             

            Best Regards,

            Marcin Kempa

            Marcin Kempa added a comment - Sorry ttbell my bad, I just assumed that you have only one directory. Limiting the query is a good idea If you are running those tests on some sort of staging environment (not a production one) and you are able to clean up those i believe you can try that for a test. I wonder if those users were at some point in the remote directory?   Please note that this communication is public and everyone can see those comments so please do not post here sensitive data.   Best Regards, Marcin Kempa

            Tom Bell added a comment -

            Marcin, are you thinking that these users that are missing an external_id are some form of corruption that is causing the directory cleanup task to crash due to the null value?  I can probably clean those up as a test.

            Tom Bell added a comment - Marcin, are you thinking that these users that are missing an external_id are some form of corruption that is causing the directory cleanup task to crash due to the null value?  I can probably clean those up as a test.

            Tom Bell added a comment -

            Limiting the user search to just the "NF Delegate" directory returns the following...

            mysql> select user_name from cwd_user where external_id = '' or external_id is null and directory_id=88702977;
            +------------------+
            | user_name        |
            +------------------+
            | mb186040         |
            | lh180000         |
            | rl186026         |
            | gd186002         |
            | jv186016         |
            | mm121743         |
            | jg121176         |
            | rb186043         |
            | sp186073         |
            | jt186018         |
            | VM255012         |
            | AT186000         |
            | MM186051         |
            | cb120469         |
            | EY120856         |
            | EA120793         |
            | JS132599         |
            | MR121771         |
            | SJ100005         |
            | DP180003         |
            | internalTestUser |
            +------------------+
            21 rows in set (0.06 sec)
             

            Tom Bell added a comment - Limiting the user search to just the "NF Delegate" directory returns the following... mysql> select user_name from cwd_user where external_id = '' or external_id is null and directory_id=88702977; +------------------+ | user_name | +------------------+ | mb186040 | | lh180000 | | rl186026 | | gd186002 | | jv186016 | | mm121743 | | jg121176 | | rb186043 | | sp186073 | | jt186018 | | VM255012 | | AT186000 | | MM186051 | | cb120469 | | EY120856 | | EA120793 | | JS132599 | | MR121771 | | SJ100005 | | DP180003 | | internalTestUser | +------------------+ 21 rows in set (0.06 sec)

            Tom Bell added a comment -

            Hi Marcin,

            That query returns 22,032 rows.  We have multiple directories though and we're only concerned about one named "NF Delegate".  Perhaps limit the query to just that directory?

            We're running Crowd v3.1.3.  

            Thanks,

            Tom

            Tom Bell added a comment - Hi Marcin, That query returns 22,032 rows.  We have multiple directories though and we're only concerned about one named "NF Delegate".  Perhaps limit the query to just that directory? We're running Crowd v3.1.3.   Thanks, Tom

            Hi ttbell,

            It looks like some of your users in the delegated directory, where you enabled pruning, do not have external Id set.

            Can you tell me which version of Crowd are you running? Do you have any integration that is using this directory?

            The following DB query will help you to identify users without external Id:

            select user_name from cwd_user where external_id = '' or external_id is null 

            is this only a small subset of users or are those all users from your directory?

             

            Best Regards,

            Marcin Kempa

            Marcin Kempa added a comment - Hi ttbell , It looks like some of your users in the delegated directory, where you enabled pruning, do not have external Id set. Can you tell me which version of Crowd are you running? Do you have any integration that is using this directory? The following DB query will help you to identify users without external Id: select user_name from cwd_user where external_id = '' or external_id is null is this only a small subset of users or are those all users from your directory?   Best Regards, Marcin Kempa

            Tom Bell added a comment -

            Thanks Marcin!  Appreciate the info about adjusting the schedule.  We're able to see the cleanup job running but failing every hour in our 3.1.3 instance.  Have you seen a similar error previously?

            2018-04-02 15:23:00,009 Caesium-2-2 INFO [directory.pruning.jobs.DelegatedDirectoryPruningJob] Pruning users for directory 'NF Delegate'
            2018-04-02 15:23:00,010 Caesium-2-2 INFO [directory.pruning.jobs.DelegatedDirectoryPruningJob] User deletion while pruning is disabled for directory '{}', users not present in remote directory will be deactivated
            2018-04-02 15:23:00,043 Caesium-2-2 ERROR [atlassian.scheduler.core.JobLauncher] Scheduled job with ID 'delegatedDirCleanupJob' failed
            java.lang.NullPointerException
                    at com.atlassian.crowd.directory.ldap.util.GuidHelper.encodeGUIDForSearch(GuidHelper.java:54)
                    at com.atlassian.crowd.search.ldap.filter.EqualsExternalIdFilter.encodeValue(EqualsExternalIdFilter.java:25)
                    at org.springframework.ldap.filter.CompareFilter.<init>(CompareFilter.java:36)
                    at org.springframework.ldap.filter.EqualsFilter.<init>(EqualsFilter.java:40)
                    at com.atlassian.crowd.search.ldap.filter.EqualsExternalIdFilter.<init>(EqualsExternalIdFilter.java:13)
                    at com.atlassian.crowd.search.ldap.ActiveDirectoryQueryTranslaterImpl.getStringTermEqualityFilter(ActiveDirectoryQueryTranslaterImpl.java:66)
                    at com.atlassian.crowd.search.ldap.LDAPQueryTranslaterImpl.stringTermRestrictionAsFilter(LDAPQueryTranslaterImpl.java:158)
                    at com.atlassian.crowd.search.ldap.LDAPQueryTranslaterImpl.searchRestrictionAsFilter(LDAPQueryTranslaterImpl.java:60)
                    at com.atlassian.crowd.search.ldap.LDAPQueryTranslaterImpl.multiTermRestrictionAsFilter(LDAPQueryTranslaterImpl.java:78)
                    at com.atlassian.crowd.search.ldap.LDAPQueryTranslaterImpl.searchRestrictionAsFilter(LDAPQueryTranslaterImpl.java:68)
                    at com.atlassian.crowd.search.ldap.LDAPQueryTranslaterImpl.asLDAPFilter(LDAPQueryTranslaterImpl.java:40)
                    at com.atlassian.crowd.directory.SpringLDAPConnector.searchUserObjects(SpringLDAPConnector.java:623)
                    at com.atlassian.crowd.directory.SpringLDAPConnector.searchUsers(SpringLDAPConnector.java:974)
                    at com.atlassian.labs.crowd.directory.pruning.jobs.DelegatedDirectoryPruningJob.loadUsersByExternalIds(DelegatedDirectoryPruningJob.java:202)
                    at com.atlassian.labs.crowd.directory.pruning.jobs.DelegatedDirectoryPruningJob.processBatch(DelegatedDirectoryPruningJob.java:180)
                    at com.atlassian.labs.crowd.directory.pruning.jobs.DelegatedDirectoryPruningJob.deleteUsersNotInRemoteDirectory(DelegatedDirectoryPruningJob.java:136)
                    at java.util.ArrayList.forEach(ArrayList.java:1249)
                    at com.atlassian.labs.crowd.directory.pruning.jobs.DelegatedDirectoryPruningJob.lambda$runJob$0(DelegatedDirectoryPruningJob.java:110)
                    at com.atlassian.sal.core.transaction.HostContextTransactionTemplate$1.doInTransaction(HostContextTransactionTemplate.java:25)
                    at com.atlassian.sal.spring.component.SpringHostContextAccessor$1.doInTransaction(SpringHostContextAccessor.java:88)
                    at org.springframework.transaction.support.TransactionTemplate.execute(TransactionTemplate.java:133)
                    at com.atlassian.sal.spring.component.SpringHostContextAccessor.doInTransaction(SpringHostContextAccessor.java:82)
                    at sun.reflect.GeneratedMethodAccessor888.invoke(Unknown Source)
                    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
                    at java.lang.reflect.Method.invoke(Method.java:498)
                    at com.atlassian.plugin.util.ContextClassLoaderSettingInvocationHandler.invoke(ContextClassLoaderSettingInvocationHandler.java:26)
                    at com.sun.proxy.$Proxy146.doInTransaction(Unknown Source)
                    at sun.reflect.GeneratedMethodAccessor888.invoke(Unknown Source)
                    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
                    at java.lang.reflect.Method.invoke(Method.java:498)
                    at com.atlassian.plugin.osgi.bridge.external.HostComponentFactoryBean$DynamicServiceInvocationHandler.invoke(HostComponentFactoryBean.java:136)
                    at com.sun.proxy.$Proxy146.doInTransaction(Unknown Source)
                    at com.atlassian.sal.core.transaction.HostContextTransactionTemplate.execute(HostContextTransactionTemplate.java:21)
                    at sun.reflect.GeneratedMethodAccessor920.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:302)
                    at org.eclipse.gemini.blueprint.service.importer.support.internal.aop.ServiceInvoker.doInvoke(ServiceInvoker.java:56)
                    at org.eclipse.gemini.blueprint.service.importer.support.internal.aop.ServiceInvoker.invoke(ServiceInvoker.java:60)
                    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
                    at org.springframework.aop.support.DelegatingIntroductionInterceptor.doProceed(DelegatingIntroductionInterceptor.java:133)
                    at org.springframework.aop.support.DelegatingIntroductionInterceptor.invoke(DelegatingIntroductionInterceptor.java:121)
                    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
                    at org.eclipse.gemini.blueprint.service.util.internal.aop.ServiceTCCLInterceptor.invokeUnprivileged(ServiceTCCLInterceptor.java:70)
                    at org.eclipse.gemini.blueprint.service.util.internal.aop.ServiceTCCLInterceptor.invoke(ServiceTCCLInterceptor.java:53)
                    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
                    at org.eclipse.gemini.blueprint.service.importer.support.LocalBundleContextAdvice.invoke(LocalBundleContextAdvice.java:57)
                    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
                    at org.springframework.aop.support.DelegatingIntroductionInterceptor.doProceed(DelegatingIntroductionInterceptor.java:133)
                    at org.springframework.aop.support.DelegatingIntroductionInterceptor.invoke(DelegatingIntroductionInterceptor.java:121)
                    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
                    at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:208)
                    at com.sun.proxy.$Proxy335.execute(Unknown Source)
                    at com.atlassian.labs.crowd.directory.pruning.jobs.DelegatedDirectoryPruningJob.runJob(DelegatedDirectoryPruningJob.java:108)
                    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)
             

            Tom Bell added a comment - Thanks Marcin!  Appreciate the info about adjusting the schedule.  We're able to see the cleanup job running but failing every hour in our 3.1.3 instance.  Have you seen a similar error previously? 2018-04-02 15:23:00,009 Caesium-2-2 INFO [directory.pruning.jobs.DelegatedDirectoryPruningJob] Pruning users for directory 'NF Delegate' 2018-04-02 15:23:00,010 Caesium-2-2 INFO [directory.pruning.jobs.DelegatedDirectoryPruningJob] User deletion while pruning is disabled for directory '{}' , users not present in remote directory will be deactivated 2018-04-02 15:23:00,043 Caesium-2-2 ERROR [atlassian.scheduler.core.JobLauncher] Scheduled job with ID 'delegatedDirCleanupJob' failed java.lang.NullPointerException at com.atlassian.crowd.directory.ldap.util.GuidHelper.encodeGUIDForSearch(GuidHelper.java:54) at com.atlassian.crowd.search.ldap.filter.EqualsExternalIdFilter.encodeValue(EqualsExternalIdFilter.java:25) at org.springframework.ldap.filter.CompareFilter.<init>(CompareFilter.java:36) at org.springframework.ldap.filter.EqualsFilter.<init>(EqualsFilter.java:40) at com.atlassian.crowd.search.ldap.filter.EqualsExternalIdFilter.<init>(EqualsExternalIdFilter.java:13) at com.atlassian.crowd.search.ldap.ActiveDirectoryQueryTranslaterImpl.getStringTermEqualityFilter(ActiveDirectoryQueryTranslaterImpl.java:66) at com.atlassian.crowd.search.ldap.LDAPQueryTranslaterImpl.stringTermRestrictionAsFilter(LDAPQueryTranslaterImpl.java:158) at com.atlassian.crowd.search.ldap.LDAPQueryTranslaterImpl.searchRestrictionAsFilter(LDAPQueryTranslaterImpl.java:60) at com.atlassian.crowd.search.ldap.LDAPQueryTranslaterImpl.multiTermRestrictionAsFilter(LDAPQueryTranslaterImpl.java:78) at com.atlassian.crowd.search.ldap.LDAPQueryTranslaterImpl.searchRestrictionAsFilter(LDAPQueryTranslaterImpl.java:68) at com.atlassian.crowd.search.ldap.LDAPQueryTranslaterImpl.asLDAPFilter(LDAPQueryTranslaterImpl.java:40) at com.atlassian.crowd.directory.SpringLDAPConnector.searchUserObjects(SpringLDAPConnector.java:623) at com.atlassian.crowd.directory.SpringLDAPConnector.searchUsers(SpringLDAPConnector.java:974) at com.atlassian.labs.crowd.directory.pruning.jobs.DelegatedDirectoryPruningJob.loadUsersByExternalIds(DelegatedDirectoryPruningJob.java:202) at com.atlassian.labs.crowd.directory.pruning.jobs.DelegatedDirectoryPruningJob.processBatch(DelegatedDirectoryPruningJob.java:180) at com.atlassian.labs.crowd.directory.pruning.jobs.DelegatedDirectoryPruningJob.deleteUsersNotInRemoteDirectory(DelegatedDirectoryPruningJob.java:136) at java.util.ArrayList.forEach(ArrayList.java:1249) at com.atlassian.labs.crowd.directory.pruning.jobs.DelegatedDirectoryPruningJob.lambda$runJob$0(DelegatedDirectoryPruningJob.java:110) at com.atlassian.sal.core.transaction.HostContextTransactionTemplate$1.doInTransaction(HostContextTransactionTemplate.java:25) at com.atlassian.sal.spring.component.SpringHostContextAccessor$1.doInTransaction(SpringHostContextAccessor.java:88) at org.springframework.transaction.support.TransactionTemplate.execute(TransactionTemplate.java:133) at com.atlassian.sal.spring.component.SpringHostContextAccessor.doInTransaction(SpringHostContextAccessor.java:82) at sun.reflect.GeneratedMethodAccessor888.invoke(Unknown Source) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.lang.reflect.Method.invoke(Method.java:498) at com.atlassian.plugin.util.ContextClassLoaderSettingInvocationHandler.invoke(ContextClassLoaderSettingInvocationHandler.java:26) at com.sun.proxy.$Proxy146.doInTransaction(Unknown Source) at sun.reflect.GeneratedMethodAccessor888.invoke(Unknown Source) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.lang.reflect.Method.invoke(Method.java:498) at com.atlassian.plugin.osgi.bridge.external.HostComponentFactoryBean$DynamicServiceInvocationHandler.invoke(HostComponentFactoryBean.java:136) at com.sun.proxy.$Proxy146.doInTransaction(Unknown Source) at com.atlassian.sal.core.transaction.HostContextTransactionTemplate.execute(HostContextTransactionTemplate.java:21) at sun.reflect.GeneratedMethodAccessor920.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:302) at org.eclipse.gemini.blueprint.service.importer.support.internal.aop.ServiceInvoker.doInvoke(ServiceInvoker.java:56) at org.eclipse.gemini.blueprint.service.importer.support.internal.aop.ServiceInvoker.invoke(ServiceInvoker.java:60) at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179) at org.springframework.aop.support.DelegatingIntroductionInterceptor.doProceed(DelegatingIntroductionInterceptor.java:133) at org.springframework.aop.support.DelegatingIntroductionInterceptor.invoke(DelegatingIntroductionInterceptor.java:121) at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179) at org.eclipse.gemini.blueprint.service.util.internal.aop.ServiceTCCLInterceptor.invokeUnprivileged(ServiceTCCLInterceptor.java:70) at org.eclipse.gemini.blueprint.service.util.internal.aop.ServiceTCCLInterceptor.invoke(ServiceTCCLInterceptor.java:53) at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179) at org.eclipse.gemini.blueprint.service.importer.support.LocalBundleContextAdvice.invoke(LocalBundleContextAdvice.java:57) at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179) at org.springframework.aop.support.DelegatingIntroductionInterceptor.doProceed(DelegatingIntroductionInterceptor.java:133) at org.springframework.aop.support.DelegatingIntroductionInterceptor.invoke(DelegatingIntroductionInterceptor.java:121) at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179) at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:208) at com.sun.proxy.$Proxy335.execute(Unknown Source) at com.atlassian.labs.crowd.directory.pruning.jobs.DelegatedDirectoryPruningJob.runJob(DelegatedDirectoryPruningJob.java:108) 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)

            Hi ttbell

            By default, when you install this plugin it is not configured. So once you install it (and at the moment you can do it by putting the jar file into {crowd.home}/shared/plugins and start Crowd itself), you need to configure it and enable disabling users or removing them.

            In order to configure the plugin just navigate to your delegated directory configuration in Crowd. You should see additional option on the left panel as on the screen below:

             

            Once you select the Configure pruning option, you should see configuration screen as follows:

             

            When Enable directory pruning option is selected, users that are no longer in the remote directory will be deactivated in Crowd. If option Delete pruned users is selected users that are no longer in the remote directory will be removed from Crowd.

            You can also configure your delegated directories pruning by navigating to Configure pruning admin menu (it is under the cog in the nav bar):

            In this view, you can see and change pruning config for all of your delegated directories.

             

            Directory pruning runs at fix rates. By default it is configured to be run at 0 23 * * * ? but you can change that by setting the java system property atlassian.delegated.dir.pruning.schedule and define there a cron expression.

             

            Hope that helps
            Marcin Kempa

            Marcin Kempa added a comment - Hi ttbell By default, when you install this plugin it is not configured. So once you install it (and at the moment you can do it by putting the jar file into {crowd.home}/shared/plugins and start Crowd itself), you need to configure it and enable disabling users or removing them. In order to configure the plugin just navigate to your delegated directory configuration in Crowd. You should see additional option on the left panel as on the screen below:   Once you select the Configure pruning option, you should see configuration screen as follows:   When Enable directory pruning option is selected, users that are no longer in the remote directory will be deactivated in Crowd. If option Delete pruned users is selected users that are no longer in the remote directory will be removed from Crowd. You can also configure your delegated directories pruning by navigating to Configure pruning admin menu (it is under the cog in the nav bar): In this view, you can see and change pruning config for all of your delegated directories.   Directory pruning runs at fix rates. By default it is configured to be run at 0 23 * * * ? but you can change that by setting the java system property atlassian.delegated.dir.pruning.schedule and define there a cron expression.   Hope that helps Marcin Kempa

            Tom Bell added a comment -

            Also, how often does it run and is there a way to configure the frequency?

            Tom Bell added a comment - Also, how often does it run and is there a way to configure the frequency?

            Tom Bell added a comment -

            Thank you for the Crowd plugin!  This is a feature that must eventually be native in Crowd.

            We've installed the plugin but it does not appear to be marking userids missing in LDAP as inactive.  Is there a way to force the plugin to run?

             

            Tom Bell added a comment - Thank you for the Crowd plugin!  This is a feature that must  eventually be native in Crowd. We've installed the plugin but it does not appear to be marking userids missing in LDAP as inactive.  Is there a way to force the plugin to run?  

            Hi all!

            We are pleased to announce an experimental (Atlassian Labs) plugin for Crowd, that is capable of doing delegated directory user pruning.

            This plugin allows to optimize your license usage by periodically cleaning inactive users from your delegated directories. After configuring pruning for a delegated directory the plugin will periodically check if the directory contains any users who have been deactivated or removed from the remote directory.

            By default such users will be deactivated in Crowd. It is also possible to enable "hard delete" mode, in which users who have been deleted in the remote directory will also be deleted in Crowd.

            Here is the link to the plugin:

            https://marketplace.atlassian.com/1218630

             

            Best Regards,

            Atlassian Crowd team

            Marcin Kempa added a comment - Hi all! We are pleased to announce an experimental (Atlassian Labs) plugin for Crowd, that is capable of doing delegated directory user pruning. This plugin allows to optimize your license usage by periodically cleaning inactive users from your delegated directories. After configuring pruning for a delegated directory the plugin will periodically check if the directory contains any users who have been deactivated or removed from the remote directory. By default such users will be deactivated in Crowd. It is also possible to enable "hard delete" mode, in which users who have been deleted in the remote directory will also be deleted in Crowd. Here is the link to the plugin: https://marketplace.atlassian.com/1218630   Best Regards, Atlassian Crowd team

            ponoole607510792 I did additional test with Bitbucket Server 5.1.2 and Confluence 6.4.2. The results after removing users that created repository in Bitbucket Server and pushed some commits are as follows:

            the user was removed from the user list in the users administration screen.

            In Confluence it looked like that:

            (jdoe was the name of the user). Again user was not visible on the list of users in administration screen.

            Also some of the Confluence macros do not render the name of the user correctly:

             

            Also there are couple of other problems reported in Confluence when removing users https://jira.atlassian.com/browse/CONFSERVER-34473 and https://jira.atlassian.com/browse/CONFSERVER-34474

            Not sure if this is something acceptable for you?

             

            Best Regards,

            Marcin Kempa

            Marcin Kempa added a comment - ponoole607510792  I did additional test with Bitbucket Server 5.1.2 and Confluence 6.4.2. The results after removing users that created repository in Bitbucket Server and pushed some commits are as follows: the user was removed from the user list in the users administration screen. In Confluence it looked like that: (jdoe was the name of the user). Again user was not visible on the list of users in administration screen. Also some of the Confluence macros do not render the name of the user correctly:   Also there are couple of other problems reported in Confluence when removing users https://jira.atlassian.com/browse/CONFSERVER-34473  and https://jira.atlassian.com/browse/CONFSERVER-34474 Not sure if this is something acceptable for you?   Best Regards, Marcin Kempa

            Hi @mkempa,

             

            I haven't noticed that this was resolved thanks for updates on this. But do you know if this implemented also in other Atlassian applications like confluence, Bitbucket and bamboo?

             

            Thanks and Regards,

            Oleg

            Oleg Ponomarenko added a comment - Hi @mkempa,   I haven't noticed that this was resolved thanks for updates on this. But do you know if this implemented also in other Atlassian applications like confluence, Bitbucket and bamboo?   Thanks and Regards, Oleg

            Hi ponoole607510792 and gasparluiz.silva1280885703, I believe the issue you are describing was already resolved in JIRA 6.1 JRASERVER-24937

            I also did additional check with following setup :

            • JIRA 7.2.8 connected to Crowd 2.11.1
            • Crowd connected to openLdap
            • There was a user Fry created in openLdap in group jira-software-users and synchronized to JIRA through Crowd

            I logged as this user to JIRA, created an issue and removed him from openLdap. Once the changes were synchronized to JIRA the user was deactivated but he was still visible as a reporter:

            I also checked how it would behave with comments and the behavior is the same (the user is not being removed, but only deactivated on JIRA side) and the comment is still linked to the inactive account of the user who commented.

            Is it something that does not work for you?

             

            Best Regards,

            Marcin Kempa

            Marcin Kempa added a comment - Hi ponoole607510792 and gasparluiz.silva1280885703 , I believe the issue you are describing was already resolved in JIRA 6.1 JRASERVER-24937 I also did additional check with following setup : JIRA 7.2.8 connected to Crowd 2.11.1 Crowd connected to openLdap There was a user Fry  created in openLdap in group jira-software-users and synchronized to JIRA through Crowd I logged as this user to JIRA, created an issue and removed him from openLdap. Once the changes were synchronized to JIRA the user was deactivated but he was still visible as a reporter: I also checked how it would behave with comments and the behavior is the same (the user is not being removed, but only deactivated on JIRA side) and the comment is still linked to the inactive account of the user who commented. Is it something that does not work for you?   Best Regards, Marcin Kempa

            Very good point @Oleg!

            Indeed: removing users will make their comments as Anonymous which is not user friendly.

            As you mentioned, best would be make those users as "INACTIVE" so that they don't count against the license.

            Best, Gaspar

            Gaspar Silva added a comment - Very good point @Oleg! Indeed: removing users will make their comments as Anonymous which is not user friendly. As you mentioned, best would be make those users as "INACTIVE" so that they don't count against the license. Best, Gaspar

            Hi Lukasz,

            This solution will not work for us as this will remove users and we do not want users to be removed as this will mark all comments made by them previously as "Anonymous", but if it will only deactivate users(even if users removed from LDAP) then this sounds like a solution. But removing users may be needed for some other instances so will be grate to have a check box for this.

            Thanks and Regards,

            Oleg

            Oleg Ponomarenko added a comment - Hi Lukasz, This solution will not work for us as this will remove users and we do not want users to be removed as this will mark all comments made by them previously as "Anonymous", but if it will only deactivate users(even if users removed from LDAP) then this sounds like a solution. But removing users may be needed for some other instances so will be grate to have a check box for this. Thanks and Regards, Oleg

            Hello @Lukasz

            This is really great news! Thanks a bunch for the update.

            Looking forward to have it in near future.

            Best, Gaspar

            Gaspar Silva added a comment - Hello @Lukasz This is really great news! Thanks a bunch for the update. Looking forward to have it in near future. Best, Gaspar

            Hi all,

            Thank you for voicing your interest in this feature. We understand that the issue is causing problems with license management in your installations.

            One short-term solution to this problem we're exploring right now is adding the ability to automatically add users to specified groups when they log in to applications (CWD-3726). We believe that using that feature, together with switching to using a synchronized directory (rather than a delegated one) might resolve some of the issues. The behavior would then be:

            • synchronization adds and removes users to Crowd every time they're added or removed in your LDAP server
            • these users don't consume licenses in products (JIRA, Confluence, etc.), until they actually log in - at that time they'd be assigned a group (like 'jira-users'), that causes them to use a licensed seat there
            • when the users are removed or deactivated in LDAP, the change would be automatically synchronized to Crowd, and they would no longer consume a license (neither in the product nor in Crowd)

            Please let us know if this sounds feasible to you, and would solve the issues you are facing.

            At the same time, we are looking at other solutions for this issue, targeting specifically delegated directories.

            Lukasz Pater added a comment - Hi all, Thank you for voicing your interest in this feature. We understand that the issue is causing problems with license management in your installations. One short-term solution to this problem we're exploring right now is adding the ability to automatically add users to specified groups when they log in to applications ( CWD-3726 ). We believe that using that feature, together with switching to using a synchronized directory (rather than a delegated one) might resolve some of the issues. The behavior would then be: synchronization adds and removes users to Crowd every time they're added or removed in your LDAP server these users don't consume licenses in products (JIRA, Confluence, etc.), until they actually log in - at that time they'd be assigned a group (like 'jira-users'), that causes them to use a licensed seat there when the users are removed or deactivated in LDAP, the change would be automatically synchronized to Crowd, and they would no longer consume a license (neither in the product nor in Crowd) Please let us know if this sounds feasible to you, and would solve the issues you are facing. At the same time, we are looking at other solutions for this issue, targeting specifically delegated directories.

            Bernardo added a comment -

            Atlassian team, it is time to assign this to somebody, don't you thing so? A ticke open from 2011 does not make you look good, and this is needed.

            Thanks!

            Bernardo added a comment - Atlassian team, it is time to assign this to somebody, don't you thing so? A ticke open from 2011 does not make you look good, and this is needed. Thanks!

            Dears, this feature would greatly help our account.
            Could you raise the priority and provide us with a delivery date?
            Thank you!

            Gaspar Silva added a comment - Dears, this feature would greatly help our account. Could you raise the priority and provide us with a delivery date? Thank you!

            I am also looking this as we are going to implement CROWD authentication. As per our use case only delegate LDAP configuration work because of this disabling/deactivating we stop for implementing it. Let me know the way to find the deleted users in AD and deactivating in CROWD. It is not possible to find the users deleted as a CROWD admin in my organization.

            Omprakash Thamsetty added a comment - I am also looking this as we are going to implement CROWD authentication. As per our use case only delegate LDAP configuration work because of this disabling/deactivating we stop for implementing it. Let me know the way to find the deleted users in AD and deactivating in CROWD. It is not possible to find the users deleted as a CROWD admin in my organization.

            Woah! This is a major flaw with the Delegated Authentication connector! This could certainly use re-prioritization since AD now syncs the Disabled flags. We're just leaving this use-case out in the dark because manual effort defeats the purpose.

            Steven F Behnke added a comment - Woah! This is a major flaw with the Delegated Authentication connector! This could certainly use re-prioritization since AD now syncs the Disabled flags. We're just leaving this use-case out in the dark because manual effort defeats the purpose.

            Especially for the new enterprise licensing this is a must-have. Manually disabling those users might work for small amounts of users. For large amounts of users there must be an automatic way to prune.

            Stephan Haslinger added a comment - Especially for the new enterprise licensing this is a must-have. Manually disabling those users might work for small amounts of users. For large amounts of users there must be an automatic way to prune.

            ... and is still marked as minor.

            Sorin Sbarnea added a comment - ... and is still marked as minor.

            This really leads to a problem for bigger companies or companies with lots of staff fluctuation (e.g. internships).
            Could you please give me feedback on this issue? I'm interested whether it'll ever be on your roadmap, so we can plan our user management processes better.

            Stephan Haslinger added a comment - This really leads to a problem for bigger companies or companies with lots of staff fluctuation (e.g. internships). Could you please give me feedback on this issue? I'm interested whether it'll ever be on your roadmap, so we can plan our user management processes better.

              Unassigned Unassigned
              48ff0be30bda Stephan Haslinger
              Votes:
              75 Vote for this issue
              Watchers:
              46 Start watching this issue

                Created:
                Updated:
                Resolved: