コード例 #1
0
def FilterIssues(mr, issues, services):
    """Return a list of issues that the user is allowed to view."""
    allowed_issues = []
    project_ids = GetAllProjectsOfIssues(issues)
    issue_projects = services.project.GetProjects(mr.cnxn, project_ids)
    configs_by_project_id = services.config.GetProjectConfigs(
        mr.cnxn, project_ids)
    perms_by_project_id = {
        pid: permissions.GetPermissions(mr.auth.user_pb, mr.auth.effective_ids,
                                        p)
        for pid, p in issue_projects.items()
    }
    for issue in issues:
        if (mr.can == 1) or not issue.closed_timestamp:
            issue_project = issue_projects[issue.project_id]
            config = configs_by_project_id[issue.project_id]
            perms = perms_by_project_id[issue.project_id]
            granted_perms = tracker_bizobj.GetGrantedPerms(
                issue, mr.auth.effective_ids, config)
            permit_view = permissions.CanViewIssue(mr.auth.effective_ids,
                                                   perms,
                                                   issue_project,
                                                   issue,
                                                   granted_perms=granted_perms)
            if permit_view:
                allowed_issues.append(issue)

    return allowed_issues
コード例 #2
0
def PaginateComments(mr, issue, issuecomment_list, config):
  """Filter and paginate the IssueComment PBs for the given issue.

  Unlike most pagination, this one starts at the end of the whole
  list so it shows only the most recent comments.  The user can use
  the "Older" and "Newer" links to page through older comments.

  Args:
    mr: common info parsed from the HTTP request.
    issue: Issue PB for the issue being viewed.
    issuecomment_list: list of IssueComment PBs for the viewed issue,
        the zeroth item in this list is the initial issue description.
    config: ProjectIssueConfig for the project that contains this issue.

  Returns:
    A tuple (descriptions, visible_comments, pagination), where descriptions
    is a list of IssueComment PBs for the issue description history,
    visible_comments is a list of IssueComment PBs for the comments that
    should be displayed on the current pagination page, and pagination is a
    VirtualPagination object that keeps track of the Older and Newer links.
  """
  if not issuecomment_list:
    return [], [], None

  # TODO(lukasperaza): update first comments' rows to is_description=TRUE
  # so [issuecomment_list[0]] can be removed
  descriptions = (
      [issuecomment_list[0]] +
      [comment for comment in issuecomment_list[1:] if comment.is_description])
  comments = issuecomment_list[1:]
  allowed_comments = []
  restrictions = permissions.GetRestrictions(issue)
  granted_perms = tracker_bizobj.GetGrantedPerms(
      issue, mr.auth.effective_ids, config)
  for c in comments:
    can_delete = permissions.CanDelete(
        mr.auth.user_id, mr.auth.effective_ids, mr.perms, c.deleted_by,
        c.user_id, mr.project, restrictions, granted_perms=granted_perms)
    if can_delete or not c.deleted_by:
      allowed_comments.append(c)

  pagination_url = '%s?id=%d' % (urls.ISSUE_DETAIL, issue.local_id)
  pagination = paginate.VirtualPagination(
      mr, len(allowed_comments),
      framework_constants.DEFAULT_COMMENTS_PER_PAGE,
      list_page_url=pagination_url,
      count_up=False, start_param='cstart', num_param='cnum',
      max_num=settings.max_comments_per_page)
  if pagination.last == 1 and pagination.start == len(allowed_comments):
    pagination.visible = ezt.boolean(False)
  visible_comments = allowed_comments[
      pagination.last - 1:pagination.start]

  return descriptions, visible_comments, pagination
コード例 #3
0
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
コード例 #4
0
 def AssertBasePermission(self, mr):
   """Check that the user has permission to even visit this page."""
   super(IssuePeek, self).AssertBasePermission(mr)
   try:
     issue = self._GetIssue(mr)
   except issue_svc.NoSuchIssueException:
     return
   if not issue:
     return
   config = self.services.config.GetProjectConfig(mr.cnxn, mr.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,
       allow_viewing_deleted=self._ALLOW_VIEWING_DELETED,
       granted_perms=granted_perms)
   if not permit_view:
     logging.warning('Issue is %r', issue)
     raise permissions.PermissionException(
         'User is not allowed to view this issue')
コード例 #5
0
def IsMergeAllowed(merge_into_issue, mr, services):
    """Check to see if user has permission to merge with specified issue."""
    merge_into_project = services.project.GetProjectByName(
        mr.cnxn, merge_into_issue.project_name)
    merge_into_config = services.config.GetProjectConfig(
        mr.cnxn, merge_into_project.project_id)
    merge_granted_perms = tracker_bizobj.GetGrantedPerms(
        merge_into_issue, mr.auth.effective_ids, merge_into_config)

    merge_view_allowed = mr.perms.CanUsePerm(
        permissions.VIEW,
        mr.auth.effective_ids,
        merge_into_project,
        permissions.GetRestrictions(merge_into_issue),
        granted_perms=merge_granted_perms)
    merge_edit_allowed = mr.perms.CanUsePerm(
        permissions.EDIT_ISSUE,
        mr.auth.effective_ids,
        merge_into_project,
        permissions.GetRestrictions(merge_into_issue),
        granted_perms=merge_granted_perms)

    return merge_view_allowed and merge_edit_allowed
