Commits

Anonymous committed 65b06e7

0.13dev: Added the `trac-admin` commands `permission export` and `permission import` for exporting and importing permissions from an environment.

Patch by Peter Suter, thanks! Part of #9336.

  • Participants
  • Parent commits 8396b9a
  • Branches trunk

Comments (0)

Files changed (3)

trac/admin/tests/console-tests.txt

 milestone remove     Remove milestone
 milestone rename     Rename milestone
 permission add       Add a new permission rule
+permission export    Export permission rules to a file
+permission import    Import permission rules from a file
 permission list      List permission rules
 permission remove    Remove a permission rule
 priority add         Add a priority value option
 Error: Cannot remove permission TICKET_VIEW for user joe.
 ===== test_permission_remove_action_not_granted =====
 Error: Cannot remove permission TICKET_CREATE for user anonymous.
+===== test_permission_export_ok =====
+anonymous,BROWSER_VIEW,CHANGESET_VIEW,FILE_VIEW,LOG_VIEW,MILESTONE_VIEW,REPORT_SQL_VIEW,REPORT_VIEW,ROADMAP_VIEW,SEARCH_VIEW,TICKET_VIEW,TIMELINE_VIEW,WIKI_VIEW
+authenticated,TICKET_CREATE,TICKET_MODIFY,WIKI_CREATE,WIKI_MODIFY
+===== test_permission_import_ok =====
+
+User           Action
+------------------------------
+anonymous      BROWSER_VIEW
+anonymous      CHANGESET_VIEW
+anonymous      FILE_VIEW
+anonymous      LOG_VIEW
+anonymous      MILESTONE_VIEW
+anonymous      REPORT_SQL_VIEW
+anonymous      REPORT_VIEW
+anonymous      ROADMAP_VIEW
+anonymous      SEARCH_VIEW
+anonymous      TICKET_VIEW
+anonymous      TIMELINE_VIEW
+anonymous      WIKI_VIEW
+authenticated  TICKET_CREATE
+authenticated  TICKET_MODIFY
+authenticated  WIKI_CREATE
+authenticated  WIKI_MODIFY
+test_user      TICKET_VIEW
+test_user      WIKI_VIEW
+
+
+Available actions:
+ BROWSER_VIEW, CHANGESET_VIEW, CONFIG_VIEW, EMAIL_VIEW, FILE_VIEW,
+ LOG_VIEW, MILESTONE_ADMIN, MILESTONE_CREATE, MILESTONE_DELETE,
+ MILESTONE_MODIFY, MILESTONE_VIEW, PERMISSION_ADMIN, PERMISSION_GRANT,
+ PERMISSION_REVOKE, REPORT_ADMIN, REPORT_CREATE, REPORT_DELETE,
+ REPORT_MODIFY, REPORT_SQL_VIEW, REPORT_VIEW, ROADMAP_ADMIN, ROADMAP_VIEW,
+ SEARCH_VIEW, TICKET_ADMIN, TICKET_APPEND, TICKET_CHGPROP, TICKET_CREATE,
+ TICKET_EDIT_CC, TICKET_EDIT_COMMENT, TICKET_EDIT_DESCRIPTION,
+ TICKET_MODIFY, TICKET_VIEW, TIMELINE_VIEW, TRAC_ADMIN,
+ VERSIONCONTROL_ADMIN, WIKI_ADMIN, WIKI_CREATE, WIKI_DELETE, WIKI_MODIFY,
+ WIKI_RENAME, WIKI_VIEW
+
 ===== test_component_list_ok =====
 
 Name        Owner   

trac/admin/tests/console.py

 from trac.config import Configuration
 from trac.db import DatabaseManager
 from trac.test import EnvironmentStub
+from trac.util import read_file
 from trac.util.datefmt import format_date, get_date_format_hint
 from trac.web.tests.session import _prep_session_table
 
         self.assertEqual(2, rv)
         self.assertEqual(self.expected_results[test_name], output)
 
