def validate_registration(self, req): if req.path_info == '/prefs': return acctmgr = AccountManager(self.env) username = acctmgr.handle_username_casing( req.args.get('username', '').strip()) if not username: raise RegistrationError(N_("Username cannot be empty.")) # Always exclude some special characters, i.e. # ':' can't be used in HtPasswdStore # '[' and ']' can't be used in SvnServePasswordStore blacklist = acctmgr.username_char_blacklist if contains_any(username, blacklist): pretty_blacklist = '' for c in blacklist: if pretty_blacklist == '': pretty_blacklist = tag(' \'', tag.b(c), '\'') else: pretty_blacklist = tag(pretty_blacklist, ', \'', tag.b(c), '\'') raise RegistrationError(N_( "The username must not contain any of these characters: %s"), tag.b(pretty_blacklist) ) # All upper-cased names are reserved for permission action names. if username.isupper(): raise RegistrationError(N_("A username with only upper-cased " "characters is not allowed.")) # Prohibit some user names, that are important for Trac and therefor # reserved, even if not in the permission store for some reason. if username.lower() in ['anonymous', 'authenticated']: raise RegistrationError(N_("Username %s is not allowed."), tag.b(username)) # NOTE: A user may exist in a password store but not in the permission # store. I.e. this happens, when the user (from the password store) # never logged in into Trac. So we have to perform this test here # and cannot just check for the user being in the permission store. # And better obfuscate whether an existing user or group name # was responsible for rejection of this user name. for store_user in acctmgr.get_users(): # Do it carefully by disregarding case. if store_user.lower() == username.lower(): raise RegistrationError(tag_( "Another account or group already exists, who's name " "differs from %(username)s only by case or is identical.", username=tag.b(username))) # Password consistency checks follow. password = req.args.get('password') if not password: raise RegistrationError(N_("Password cannot be empty.")) elif password != req.args.get('password_confirm'): raise RegistrationError(N_("The passwords must match."))
def process_request(self, req): req.perm.assert_permission('TRAC_ADMIN') if not re.match(r'/autocompleteperms/autocompleteperms\.js$', req.path_info): return subjects = set([]) db = self.env.get_db_cnx() cursor = db.cursor() cursor.execute("SELECT username,action FROM permission") rows = cursor.fetchall() while True: num_users = len(subjects) for user, action in rows: if user not in subjects: subjects.add(user) for provider in self.group_providers: subjects.update(provider.get_permission_groups(action)) if not action.isupper() and action not in subjects: subjects.add(action) for provider in self.group_providers: subjects.update(provider.get_permission_groups(action)) if num_users == len(subjects): break try: from acct_mgr.api import AccountManager acc_mgr = AccountManager(self.env) users = acc_mgr.get_users() except: users = [x[0] for x in self.env.get_known_users()] group_list = list(subjects - set(users) - set('#%s' % user for user in users)) subjects.update(users) user_list = list(subjects) user_list.sort() group_list.sort() out = """ var data = "%s".split(" "); var data_groups = "%s".split(" "); $(document).ready(function() { $("#gp_subject").autocomplete(data, {minChars: 0, max:9999}); $("#sg_subject").autocomplete(data, {minChars: 0, max:9999}); $("#sg_group").autocomplete(data_groups, {minChars: 0, max:9999}); }); """ % (" ".join(user_list), " ".join(group_list)) req.send(out.encode("utf-8"), "text/javascript")
def process_admin_request(self, req, category, page, path_info): mgr = AccountManager(self.env) if path_info: return self._process_user_request(req, category, page, path_info, mgr) else: if req.method == 'POST': if req.args.get('add'): self._do_add(req, mgr) elif req.args.get('remove'): self._do_remove(req, mgr) # run user list through a set to work around a bug in # account manager # see http://trac-hacks.org/ticket/180 users = list(sets.Set(mgr.get_users())) users.sort() req.hdf['admin.users'] = \ [{'name': u, 'key': u, 'href': self.env.href.admin(category, page, u) } for u in users] return 'admin_users.cs', None
def get_users(self): users = {} try: from acct_mgr.api import AccountManager, get_user_attribute acct_mgr = AccountManager(self.env) for username in acct_mgr.get_users(): users[username] = { 'username': username } for username, status in get_user_attribute(self.env, username=None, authenticated=None).iteritems(): user = users.get(username) if user is not None and 1 in status: user['name'] = status[1].get('name') user['email'] = status[1].get('email') user.update(self.get_last_login(username)) except: for username, name, email in self.env.get_known_users(): user = { 'username': username, 'name': name, 'email': email } user.update(self.get_last_login(username)) users[username] = user return users
def fetch_user_data(env, req): acctmgr = AccountManager(env) guard = AccountGuard(env) accounts = {} for username in acctmgr.get_users(): if req.perm.has_permission('ACCTMGR_USER_ADMIN'): url = req.href.admin('accounts', 'users', user=username) else: url = None accounts[username] = {'username': username, 'review_url': url} if guard.user_locked(username): accounts[username]['locked'] = True t_lock = guard.lock_time(username) if t_lock > 0: t_release = guard.pretty_release_time(req, username) accounts[username]['release_hint'] = _( "Locked until %(t_release)s", t_release=t_release) for acct, status in get_user_attribute(env, username=None, authenticated=None).iteritems(): account = accounts.get(acct) if account is not None and 1 in status: # Only use attributes related to authenticated # accounts. account['name'] = status[1].get('name') account['email'] = status[1].get('email') if account['email']: account['email'] = Chrome(env).format_author(req, account['email']) ts_seen = last_seen(env) if ts_seen is not None: for username, last_visit in ts_seen: account = accounts.get(username) if account and last_visit: account['last_visit'] = to_datetime(last_visit) return sorted(accounts.itervalues(), key=lambda acct: acct['username'])
def fetch_user_data(env, req): acctmgr = AccountManager(env) guard = AccountGuard(env) accounts = {} for username in acctmgr.get_users(): if req.perm.has_permission('ACCTMGR_USER_ADMIN'): url = req.href.admin('accounts', 'users', user=username) else: url = None accounts[username] = {'username': username, 'review_url': url} if guard.user_locked(username): accounts[username]['locked'] = True t_lock = guard.lock_time(username) if t_lock > 0: t_release = guard.pretty_release_time(req, username) accounts[username]['release_hint'] = _( "Locked until %(t_release)s", t_release=t_release) for acct, status in get_user_attribute(env, username=None, authenticated=None).iteritems(): account = accounts.get(acct) if account is not None and 1 in status: # Only use attributes related to authenticated # accounts. account['name'] = status[1].get('name') account['email'] = status[1].get('email') if account['email']: account['email'] = Chrome(env).format_author( req, account['email']) ts_seen = last_seen(env) if ts_seen is not None: for username, last_visit in ts_seen: account = accounts.get(username) if account and last_visit: account['last_visit'] = to_datetime(last_visit) return sorted(accounts.itervalues(), key=lambda acct: acct['username'])
class GroupsEditorPlugin(Component): """ Trac Groups Editor plugin Edit the groups in the group file. Select a group Display it's contents Delete or add listed members If the fine grained page permissions plugine is enabled, then as an option also update it. """ implements(IAdminPanelProvider, ITemplateProvider) def __init__(self): self.account_manager = AccountManager(self.env) ### ITemplateProvider methods def get_templates_dirs(self): from pkg_resources import resource_filename return [resource_filename(__name__, 'templates')] def get_htdocs_dirs(self): from pkg_resources import resource_filename return [('ge', resource_filename(__name__, 'htdocs'))] ### IAdminPanelProvider methods def get_admin_panels(self, req): if 'TRAC_ADMIN' in req.perm: yield ('accounts', _("Accounts"), 'groups', _("Groups")) def _get_filename(self, section, name): file_name = self.config.get(section, name) if len(file_name): if not file_name.startswith(os.path.sep) and \ not file_name[1] == ':': file_name = os.path.join(self.env.path, file_name) return file_name else: return None def _group_filename(self): group_file_name = self._get_filename('account-manager', 'group_file') if not group_file_name: group_file_name = self._get_filename('htgroups', 'group_file') if not group_file_name: raise TracError("""Group filename not found in the config file. In neither sections "account-manager" nor "htgroups" under the name "group_file".""") if not os.path.exists(group_file_name): raise TracError('Group filename not found: %s.' % group_file_name) return group_file_name def _get_groups_and_members(self): """Get the groups and their members as a dictionary of lists. """ # could be in one of two places, depending if the account-manager # is installed or not group_file_name = self._group_filename() groups_dict = dict() group_file = file(group_file_name) try: for group_line in group_file: # Ignore blank lines and lines starting with # group_line = group_line.strip() if group_line and not group_line.startswith('#'): group_name = group_line.split(':', 1)[0] group_members = group_line.split(':', 2)[1].split(' ') groups_dict[group_name] = [member.strip() for member in group_members if member] finally: group_file.close() if len(groups_dict): return groups_dict else: return None def _write_groups_file(self, entries): """ Write the groups and members to the groups file """ group_file = open(self._group_filename(), 'w') for group_name in entries.keys(): group_file.write(group_name + ': ' + ' '.join(entries[group_name]) + '\n') group_file.close() def _check_for_finegrained(self): """Check if the fine grained permission system is enabled.""" component = self.config.get('components', 'authzpolicy.authz_policy.authzpolicy') return component == 'enabled' def _update_fine_grained(self, group_details): authz_policy_file_name = self._get_filename('authz_policy', 'authz_file') authz_policy_dict = ConfigObj(authz_policy_file_name) # If there isn't a group file, don't destroy the existing entries if group_details: authz_policy_dict['groups'] = group_details authz_policy_dict.write() def render_admin_panel(self, req, cat, page, path_info): """Render the panel. When applying deletions and additions, additions happen post deletions, so additions in effect have a higher precedence. The way it is done, it shouldn't be possible to have both """ req.perm.require('TRAC_ADMIN') add_stylesheet(req, 'ge/css/htgroupeditor.css') page_args = {} group_details = self._get_groups_and_members() # For ease of understanding and future reading # being done in the ORDER displayed. if not req.method == 'POST': groups_list = [''] if group_details is not None: groups_list += group_details.keys() page_args['groups_list'] = groups_list else: group_name = str(req.args.get('group_name')) # put the selected entry at the top of the list groups_list = group_details.keys() groups_list.remove(group_name) groups_list.insert(0, group_name) # Get rid of duplicates users_list = list() for name in group_details[group_name]: if name not in users_list: users_list.append(name) group_details[group_name] = users_list if req.args.get('deletions'): deletions = req.args.get('deletions') if not isinstance(deletions, list): deletions = [deletions] for deletion in deletions: # In case there arer multiple entries while deletion in group_details[group_name]: group_details[group_name].remove(deletion) if req.args.get('additional_names'): additional_names = req.args.get('additional_names') if not isinstance(additional_names, list): additional_names = [additional_names] # If a reload is done after an add, a duplicate can be created for name in additional_names: if name not in group_details[group_name]: group_details[group_name].append(name) # get the list of users not in the group addable_usernames = [] for username in self.account_manager.get_users(): username = username.strip() if len(username) and not username in group_details[group_name]: addable_usernames.append(username) group_details[group_name].sort() page_args['groups_list'] = groups_list page_args['users_list'] = group_details[group_name] page_args['addable_usernames'] = addable_usernames page_args['group_name'] = group_name page_args['finegrained'] = self._check_for_finegrained() if req.args.get('apply_changes'): self._write_groups_file(group_details) # update the fine grained permissions, if it is installed if req.args.get('finegrained_check'): self._update_fine_grained(group_details) return 'htgroupeditor.html', page_args
class PageAuthzPolicyEditor(Component): implements(IAdminPanelProvider, ITemplateProvider) def __init__(self): self.account_manager = AccountManager(self.env) # ITemplateProvider methods def get_templates_dirs(self): return [resource_filename(__name__, 'templates')] def get_htdocs_dirs(self): return [] # IAdminPanelProvider methods def get_admin_panels(self, req): if 'TRAC_ADMIN' in req.perm: yield ('accounts', translation._('Accounts'), 'pages', translation._('Page Permissions')) def _get_filename(self, section, name): file_name = self.config.get(section, name) if len(file_name): if (not file_name.startswith(os.path.sep)) and (not file_name[1] == (':')): file_name = os.path.join(self.env.path, file_name) return (file_name) else: return (None) def _get_users(self): user_list = ', '.join(self.account_manager.get_users()) return (user_list) def _group_filename(self): group_file_name = self._get_filename('account-manager', 'group_file') if not group_file_name: group_file_name = self._get_filename('htgroups', 'group_file') if not group_file_name: raise TracError( 'Group filename not found in the config file. In neither sections\ "account-manager" nor "htgroups" under the name "group_file".' ) if not os.path.exists(group_file_name): raise TracError('Group filename not found: %s.' % group_file_name) return (group_file_name) # Get the groups and their members so they can easily be included in the # groups section of the authz file. Need it as a dictionary of arrays so it be easily # iterated. def _get_groups_and_members(self): """ Get the groups and their members as a dictionary of lists. """ # could be in one of two places, depending if the # account-manager is installed or not group_file_name = self._group_filename() groups_dict = dict() group_file = file(group_file_name) try: for group_line in group_file: # Ignore blank lines and lines starting with # group_line = group_line.strip() if group_line and not group_line.startswith('#'): group_name = group_line.split(':', 1)[0] group_members = group_line.split(':', 2)[1].split(' ') groups_dict[group_name] = [ x for x in [member.strip() for member in group_members] if x ] finally: group_file.close() if len(groups_dict): return groups_dict else: return None def render_admin_panel(self, req, cat, page, path_info): req.perm.require('TRAC_ADMIN') authz_policy_file_name = self._get_filename('authz_policy', 'authz_file') group_details = self._get_groups_and_members() # Handle the return data if req.method == 'POST': if req.args.get('authz_file_contents'): # The data needs to be validated, otherwise duplicate # entries can break things. edited_contents = str(req.args.get('authz_file_contents')) edited_contents_stringio = StringIO(edited_contents) try: test_authz_policy_dict = ConfigObj( edited_contents_stringio) except: raise TracError( _('Error in edited file. Re-edit and check for duplicate entries.' )) authz_policy_file = open(authz_policy_file_name, 'w') test_authz_policy_dict.write(authz_policy_file) authz_policy_file.close() authz_policy_dict = ConfigObj(authz_policy_file_name) # If there isn't a group file, don't destroy the existing entries if (group_details): authz_policy_dict['groups'] = group_details # This is purely to fill in the text area with the contents. contents = StringIO() authz_policy_dict.write(contents) #contents = open(authz_policy_file_name).readlines() data = { 'file_name': authz_policy_file_name, 'contents': contents.getvalue(), 'users': self._get_users() } return 'page_authz_policy_editor.html', {'pages_authz': data}
class GroupsEditorPlugin(Component): """ Trac Groups Editor plugin Edit the groups in the group file. Select a group Display it's contents Delete or add listed members If the fine grained page permissions plugine is enabled, then as an option also update it. """ implements(IAdminPanelProvider, ITemplateProvider) def __init__(self): self.account_manager = AccountManager(self.env) ### ITemplateProvider methods def get_templates_dirs(self): from pkg_resources import resource_filename return [resource_filename(__name__, 'templates')] def get_htdocs_dirs(self): from pkg_resources import resource_filename return [('ge', resource_filename(__name__, 'htdocs'))] ### IAdminPanelProvider methods def get_admin_panels(self, req): if 'TRAC_ADMIN' in req.perm: yield ('accounts', _("Accounts"), 'groups', _("Groups")) def _get_filename(self, section, name): file_name = self.config.get(section, name) if len(file_name): if not file_name.startswith(os.path.sep) and \ not file_name[1] == ':': file_name = os.path.join(self.env.path, file_name) return file_name else: return None def _group_filename(self): group_file_name = self._get_filename('account-manager', 'group_file') if not group_file_name: group_file_name = self._get_filename('htgroups', 'group_file') if not group_file_name: raise TracError("""Group filename not found in the config file. In neither sections "account-manager" nor "htgroups" under the name "group_file".""") if not os.path.exists(group_file_name): raise TracError('Group filename not found: %s.' % group_file_name) return group_file_name def _get_groups_and_members(self): """Get the groups and their members as a dictionary of lists. """ # could be in one of two places, depending if the account-manager # is installed or not group_file_name = self._group_filename() groups_dict = dict() group_file = file(group_file_name) try: for group_line in group_file: # Ignore blank lines and lines starting with # group_line = group_line.strip() if group_line and not group_line.startswith('#'): group_name = group_line.split(':', 1)[0] group_members = group_line.split(':', 2)[1].split(' ') groups_dict[group_name] = [ member.strip() for member in group_members if member ] finally: group_file.close() if len(groups_dict): return groups_dict else: return None def _write_groups_file(self, entries): """ Write the groups and members to the groups file """ group_file = open(self._group_filename(), 'w') for group_name in entries.keys(): group_file.write(group_name + ': ' + ' '.join(entries[group_name]) + '\n') group_file.close() def _check_for_finegrained(self): """Check if the fine grained permission system is enabled.""" component = self.config.get('components', 'authzpolicy.authz_policy.authzpolicy') return component == 'enabled' def _update_fine_grained(self, group_details): authz_policy_file_name = self._get_filename('authz_policy', 'authz_file') authz_policy_dict = ConfigObj(authz_policy_file_name) # If there isn't a group file, don't destroy the existing entries if group_details: authz_policy_dict['groups'] = group_details authz_policy_dict.write() def render_admin_panel(self, req, cat, page, path_info): """Render the panel. When applying deletions and additions, additions happen post deletions, so additions in effect have a higher precedence. The way it is done, it shouldn't be possible to have both """ req.perm.require('TRAC_ADMIN') add_stylesheet(req, 'ge/css/htgroupeditor.css') page_args = {} group_details = self._get_groups_and_members() # For ease of understanding and future reading # being done in the ORDER displayed. if not req.method == 'POST': groups_list = [''] if group_details is not None: groups_list += group_details.keys() page_args['groups_list'] = groups_list else: group_name = str(req.args.get('group_name')) # put the selected entry at the top of the list groups_list = group_details.keys() groups_list.remove(group_name) groups_list.insert(0, group_name) # Get rid of duplicates users_list = list() for name in group_details[group_name]: if name not in users_list: users_list.append(name) group_details[group_name] = users_list if req.args.get('deletions'): deletions = req.args.get('deletions') if not isinstance(deletions, list): deletions = [deletions] for deletion in deletions: # In case there arer multiple entries while deletion in group_details[group_name]: group_details[group_name].remove(deletion) if req.args.get('additional_names'): additional_names = req.args.get('additional_names') if not isinstance(additional_names, list): additional_names = [additional_names] # If a reload is done after an add, a duplicate can be created for name in additional_names: if name not in group_details[group_name]: group_details[group_name].append(name) # get the list of users not in the group addable_usernames = [] for username in self.account_manager.get_users(): username = username.strip() if len(username) and not username in group_details[group_name]: addable_usernames.append(username) group_details[group_name].sort() page_args['groups_list'] = groups_list page_args['users_list'] = group_details[group_name] page_args['addable_usernames'] = addable_usernames page_args['group_name'] = group_name page_args['finegrained'] = self._check_for_finegrained() if req.args.get('apply_changes'): self._write_groups_file(group_details) # update the fine grained permissions, if it is installed if req.args.get('finegrained_check'): self._update_fine_grained(group_details) return 'htgroupeditor.html', page_args
def expand_macro(self, formatter, name, content): env = formatter.env req = formatter.req if not content: args = [] kw = {} else: args, kw = parse_args(content) if name == 'ProjectStats': if 'wiki' in kw.keys(): prefix = 'prefix' in kw.keys() and kw['prefix'] or None wiki = WikiSystem(env) if kw['wiki'] == 'count' or 'count' in args: return tag(len(list(wiki.get_pages(prefix)))) elif name == 'UserQuery': msg_no_perm = tag.p(tag_("(required %(perm)s missing)", perm=tag.strong('USER_VIEW')), class_='hint') if 'perm' in kw.keys(): perm_sys = PermissionSystem(self.env) users = perm_sys.get_users_with_permission(kw['perm'].upper()) else: acct_mgr = AccountManager(env) users = list(set(acct_mgr.get_users())) if 'locked' in kw.keys() or 'locked' in args: guard = AccountGuard(env) locked = [] for user in users: if guard.user_locked(user): locked.append(user) if kw.get('locked', 'True').lower() in ('true', 'yes', '1'): users = locked else: users = list(set(users) - set(locked)) elif 'visit' in kw.keys() or 'visit' in args: if 'USER_VIEW' not in req.perm: return msg_no_perm cols = [] data = {'accounts': fetch_user_data(env, req), 'cls': 'wiki'} for col in ('email', 'name'): if col in args: cols.append(col) data['cols'] = cols return Chrome(env).render_template(req, 'user_table.html', data, 'text/html', True) if kw.get('format') == 'count' or 'count' in args: return tag(len(users)) if 'USER_VIEW' not in req.perm: return msg_no_perm if 'email' in args or 'name' in args: # Replace username with full name, add email if available. for username, name, email in self.env.get_known_users(): if username in users: if 'name' not in args or name is None: name = username if 'email' in args and email is not None: email = ''.join(['<', email, '>']) name = ' '.join([name, email]) if not username == name: users.pop(users.index(username)) users.append(name) if not users and 'nomatch' in kw.keys(): return format_to_oneliner(env, formatter.context, kw['nomatch']) users = sorted(users) if kw.get('format') == 'list': return tag.ul([ tag.li(Chrome(env).format_author(req, user)) for user in users ]) else: # Default output format: comma-separated list. return tag(', '.join( [Chrome(env).format_author(req, user) for user in users]))
def expand_macro(self, formatter, name, content): env = formatter.env req = formatter.req if not content: args = [] kw = {} else: args, kw = parse_args(content) if name == 'ProjectStats': if 'wiki' in kw.keys(): prefix = 'prefix' in kw.keys() and kw['prefix'] or None wiki = WikiSystem(env) if kw['wiki'] == 'count' or 'count' in args: return tag(len(list(wiki.get_pages(prefix)))) elif name == 'UserQuery': msg_no_perm = tag.p(tag_("(required %(perm)s missing)", perm=tag.strong('USER_VIEW')), class_='hint') if 'perm' in kw.keys(): perm_sys = PermissionSystem(self.env) users = perm_sys.get_users_with_permission(kw['perm'].upper()) else: acct_mgr = AccountManager(env) users = list(set(acct_mgr.get_users())) if 'locked' in kw.keys() or 'locked' in args: guard = AccountGuard(env) locked = [] for user in users: if guard.user_locked(user): locked.append(user) if kw.get('locked', 'True').lower() in ('true', 'yes', '1'): users = locked else: users = list(set(users) - set(locked)) elif 'visit' in kw.keys() or 'visit' in args: if 'USER_VIEW' not in req.perm: return msg_no_perm cols = [] data = {'accounts': fetch_user_data(env, req), 'cls': 'wiki'} for col in ('email', 'name'): if col in args: cols.append(col) data['cols'] = cols return Chrome(env).render_template( req, 'user_table.html', data, 'text/html', True) if kw.get('format') == 'count' or 'count' in args: return tag(len(users)) if 'USER_VIEW' not in req.perm: return msg_no_perm if 'email' in args or 'name' in args: # Replace username with full name, add email if available. for username, name, email in self.env.get_known_users(): if username in users: if 'name' not in args or name is None: name = username if 'email' in args and email is not None: email = ''.join(['<', email, '>']) name = ' '.join([name, email]) if not username == name: users.pop(users.index(username)) users.append(name) if not users and 'nomatch' in kw.keys(): return format_to_oneliner(env, formatter.context, kw['nomatch']) users = sorted(users) if kw.get('format') == 'list': return tag.ul([tag.li(Chrome(env).format_author(req, user)) for user in users]) else: # Default output format: comma-separated list. return tag(', '.join([Chrome(env).format_author(req, user) for user in users]))
class GroupsEditorPlugin(Component): implements(IAdminPanelProvider, ITemplateProvider, IPermissionGroupProvider) def __init__(self): self.account_manager = AccountManager(self.env) # ITemplateProvider methods # Used to add the plugin's templates and htdocs def get_templates_dirs(self): return [resource_filename(__name__, 'templates')] def get_htdocs_dirs(self): return [] # IPermissionGroupProvider methdos def get_permission_groups(self, username): """Return a list of names of the groups that the user with the specified name is a member of.""" groups_list = [] groups_dict = self._get_groups_and_members() or {} for group,usernames in groups_dict.items(): if username in usernames: groups_list.append(group) return groups_list # IAdminPanelProvider methods def get_admin_panels(self, req): """Return a list of available admin panels. The items returned by this function must be tuples of the form `(category, category_label, page, page_label)`. """ if 'TRAC_ADMIN' in req.perm: # Simply put, it's what goes in the menu on the left! # the page is the name of the page that will be called for the menu entry # it will go ...../category/page yield ('accounts', translation._('Accounts'), 'groups', translation._('Groups')) def _get_filename(self, section, name): file_name = self.config.get(section, name) if len(file_name): if (not file_name.startswith(os.path.sep)) and (not file_name[1] == (':')): file_name = os.path.join(self.env.path, file_name) return(file_name) else: return(None) def _group_filename(self): group_file_name = self._get_filename('account-manager', 'group_file') if not group_file_name: group_file_name = self._get_filename('htgroups', 'group_file') if not group_file_name: raise TracError('Group filename not found in the config file. In neither sections\ "account-manager" nor "htgroups" under the name "group_file".') if not os.path.exists(group_file_name): raise TracError('Group filename not found: %s.' % group_file_name) return(group_file_name) def _get_groups_and_members(self): """ Get the groups and their members as a dictionary of lists. """ # could be in one of two places, depending if the # account-manager is installed or not group_file_name = self._group_filename() groups_dict = dict() group_file = file(group_file_name) try: for group_line in group_file: # Ignore blank lines and lines starting with # group_line = group_line.strip() if group_line and not group_line.startswith('#'): group_name = group_line.split(':', 1)[0] group_members = group_line.split(':', 2)[1].split(' ') groups_dict[group_name] = [ x for x in [member.strip() for member in group_members] if x ] finally: group_file.close() if len(groups_dict): return groups_dict else: return None def _write_groups_file(self, entries): """ Write the groups and members to the groups file """ group_file = open(self._group_filename(), 'w') for group_name in entries.keys(): group_file.write(group_name + ': ' + ' '.join(entries[group_name]) + '\n') group_file.close() def _check_for_finegrained(self): """ Check if the fine grained permission system is installed """ return (self.config.getbool('components', 'authzpolicy.authz_policy.authzpolicy') or self.config.getbool('components', 'authz_policy.authzpolicy') ) def _check_for_svnauthz(self): """ Check if the SVN Authz Plugin is installed """ return self.config.getbool('components','svnauthz.svnauthz.svnauthzplugin') def _update_fine_grained(self, group_details): #import ConfigObj authz_policy_file_name = self._get_filename('authz_policy', 'authz_file') authz_policy_dict = ConfigObj(authz_policy_file_name) # If there isn't a group file, don't destroy the existing entries if (group_details): authz_policy_dict['groups'] = group_details authz_policy_dict.write() def _update_svnauthz(self, group_details): svnauthz_policy_file_name = self._get_filename('trac','authz_file') svnauthz_policy_dict = ConfigObj(svnauthz_policy_file_name) # If there isn't a group file, don't destroy the existing entries if (group_details): svnauthz_policy_dict['groups'] = group_details svnauthz_policy_dict.write() def render_admin_panel(self, req, cat, page, path_info): """ Render up the panel. When applying deletions and additions, additions happen post deletions, so additions in effect have a higher precedence. The way it is done, it shouldn't be possible to have both """ req.perm.require('TRAC_ADMIN') add_stylesheet(req, 'ge/css/htgroupeditor.css') page_args = {} group_details = self._get_groups_and_members() # This option needs to be set if the admin can add and delete groups # usage in trac.ini: # [htgroupedit] # allowgroupedit = enabled allowgroupedit = self.config.getbool("htgroupeditor","allowgroupedit") # For ease of understanding and future reading # being done in the ORDER displayed. if not req.method == 'POST': groups_list = ['']; if group_details is not None: groups_list = groups_list + group_details.keys() page_args['groups_list'] = groups_list page_args['allowgroupedit'] = allowgroupedit return 'htgroupeditor.html', page_args else: if req.args.get('new_group') and group_details is None: # It can only happen for new_group, that group_details is None groups_list = [] group_details = {} else: groups_list = group_details.keys() if req.args.get('new_group') and allowgroupedit: # Create new group and select it if not req.args.get('new_group_name') or len(str(req.args.get('new_group_name')).strip()) == 0: raise TracError("Group name was empty") group_name = str(req.args.get('new_group_name')).strip() if group_name in group_details.keys(): raise TracError("Group already exists") group_details[group_name] = [] groups_list.append(group_name) elif req.args.get('delete_group') and allowgroupedit: # Deleting a group involves removing it from list and details delete_group_name = str(req.args.get('group_name')).strip() if not delete_group_name in groups_list: raise TracError("Invalid group for deletion") del group_details[delete_group_name] groups_list.remove(delete_group_name) if len(groups_list) == 0: # In case that was the last group, the subsequent won't work # Thus we have to do the writing of files here and return the empty page self._write_groups_file(group_details) if req.args.get('finegrained_check'): self._update_fine_grained(group_details) if req.args.get('svnauthz_check'): self._update_svnauthz(group_details) # Finally send the empty page page_args['groups_list'] = [''] page_args['allowgroupedit'] = allowgroupedit return 'htgroupeditor.html', page_args # Select first group in list group_name = groups_list[0] else: # Select group based on the request group_name = str(req.args.get('group_name')) # put the selected entry at the top of the list groups_list.remove(group_name) groups_list.insert(0, group_name) # Get rid of duplicates users_list = list() for name in group_details[group_name]: if name not in users_list: users_list.append(name) group_details[group_name] = sorted(users_list) if req.args.get('deletions'): deletions = req.args.get('deletions') # if only on entry it will be a string, so need to make it a list if not isinstance(deletions, list): deletions = [deletions] for deletion in deletions: # In case there arer multiple entries while deletion in group_details[group_name]: group_details[group_name].remove(deletion) if req.args.get('additional_names'): additional_names = req.args.get('additional_names') if not isinstance(additional_names, list): additional_names = [additional_names] # If a reload is done after an add, a duplicate can be created. for name in additional_names: if name not in group_details[group_name]: group_details[group_name].append(name) # get the list of users not in the group addable_usernames = [] for username in self.account_manager.get_users(): username = username.strip() if len(username) and not username in group_details[group_name]: addable_usernames.append(username) group_details[group_name].sort() addable_usernames.sort() page_args['groups_list'] = groups_list page_args['users_list'] = group_details[group_name] page_args['addable_usernames'] = addable_usernames page_args['group_name'] = group_name page_args['finegrained'] = self._check_for_finegrained() page_args['svnauthz'] = self._check_for_svnauthz() page_args['allowgroupedit'] = allowgroupedit if req.args.get('apply_changes') or req.args.get('new_group') or req.args.get('delete_group'): self._write_groups_file(group_details) # update the fine grained permissions, if it is installed if req.args.get('finegrained_check'): self._update_fine_grained(group_details) if req.args.get('svnauthz_check'): self._update_svnauthz(group_details) return 'htgroupeditor.html', page_args
class PageAuthzPolicyEditor(Component): implements(IAdminPanelProvider, ITemplateProvider) def __init__(self): self.account_manager = AccountManager(self.env) # ITemplateProvider methods def get_templates_dirs(self): return [resource_filename(__name__, 'templates')] def get_htdocs_dirs(self): return [] # IAdminPanelProvider methods def get_admin_panels(self, req): if 'TRAC_ADMIN' in req.perm: yield ('accounts', translation._('Accounts'), 'pages', translation._('Page Permissions')) def _get_filename(self, section, name): file_name = self.config.get(section, name) if len(file_name): if (not file_name.startswith(os.path.sep)) and (not file_name[1] == (':')): file_name = os.path.join(self.env.path, file_name) return(file_name) else: return(None) def _get_users(self): user_list = ', '.join(self.account_manager.get_users()) return(user_list) def _group_filename(self): group_file_name = self._get_filename('account-manager', 'group_file') if not group_file_name: group_file_name = self._get_filename('htgroups', 'group_file') if not group_file_name: raise TracError('Group filename not found in the config file. In neither sections\ "account-manager" nor "htgroups" under the name "group_file".') if not os.path.exists(group_file_name): raise TracError('Group filename not found: %s.' % group_file_name) return(group_file_name) # Get the groups and their members so they can easily be included in the # groups section of the authz file. Need it as a dictionary of arrays so it be easily # iterated. def _get_groups_and_members(self): """ Get the groups and their members as a dictionary of lists. """ # could be in one of two places, depending if the # account-manager is installed or not group_file_name = self._group_filename() groups_dict = dict() group_file = file(group_file_name) try: for group_line in group_file: # Ignore blank lines and lines starting with # group_line = group_line.strip() if group_line and not group_line.startswith('#'): group_name = group_line.split(':', 1)[0] group_members = group_line.split(':', 2)[1].split(' ') groups_dict[group_name] = [ x for x in [member.strip() for member in group_members] if x ] finally: group_file.close() if len(groups_dict): return groups_dict else: return None def render_admin_panel(self, req, cat, page, path_info): req.perm.require('TRAC_ADMIN') authz_policy_file_name = self._get_filename('authz_policy', 'authz_file') group_details = self._get_groups_and_members() # Handle the return data if req.method == 'POST': if req.args.get('authz_file_contents'): # The data needs to be validated, otherwise duplicate # entries can break things. edited_contents = str(req.args.get('authz_file_contents')) edited_contents_stringio = StringIO(edited_contents) try: test_authz_policy_dict = ConfigObj(edited_contents_stringio) except: raise TracError(_('Error in edited file. Re-edit and check for duplicate entries.')) authz_policy_file = open(authz_policy_file_name, 'w') test_authz_policy_dict.write(authz_policy_file) authz_policy_file.close() authz_policy_dict = ConfigObj(authz_policy_file_name) # If there isn't a group file, don't destroy the existing entries if (group_details): authz_policy_dict['groups'] = group_details # This is purely to fill in the text area with the contents. contents = StringIO() authz_policy_dict.write(contents) #contents = open(authz_policy_file_name).readlines() data = { 'file_name' : authz_policy_file_name, 'contents': contents.getvalue(), 'users' : self._get_users() } return 'page_authz_policy_editor.html', {'pages_authz': data}