コード例 #6
0
def FilterOutNonViewableIssues(effective_ids, user, project_dict, config_dict,
                               issues):
    """Return a filtered list of issues that the user can view."""
    perms_dict = GetPermissionsInAllProjects(user, effective_ids,
                                             project_dict.values())

    denied_project_ids = {
        pid
        for pid, p in project_dict.iteritems()
        if not permissions.CanView(effective_ids, perms_dict[pid], p, [])
    }

    results = []
    for issue in issues:
        if issue.deleted or issue.project_id in denied_project_ids:
            continue

        if not permissions.HasRestrictions(issue):
            may_view = True
        else:
            perms = perms_dict[issue.project_id]
            project = project_dict[issue.project_id]
            config = config_dict.get(issue.project_id,
                                     config_dict.get('harmonized'))
            granted_perms = tracker_bizobj.GetGrantedPerms(
                issue, effective_ids, config)
            may_view = permissions.CanViewRestrictedIssueInVisibleProject(
                effective_ids,
                perms,
                project,
                issue,
                granted_perms=granted_perms)

        if may_view:
            results.append(issue)

    return results
コード例 #7
0
ファイル: monorailrequest.py プロジェクト: xinghun61/infra
    def __init__(self, request, services, cnxn=None):
        requester_object = (endpoints.get_current_user()
                            or oauth.get_current_user(
                                framework_constants.OAUTH_SCOPE))
        requester = requester_object.email().lower()
        super(MonorailApiRequest, self).__init__(services,
                                                 requester=requester,
                                                 cnxn=cnxn)
        self.me_user_id = self.auth.user_id
        self.viewed_username = None
        self.viewed_user_auth = None
        self.issue = None
        self.granted_perms = set()

        # query parameters
        self.params = {
            'can': 1,
            'start': 0,
            'num': tracker_constants.DEFAULT_RESULTS_PER_PAGE,
            'q': '',
            'sort': '',
            'groupby': '',
            'projects': [],
            'hotlists': []
        }
        self.use_cached_searches = True
        self.mode = None

        if hasattr(request, 'projectId'):
            self.project_name = request.projectId
            with work_env.WorkEnv(self, services) as we:
                self.project = we.GetProjectByName(self.project_name)
                self.params['projects'].append(self.project_name)
                self.config = we.GetProjectConfig(self.project_id)
                if hasattr(request, 'additionalProject'):
                    self.params['projects'].extend(request.additionalProject)
                    self.params['projects'] = list(set(
                        self.params['projects']))
        self.LookupLoggedInUserPerms(self.project)
        if hasattr(request, 'projectId'):
            with work_env.WorkEnv(self, services) as we:
                if hasattr(request, 'issueId'):
                    self.issue = we.GetIssueByLocalID(self.project_id,
                                                      request.issueId,
                                                      use_cache=False)
                    self.granted_perms = tracker_bizobj.GetGrantedPerms(
                        self.issue, self.auth.effective_ids, self.config)
        if hasattr(request, 'userId'):
            self.viewed_username = request.userId.lower()
            if self.viewed_username == 'me':
                self.viewed_username = requester
            self.viewed_user_auth = authdata.AuthData.FromEmail(
                self.cnxn, self.viewed_username, services)
        elif hasattr(request, 'groupName'):
            self.viewed_username = request.groupName.lower()
            try:
                self.viewed_user_auth = authdata.AuthData.FromEmail(
                    self.cnxn, self.viewed_username, services)
            except exceptions.NoSuchUserException:
                self.viewed_user_auth = None

        # Build q.
        if hasattr(request, 'q') and request.q:
            self.params['q'] = request.q
        if hasattr(request, 'publishedMax') and request.publishedMax:
            self.params['q'] += ' opened<=%d' % request.publishedMax
        if hasattr(request, 'publishedMin') and request.publishedMin:
            self.params['q'] += ' opened>=%d' % request.publishedMin
        if hasattr(request, 'updatedMax') and request.updatedMax:
            self.params['q'] += ' modified<=%d' % request.updatedMax
        if hasattr(request, 'updatedMin') and request.updatedMin:
            self.params['q'] += ' modified>=%d' % request.updatedMin
        if hasattr(request, 'owner') and request.owner:
            self.params['q'] += ' owner:%s' % request.owner
        if hasattr(request, 'status') and request.status:
            self.params['q'] += ' status:%s' % request.status
        if hasattr(request, 'label') and request.label:
            self.params['q'] += ' label:%s' % request.label

        if hasattr(request, 'can') and request.can:
            if request.can == api_pb2_v1.CannedQuery.all:
                self.params['can'] = 1
            elif request.can == api_pb2_v1.CannedQuery.new:
                self.params['can'] = 6
            elif request.can == api_pb2_v1.CannedQuery.open:
                self.params['can'] = 2
            elif request.can == api_pb2_v1.CannedQuery.owned:
                self.params['can'] = 3
            elif request.can == api_pb2_v1.CannedQuery.reported:
                self.params['can'] = 4
            elif request.can == api_pb2_v1.CannedQuery.starred:
                self.params['can'] = 5
            elif request.can == api_pb2_v1.CannedQuery.to_verify:
                self.params['can'] = 7
            else:  # Endpoints should have caught this.
                raise exceptions.InputException(
                    'Canned query %s is not supported.', request.can)
        if hasattr(request, 'startIndex') and request.startIndex:
            self.params['start'] = request.startIndex
        if hasattr(request, 'maxResults') and request.maxResults:
            self.params['num'] = request.maxResults
        if hasattr(request, 'sort') and request.sort:
            self.params['sort'] = request.sort

        self.query_project_names = self.GetParam('projects')
        self.group_by_spec = self.GetParam('groupby')
        self.group_by_spec = ' '.join(
            ParseColSpec(self.group_by_spec,
                         ignore=tracker_constants.NOT_USED_IN_GRID_AXES))
        self.sort_spec = self.GetParam('sort')
        self.sort_spec = ' '.join(ParseColSpec(self.sort_spec))
        self.query = self.GetParam('q')
        self.can = self.GetParam('can')
        self.start = self.GetParam('start')
        self.num = self.GetParam('num')