+    def test_permission_export_ok(self):
+        """
+        Tests the 'permission export' command in trac-admin.  This particular
+        test exports the default permissions to a file.
+        """
+        test_name = sys._getframe().f_code.co_name
+        filename = 'permissions.csv'
+        rv, output = self._execute('permission export ' + filename)
+        self.assertEqual(0, rv)
+        self.assertEqual('', output)
+        filecontent = read_file(filename, 'rb')
+        os.unlink(filename)
+        self.assertEqual(self.expected_results[test_name], filecontent)
+
+    def test_permission_import_ok(self):
+        """
+        Tests the 'permission import' command in trac-admin.  This particular
+        test exports additional permissions, removes them and imports them back.
+        """
+        test_name = sys._getframe().f_code.co_name
+        filename = 'permissions.csv'
+        self._execute('permission add test_user WIKI_VIEW')
+        self._execute('permission add test_user TICKET_VIEW')
+        self._execute('permission export ' + filename)
+        self._execute('permission remove test_user *')
+        rv, output = self._execute('permission import ' + filename)
+        self.assertEqual(0, rv)
+        self.assertEqual('', output)
+        os.unlink(filename)
+        rv, output = self._execute('permission list')
+        self.assertEqual(0, rv)
+        self.assertEqual(self.expected_results[test_name], output)
+
     # Component tests
 
     def test_component_list_ok(self):
 # Author: Jonas Borgström <jonas@edgewall.com>
 #         Christopher Lenz <cmlenz@gmx.de>
 
+from __future__ import with_statement
+
+import csv
+import os
 from time import time
 
-from trac.admin import AdminCommandError, IAdminCommandProvider
+from trac.admin import AdminCommandError, IAdminCommandProvider, get_dir_list
 from trac.config import ExtensionOption, OrderedExtensionsOption
 from trac.core import *
 from trac.resource import Resource, get_resource_name
         yield ('permission remove', '<user> <action> [action] [...]',
                'Remove a permission rule',
                self._complete_remove, self._do_remove)
+        yield ('permission export', '<filename>',
+               'Export permission rules to a file',
+               self._complete_import_export, self._do_export)
+        yield ('permission import', '<filename>',
+               'Import permission rules from a file',
+               self._complete_import_export, self._do_import)
     
     def get_user_list(self):
         return set(user for (user, action) in 
             return self.get_user_list()
         elif len(args) >= 2:
             return set(self.get_user_perms(args[0])) - set(args[1:-1])
+
+    def _complete_import_export(self, args):
+        if len(args) == 1:
+            return get_dir_list(args[-1])
     
     def _do_list(self, user=None):
         permsys = PermissionSystem(self.env)
                 raise AdminCommandError(
                     _("Cannot remove permission %(action)s for user %(user)s.",
                       action=action, user=user))
+    
+    def _do_export(self, filename):
+        try:
+            with open(filename, 'wb') as f:
+                writer = csv.writer(f, lineterminator=os.linesep)
+                users = self.get_user_list()
+                for user in sorted(users):
+                    actions = sorted(self.get_user_perms(user))
+                    writer.writerow([user] + actions)
+        except IOError, e:
+            raise AdminCommandError(
+                _("Cannot export to %(filename)s: %(error)s",
+                  filename=filename, error=e.strerror))
+    
+    def _do_import(self, filename):
+        permsys = PermissionSystem(self.env)
+        try:
+            with open(filename, 'rb') as f:
+                reader = csv.reader(f, lineterminator=os.linesep)
+                for row in reader:
+                    if len(row) < 2:
+                        raise AdminCommandError(
+                            _("Invalid row %(line)d. Expected <user>, "
+                              "<action>, [action], [...]",
+                              line=reader.line_num))
+                    user, actions = row[0], row[1:]
+                    if user.isupper():
+                        raise AdminCommandError(
+                            _("Invalid user %(user)s on line %(line)d: All "
+                              "upper-cased tokens are reserved for permission "
+                              "names.", user=user, line=reader.line_num))
+                    old_actions = self.get_user_perms(user)
+                    for action in set(actions) - set(old_actions):
+                        permsys.grant_permission(user, action)
+        except csv.Error, e:
+            raise AdminCommandError(
+                _("Cannot import from %(filename)s line %(line)d: %(error)s ",
+                  filename=filename, line=reader.line_num, error=e))
+        except IOError, e:
+            raise AdminCommandError(
+                _("Cannot import from %(filename)s: %(error)s",
+                  filename=filename, error=e.strerror))