def test_can_edit_ticket_if_he_can_create_referenced_tickets(self): self.teh.grant_permission(name_team_member, Role.TEAM_MEMBER) perm_cache = PermissionCache(self.env, name_team_member) story = self.teh.create_ticket(Type.USER_STORY) self.assert_true(perm_cache.has_permission(Action.TICKET_EDIT_PAGE_ACCESS, story.resource)) self.assert_false(perm_cache.has_permission(Action.TICKET_EDIT, story.resource))
def test_can_edit_ticket_if_he_can_create_referenced_tickets(self): self.teh.grant_permission(name_team_member, Role.TEAM_MEMBER) perm_cache = PermissionCache(self.env, name_team_member) story = self.teh.create_ticket(Type.USER_STORY) self.assert_true( perm_cache.has_permission(Action.TICKET_EDIT_PAGE_ACCESS, story.resource)) self.assert_false( perm_cache.has_permission(Action.TICKET_EDIT, story.resource))
def test_can_edit_ticket_for_custom_types(self): custom_type_name = 'MaintenanceTask' self._create_custom_ticket_type(custom_type_name, [Key.COMPONENT]) permission_name = 'CREATE_' + custom_type_name.upper() self.teh.grant_permission(name_team_member, permission_name) self.teh.grant_permission(name_team_member, Role.TEAM_MEMBER) perm_cache = PermissionCache(self.env, name_team_member) ticket = self.teh.create_ticket(custom_type_name) self.assert_true(perm_cache.has_permission(Action.TICKET_EDIT, ticket.resource)) self.assert_true(perm_cache.has_permission(Action.TICKET_EDIT_PAGE_ACCESS, ticket.resource))
def is_authorised(authname, perms, env, permission=None, list_permission='PROJECT_ACCESS'): if not authname: authname = 'anonymous' perms = PermissionCache(env, authname) if not perms.has_permission(list_permission): return False if permission: return perms.has_permission(permission) return True
def test_can_edit_ticket_for_custom_types(self): custom_type_name = 'MaintenanceTask' self._create_custom_ticket_type(custom_type_name, [Key.COMPONENT]) permission_name = 'CREATE_' + custom_type_name.upper() self.teh.grant_permission(name_team_member, permission_name) self.teh.grant_permission(name_team_member, Role.TEAM_MEMBER) perm_cache = PermissionCache(self.env, name_team_member) ticket = self.teh.create_ticket(custom_type_name) self.assert_true( perm_cache.has_permission(Action.TICKET_EDIT, ticket.resource)) self.assert_true( perm_cache.has_permission(Action.TICKET_EDIT_PAGE_ACCESS, ticket.resource))
def filter_subscriptions(self, event, subscriptions): action = '%s_VIEW' % event.realm.upper() for subscription in subscriptions: if event.realm in self.exception_realms: yield subscription continue sid, auth = subscription[1:3] # PermissionCache already takes care of sid = None if not auth: sid = 'anonymous' perm = PermissionCache(self.env, sid) resource_id = get_target_id(event.target) self.log.debug( 'Checking *_VIEW permission on event for resource %s:%s' % (event.realm, resource_id) ) if perm.has_permission(action) and action in perm(event.realm, resource_id): yield subscription else: self.log.debug( "Filtering %s because of %s rule" % (sid, self.__class__.__name__) )
def _send_index(self, environ, start_response): projects = [] for env_name in os.listdir(self.path): env_path = os.path.join(self.path, env_name) try: env = open_environment(env_path) env_perm = PermissionCache( PermissionSystem(env).get_user_permissions( environ.get("REMOTE_USER", "anonymous"))) if env_perm.has_permission('WIKI_VIEW'): projects.append({ 'name': env.project_name, 'description': env.project_description, # XXX: get rid of the double / in the beginning 'href': construct_url(environ, path_info="/" + env_name), }) except Exception: pass projects.sort(lambda x, y: cmp(x['name'].lower(), y['name'].lower())) start_response("200 OK", [('content-type', 'text/html')]) return self.template.render({"projects": projects}, format='xhtml', template="wsgiplugin.index")
def match(self, message): reporter = emailaddr2user(self.env, message['from']) reply_to_ticket = ReplyToTicket(self.env) perm = PermissionCache(self.env, reporter) if not perm.has_permission('TICKET_ADD_HOURS'): return False return bool(reply_to_ticket.ticket(message))
def test_edit_description_action_is_scoped_as_well(self): self.teh.grant_permission(name_team_member, Role.TEAM_MEMBER) perm = PermissionCache(self.env, name_team_member) requirement = self.teh.create_ticket(Type.REQUIREMENT) requirement_resource = requirement.resource self.assert_false( perm.has_permission(Action.TICKET_EDIT_DESCRIPTION, requirement_resource))
def ticket_changed(self, ticket, comment, author, old_values): """Called when a ticket is modified. `old_values` is a dictionary containing the previous values of the fields that have changed. """ perm = PermissionCache(self.env, author) if perm.has_permission('TICKET_ADD_HOURS'): self.add_hours_by_comment(comment, ticket.id, author)
def watch_hours(self, ticket): def readTicketValue(name, tipe, default=0): if ticket.values.has_key(name): return tipe(ticket.values[name] or default) else: cursor = self.env.get_db_cnx().cursor() cursor.execute( "SELECT * FROM ticket_custom where ticket=%s and name=%s", (ticket.id, name)) val = cursor.fetchone() if val: return tipe(val[2] or default) return default hours = readTicketValue("hours", convertfloat) totalHours = readTicketValue("totalhours", convertfloat) db = self.env.get_db_cnx() ticket_id = ticket.id cl = ticket.get_changelog() self.log.debug("found hours: " + str(hours)) #self.log.debug("Dir_ticket:"+str(dir(ticket))) #self.log.debug("ticket.values:"+str(ticket.values)) #self.log.debug("changelog:"+str(cl)) most_recent_change = None if cl: most_recent_change = cl[-1] change_time = most_recent_change[0] author = most_recent_change[1] else: change_time = ticket.time_created author = ticket.values["reporter"] self.log.debug("Checking permissions") perm = PermissionCache(self.env, author) if not perm or not perm.has_permission("TIME_RECORD"): self.log.debug( "Skipping recording because no permission to affect time") if hours != 0: tup = (ticket_id, author, change_time, "hours") self.log.debug("deleting ticket change %s %s %s %s" % tup) try: delete_ticket_change(self, ticket_id, author, change_time, "hours") except Exception, e: self.log.debug("FAIL: %s" % e) self.log.debug("hours change deleted") return
def filter_subscriptions(self, event, subscriptions): action = '%s_VIEW'%event.realm.upper() for subscription in subscriptions: sid, auth = subscription[1:3] # PermissionCache already takes care of sid = None if not auth: sid = 'anonymous' perm = PermissionCache(self.env, sid) if event.realm in self.exception_realms or perm.has_permission(action): yield subscription else: self.log.debug( "Filtering %s with realm %s because of rule: DefaultPermissionFilter"\ % (sid, event.realm) )
def watch_hours(self, ticket): def readTicketValue(name, tipe, default=0): if ticket.values.has_key(name): return tipe(ticket.values[name] or default) else: cursor = self.env.get_db_cnx().cursor() cursor.execute("SELECT * FROM ticket_custom where ticket=%s and name=%s" , (ticket.id, name)) val = cursor.fetchone() if val: return tipe(val[2] or default) return default hours = readTicketValue("hours", convertfloat) totalHours = readTicketValue("totalhours", convertfloat) db = self.env.get_db_cnx() ticket_id = ticket.id cl = ticket.get_changelog() self.log.debug("found hours: "+str(hours )); #self.log.debug("Dir_ticket:"+str(dir(ticket))) #self.log.debug("ticket.values:"+str(ticket.values)) #self.log.debug("changelog:"+str(cl)) most_recent_change = None if cl: most_recent_change = cl[-1]; change_time = most_recent_change[0] author = most_recent_change[1] else: change_time = ticket.time_created author = ticket.values["reporter"] self.log.debug("Checking permissions") perm = PermissionCache(self.env, author) if not perm or not perm.has_permission("TIME_RECORD"): self.log.debug("Skipping recording because no permission to affect time") if hours != 0: tup = (ticket_id, author, change_time, "hours") self.log.debug("deleting ticket change %s %s %s %s" % tup) try: delete_ticket_change(self, ticket_id, author, change_time, "hours") except Exception, e: self.log.debug("FAIL: %s" % e) self.log.debug("hours change deleted") return
def filter_subscriptions(self, event, subscriptions): action = 'ACCTMGR_USER_ADMIN' for subscription in subscriptions: if event.realm != 'acct_mgr': yield subscription continue # Make acct_mgr subscriptions available only for admins. sid, auth = subscription[1:3] # PermissionCache already takes care of sid = None if not auth: sid = 'anonymous' perm = PermissionCache(self.env, sid) if perm.has_permission(action): yield subscription else: self.log.debug("Filtering %s because of %s rule" % (sid, self.__class__.__name__))
def filter_subscriptions(self, event, subscriptions): action = 'ACCTMGR_USER_ADMIN' for subscription in subscriptions: if event.realm != 'acct_mgr': yield subscription continue # Make acct_mgr subscriptions available only for admins. sid, auth = subscription[1:3] # PermissionCache already takes care of sid = None if not auth: sid = 'anonymous' perm = PermissionCache(self.env, sid) if perm.has_permission(action): yield subscription else: self.log.debug( "Filtering %s because of %s rule" % (sid, self.__class__.__name__) )
def process_request(self, req): parent_dir = os.path.dirname(self.env.path) #env_paths = dict([(filename, os.path.join(parent_dir, filename)) # for filename in os.listdir(parent_dir)]) projects = [] for env_name in os.listdir(parent_dir): env_path = os.path.join(parent_dir, env_name) # Don't list this environment if env_path == self.env.path: continue try: env = open_environment(env_path) try: #self.log.debug(env.path) env_perm = PermissionCache(env, req.authname) #self.log.debug(env_perm.perms) if env_perm.has_permission('PROJECT_VIEW'): projects.append({ 'name': env.project_name, 'description': env.project_description, 'href': req.href.projects(env_name), }) except Exception, e: # Only show errors to admins to prevent excessive disclosure if req.perm.has_permission('TRACFORGE_ADMIN'): projects.append({ 'name': env.project_name, 'description': to_unicode(e) }) except Exception, e: if req.perm.has_permission('TRACFORGE_ADMIN'): projects.append({ 'name': env_path, 'description': to_unicode(e), })
def _send_index(self, environ, start_response): projects = [] for env_name in os.listdir(self.path): env_path = os.path.join(self.path, env_name) try: env = open_environment(env_path) env_perm = PermissionCache(PermissionSystem(env).get_user_permissions(environ.get("REMOTE_USER", "anonymous"))) if env_perm.has_permission('WIKI_VIEW'): projects.append({ 'name': env.project_name, 'description': env.project_description, # XXX: get rid of the double / in the beginning 'href': construct_url(environ, path_info="/"+env_name), }) except Exception: pass projects.sort(lambda x, y: cmp(x['name'].lower(), y['name'].lower())) start_response("200 OK", [('content-type', 'text/html')]) return self.template.render({"projects":projects}, format='xhtml', template = "wsgiplugin.index")
def filter_subscriptions(self, event, subscriptions): action = '%s_VIEW' % event.realm.upper() for subscription in subscriptions: if event.realm in self.exception_realms: yield subscription continue sid, auth = subscription[1:3] # PermissionCache already takes care of sid = None if not auth: sid = 'anonymous' perm = PermissionCache(self.env, sid) resource_id = get_target_id(event.target) self.log.debug( 'Checking *_VIEW permission on event for resource %s:%s' % (event.realm, resource_id)) if perm.has_permission(action) and action in perm( event.realm, resource_id): yield subscription else: self.log.debug("Filtering %s because of %s rule" % (sid, self.__class__.__name__))
def edit_user(self, req): """ Handle user edit: view & save """ changes = {} username = req.args.get('username') if not username: add_warning(req, _('Invalid username')) return self.list_users(req) # Load user being edited userstore = get_userstore() user = userstore.getUser(username) if not user: add_warning(req, _('Invalid username (or connection error)')) return self.list_users(req) # Load user who's doing the edit changed_by = userstore.getUser(req.authname) papi = Projects() # Check permissions and redirect to user listing (handy after editing the user) req.perm.require('USER_AUTHOR', Resource('user', id=user.id)) data = req.args data['user'] = user data['author'] = userstore.getUserWhereId(user.author_id) if user.author_id else None data['base_path'] = req.base_path data['dateformats'] = DATEFORMATS data['is_local'] = userstore.is_local(user) data['now'] = datetime.utcnow() data['expired'] = user.expires and ((user.expires - datetime.utcnow()).days < 0) data['states'] = userstore.USER_STATUS_LABELS data['projects'] = papi.get_authored_projects(user) # Add javascript libraries for datepicker and autocomplete add_script(req, 'multiproject/js/jquery-ui.js') add_stylesheet(req, 'multiproject/css/jquery-ui.css') add_script(req, 'multiproject/js/multiproject.js') add_script(req, 'multiproject/js/admin_user_edit.js') # If get request show edit if req.method.upper() == 'GET': return 'admin_user_edit.html', data # Close pressed: get back to user listing if req.args.get('close'): return req.redirect(req.href('admin/users/manage')) # Handle save if 'limitexceeded' in req.args: add_warning(req, _('Picture you tried to upload was too big. Try a smaller one')) # Update author if changed author_id = req.args.get('author_id', None) # If id field is empty but name is not: manual input if not author_id and req.args.get('author_text'): add_warning(req, _('Author cannot be found')) return 'admin_user_edit.html', data # Check set reset the author if author_id: author = userstore.getUserWhereId(int(author_id)) if not author: add_warning(req, _('Author cannot be found')) return 'admin_user_edit.html', data # Check if author is valid: has permission to author? perm = PermissionCache(self.env, author.username) if 'USER_AUTHOR' not in perm: add_warning(req, _('User %s cannot work as an author (does not have USER_AUTHOR permissions)' % author)) return self.back(req) user.author_id = author.id changes['author'] = author else: user.author_id = None user.lastName = req.args.get('last') if not user.lastName: add_warning(req, _('Last name required')) return self.back(req) old_mail = user.mail user.mail = req.args.get('email') if not user.mail: add_warning(req, _('Email address required')) return self.back(req) if old_mail != user.mail: changes['email'] = user.mail org_store = CQDEOrganizationStore.instance() # TODO: is this correct? # When changing email, reset organizations to which the user belongs in user.organization_keys = org_store.get_organization_keys(user) or None # Update password if changed password = req.args.get('password') if password: if not userstore.is_local(user): add_warning(req, _("Can't change password for user that uses external authentication method")) return self.back(req) if len(password) < 7: add_warning(req, _("Password must be at least 7 characters long - please provide longer password")) return self.back(req) if password != req.args.get('confirmpw'): add_warning(req, _("Password do not match - please check")) return self.back(req) user.givenName = req.args.get('first') user.mobile = req.args.get('mobile') # Set or reset account expiration date expiration_str = req.args.get('expires', '') if expiration_str: try: # Parse date and set expiration time in the end of the day expires = datetime.strptime(expiration_str, DATEFORMATS['py']) expires += timedelta(hours=23, minutes=59, seconds=59) # If changed if expires != user.expires: user.expires = expires changes['expiration_date'] = user.expires.strftime(DATEFORMATS['py']) except Exception: self.log.exception('Date formatting failed') add_warning(req, _('Non-recognized expiration format')) pass # Remove expiration date elif user.expires: changes['expiration_date'] = 'Never expires' user.expires = None # Update status if set status = int(req.args.get('status', 0)) if status and status in userstore.USER_STATUS_LABELS.keys() and user.status != status: changes['status'] = userstore.USER_STATUS_LABELS[status] user.status = status if req.args.get('removeicon'): user.icon = None else: icon = req.args.get('icon') if not isinstance(icon, unicode) and icon.filename: user.createIcon(req.args.get('icon')) self.log.info('Saving changes to user: %s' % user) ok = userstore.updateUser(user) if ok and password: changes['password'] = password ok = userstore.updatePassword(user, password) if not ok: add_warning(req, _("Could not save changes")) add_notice(req, _("User %s updated" % username)) # Notify user about changes via email? if req.args.get('notify'): data = { 'user':user, 'changed_by':changed_by, 'changes':changes } try: enotify = EmailNotifier(self.env, "Account updated", data) enotify.template_name = 'account_edited.txt' enotify.notify(user.mail) add_notice(req, _("Notified user about the changes")) except TracError: add_warning(req, _("Failed to send email notification - user changed anyway")) # Check if user has still (after modification) permission to modify user # NOTE: req.perm cannot be used here because it is not updated yet resource = Resource('user', id=user.id) perm = PermissionCache(self.env, username=req.authname) if perm.has_permission('USER_AUTHOR', resource): return self.back(req) add_notice(req, _('You have no longer permission to modify the account: %s' % user.username)) return req.redirect(req.href('admin/users/manage'))
def test_team_members_can_edit_bugs(self): bug = self.teh.create_ticket(Type.BUG, {Key.SUMMARY: 'A bug'}) self.teh.grant_permission(name_team_member, Role.TEAM_MEMBER) perm = PermissionCache(self.env, name_team_member) self.assert_true(perm.has_permission(Action.TICKET_EDIT, bug.resource))
def ticket_changed(self, ticket, comment, author, old_values): perm = PermissionCache(self.env, author) if perm.has_permission('TICKET_ADD_HOURS'): self.add_hours_by_comment(comment, ticket.id, author)
def test_no_endless_loop_if_permission_is_checked_with_string_instead_of_resource( self): perm = PermissionCache(self.env, 'foo') perm.has_permission('AGILO_BACKLOG_EDIT', '%s:Sprint Backlog' % Realm.BACKLOG)
def test_edit_description_action_is_scoped_as_well(self): self.teh.grant_permission(name_team_member, Role.TEAM_MEMBER) perm = PermissionCache(self.env, name_team_member) requirement = self.teh.create_ticket(Type.REQUIREMENT) requirement_resource = requirement.resource self.assert_false(perm.has_permission(Action.TICKET_EDIT_DESCRIPTION, requirement_resource))
def test_no_endless_loop_if_permission_is_checked_with_string_instead_of_resource(self): perm = PermissionCache(self.env, 'foo') perm.has_permission('AGILO_BACKLOG_EDIT', '%s:Sprint Backlog' % Realm.BACKLOG)
def edit_user(self, req): """ Handle user edit: view & save """ changes = {} username = req.args.get('username') if not username: add_warning(req, _('Invalid username')) return self.list_users(req) # Load user being edited userstore = get_userstore() user = userstore.getUser(username) if not user: add_warning(req, _('Invalid username (or connection error)')) return self.list_users(req) # Load user who's doing the edit changed_by = userstore.getUser(req.authname) papi = Projects() # Check permissions and redirect to user listing (handy after editing the user) #req.perm.require('USER_AUTHOR', Resource('user', id=user.id)) if self.check_author_and_deputies(changed_by.id, user.author_id, userstore.get_deputies(user.id), req, user.id) == False: add_warning(req, _("You don't have rights to edit user")) req.redirect(req.href("admin")) data = req.args data['user'] = user data['author'] = userstore.getUserWhereId(user.author_id) if user.author_id else None data['deputies'] = userstore.get_deputies(user.id) data['base_path'] = req.base_path data['dateformats'] = DATEFORMATS data['is_local'] = userstore.is_local(user) data['now'] = datetime.utcnow() data['expired'] = user.expires and ((user.expires - datetime.utcnow()).days < 0) data['states'] = userstore.USER_STATUS_LABELS data['projects'] = papi.get_authored_projects(user) # Add javascript libraries for datepicker and autocomplete add_script(req, 'multiproject/js/jquery-ui.js') add_stylesheet(req, 'multiproject/css/jquery-ui.css') add_script(req, 'multiproject/js/multiproject.js') add_script(req, 'multiproject/js/admin_user_edit.js') # If get request show edit if req.method.upper() == 'GET' and req.args.get('remove_deputy'): deputy = userstore.getUser(req.args.get('remove_deputy').strip()) remove_res = userstore.remove_deputy(user.id, deputy.id) return req.send(remove_res, content_type='text/plain', status=200) elif req.method.upper() == 'GET': return 'admin_user_edit.html', data # Close pressed: get back to user listing if req.args.get('close'): return req.redirect(req.href('admin/users/manage')) if req.args.get('deputy_name'): deputy = userstore.getUser(req.args.get('deputy_name').strip()) resource = Resource('user', id=deputy.id) perm = PermissionCache(self.env, username=deputy.username) if perm.has_permission('USER_AUTHOR', resource): if(userstore.add_deputy(user.id, deputy.username)): add_notice(req, _("Deputy "+deputy.username+" added.")) return_url = 'home/admin/users/manage?username='******'home/admin/users/manage?username='******'t have enough rights")) return_url = 'home/admin/users/manage?username='******'limitexceeded' in req.args: add_warning(req, _('Picture you tried to upload was too big. Try a smaller one')) # Update author if changed author_id = req.args.get('author_id', None) # If id field is empty but name is not: manual input if not author_id and req.args.get('author_text'): add_warning(req, _('Author cannot be found')) return 'admin_user_edit.html', data # Check set reset the author if author_id: author = userstore.getUserWhereId(int(author_id)) if not author: add_warning(req, _('Author cannot be found')) return 'admin_user_edit.html', data # Check if author is valid: has permission to author? perm = PermissionCache(self.env, author.username) if 'USER_AUTHOR' not in perm: add_warning(req, _('User %s cannot work as an author (does not have USER_AUTHOR permissions)' % author)) return self.back(req) user.author_id = author.id changes['author'] = author else: user.author_id = None user.lastName = req.args.get('last') if not user.lastName: add_warning(req, _('Last name required')) return self.back(req) old_mail = user.mail user.mail = req.args.get('email') if not user.mail: add_warning(req, _('Email address required')) return self.back(req) if old_mail != user.mail: changes['email'] = user.mail org_store = CQDEOrganizationStore.instance() # TODO: is this correct? # When changing email, reset organizations to which the user belongs in user.organization_keys = org_store.get_organization_keys(user) or None # Update password if changed password = req.args.get('password') if password: if not userstore.is_local(user): add_warning(req, _("Can't change password for user that uses external authentication method")) return self.back(req) if len(password) < 7: add_warning(req, _("Password must be at least 7 characters long - please provide longer password")) return self.back(req) if password != req.args.get('confirmpw'): add_warning(req, _("Password do not match - please check")) return self.back(req) user.givenName = req.args.get('first') user.mobile = req.args.get('mobile') # Set or reset account expiration date expiration_str = req.args.get('expires', '') if expiration_str: try: # Parse date and set expiration time in the end of the day expires = datetime.strptime(expiration_str, DATEFORMATS['py']) expires += timedelta(hours=23, minutes=59, seconds=59) # If changed if expires != user.expires: user.expires = expires changes['expiration_date'] = user.expires.strftime(DATEFORMATS['py']) except Exception: self.log.exception('Date formatting failed') add_warning(req, _('Non-recognized expiration format')) pass # Remove expiration date elif user.expires: changes['expiration_date'] = 'Never expires' user.expires = None # Update status if set status = int(req.args.get('status', 0)) if status and status in userstore.USER_STATUS_LABELS.keys() and user.status != status: changes['status'] = userstore.USER_STATUS_LABELS[status] user.status = status if req.args.get('removeicon'): user.icon = None else: icon = req.args.get('icon') if not isinstance(icon, unicode) and icon.filename: user.createIcon(req.args.get('icon')) self.log.info('Saving changes to user: %s' % user) ok = userstore.updateUser(user) if ok and password: changes['password'] = password ok = userstore.updatePassword(user, password) if not ok: add_warning(req, _("Could not save changes")) add_notice(req, _("User %s updated" % username)) # Notify user about changes via email? if req.args.get('notify'): data = { 'user':user, 'changed_by':changed_by, 'changes':changes } try: enotify = EmailNotifier(self.env, "Account updated", data) enotify.template_name = 'account_edited.txt' enotify.notify(user.mail) add_notice(req, _("Notified user about the changes")) except TracError: add_warning(req, _("Failed to send email notification - user changed anyway")) # Check if user has still (after modification) permission to modify user # NOTE: req.perm cannot be used here because it is not updated yet #resource = Resource('user', id=user.id) #perm = PermissionCache(self.env, username=req.authname) #if perm.has_permission('USER_AUTHOR', resource): # return self.back(req) if self.check_author_and_deputies(changed_by.id, user.author_id, userstore.get_deputies(user.id), req, user.id) == True: return_url = 'home/admin/users/manage?username='******'You have no longer permission to modify the account: %s' % user.username)) return req.redirect(req.href('admin/users/manage'))