コード例 #8
0
def ComputeIssueChangeAddressPermList(
    cnxn, ids_to_consider, project, issue, services, omit_addrs,
    users_by_id, pref_check_function=lambda u: u.notify_issue_change):
  """Return a list of user email addresses to notify of an issue change.

  User email addresses are determined by looking up the given user IDs
  in the given users_by_id dict.

  Args:
    cnxn: connection to SQL database.
    ids_to_consider: list of user IDs for users interested in this issue.
    project: Project PB for the project containing this issue.
    issue: Issue PB for the issue that was updated.
    services: Services.
    omit_addrs: set of strings for email addresses to not notify because
        they already know.
    users_by_id: dict {user_id: user_view} user info.
    pref_check_function: optional function to use to check if a certain
        User PB has a preference set to receive the email being sent.  It
        defaults to "If I am in the issue's owner or cc field", but it
        can be set to check "If I starred the issue."

  Returns:
    A list of AddrPerm objects.
  """
  memb_addr_perm_list = []
  logging.info('Considering %r ', ids_to_consider)
  all_user_prefs = services.user.GetUsersPrefs(cnxn, ids_to_consider)
  for user_id in ids_to_consider:
    if user_id == framework_constants.NO_USER_SPECIFIED:
      continue
    user = services.user.GetUser(cnxn, user_id)
    # Notify people who have a pref set, or if they have no User PB
    # because the pref defaults to True.
    if user and not pref_check_function(user):
      logging.info('Not notifying %r: user preference', user.email)
      continue
    # TODO(jrobbins): doing a bulk operation would reduce DB load.
    auth = authdata.AuthData.FromUserID(cnxn, user_id, services)
    perms = permissions.GetPermissions(user, auth.effective_ids, project)
    config = services.config.GetProjectConfig(cnxn, project.project_id)
    granted_perms = tracker_bizobj.GetGrantedPerms(
        issue, auth.effective_ids, config)

    if not permissions.CanViewIssue(
        auth.effective_ids, perms, project, issue,
        granted_perms=granted_perms):
      logging.info('Not notifying %r: user cannot view issue', user.email)
      continue

    addr = users_by_id[user_id].email
    if addr in omit_addrs:
      logging.info('Not notifying %r: user already knows', user.email)
      continue

    recipient_is_member = bool(framework_bizobj.UserIsInProject(
        project, auth.effective_ids))

    reply_perm = REPLY_NOT_ALLOWED
    if project.process_inbound_email:
      if permissions.CanEditIssue(auth.effective_ids, perms, project, issue):
        reply_perm = REPLY_MAY_UPDATE
      elif permissions.CanCommentIssue(
          auth.effective_ids, perms, project, issue):
        reply_perm = REPLY_MAY_COMMENT

    memb_addr_perm_list.append(
      AddrPerm(recipient_is_member, addr, user, reply_perm,
               all_user_prefs[user_id]))

  logging.info('For %s %s, will notify: %r',
               project.project_name, issue.local_id,
               [ap.address for ap in memb_addr_perm_list])

  return memb_addr_perm_list
コード例 #9
0
ファイル: permissions.py プロジェクト: xinghun61/infra
def UpdateIssuePermissions(
    perms, project, issue, effective_ids, granted_perms=None, config=None):
  """Update the PermissionSet for an specific issue.

  Take into account granted permissions and label restrictions to filter the
  permissions, and updates the VIEW and EDIT_ISSUE permissions depending on the
  role of the user in the issue (i.e. owner, reporter, cc or approver).

  Args:
    perms: The PermissionSet to update.
    project: The Project PB for the issue project.
    issue: The Issue PB.
    effective_ids: Set of int user IDs for the current user and all user
        groups that s/he is a member of.  This will be an empty set for
        anonymous users.
    granted_perms: optional list of strings of permissions that the user is
        granted only within the scope of one issue, e.g., by being named in
        a user-type custom field that grants permissions.
    config: optional ProjectIssueConfig PB where granted perms should be
        extracted from, if granted_perms is not given.
  """
  if config:
    granted_perms = tracker_bizobj.GetGrantedPerms(
        issue, effective_ids, config)
  elif granted_perms is None:
    granted_perms = []

  # If the user has no permission to view the project, it has no permissions on
  # this issue.
  if not perms.HasPerm(VIEW, None, None):
    return EMPTY_PERMISSIONSET

  # Compute the restrictions for the given issue and store them in a dictionary
  # of {perm: set(needed_perms)}.
  restrictions = collections.defaultdict(set)
  if perms.consider_restrictions:
    for label in GetRestrictions(issue):
      label = label.lower()
      # format: Restrict-Action-ToThisPerm
      _, requested_perm, needed_perm = label.split('-', 2)
      restrictions[requested_perm.lower()].add(needed_perm.lower())

  # Store the user permissions, and the extra permissions of all effective IDs
  # in the given project.
  all_perms = set(perms.perm_names)
  for effective_id in effective_ids:
    all_perms.update(p.lower() for p in GetExtraPerms(project, effective_id))

  # And filter them applying the restriction labels.
  filtered_perms = set()
  for perm_name in all_perms:
    perm_name = perm_name.lower()
    restricted = any(
        restriction not in all_perms and restriction not in granted_perms
        for restriction in restrictions.get(perm_name, []))
    if not restricted:
      filtered_perms.add(perm_name)

  # Add any granted permissions.
  filtered_perms.update(granted_perms)

  # The VIEW perm might have been removed due to restrictions, but the issue
  # owner, reporter, cc and approvers can always be an issue.
  allowed_ids = set(
      tracker_bizobj.GetCcIds(issue)
      + tracker_bizobj.GetApproverIds(issue)
      + [issue.reporter_id, tracker_bizobj.GetOwnerId(issue)])
  if effective_ids and not allowed_ids.isdisjoint(effective_ids):
    filtered_perms.add(VIEW.lower())

  # If the issue is deleted, only the VIEW and DELETE_ISSUE permissions are
  # relevant.
  if issue.deleted:
    if VIEW.lower() not in filtered_perms:
      return EMPTY_PERMISSIONSET
    if DELETE_ISSUE.lower() in filtered_perms:
      return PermissionSet([VIEW, DELETE_ISSUE], perms.consider_restrictions)
    return PermissionSet([VIEW], perms.consider_restrictions)

  # The EDIT_ISSUE permission might have been removed due to restrictions, but
  # the owner has always permission to edit it.
  if effective_ids and tracker_bizobj.GetOwnerId(issue) in effective_ids:
    filtered_perms.add(EDIT_ISSUE.lower())

  return PermissionSet(filtered_perms, perms.consider_restrictions)
