You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@allura.apache.org by je...@apache.org on 2015/11/18 17:06:06 UTC

[07/16] allura git commit: [#7999] ticket:856 Disable users

[#7999] ticket:856 Disable users


Project: http://git-wip-us.apache.org/repos/asf/allura/repo
Commit: http://git-wip-us.apache.org/repos/asf/allura/commit/caa56721
Tree: http://git-wip-us.apache.org/repos/asf/allura/tree/caa56721
Diff: http://git-wip-us.apache.org/repos/asf/allura/diff/caa56721

Branch: refs/heads/ib/7999a
Commit: caa56721ff9fb85853802e5bf709ce0a76577cdb
Parents: 65ea094
Author: Igor Bondarenko <je...@gmail.com>
Authored: Wed Nov 4 15:26:02 2015 +0200
Committer: Igor Bondarenko <je...@gmail.com>
Committed: Mon Nov 16 14:18:36 2015 +0200

----------------------------------------------------------------------
 Allura/allura/controllers/site_admin.py         |  7 ++--
 Allura/allura/scripts/delete_projects.py        | 38 ++++++++++++++++----
 .../templates/site_admin_delete_projects.html   |  4 +++
 .../allura/tests/functional/test_site_admin.py  |  9 +++++
 Allura/allura/tests/test_delete_projects.py     | 33 +++++++++++++++--
 5 files changed, 79 insertions(+), 12 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/allura/blob/caa56721/Allura/allura/controllers/site_admin.py
----------------------------------------------------------------------
diff --git a/Allura/allura/controllers/site_admin.py b/Allura/allura/controllers/site_admin.py
index cec6b3a..c74203a 100644
--- a/Allura/allura/controllers/site_admin.py
+++ b/Allura/allura/controllers/site_admin.py
@@ -322,8 +322,9 @@ class SiteAdminController(object):
     @without_trailing_slash
     @expose('jinja:allura:templates/site_admin_delete_projects.html')
     @validate(validators=dict(projects=validators.UnicodeString(if_empty=None),
-                              reason=validators.UnicodeString(if_empty=None)))
-    def delete_projects(self, projects=None, reason=None, **kw):
+                              reason=validators.UnicodeString(if_empty=None),
+                              disable_users=validators.StringBool(if_empty=False)))
+    def delete_projects(self, projects=None, reason=None, disable_users=False, **kw):
         if request.method == "POST":
             if not projects:
                 flash(u'No projects specified', 'warning')
@@ -338,6 +339,8 @@ class SiteAdminController(object):
             task_params = u' '.join(task_params)
             if reason:
                 task_params = u'-r {} {}'.format(pipes.quote(reason), task_params)
+            if disable_users:
+                task_params = u'--disable-users {}'.format(task_params)
             DeleteProjects.post(task_params)
             flash(u'Delete scheduled for %s' % projects, 'ok')
             redirect('delete_projects')

http://git-wip-us.apache.org/repos/asf/allura/blob/caa56721/Allura/allura/scripts/delete_projects.py
----------------------------------------------------------------------
diff --git a/Allura/allura/scripts/delete_projects.py b/Allura/allura/scripts/delete_projects.py
index 0cd1ca8..cedf87b 100644
--- a/Allura/allura/scripts/delete_projects.py
+++ b/Allura/allura/scripts/delete_projects.py
@@ -20,9 +20,11 @@ import logging
 
 from ming.odm import Mapper, session
 from pylons import app_globals as g
+from webob import Request
 
 from allura.scripts import ScriptTask
 from allura import model as M
+from allura.lib.plugin import AuthenticationProvider
 from allura.tasks.index_tasks import solr_del_project_artifacts
 
 
@@ -37,9 +39,7 @@ class DeleteProjects(ScriptTask):
             proj = cls.get_project(proj)
             if proj:
                 log.info('Purging %s%s. Reason: %s', proj.neighborhood.url_prefix, proj.shortname, options.reason)
-                pid = proj._id
-                cls.purge_project(proj)
-                g.post_event('project_deleted', project_id=pid, reason=options.reason)
+                cls.purge_project(proj, options)
 
     @classmethod
     def get_project(cls, proj):
@@ -55,20 +55,42 @@ class DeleteProjects(ScriptTask):
         return p
 
     @classmethod
-    def purge_project(cls, project):
+    def purge_project(cls, project, options):
         pid = project._id
         solr_del_project_artifacts.post(pid)
+        if options.disable_users:
+            # Disable users if necessary BEFORE removing all project-related documents
+            cls.disable_users(project, options)
         app_config_ids = [ac._id for ac in M.AppConfig.query.find(dict(project_id=pid))]
         for m in Mapper.all_mappers():
-            cls = m.mapped_class
+            mcls = m.mapped_class
             if 'project_id' in m.property_index:
                 # Purge the things directly related to the project
-                cls.query.remove(dict(project_id=pid))
+                mcls.query.remove(dict(project_id=pid))
             elif 'app_config_id' in m.property_index:
                 # ... and the things related to its apps
-                cls.query.remove(dict(app_config_id={'$in': app_config_ids}))
+                mcls.query.remove(dict(app_config_id={'$in': app_config_ids}))
         project.delete()
         session(project).flush()
