def components_delete(self, request): """Delete a component.""" mar = self.mar_factory(request) config = self._services.config.GetProjectConfig(mar.cnxn, mar.project_id) component_path = request.componentPath component_def = tracker_bizobj.FindComponentDef( component_path, config) if not component_def: raise config_svc.NoSuchComponentException( 'The component %s does not exist.' % component_path) if not permissions.CanViewComponentDef( mar.auth.effective_ids, mar.perms, mar.project, component_def): raise permissions.PermissionException( 'User is not allowed to view this component %s' % component_path) if not permissions.CanEditComponentDef( mar.auth.effective_ids, mar.perms, mar.project, component_def, config): raise permissions.PermissionException( 'User is not allowed to delete this component %s' % component_path) allow_delete = not tracker_bizobj.FindDescendantComponents( config, component_def) if not allow_delete: raise permissions.PermissionException( 'User tried to delete component that had subcomponents') self._services.issue.DeleteComponentReferences( mar.cnxn, component_def.component_id) self._services.config.DeleteComponentDef( mar.cnxn, mar.project_id, component_def.component_id) return message_types.VoidMessage()
def ProcessFormData(self, mr, post_data): """Validate and store the contents of the issues tracker admin page. Args: mr: commonly used info parsed from the request. post_data: HTML form data from the request. Returns: String URL to redirect the user to, or None if response was already sent. """ config, component_def = self._GetComponentDef(mr) allow_edit = permissions.CanEditComponentDef( mr.auth.effective_ids, mr.perms, mr.project, component_def, config) if not allow_edit: raise permissions.PermissionException( 'User is not allowed to edit or delete this component') if 'deletecomponent' in post_data: allow_delete = not tracker_bizobj.FindDescendantComponents( config, component_def) if not allow_delete: raise permissions.PermissionException( 'User tried to delete component that had subcomponents') return self._ProcessDeleteComponent(mr, component_def) else: return self._ProcessEditComponent(mr, post_data, config, component_def)
def components_create(self, request): """Create a component.""" mar = self.mar_factory(request) if not mar.perms.CanUsePerm( permissions.EDIT_PROJECT, mar.auth.effective_ids, mar.project, []): raise permissions.PermissionException( 'User is not allowed to create components for this project') config = self._services.config.GetProjectConfig(mar.cnxn, mar.project_id) leaf_name = request.componentName if not tracker_constants.COMPONENT_NAME_RE.match(leaf_name): raise config_svc.InvalidComponentNameException( 'The component name %s is invalid.' % leaf_name) parent_path = request.parentPath if parent_path: parent_def = tracker_bizobj.FindComponentDef(parent_path, config) if not parent_def: raise config_svc.NoSuchComponentException( 'Parent component %s does not exist.' % parent_path) if not permissions.CanEditComponentDef( mar.auth.effective_ids, mar.perms, mar.project, parent_def, config): raise permissions.PermissionException( 'User is not allowed to add a subcomponent to component %s' % parent_path) path = '%s>%s' % (parent_path, leaf_name) else: path = leaf_name if tracker_bizobj.FindComponentDef(path, config): raise config_svc.InvalidComponentNameException( 'The name %s is already in use.' % path) created = int(time.time()) user_emails = set() user_emails.update([mar.auth.email] + request.admin + request.cc) user_ids_dict = self._services.user.LookupUserIDs( mar.cnxn, list(user_emails), autocreate=False) admin_ids = [user_ids_dict[uname] for uname in request.admin] cc_ids = [user_ids_dict[uname] for uname in request.cc] label_ids = [] # TODO(jrobbins): allow API clients to specify this too. component_id = self._services.config.CreateComponentDef( mar.cnxn, mar.project_id, path, request.description, request.deprecated, admin_ids, cc_ids, created, user_ids_dict[mar.auth.email], label_ids) return api_pb2_v1.Component( componentId=component_id, projectName=request.projectId, componentPath=path, description=request.description, admin=request.admin, cc=request.cc, deprecated=request.deprecated, created=datetime.datetime.fromtimestamp(created), creator=mar.auth.email)
def _ProcessPublishingOptions(self, mr, post_data): """Process form data to update project state.""" # Note that EDIT_PROJECT is the base permission for this servlet, but # dooming and undooming projects also requires PUBLISH_PROJECT. state = mr.project.state if 'archivebtn' in post_data and not mr.project.delete_time: self.services.project.UpdateProject( mr.cnxn, mr.project.project_id, state=project_pb2.ProjectState.ARCHIVED) elif 'deletebtn' in post_data: # Mark the project for immediate deletion. if state != project_pb2.ProjectState.ARCHIVED: raise permissions.PermissionException( 'Projects must be archived before being deleted') self.services.project.MarkProjectDeletable(mr.cnxn, mr.project_id, self.services.config) elif 'doombtn' in post_data: # Go from any state to forced ARCHIVED. if not self.CheckPerm(mr, permissions.PUBLISH_PROJECT): raise permissions.PermissionException( 'User is not allowed to doom projects') reason = post_data.get('reason') delete_time = time.time() + framework_constants.DEFAULT_DOOM_PERIOD self.services.project.UpdateProject( mr.cnxn, mr.project.project_id, state=project_pb2.ProjectState.ARCHIVED, state_reason=reason, delete_time=delete_time) elif 'publishbtn' in post_data: # Go from any state to LIVE if (mr.project.delete_time and not self.CheckPerm(mr, permissions.PUBLISH_PROJECT)): raise permissions.PermissionException( 'User is not allowed to unarchive doomed projects') self.services.project.UpdateProject( mr.cnxn, mr.project.project_id, state=project_pb2.ProjectState.LIVE, state_reason='', delete_time=0, read_only_reason='') elif 'movedbtn' in post_data: # Record the moved_to location. if state != project_pb2.ProjectState.LIVE: raise permissions.PermissionException( 'This project is not live, no user can move it') moved_to = post_data.get('moved_to', '') self.services.project.UpdateProject(mr.cnxn, mr.project.project_id, moved_to=moved_to)
def GetAttachmentIfAllowed(mr, services): """Retrieve the requested attachment, or raise an appropriate exception. Args: mr: commonly used info parsed from the request. services: connections to backend services. Returns: The requested Attachment PB, and the Issue that it belongs to. Raises: NoSuchAttachmentException: attachment was not found or was marked deleted. NoSuchIssueException: issue that contains attachment was not found. PermissionException: the user is not allowed to view the attachment. """ attachment = None attachment, cid, issue_id = services.issue.GetAttachmentAndContext( mr.cnxn, mr.aid) issue = services.issue.GetIssue(mr.cnxn, issue_id) config = services.config.GetProjectConfig(mr.cnxn, issue.project_id) granted_perms = tracker_bizobj.GetGrantedPerms(issue, mr.auth.effective_ids, config) permit_view = permissions.CanViewIssue(mr.auth.effective_ids, mr.perms, mr.project, issue, granted_perms=granted_perms) if not permit_view: raise permissions.PermissionException( 'Cannot view attachment\'s issue') comment = services.issue.GetComment(mr.cnxn, cid) can_delete = False if mr.auth.user_id and mr.project: can_delete = permissions.CanDelete(mr.auth.user_id, mr.auth.effective_ids, mr.perms, comment.deleted_by, comment.user_id, mr.project, permissions.GetRestrictions(issue), granted_perms=granted_perms) if comment.deleted_by and not can_delete: raise permissions.PermissionException( 'Cannot view attachment\'s comment') return attachment, issue
def AssertBasePermission(self, mr): super(AddToHotlist, self).AssertBasePermission(mr) if mr.hotlist_ids: hotlist_ids_dict = self.services.features.GetHotlists( mr.cnxn, mr.hotlist_ids) for _id, hotlist in hotlist_ids_dict.iteritems(): if not permissions.CanEditHotlist(mr.auth.effective_ids, hotlist): raise permissions.PermissionException( 'You are not allowed to edit hotlist %s' % hotlist.name) else: if not permissions.CanCreateHotlist(mr.perms): raise permissions.PermissionException( 'User is not allowed to create a hotlist.')
def AssertBasePermission(self, mr): super(PeopleList, self).AssertBasePermission(mr) # For now, contributors who cannot view other contributors are further # restricted from viewing any part of the member list or detail pages. if not permissions.CanViewContributorList(mr): raise permissions.PermissionException( 'User is not allowed to view the project people list')
def AssertBasePermission(self, mr): """Assert that the user has the permissions needed to view this page.""" super(GroupList, self).AssertBasePermission(mr) if not mr.perms.HasPerm(permissions.VIEW_GROUP, None, None): raise permissions.PermissionException( 'User is not allowed to view list of user groups')
def ProcessRemove(self, mr, member_id): """Process the posted form when the user pressed 'Remove'.""" if not self.CanRemoveRole(mr, member_id): raise permissions.PermissionException( 'User is not allowed to remove this member from the project') self.RemoveRole(mr.cnxn, mr.project, member_id)
def ProcessFormData(self, mr, post_data): """Validate and store the contents of the issues tracker admin page. Args: mr: commonly used info parsed from the request. post_data: HTML form data from the request. Returns: String URL to redirect the user to, or None if response was already sent. """ config, field_def = self._GetFieldDef(mr) allow_edit = permissions.CanEditFieldDef( mr.auth.effective_ids, mr.perms, mr.project, field_def) if not allow_edit: raise permissions.PermissionException( 'User is not allowed to delete this field') if 'deletefield' in post_data: self._ProcessDeleteField(mr, field_def) return framework_helpers.FormatAbsoluteURL( mr, urls.ADMIN_LABELS, deleted=1, ts=int(time.time())) else: self._ProcessEditField(mr, post_data, config, field_def) return framework_helpers.FormatAbsoluteURL( mr, urls.FIELD_DETAIL, field=field_def.field_name, saved=1, ts=int(time.time()))
def groups_get(self, request): """Get a group's settings and users.""" mar = self.mar_factory(request) if not mar.viewed_user_auth: raise user_svc.NoSuchUserException(request.groupName) group_id = mar.viewed_user_auth.user_id group_settings = self._services.usergroup.GetGroupSettings( mar.cnxn, group_id) member_ids, owner_ids = self._services.usergroup.LookupAllMembers( mar.cnxn, [group_id]) (owned_project_ids, membered_project_ids, contrib_project_ids) = self._services.project.GetUserRolesInAllProjects( mar.cnxn, mar.auth.effective_ids) project_ids = owned_project_ids.union( membered_project_ids).union(contrib_project_ids) if not permissions.CanViewGroup( mar.perms, mar.auth.effective_ids, group_settings, member_ids[group_id], owner_ids[group_id], project_ids): raise permissions.PermissionException( 'The user is not allowed to view this group.') member_ids, owner_ids = self._services.usergroup.LookupMembers( mar.cnxn, [group_id]) member_emails = self._services.user.LookupUserEmails( mar.cnxn, member_ids[group_id]).values() owner_emails = self._services.user.LookupUserEmails( mar.cnxn, owner_ids[group_id]).values() return api_pb2_v1.GroupsGetResponse( groupID=group_id, groupSettings=api_pb2_v1_helpers.convert_group_settings( request.groupName, group_settings), groupOwners=owner_emails, groupMembers=member_emails)
def issues_list(self, request): """List issues for projects.""" mar = self.mar_factory(request) if request.additionalProject: for project_name in request.additionalProject: project = self._services.project.GetProjectByName( mar.cnxn, project_name) if project and not permissions.UserCanViewProject( mar.auth.user_pb, mar.auth.effective_ids, project): raise permissions.PermissionException( 'The user %s has no permission for project %s' % (mar.auth.email, project_name)) prof = profiler.Profiler() pipeline = frontendsearchpipeline.FrontendSearchPipeline( mar, self._services, prof, mar.num) if not mar.errors.AnyErrors(): pipeline.SearchForIIDs() pipeline.MergeAndSortIssues() pipeline.Paginate() else: raise endpoints.BadRequestException(mar.errors.query) issue_list = [ api_pb2_v1_helpers.convert_issue( api_pb2_v1.IssueWrapper, r, mar, self._services) for r in pipeline.visible_results] return api_pb2_v1.IssuesListResponse( kind='monorail#issueList', totalResults=pipeline.total_count, items=issue_list)
def aux_delete_comment(self, request, delete=True): mar = self.mar_factory(request) action_name = 'delete' if delete else 'undelete' issue = self._services.issue.GetIssueByLocalID( mar.cnxn, mar.project_id, request.issueId) all_comments = self._services.issue.GetCommentsForIssue( mar.cnxn, issue.issue_id) try: issue_comment = all_comments[request.commentId] except IndexError: raise issue_svc.NoSuchIssueException( 'The issue %s:%d does not have comment %d.' % (mar.project_name, request.issueId, request.commentId)) if not permissions.CanDelete( mar.auth.user_id, mar.auth.effective_ids, mar.perms, issue_comment.deleted_by, issue_comment.user_id, mar.project, permissions.GetRestrictions(issue), mar.granted_perms): raise permissions.PermissionException( 'User is not allowed to %s the comment %d of issue %s:%d' % (action_name, request.commentId, mar.project_name, request.issueId)) self._services.issue.SoftDeleteComment( mar.cnxn, mar.project_id, request.issueId, request.commentId, mar.auth.user_id, self._services.user, delete=delete) return api_pb2_v1.IssuesCommentsDeleteResponse()
def AssertBasePermission(self, mr): super(UpdateHotlistIssueNote, self).AssertBasePermission(mr) edit_perm = permissions.CanEditHotlist(mr.auth.effective_ids, mr.hotlist) if not edit_perm: raise permissions.PermissionException( 'User is not allowed to edit this hotlist')
def AssertBasePermission(self, mr): """Assert that the user has the permissions needed to view this page.""" super(GroupCreate, self).AssertBasePermission(mr) if not permissions.CanCreateGroup(mr.perms): raise permissions.PermissionException( 'User is not allowed to create a user group')
def testProcessException(self): """Expected exceptions are converted to pRPC codes, expected not.""" self.CheckExceptionStatus( exceptions.NoSuchUserException(), codes.StatusCode.NOT_FOUND) self.CheckExceptionStatus( exceptions.NoSuchProjectException(), codes.StatusCode.NOT_FOUND) self.CheckExceptionStatus( exceptions.NoSuchIssueException(), codes.StatusCode.NOT_FOUND) self.CheckExceptionStatus( exceptions.NoSuchComponentException(), codes.StatusCode.NOT_FOUND) self.CheckExceptionStatus( permissions.BannedUserException(), codes.StatusCode.PERMISSION_DENIED) self.CheckExceptionStatus( permissions.PermissionException(), codes.StatusCode.PERMISSION_DENIED) self.CheckExceptionStatus( exceptions.GroupExistsException(), codes.StatusCode.INVALID_ARGUMENT) self.CheckExceptionStatus( exceptions.InvalidComponentNameException(), codes.StatusCode.INVALID_ARGUMENT) self.CheckExceptionStatus( ratelimiter.ApiRateLimitExceeded('client_id', 'email'), codes.StatusCode.PERMISSION_DENIED) self.CheckExceptionStatus( features_svc.HotlistAlreadyExists(), codes.StatusCode.INVALID_ARGUMENT) self.CheckExceptionStatus(NotImplementedError(), None)
def AssertBasePermission(self, mr): """Assert that the user has the permissions needed to view this page.""" super(UserSettings, self).AssertBasePermission(mr) if not mr.auth.user_id: raise permissions.PermissionException( 'Anonymous users are not allowed to edit user settings')
def GatherPageData(self, mr): if not mr.auth.user_id: raise permissions.PermissionException( 'Anonymous users are not allowed to download issue list CSV') xsrf.ValidateToken(mr.token, mr.auth.user_id, '/p/%s%s.do' % (mr.project_name, urls.ISSUE_LIST)) # Sets headers to allow the response to be downloaded. self.content_type = 'text/csv; charset=UTF-8' download_filename = '%s-issues.csv' % mr.project_name self.response.headers.add( 'Content-Disposition', 'attachment; filename=%s' % download_filename) self.response.headers.add('X-Content-Type-Options', 'nosniff') # Rewrite the colspec to add some extra columns that make the CSV # file more complete. with mr.profiler.Phase('finishing config work'): config = self.services.config.GetProjectConfig( mr.cnxn, mr.project_id) mr.ComputeColSpec(config) mr.col_spec = csv_helpers.RewriteColspec(mr.col_spec) page_data = issuelist.IssueList.GatherPageData(self, mr) return csv_helpers.ReformatRowsForCSV(mr, page_data, urls.ISSUE_LIST_CSV)
def ProcessSubtabForm(self, post_data, mr): """Process the Views subtab. Args: post_data: HTML form data for the HTTP request being processed. mr: commonly used info parsed from the request. Returns: The URL of the page to show after processing. """ if not self.CheckPerm(mr, permissions.EDIT_PROJECT): raise permissions.PermissionException( 'Only project owners may edit the default views') existing_queries = savedqueries_helpers.ParseSavedQueries( mr.cnxn, post_data, self.services.project) added_queries = savedqueries_helpers.ParseSavedQueries( mr.cnxn, post_data, self.services.project, prefix='new_') canned_queries = existing_queries + added_queries list_prefs = _ParseListPreferences(post_data) if mr.errors.AnyErrors(): self.PleaseCorrect(mr) return self.services.config.UpdateConfig(mr.cnxn, mr.project, list_prefs=list_prefs) self.services.features.UpdateCannedQueries(mr.cnxn, mr.project_id, canned_queries) return urls.ADMIN_VIEWS
def GatherPageData(self, mr): if not mr.auth.user_id: raise permissions.PermissionException( 'Anonymous users are not allowed to download hotlist CSV') owner_id = mr.hotlist.owner_ids[0] # only one owner allowed users_by_id = framework_views.MakeAllUserViews(mr.cnxn, self.services.user, [owner_id]) owner = users_by_id[owner_id] # Try to validate XSRF by either user email or user ID. try: xsrf.ValidateToken( mr.token, mr.auth.user_id, '/u/%s/hotlists/%s.do' % (owner.email, mr.hotlist.name)) except xsrf.TokenIncorrect: xsrf.ValidateToken( mr.token, mr.auth.user_id, '/u/%s/hotlists/%s.do' % (owner.user_id, mr.hotlist.name)) # Sets headers to allow the response to be downloaded. self.content_type = 'text/csv; charset=UTF-8' download_filename = 'hotlist_%d-issues.csv' % mr.hotlist_id self.response.headers.add( 'Content-Disposition', 'attachment; filename=%s' % download_filename) self.response.headers.add('X-Content-Type-Options', 'nosniff') mr.ComputeColSpec(mr.hotlist) mr.col_spec = csv_helpers.RewriteColspec(mr.col_spec) page_data = hotlistissues.HotlistIssues.GatherPageData(self, mr) return csv_helpers.ReformatRowsForCSV(mr, page_data, '%d/csv' % mr.hotlist_id)
def ProcessSubtabForm(self, post_data, mr): """Process changes to new issue templates. Args: post_data: HTML form data for the HTTP request being processed. mr: commonly used info parsed from the request. Returns: The URL of the page to show after processing. """ if not self.CheckPerm(mr, permissions.EDIT_PROJECT): raise permissions.PermissionException( 'Only project owners may edit the default templates') config = self.services.config.GetProjectConfig(mr.cnxn, mr.project_id) templates = self.services.template.GetProjectTemplates( mr.cnxn, config.project_id) default_template_id_for_developers, default_template_id_for_users = ( self._ParseDefaultTemplateSelections(post_data, templates)) if default_template_id_for_developers or default_template_id_for_users: self.services.config.UpdateConfig( mr.cnxn, mr.project, default_template_for_developers= default_template_id_for_developers, default_template_for_users=default_template_id_for_users) return urls.ADMIN_TEMPLATES
def issues_insert(self, request): """Add a new issue.""" mar = self.mar_factory(request) if not mar.perms.CanUsePerm( permissions.CREATE_ISSUE, mar.auth.effective_ids, mar.project, []): raise permissions.PermissionException( 'The requester %s is not allowed to create issues for project %s.' % (mar.auth.email, mar.project_name)) owner_id = None if request.owner: try: owner_id = self._services.user.LookupUserID( mar.cnxn, request.owner.name) except user_svc.NoSuchUserException: raise endpoints.BadRequestException( 'The specified owner %s does not exist.' % request.owner.name) cc_ids = [] if request.cc: cc_ids = self._services.user.LookupUserIDs( mar.cnxn, [ap.name for ap in request.cc], autocreate=True).values() comp_ids = api_pb2_v1_helpers.convert_component_ids( mar.config, request.components) fields_add, _, _, fields_labels, _ = ( api_pb2_v1_helpers.convert_field_values( request.fieldValues, mar, self._services)) field_helpers.ValidateCustomFields( mar, self._services, fields_add, mar.config, mar.errors) if mar.errors.AnyErrors(): raise endpoints.BadRequestException( 'Invalid field values: %s' % mar.errors.custom_fields) local_id = self._services.issue.CreateIssue( mar.cnxn, self._services, mar.project_id, request.summary, request.status, owner_id, cc_ids, request.labels + fields_labels, fields_add, comp_ids, mar.auth.user_id, request.description, blocked_on=api_pb2_v1_helpers.convert_issueref_pbs( request.blockedOn, mar, self._services), blocking=api_pb2_v1_helpers.convert_issueref_pbs( request.blocking, mar, self._services)) new_issue = self._services.issue.GetIssueByLocalID( mar.cnxn, mar.project_id, local_id) self._services.issue_star.SetStar( mar.cnxn, self._services, mar.config, new_issue.issue_id, mar.auth.user_id, True) if request.sendEmail: notify.PrepareAndSendIssueChangeNotification( new_issue.issue_id, framework_helpers.GetHostPort(), new_issue.reporter_id, 0) return api_pb2_v1_helpers.convert_issue( api_pb2_v1.IssuesGetInsertResponse, new_issue, mar, self._services)
def AssertBasePermission(self, mr): """Check whether the user has any permission to visit this page. Args: mr: commonly used info parsed from the request. """ super(IssueEntry, self).AssertBasePermission(mr) if not self.CheckPerm(mr, permissions.CREATE_ISSUE): raise permissions.PermissionException( 'User is not allowed to enter an issue')
def AssertBasePermission(self, mr): """Check whether the user has any permission to visit this page. Args: mr: commonly used info parsed from the request. """ super(IssueReindex, self).AssertBasePermission(mr) if not self.CheckPerm(mr, permissions.EDIT_PROJECT): raise permissions.PermissionException( 'You are not allowed to administer this project')
def AssertBasePermission(self, mr): """Check whether the user has any permission to visit this page. Args: mr: commonly used info parsed from the request. """ super(HotlistCreate, self).AssertBasePermission(mr) if not permissions.CanCreateHotlist(mr.perms): raise permissions.PermissionException( 'User is not allowed to create a hotlist.')
def AssertBasePermission(self, mr): """Check whether the user has any permission to ban users. Args: mr: commonly used info parsed from the request. """ super(BanSpammer, self).AssertBasePermission(mr) if not permissions.CanBan(mr, self.services): raise permissions.PermissionException( 'User is not allowed to ban users.')
def AssertBasePermission(self, mr): """Make sure that the logged in user has permission to view this page. Args: mr: commonly used info parsed from the request. """ super(ProjectAdminAdvanced, self).AssertBasePermission(mr) if not self.CheckPerm(mr, permissions.EDIT_PROJECT): raise permissions.PermissionException( 'User is not allowed to administer this project')
def AssertBasePermission(self, mr): """Assert that the user has the permissions needed to view this page.""" super(GroupAdmin, self).AssertBasePermission(mr) _, owner_ids_dict = self.services.usergroup.LookupMembers( mr.cnxn, [mr.viewed_user_auth.user_id]) owner_ids = owner_ids_dict[mr.viewed_user_auth.user_id] if not permissions.CanEditGroup( mr.perms, mr.auth.effective_ids, owner_ids): raise permissions.PermissionException( 'User is not allowed to edit a user group')
def AssertBasePermission(self, mr): """Check that the user has permission to even visit this page.""" super(HotlistIssues, self).AssertBasePermission(mr) try: hotlist = self._GetHotlist(mr) except features_svc.NoSuchHotlistException: return permit_view = permissions.CanViewHotlist(mr.auth.effective_ids, hotlist) if not permit_view: raise permissions.PermissionException( 'User is not allowed to view this hotlist')
def AssertBasePermission(self, mr): """Check that the user is allowed to access this servlet.""" super(PeopleDetail, self).AssertBasePermission(mr) member_id = self.ValidateMemberID(mr.cnxn, mr.specified_user_id, mr.project) # For now, contributors who cannot view other contributors are further # restricted from viewing any part of the member list or detail pages. if (not permissions.CanViewContributorList(mr, mr.project) and member_id != mr.auth.user_id): raise permissions.PermissionException( 'User is not allowed to view other people\'s details')