コード例 #10
0
    def __init__(self, request, services):
        requester = (endpoints.get_current_user() or oauth.get_current_user(
            framework_constants.OAUTH_SCOPE))
        requester_email = requester.email().lower()
        self.cnxn = sql.MonorailConnection()
        self.auth = AuthData.FromEmail(self.cnxn, requester_email, services)
        self.me_user_id = self.auth.user_id
        self.viewed_username = None
        self.viewed_user_auth = None
        self.project_name = None
        self.project = None
        self.issue = None
        self.config = None
        self.granted_perms = set()

        # query parameters
        self.params = {
            'can': 1,
            'start': 0,
            'num': 100,
            'q': '',
            'sort': '',
            'groupby': '',
            'projects': [],
            'hotlists': []
        }
        self.use_cached_searches = True
        self.warnings = []
        self.errors = template_helpers.EZTError()
        self.mode = None

        if hasattr(request, 'projectId'):
            self.project_name = request.projectId
            self.project = services.project.GetProjectByName(
                self.cnxn, self.project_name)
            self.params['projects'].append(self.project_name)
            self.config = services.config.GetProjectConfig(
                self.cnxn, self.project_id)
            if hasattr(request, 'additionalProject'):
                self.params['projects'].extend(request.additionalProject)
                self.params['projects'] = list(set(self.params['projects']))
            if hasattr(request, 'issueId'):
                self.issue = services.issue.GetIssueByLocalID(
                    self.cnxn, self.project_id, request.issueId)
                self.granted_perms = tracker_bizobj.GetGrantedPerms(
                    self.issue, self.auth.effective_ids, self.config)
        if hasattr(request, 'userId'):
            self.viewed_username = request.userId.lower()
            if self.viewed_username == 'me':
                self.viewed_username = requester_email
            self.viewed_user_auth = AuthData.FromEmail(self.cnxn,
                                                       self.viewed_username,
                                                       services)
        elif hasattr(request, 'groupName'):
            self.viewed_username = request.groupName.lower()
            try:
                self.viewed_user_auth = AuthData.FromEmail(
                    self.cnxn, self.viewed_username, services)
            except user_svc.NoSuchUserException:
                self.viewed_user_auth = None
        self.perms = permissions.GetPermissions(self.auth.user_pb,
                                                self.auth.effective_ids,
                                                self.project)

        # Build q.
        if hasattr(request, 'q') and request.q:
            self.params['q'] = request.q
        if hasattr(request, 'publishedMax') and request.publishedMax:
            self.params['q'] += ' opened<=%d' % request.publishedMax
        if hasattr(request, 'publishedMin') and request.publishedMin:
            self.params['q'] += ' opened>=%d' % request.publishedMin
        if hasattr(request, 'updatedMax') and request.updatedMax:
            self.params['q'] += ' modified<=%d' % request.updatedMax
        if hasattr(request, 'updatedMin') and request.updatedMin:
            self.params['q'] += ' modified>=%d' % request.updatedMin
        if hasattr(request, 'owner') and request.owner:
            self.params['q'] += ' owner:%s' % request.owner
        if hasattr(request, 'status') and request.status:
            self.params['q'] += ' status:%s' % request.status
        if hasattr(request, 'label') and request.label:
            self.params['q'] += ' label:%s' % request.label

        if hasattr(request, 'can') and request.can:
            if request.can == api_pb2_v1.CannedQuery.all:
                self.params['can'] = 1
            elif request.can == api_pb2_v1.CannedQuery.new:
                self.params['can'] = 6
            elif request.can == api_pb2_v1.CannedQuery.open:
                self.params['can'] = 2
            elif request.can == api_pb2_v1.CannedQuery.owned:
                self.params['can'] = 3
            elif request.can == api_pb2_v1.CannedQuery.reported:
                self.params['can'] = 4
            elif request.can == api_pb2_v1.CannedQuery.starred:
                self.params['can'] = 5
            elif request.can == api_pb2_v1.CannedQuery.to_verify:
                self.params['can'] = 7
            else:  # Endpoints should have caught this.
                raise InputException('Canned query %s is not supported.',
                                     request.can)
        if hasattr(request, 'startIndex') and request.startIndex:
            self.params['start'] = request.startIndex
        if hasattr(request, 'maxResults') and request.maxResults:
            self.params['num'] = request.maxResults
        if hasattr(request, 'sort') and request.sort:
            self.params['sort'] = request.sort

        self.query_project_names = self.GetParam('projects')
        self.group_by_spec = self.GetParam('groupby')
        self.sort_spec = self.GetParam('sort')
        self.query = self.GetParam('q')
        self.can = self.GetParam('can')
        self.start = self.GetParam('start')
        self.num = self.GetParam('num')