+        g.post_event('project_deleted', project_id=pid, reason=options.reason)
+
+    @classmethod
+    def disable_users(cls, project, options):
+        provider = AuthenticationProvider.get(Request.blank('/'))
+        users = project.admins() + project.users_with_role('Developer')
+        for user in users:
+            if user.disabled:
+                continue
+            log.info(u'Disabling user %s', user.username)
+            provider.disable_user(user, audit=False)
+            msg = u'Account disabled because project {}{} is deleted. Reason: {}'.format(
+                project.neighborhood.url_prefix,
+                project.shortname,
+                options.reason)
+            M.AuditLog.log_user(msg, user=user)
+            # `users` can contain duplicates. Make sure changes are visible
+            # to next iterations, so that `user.disabled` check works.
+            session(user).expunge(user)
 
     @classmethod
     def parser(cls):
@@ -77,6 +99,8 @@ class DeleteProjects(ScriptTask):
                             help='Project to delete in a form nbhd_prefix/shortname')
         parser.add_argument('-r', '--reason', type=str,
                             help='Reason why these projects being deleted')
+        parser.add_argument('--disable-users', action='store_true', default=False,
+                            help='Disable all project members')
         return parser
 
 

http://git-wip-us.apache.org/repos/asf/allura/blob/caa56721/Allura/allura/templates/site_admin_delete_projects.html
----------------------------------------------------------------------
diff --git a/Allura/allura/templates/site_admin_delete_projects.html b/Allura/allura/templates/site_admin_delete_projects.html
index bae6a05..3f15ec1 100644
--- a/Allura/allura/templates/site_admin_delete_projects.html
+++ b/Allura/allura/templates/site_admin_delete_projects.html
@@ -38,6 +38,10 @@
         <textarea name="reason"></textarea>
       </div>
       <div class="grid-18">
+        <input id="disable_users" name="disable_users" type="checkbox">
+        <label for="disable_users">Disable all project members</label>
+      </div>
+      <div class="grid-18">
         <input type="submit" value="Delete">
       </div>
       {{lib.csrf_token()}}

http://git-wip-us.apache.org/repos/asf/allura/blob/caa56721/Allura/allura/tests/functional/test_site_admin.py
----------------------------------------------------------------------
diff --git a/Allura/allura/tests/functional/test_site_admin.py b/Allura/allura/tests/functional/test_site_admin.py
index f6c1a31..fed8fa4 100644
--- a/Allura/allura/tests/functional/test_site_admin.py
+++ b/Allura/allura/tests/functional/test_site_admin.py
@@ -602,6 +602,15 @@ class TestDeleteProjects(TestController):
         form.submit()
         dp.post.assert_called_once_with('p/test adobe/adobe-1 p/test2')
 
+    @patch('allura.controllers.site_admin.DeleteProjects', autospec=True)
+    def test_admins_and_devs_are_disabled(self, dp):
+        r = self.app.get('/nf/admin/delete_projects')
+        form = self.form(r)
+        form['projects'] = 'p/test p/test2'
+        form['disable_users'] = True
+        form.submit()
+        dp.post.assert_called_once_with('--disable-users p/test p/test2')
+
 
 @task
 def test_task(*args, **kw):

http://git-wip-us.apache.org/repos/asf/allura/blob/caa56721/Allura/allura/tests/test_delete_projects.py
----------------------------------------------------------------------
diff --git a/Allura/allura/tests/test_delete_projects.py b/Allura/allura/tests/test_delete_projects.py
index f8cc62e..784a5b9 100644
--- a/Allura/allura/tests/test_delete_projects.py
+++ b/Allura/allura/tests/test_delete_projects.py
@@ -15,11 +15,12 @@
 #       specific language governing permissions and limitations
 #       under the License.
 
-from ming.odm import session, Mapper
+from ming.odm import session, Mapper, ThreadLocalODMSession
 from mock import patch
+from pylons import app_globals as g
 
 from alluratest.controller import TestController
-
+from allura.tests.decorators import audits, out_audits
 from allura import model as M
 from allura.scripts import delete_projects
 
@@ -31,7 +32,7 @@ class TestDeleteProjects(TestController):
         n = M.Neighborhood.query.get(name='Projects')
         admin = M.User.by_username('test-admin')
         self.p_shortname = 'test-delete'
-        n.register_project(self.p_shortname, admin)
+        self.proj = n.register_project(self.p_shortname, admin)
 
     def run_script(self, options):
         cls = delete_projects.DeleteProjects
@@ -92,3 +93,29 @@ class TestDeleteProjects(TestController):
         assert p is None, 'Project is not deleted'
         log.info.assert_called_once_with('Purging %s%s. Reason: %s', '/p/', 'test-delete', 'The Reason')
         post_event.assert_called_once_with('project_deleted', project_id=pid, reason='The Reason')
+
+    def _disable_users(self, disable):
+        dev = M.User.by_username('test-user')
+        self.proj.add_user(dev, ['Developer'])
+        ThreadLocalODMSession.flush_all()
+        g.credentials.clear()
+        proj = u'p/{}'.format(self.p_shortname)
+        msg = u'Account disabled because project /{} is deleted. Reason: The Reason'.format(proj)
+        opts = ['-r', 'The Reason', proj]
+        if disable:
+            opts.insert(0, '--disable-users')
+        _audit = audits if disable else out_audits
+        with _audit(msg):
+            self.run_script(opts)
+        admin = M.User.by_username('test-admin')
+        dev = M.User.by_username('test-user')
+        assert admin.disabled is disable
+        assert dev.disabled is disable
+
+    @patch('allura.model.auth.request', autospec=True)
+    def test_disable_users(self, req):
+        req.url = None
+        self._disable_users(disable=True)
+
+    def test_not_disable_users(self):
+        self._disable_users(disable=False)