コード例 #11
0
def convert_issue(cls, issue, mar, services):
    """Convert Monorail Issue PB to API IssuesGetInsertResponse."""

    config = services.config.GetProjectConfig(mar.cnxn, issue.project_id)
    granted_perms = tracker_bizobj.GetGrantedPerms(issue,
                                                   mar.auth.effective_ids,
                                                   config)
    issue_project = services.project.GetProject(mar.cnxn, issue.project_id)
    component_list = []
    for cd in config.component_defs:
        cid = cd.component_id
        if cid in issue.component_ids:
            component_list.append(cd.path)
    cc_list = [convert_person(p, mar.cnxn, services) for p in issue.cc_ids]
    cc_list = [p for p in cc_list if p is not None]
    field_values_list = []
    fds_by_id = {fd.field_id: fd for fd in config.field_defs}
    phases_by_id = {phase.phase_id: phase for phase in issue.phases}
    for fv in issue.field_values:
        fd = fds_by_id.get(fv.field_id)
        if not fd:
            logging.warning('Custom field %d of project %s does not exist',
                            fv.field_id, issue_project.project_name)
            continue
        val = None
        if fv.user_id:
            val = _get_user_email(services.user, mar.cnxn, fv.user_id)
        else:
            val = tracker_bizobj.GetFieldValue(fv, {})
            if not isinstance(val, string_types):
                val = str(val)
        new_fv = api_pb2_v1.FieldValue(fieldName=fd.field_name,
                                       fieldValue=val,
                                       derived=fv.derived)
        if fd.approval_id:  # Attach parent approval name
            approval_fd = fds_by_id.get(fd.approval_id)
            if not approval_fd:
                logging.warning(
                    'Parent approval field %d of field %s does not exist',
                    fd.approval_id, fd.field_name)
            else:
                new_fv.approvalName = approval_fd.field_name
        elif fv.phase_id:  # Attach phase name
            phase = phases_by_id.get(fv.phase_id)
            if not phase:
                logging.warning('Phase %d for field %s does not exist',
                                fv.phase_id, fd.field_name)
            else:
                new_fv.phaseName = phase.name
        field_values_list.append(new_fv)
    approval_values_list = convert_approvals(mar.cnxn, issue.approval_values,
                                             services, config, issue.phases)
    phases_list = convert_phases(issue.phases)
    with work_env.WorkEnv(mar, services) as we:
        starred = we.IsIssueStarred(issue)
    resp = cls(
        kind='monorail#issue',
        id=issue.local_id,
        title=issue.summary,
        summary=issue.summary,
        projectId=issue_project.project_name,
        stars=issue.star_count,
        starred=starred,
        status=issue.status,
        state=(api_pb2_v1.IssueState.open
               if tracker_helpers.MeansOpenInProject(
                   tracker_bizobj.GetStatus(issue), config) else
               api_pb2_v1.IssueState.closed),
        labels=issue.labels,
        components=component_list,
        author=convert_person(issue.reporter_id, mar.cnxn, services),
        owner=convert_person(issue.owner_id, mar.cnxn, services),
        cc=cc_list,
        updated=datetime.datetime.fromtimestamp(issue.modified_timestamp),
        published=datetime.datetime.fromtimestamp(issue.opened_timestamp),
        blockedOn=convert_issue_ids(issue.blocked_on_iids, mar, services),
        blocking=convert_issue_ids(issue.blocking_iids, mar, services),
        canComment=permissions.CanCommentIssue(mar.auth.effective_ids,
                                               mar.perms,
                                               issue_project,
                                               issue,
                                               granted_perms=granted_perms),
        canEdit=permissions.CanEditIssue(mar.auth.effective_ids,
                                         mar.perms,
                                         issue_project,
                                         issue,
                                         granted_perms=granted_perms),
        fieldValues=field_values_list,
        approvalValues=approval_values_list,
        phases=phases_list)
    if issue.closed_timestamp > 0:
        resp.closed = datetime.datetime.fromtimestamp(issue.closed_timestamp)
    if issue.merged_into:
        resp.mergedInto = convert_issue_ids([issue.merged_into], mar,
                                            services)[0]
    if issue.owner_modified_timestamp:
        resp.owner_modified = datetime.datetime.fromtimestamp(
            issue.owner_modified_timestamp)
    if issue.status_modified_timestamp:
        resp.status_modified = datetime.datetime.fromtimestamp(
            issue.status_modified_timestamp)
    if issue.component_modified_timestamp:
        resp.component_modified = datetime.datetime.fromtimestamp(
            issue.component_modified_timestamp)
    return resp
コード例 #12
0
ファイル: notify.py プロジェクト: xinghun61/infra
  def _BulkEditEmailTasks(
      self, cnxn, issues, old_owner_ids, omit_addrs, project,
      non_private_issues, users_by_id, ids_in_issues, starrers,
      commenter_view, hostport, comment_text, amendments, config):
    """Generate Email PBs to notify interested users after a bulk edit."""
    # 1. Get the user IDs of everyone who could be notified,
    # and make all their user proxies. Also, build a dictionary
    # of all the users to notify and the issues that they are
    # interested in.  Also, build a dictionary of additional email
    # addresses to notify and the issues to notify them of.
    users_by_id = {}
    ids_to_notify_of_issue = {}
    additional_addrs_to_notify_of_issue = collections.defaultdict(list)

    users_to_queries = notify_reasons.GetNonOmittedSubscriptions(
        cnxn, self.services, [project.project_id], {})
    config = self.services.config.GetProjectConfig(
        cnxn, project.project_id)
    for issue, old_owner_id in zip(issues, old_owner_ids):
      issue_participants = set(
          [tracker_bizobj.GetOwnerId(issue), old_owner_id] +
          tracker_bizobj.GetCcIds(issue))
      # users named in user-value fields that notify.
      for fd in config.field_defs:
        issue_participants.update(
            notify_reasons.ComputeNamedUserIDsToNotify(issue.field_values, fd))
      for user_id in ids_in_issues[issue.local_id]:
        # TODO(jrobbins): implement batch GetUser() for speed.
        if not user_id:
          continue
        auth = authdata.AuthData.FromUserID(
            cnxn, user_id, self.services)
        if (auth.user_pb.notify_issue_change and
            not auth.effective_ids.isdisjoint(issue_participants)):
          ids_to_notify_of_issue.setdefault(user_id, []).append(issue)
        elif (auth.user_pb.notify_starred_issue_change and
              user_id in starrers[issue.local_id]):
          # Skip users who have starred issues that they can no longer view.
          starrer_perms = permissions.GetPermissions(
              auth.user_pb, auth.effective_ids, project)
          granted_perms = tracker_bizobj.GetGrantedPerms(
              issue, auth.effective_ids, config)
          starrer_can_view = permissions.CanViewIssue(
              auth.effective_ids, starrer_perms, project, issue,
              granted_perms=granted_perms)
          if starrer_can_view:
            ids_to_notify_of_issue.setdefault(user_id, []).append(issue)
        logging.info(
            'ids_to_notify_of_issue[%s] = %s',
            user_id,
            [i.local_id for i in ids_to_notify_of_issue.get(user_id, [])])

      # Find all subscribers that should be notified.
      subscribers_to_consider = notify_reasons.EvaluateSubscriptions(
          cnxn, issue, users_to_queries, self.services, config)
      for sub_id in subscribers_to_consider:
        auth = authdata.AuthData.FromUserID(cnxn, sub_id, self.services)
        sub_perms = permissions.GetPermissions(
            auth.user_pb, auth.effective_ids, project)
        granted_perms = tracker_bizobj.GetGrantedPerms(
            issue, auth.effective_ids, config)
        sub_can_view = permissions.CanViewIssue(
            auth.effective_ids, sub_perms, project, issue,
            granted_perms=granted_perms)
        if sub_can_view:
          ids_to_notify_of_issue.setdefault(sub_id, [])
          if issue not in ids_to_notify_of_issue[sub_id]:
            ids_to_notify_of_issue[sub_id].append(issue)

      if issue in non_private_issues:
        for notify_addr in issue.derived_notify_addrs:
          additional_addrs_to_notify_of_issue[notify_addr].append(issue)

    # 2. Compose an email specifically for each user, and one email to each
    # notify_addr with all the issues that it.
    # Start from non-members first, then members to reveal email addresses.
    email_tasks = []
    needed_user_view_ids = [uid for uid in ids_to_notify_of_issue
                            if uid not in users_by_id]
    users_by_id.update(framework_views.MakeAllUserViews(
        cnxn, self.services.user, needed_user_view_ids))
    member_ids_to_notify_of_issue = {}
    non_member_ids_to_notify_of_issue = {}
    member_additional_addrs = {}
    non_member_additional_addrs = {}
    addr_to_addrperm = {}  # {email_address: AddrPerm object}
    all_user_prefs = self.services.user.GetUsersPrefs(
        cnxn, ids_to_notify_of_issue)

    # TODO(jrobbins): Merge ids_to_notify_of_issue entries for linked accounts.

    for user_id in ids_to_notify_of_issue:
      if not user_id:
        continue  # Don't try to notify NO_USER_SPECIFIED
      if users_by_id[user_id].email in omit_addrs:
        logging.info('Omitting %s', user_id)
        continue
      user_issues = ids_to_notify_of_issue[user_id]
      if not user_issues:
        continue  # user's prefs indicate they don't want these notifications
      auth = authdata.AuthData.FromUserID(
          cnxn, user_id, self.services)
      is_member = bool(framework_bizobj.UserIsInProject(
          project, auth.effective_ids))
      if is_member:
        member_ids_to_notify_of_issue[user_id] = user_issues
      else:
        non_member_ids_to_notify_of_issue[user_id] = user_issues
      addr = users_by_id[user_id].email
      omit_addrs.add(addr)
      addr_to_addrperm[addr] = notify_reasons.AddrPerm(
          is_member, addr, users_by_id[user_id].user,
          notify_reasons.REPLY_NOT_ALLOWED, all_user_prefs[user_id])

    for addr, addr_issues in additional_addrs_to_notify_of_issue.items():
      auth = None
      try:
        auth = authdata.AuthData.FromEmail(cnxn, addr, self.services)
      except:  # pylint: disable=bare-except
        logging.warning('Cannot find user of email %s ', addr)
      if auth:
        is_member = bool(framework_bizobj.UserIsInProject(
            project, auth.effective_ids))
      else:
        is_member = False
      if is_member:
        member_additional_addrs[addr] = addr_issues
      else:
        non_member_additional_addrs[addr] = addr_issues
      omit_addrs.add(addr)
      addr_to_addrperm[addr] = notify_reasons.AddrPerm(
          is_member, addr, None, notify_reasons.REPLY_NOT_ALLOWED, None)

    for user_id, user_issues in non_member_ids_to_notify_of_issue.items():
      addr = users_by_id[user_id].email
      email = self._FormatBulkIssuesEmail(
          addr_to_addrperm[addr], user_issues, users_by_id,
          commenter_view, hostport, comment_text, amendments, config, project)
      email_tasks.append(email)
      logging.info('about to bulk notify non-member %s (%s) of %s',
                   users_by_id[user_id].email, user_id,
                   [issue.local_id for issue in user_issues])

    for addr, addr_issues in non_member_additional_addrs.items():
      email = self._FormatBulkIssuesEmail(
          addr_to_addrperm[addr], addr_issues, users_by_id, commenter_view,
          hostport, comment_text, amendments, config, project)
      email_tasks.append(email)
      logging.info('about to bulk notify non-member additional addr %s of %s',
                   addr, [addr_issue.local_id for addr_issue in addr_issues])

    framework_views.RevealAllEmails(users_by_id)
    commenter_view.RevealEmail()

    for user_id, user_issues in member_ids_to_notify_of_issue.items():
      addr = users_by_id[user_id].email
      email = self._FormatBulkIssuesEmail(
          addr_to_addrperm[addr], user_issues, users_by_id,
          commenter_view, hostport, comment_text, amendments, config, project)
      email_tasks.append(email)
      logging.info('about to bulk notify member %s (%s) of %s',
                   addr, user_id, [issue.local_id for issue in user_issues])

    for addr, addr_issues in member_additional_addrs.items():
      email = self._FormatBulkIssuesEmail(
          addr_to_addrperm[addr], addr_issues, users_by_id, commenter_view,
          hostport, comment_text, amendments, config, project)
      email_tasks.append(email)
      logging.info('about to bulk notify member additional addr %s of %s',
                   addr, [addr_issue.local_id for addr_issue in addr_issues])

    # 4. Add in the project's issue_notify_address.  This happens even if it
    # is the same as the commenter's email address (which would be an unusual
    # but valid project configuration).  Only issues that any contributor could
    # view are included in emails to the all-issue-activity mailing lists.
    if (project.issue_notify_address
        and project.issue_notify_address not in omit_addrs):
      non_private_issues_live = []
      for issue in issues:
        contributor_could_view = permissions.CanViewIssue(
            set(), permissions.CONTRIBUTOR_ACTIVE_PERMISSIONSET,
            project, issue)
        if contributor_could_view:
          non_private_issues_live.append(issue)

      if non_private_issues_live:
        project_notify_addrperm = notify_reasons.AddrPerm(
            True, project.issue_notify_address, None,
            notify_reasons.REPLY_NOT_ALLOWED, None)
        email = self._FormatBulkIssuesEmail(
            project_notify_addrperm, non_private_issues_live,
            users_by_id, commenter_view, hostport, comment_text, amendments,
            config, project)
        email_tasks.append(email)
        omit_addrs.add(project.issue_notify_address)
        logging.info('about to bulk notify all-issues %s of %s',
                     project.issue_notify_address,
                     [issue.local_id for issue in non_private_issues])

    return email_tasks
コード例 #13
0
def convert_issue(cls, issue, mar, services):
  """Convert Monorail Issue PB to API IssuesGetInsertResponse."""

  config = services.config.GetProjectConfig(mar.cnxn, issue.project_id)
  granted_perms = tracker_bizobj.GetGrantedPerms(
      issue, mar.auth.effective_ids, config)
  issue_project = services.project.GetProject(mar.cnxn, issue.project_id)
  component_list = []
  for cd in config.component_defs:
    cid = cd.component_id
    if cid in issue.component_ids:
      component_list.append(cd.path)
  cc_list = [convert_person(p, mar.cnxn, services) for p in issue.cc_ids]
  cc_list = [p for p in cc_list if p is not None]
  field_values_list = []
  field_id_dict = {
      fd.field_id: fd.field_name for fd in config.field_defs}
  for fv in issue.field_values:
    field_name = field_id_dict.get(fv.field_id)
    if not field_name:
      logging.warning('Custom field %d of project %s does not exist',
                      fv.field_id, issue_project.project_name)
      continue
    val = None
    if fv.user_id:
      val = _get_user_email(
          services.user, mar.cnxn, fv.user_id)
    elif fv.str_value:
      val = fv.str_value
    elif fv.int_value:
      val = str(fv.int_value)
    new_fv = api_pb2_v1.FieldValue(
        fieldName=field_name,
        fieldValue=val,
        derived=fv.derived)
    field_values_list.append(new_fv)
  resp = cls(
      kind='monorail#issue',
      id=issue.local_id,
      title=issue.summary,
      summary=issue.summary,
      projectId=issue_project.project_name,
      stars=issue.star_count,
      starred=services.issue_star.IsItemStarredBy(
          mar.cnxn, issue.issue_id, mar.auth.user_id),
      status=issue.status,
      state=(api_pb2_v1.IssueState.open if
             tracker_helpers.MeansOpenInProject(
                 tracker_bizobj.GetStatus(issue), config)
             else api_pb2_v1.IssueState.closed),
      labels=issue.labels,
      components=component_list,
      author=convert_person(issue.reporter_id, mar.cnxn, services),
      owner=convert_person(issue.owner_id, mar.cnxn, services),
      cc=cc_list,
      updated=datetime.datetime.fromtimestamp(issue.modified_timestamp),
      published=datetime.datetime.fromtimestamp(issue.opened_timestamp),
      blockedOn=convert_issue_ids(issue.blocked_on_iids, mar, services),
      blocking=convert_issue_ids(issue.blocking_iids, mar, services),
      canComment=permissions.CanCommentIssue(
          mar.auth.effective_ids, mar.perms, issue_project, issue,
          granted_perms=granted_perms),
      canEdit=permissions.CanEditIssue(
          mar.auth.effective_ids, mar.perms, issue_project, issue,
          granted_perms=granted_perms),
      fieldValues=field_values_list)
  if issue.closed_timestamp > 0:
    resp.closed = datetime.datetime.fromtimestamp(issue.closed_timestamp)
  if issue.merged_into:
    resp.mergedInto=convert_issue_ids([issue.merged_into], mar, services)[0]
  if issue.owner_modified_timestamp:
    resp.owner_modified = datetime.datetime.fromtimestamp(
        issue.owner_modified_timestamp)
  if issue.status_modified_timestamp:
    resp.status_modified = datetime.datetime.fromtimestamp(
        issue.status_modified_timestamp)
  if issue.component_modified_timestamp:
    resp.component_modified = datetime.datetime.fromtimestamp(
        issue.component_modified_timestamp)
  return resp
コード例 #14
0
def api_base_checks(request, requester, services, cnxn,
                    auth_client_ids, auth_emails):
  """Base checks for API users.

  Args:
    request: The HTTP request from Cloud Endpoints.
    requester: The user who sends the request.
    services: Services object.
    cnxn: connection to the SQL database.
    auth_client_ids: authorized client ids.
    auth_emails: authorized emails when client is anonymous.

  Returns:
    Client ID and client email.

  Raises:
    endpoints.UnauthorizedException: If the requester is anonymous.
    user_svc.NoSuchUserException: If the requester does not exist in Monorail.
    project_svc.NoSuchProjectException: If the project does not exist in
        Monorail.
    permissions.BannedUserException: If the requester is banned.
    permissions.PermissionException: If the requester does not have
        permisssion to view.
  """
  valid_user = False
  auth_err = ''
  client_id = None

  try:
    client_id = oauth.get_client_id(framework_constants.OAUTH_SCOPE)
    logging.info('Oauth client ID %s', client_id)
  except oauth.Error as ex:
    auth_err = 'oauth.Error: %s' % ex

  if not requester:
    try:
      requester = oauth.get_current_user(framework_constants.OAUTH_SCOPE)
      logging.info('Oauth requester %s', requester.email())
    except oauth.Error as ex:
      auth_err = 'oauth.Error: %s' % ex

  if client_id and requester:
    if client_id != 'anonymous':
      if client_id in auth_client_ids:
        valid_user = True
      else:
        auth_err = 'Client ID %s is not whitelisted' % client_id
    # Some service accounts may have anonymous client ID
    else:
      if requester.email() in auth_emails:
        valid_user = True
      else:
        auth_err = 'Client email %s is not whitelisted' % requester.email()

  if not valid_user:
    raise endpoints.UnauthorizedException('Auth error: %s' % auth_err)
  else:
    logging.info('API request from user %s:%s', client_id, requester.email())

  project_name = None
  if hasattr(request, 'projectId'):
    project_name = request.projectId
  issue_local_id = None
  if hasattr(request, 'issueId'):
    issue_local_id = request.issueId
  # This could raise user_svc.NoSuchUserException
  requester_id = services.user.LookupUserID(cnxn, requester.email())
  requester_pb = services.user.GetUser(cnxn, requester_id)
  requester_view = framework_views.UserView(requester_pb)
  if permissions.IsBanned(requester_pb, requester_view):
    raise permissions.BannedUserException(
        'The user %s has been banned from using Monorail' %
        requester.email())
  if project_name:
    project = services.project.GetProjectByName(
        cnxn, project_name)
    if not project:
      raise project_svc.NoSuchProjectException(
          'Project %s does not exist' % project_name)
    if project.state != project_pb2.ProjectState.LIVE:
      raise permissions.PermissionException(
          'API may not access project %s because it is not live'
          % project_name)
    requester_effective_ids = services.usergroup.LookupMemberships(
        cnxn, requester_id)
    requester_effective_ids.add(requester_id)
    if not permissions.UserCanViewProject(
        requester_pb, requester_effective_ids, project):
      raise permissions.PermissionException(
          'The user %s has no permission for project %s' %
          (requester.email(), project_name))
    if issue_local_id:
      # This may raise a NoSuchIssueException.
      issue = services.issue.GetIssueByLocalID(
          cnxn, project.project_id, issue_local_id)
      perms = permissions.GetPermissions(
          requester_pb, requester_effective_ids, project)
      config = services.config.GetProjectConfig(cnxn, project.project_id)
      granted_perms = tracker_bizobj.GetGrantedPerms(
          issue, requester_effective_ids, config)
      if not permissions.CanViewIssue(
          requester_effective_ids, perms, project, issue,
          granted_perms=granted_perms):
        raise permissions.PermissionException(
            'User is not allowed to view this issue %s:%d' %
            (project_name, issue_local_id))

  return client_id, requester.email()
コード例 #15
0
    def GatherPageData(self, mr):
        """Parse the attachment ID from the request and serve its content.

    Args:
      mr: commonly used info parsed from the request.

    Returns:
      Dict of values used by EZT for rendering almost the page.
    """
        with mr.profiler.Phase('get issue, comment, and attachment'):
            try:
                attachment, issue = tracker_helpers.GetAttachmentIfAllowed(
                    mr, self.services)
            except exceptions.NoSuchIssueException:
                webapp2.abort(404, 'issue not found')
            except exceptions.NoSuchAttachmentException:
                webapp2.abort(404, 'attachment not found')
            except exceptions.NoSuchCommentException:
                webapp2.abort(404, 'comment not found')

        content = []
        if attachment.gcs_object_id:
            bucket_name = app_identity.get_default_gcs_bucket_name()
            full_path = '/' + bucket_name + attachment.gcs_object_id
            logging.info("reading gcs: %s" % full_path)
            with cloudstorage.open(full_path, 'r') as f:
                content = f.read()

        filesize = len(content)

        # This servlet only displays safe textual attachments. The user should
        # not have been given a link to this servlet for any other kind.
        if not attachment_helpers.IsViewableText(attachment.mimetype,
                                                 filesize):
            self.abort(400, 'not a text file')

        u_text, is_binary, too_large = filecontent.DecodeFileContents(content)
        lines = prettify.PrepareSourceLinesForHighlighting(
            u_text.encode('utf8'))

        config = self.services.config.GetProjectConfig(mr.cnxn, mr.project_id)
        granted_perms = tracker_bizobj.GetGrantedPerms(issue,
                                                       mr.auth.effective_ids,
                                                       config)
        page_perms = self.MakePagePerms(mr,
                                        issue,
                                        permissions.DELETE_ISSUE,
                                        permissions.CREATE_ISSUE,
                                        granted_perms=granted_perms)

        page_data = {
            'issue_tab_mode': 'issueDetail',
            'local_id': issue.local_id,
            'filename': attachment.filename,
            'filesize': template_helpers.BytesKbOrMb(filesize),
            'file_lines': lines,
            'is_binary': ezt.boolean(is_binary),
            'too_large': ezt.boolean(too_large),
            'code_reviews': None,
            'page_perms': page_perms,
        }
        if is_binary or too_large:
            page_data['should_prettify'] = ezt.boolean(False)
        else:
            page_data.update(
                prettify.BuildPrettifyData(len(lines), attachment.filename))

        return page_data