Beispiel #1
0
  def CheckAssertBasePermissions(
      self, restriction, expect_admin_ok, expect_nonadmin_ok):
    old_group_creation_restriction = settings.group_creation_restriction
    settings.group_creation_restriction = restriction

    # Anon users can never do it
    mr = testing_helpers.MakeMonorailRequest(
        perms=permissions.GetPermissions(None, {}, None))
    self.assertRaises(
        permissions.PermissionException,
        self.servlet.AssertBasePermission, mr)

    mr = testing_helpers.MakeMonorailRequest()
    if expect_admin_ok:
      self.servlet.AssertBasePermission(mr)
    else:
      self.assertRaises(
          permissions.PermissionException,
          self.servlet.AssertBasePermission, mr)

    mr = testing_helpers.MakeMonorailRequest(
        perms=permissions.GetPermissions(mr.auth.user_pb, {111}, None))
    if expect_nonadmin_ok:
      self.servlet.AssertBasePermission(mr)
    else:
      self.assertRaises(
          permissions.PermissionException,
          self.servlet.AssertBasePermission, mr)

    settings.group_creation_restriction = old_group_creation_restriction
Beispiel #2
0
def GetPermissionsInAllProjects(user, effective_ids, projects):
    """Look up the permissions for the given user in each project."""
    return {
        project.project_id: permissions.GetPermissions(user, effective_ids,
                                                       project)
        for project in projects
    }
Beispiel #3
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
Beispiel #4
0
 def testAssertBasePermission_IgnoreNoSuchGroup(self):
     """The permission check does not crash for non-existent user groups."""
     mr = testing_helpers.MakeMonorailRequest(
         perms=permissions.GetPermissions(None, {}, None))
     mr.viewed_user_auth.user_id = 404
     mr.auth.effective_ids = set([111])
     self.servlet.AssertBasePermission(mr)
Beispiel #5
0
def UsersWithPermsInProject(project, perms_needed, users_by_id,
                            effective_ids_by_user):
    # Users that have the given permission are stored in direct_users_for_perm,
    # users whose effective ids have the given permission are stored in
    # indirect_users_for_perm.
    direct_users_for_perm = {perm: set() for perm in perms_needed}
    indirect_users_for_perm = {perm: set() for perm in perms_needed}

    # Iterate only over users that have extra permissions, so we don't
    # have to search the extra perms more than once for each user.
    for extra_perm_pb in project.extra_perms:
        extra_perms = set(perm.lower() for perm in extra_perm_pb.perms)
        for perm, users in direct_users_for_perm.items():
            if perm.lower() in extra_perms:
                users.add(extra_perm_pb.member_id)

    # Then, iterate over all users, but don't compute extra permissions.
    for user_id, user_view in users_by_id.items():
        effective_ids = effective_ids_by_user[user_id].union([user_id])
        user_perms = permissions.GetPermissions(user_view.user, effective_ids,
                                                project)
        for perm, users in direct_users_for_perm.items():
            if not effective_ids.isdisjoint(users):
                indirect_users_for_perm[perm].add(user_id)
            if user_perms.HasPerm(perm, None, None, []):
                users.add(user_id)

    for perm, users in direct_users_for_perm.items():
        users.update(indirect_users_for_perm[perm])

    return direct_users_for_perm
Beispiel #6
0
 def testProcessFormData_OwnerPermission(self):
     """Group owners cannot edit group."""
     self.services.usergroup.TestAddMembers(888, [111], 'owner')
     mr = testing_helpers.MakeMonorailRequest(
         perms=permissions.GetPermissions(None, {}, None))
     mr.viewed_user_auth.user_id = 888
     mr.auth.effective_ids = set([111])
     self.servlet.ProcessFormData(mr, {})
Beispiel #7
0
 def testAssertBasePermission(self):
     mr = testing_helpers.MakeMonorailRequest(
         perms=permissions.GetPermissions(None, {}, None))
     mr.viewed_user_auth.user_id = 888
     self.assertRaises(permissions.PermissionException,
                       self.servlet.AssertBasePermission, mr)
     self.services.usergroup.TestAddMembers(888, [111], 'owner')
     self.servlet.AssertBasePermission(self.mr)
 def testAssertBasePermission(self):
     mr = testing_helpers.MakeMonorailRequest(
         perms=permissions.GetPermissions(None, {}, None))
     mr.viewed_user_auth.user_id = 888L
     mr.auth.effective_ids = set([111L])
     self.assertRaises(permissions.PermissionException,
                       self.servlet.AssertBasePermission, mr)
     self.services.usergroup.TestAddMembers(888L, [111L], 'member')
     self.servlet.AssertBasePermission(mr)
 def testProcessFormData_NoPermission(self):
     """Group members cannot edit group."""
     self.services.usergroup.TestAddMembers(888L, [111L], 'member')
     mr = testing_helpers.MakeMonorailRequest(
         perms=permissions.GetPermissions(None, {}, None))
     mr.viewed_user_auth.user_id = 888L
     mr.auth.effective_ids = set([111L])
     self.assertRaises(permissions.PermissionException,
                       self.servlet.ProcessFormData, mr, {})
Beispiel #10
0
def ValidateCustomField(mr, project, services, field_def, field_val):
  """Validate one custom field value and return an error string or None."""
  if field_def.field_type == tracker_pb2.FieldTypes.INT_TYPE:
    if (field_def.min_value is not None and
        field_val.int_value < field_def.min_value):
      return 'Value must be >= %d' % field_def.min_value
    if (field_def.max_value is not None and
        field_val.int_value > field_def.max_value):
      return 'Value must be <= %d' % field_def.max_value

  elif field_def.field_type == tracker_pb2.FieldTypes.STR_TYPE:
    if field_def.regex and field_val.str_value:
      try:
        regex = re.compile(field_def.regex)
        if not regex.match(field_val.str_value):
          return 'Value must match regular expression: %s' % field_def.regex
      except re.error:
        logging.info('Failed to process regex %r with value %r. Allowing.',
                     field_def.regex, field_val.str_value)
        return None

  elif field_def.field_type == tracker_pb2.FieldTypes.USER_TYPE:
    field_val_user = services.user.GetUser(mr.cnxn, field_val.user_id)
    auth = authdata.AuthData.FromUser(mr.cnxn, field_val_user, services)
    if auth.user_pb.user_id == INVALID_USER_ID:
      return 'User not found'
    if field_def.needs_member:
      user_value_in_project = framework_bizobj.UserIsInProject(
          project, auth.effective_ids)
      if not user_value_in_project:
        return 'User must be a member of the project'
      if field_def.needs_perm:
        user_perms = permissions.GetPermissions(
            auth.user_pb, auth.effective_ids, project)
        has_perm = user_perms.CanUsePerm(
            field_def.needs_perm, auth.effective_ids, project, [])
        if not has_perm:
          return 'User must have permission "%s"' % field_def.needs_perm
    return None

  elif field_def.field_type == tracker_pb2.FieldTypes.DATE_TYPE:
    # TODO(jrobbins): date validation
    pass

  elif field_def.field_type == tracker_pb2.FieldTypes.URL_TYPE:
    if field_val.url_value:
      if not (validate.IsValidURL(field_val.url_value)
              or autolink_constants.IS_A_SHORT_LINK_RE.match(
                  field_val.url_value)
              or autolink_constants.IS_A_NUMERIC_SHORT_LINK_RE.match(
                  field_val.url_value)
              or autolink_constants.IS_IMPLIED_LINK_RE.match(
                  field_val.url_value)):
        return 'Value must be a valid url'

  return None
Beispiel #11
0
 def testAssertBasePermission_IndirectMembership(self):
     self.services.usergroup.TestAddGroupSettings(999,
                                                  '*****@*****.**')
     mr = testing_helpers.MakeMonorailRequest(
         perms=permissions.GetPermissions(None, {}, None))
     mr.viewed_user_auth.user_id = 888
     mr.auth.effective_ids = set([111])
     self.assertRaises(permissions.PermissionException,
                       self.servlet.AssertBasePermission, mr)
     self.services.usergroup.TestAddMembers(888, [999], 'member')
     self.services.usergroup.TestAddMembers(999, [111], 'member')
     self.servlet.AssertBasePermission(mr)
Beispiel #12
0
    def _LookupLoggedInUser(self, services, prof):
        """Get information about the signed-in user (if any) from the request."""
        with prof.Phase('get user info, if any'):
            self.auth = AuthData.FromRequest(self.cnxn, services)
        self.me_user_id = (self.GetIntParam('me')
                           or self.viewed_user_auth.user_id
                           or self.auth.user_id)

        with prof.Phase('looking up signed in user permissions'):
            self.perms = permissions.GetPermissions(self.auth.user_pb,
                                                    self.auth.effective_ids,
                                                    self.project)
Beispiel #13
0
  def __init__(self, request, services, perms=None):
    self.cnxn = None
    self.auth = monorailrequest.AuthData.FromEmail(
        self.cnxn, request['requester'], services)
    self.me_user_id = self.auth.user_id
    self.project_name = None
    self.project = None
    self.viewed_username = None
    self.viewed_user_auth = None
    self.config = None
    if 'userId' in request:
      self.viewed_username = request['userId']
      self.viewed_user_auth = monorailrequest.AuthData.FromEmail(
          self.cnxn, self.viewed_username, services)
    elif 'groupName' in request:
      self.viewed_username = request['groupName']
      try:
        self.viewed_user_auth = monorailrequest.AuthData.FromEmail(
          self.cnxn, self.viewed_username, services)
      except user_svc.NoSuchUserException:
        self.viewed_user_auth = None
    if 'projectId' in request:
      self.project_name = request['projectId']
      self.project = services.project.GetProjectByName(
        self.cnxn, self.project_name)
      self.config = services.config.GetProjectConfig(
          self.cnxn, self.project_id)
    self.perms = perms or permissions.GetPermissions(
        self.auth.user_pb, self.auth.effective_ids, self.project)
    self.granted_perms = set()

    self.params = {
      'can': request.get('can', 1),
      'start': request.get('startIndex', 0),
      'num': request.get('maxResults', 100),
      'q': request.get('q', ''),
      'sort': request.get('sort', ''),
      'groupby': '',
      'projects': request.get('additionalProject', []) + [self.project_name]}
    self.use_cached_searches = True
    self.errors = template_helpers.EZTError()
    self.mode = None

    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')
Beispiel #14
0
def _ValidateOneCustomField(mr, services, field_def, field_val):
    """Validate one custom field value and return an error string or None."""
    if field_def.field_type == tracker_pb2.FieldTypes.INT_TYPE:
        if (field_def.min_value is not None
                and field_val.int_value < field_def.min_value):
            return 'Value must be >= %d' % field_def.min_value
        if (field_def.max_value is not None
                and field_val.int_value > field_def.max_value):
            return 'Value must be <= %d' % field_def.max_value

    elif field_def.field_type == tracker_pb2.FieldTypes.STR_TYPE:
        if field_def.regex and field_val.str_value:
            try:
                regex = re.compile(field_def.regex)
                if not regex.match(field_val.str_value):
                    return 'Value must match regular expression: %s' % field_def.regex
            except re.error:
                logging.info(
                    'Failed to process regex %r with value %r. Allowing.',
                    field_def.regex, field_val.str_value)
                return None

    elif field_def.field_type == tracker_pb2.FieldTypes.USER_TYPE:
        if field_val.user_id == INVALID_USER_ID:
            return 'User not found'
        if field_def.needs_member:
            auth = monorailrequest.AuthData.FromUserID(mr.cnxn,
                                                       field_val.user_id,
                                                       services)
            user_value_in_project = framework_bizobj.UserIsInProject(
                mr.project, auth.effective_ids)
            if not user_value_in_project:
                return 'User must be a member of the project'
            if field_def.needs_perm:
                field_val_user = services.user.GetUser(mr.cnxn,
                                                       field_val.user_id)
                user_perms = permissions.GetPermissions(
                    field_val_user, auth.effective_ids, mr.project)
                has_perm = user_perms.CanUsePerm(field_def.needs_perm,
                                                 auth.effective_ids,
                                                 mr.project, [])
                if not has_perm:
                    return 'User must have permission "%s"' % field_def.needs_perm

    elif field_def.field_type == tracker_pb2.FieldTypes.DATE_TYPE:
        # TODO(jrobbins): date validation
        pass

    return None
Beispiel #15
0
def CheckPermForProject(mr, perm, project, art=None):
  """Convenience method that makes permission checks for projects easier.

  Args:
    mr: common information parsed from the HTTP request.
    perm: A permission constant, defined in module framework.permissions
    project: The project to enforce permissions for.
    art: Optional artifact pb

  Returns:
    A boolean, whether the request can be satisfied, given the permission.
  """
  perms = permissions.GetPermissions(
      mr.auth.user_pb, mr.auth.effective_ids, project)
  return perms.CanUsePerm(
      perm, mr.auth.effective_ids, project, permissions.GetRestrictions(art))
  def HandleRequest(self, mr):
    """Get all the user IDs that the specified user cannot view.

    Args:
      mr: common information parsed from the HTTP request.

    Returns:
      Results dictionary {project_id: [issue_id]} in JSON format.
    """
    if mr.shard_id is None:
      return {'message': 'Cannot proceed without a valid shard_id.'}
    user_id = mr.specified_logged_in_user_id
    user = self.services.user.GetUser(mr.cnxn, user_id)
    effective_ids = self.services.usergroup.LookupMemberships(mr.cnxn, user_id)
    if user_id:
      effective_ids.add(user_id)
    project_id = mr.specified_project_id
    project = self.services.project.GetProject(mr.cnxn, project_id)

    perms = permissions.GetPermissions(user, effective_ids, project)

    nonviewable_iids = self.GetNonviewableIIDs(
      mr.cnxn, user, effective_ids, project, perms, mr.shard_id)

    cached_ts = mr.invalidation_timestep
    if mr.specified_project_id:
      memcache.set(
        'nonviewable:%d;%d;%d' % (project_id, user_id, mr.shard_id),
        (nonviewable_iids, cached_ts),
        time=NONVIEWABLE_MEMCACHE_EXPIRATION)
    else:
      memcache.set(
        'nonviewable:all;%d;%d' % (user_id, mr.shard_id),
        (nonviewable_iids, cached_ts),
        time=NONVIEWABLE_MEMCACHE_EXPIRATION)

    logging.info('set nonviewable:%s;%d;%d to %r', project_id, user_id,
                 mr.shard_id, nonviewable_iids)

    return {
      'nonviewable': nonviewable_iids,

      # These are not used in the frontend, but useful for debugging.
      'project_id': project_id,
      'user_id': user_id,
      'shard_id': mr.shard_id,
      }
 def testProcessFormData_noPermission(self):
   self.servlet.services.user.TestAddUser('member', 222L)
   self.servlet.services.user.TestAddUser('*****@*****.**', 111L)
   mr = testing_helpers.MakeMonorailRequest(
       path='/u/[email protected]/banSpammer.do',
       perms=permissions.GetPermissions(None, {}, None))
   mr.viewed_user_auth.user_view = framework_views.MakeUserView(mr.cnxn,
       self.servlet.services.user, 111L)
   mr.auth.user_id = 222L
   self.assertRaises(permissions.PermissionException,
       self.servlet.AssertBasePermission, mr)
   try:
     self.servlet.ProcessFormData(mr, {})
   except permissions.PermissionException:
     pass
   tasks = self.taskqueue_stub.get_filtered_tasks(
       url=urls.BAN_SPAMMER_TASK + '.do')
   self.assertEqual(0, len(tasks))
Beispiel #18
0
    def testProcessMail_Success(self):
        self.services.user.TestAddUser('*****@*****.**', 111L)

        class MockAuthData:
            def __init__(self):
                self.user_pb = user_pb2.MakeUser(111L)
                self.effective_ids = set([1, 2, 3])
                self.user_id = 111L

        mock_auth_data = MockAuthData()

        self.mox.StubOutWithMock(emailfmt, 'ValidateReferencesHeader')
        emailfmt.ValidateReferencesHeader(mox.IgnoreArg(), self.project,
                                          mox.IgnoreArg(),
                                          mox.IgnoreArg()).AndReturn(True)

        self.mox.StubOutWithMock(monorailrequest.AuthData, 'FromEmail')
        monorailrequest.AuthData.FromEmail(
            mox.IgnoreArg(),
            '*****@*****.**',
            self.services,
            autocreate=False).AndReturn(mock_auth_data)

        self.mox.StubOutWithMock(permissions, 'GetPermissions')
        permissions.GetPermissions(mock_auth_data.user_pb,
                                   mock_auth_data.effective_ids,
                                   self.project).AndReturn('test permissions')

        self.mox.StubOutWithMock(self.inbound, 'ProcessIssueReply')
        self.inbound.ProcessIssueReply(mox.IgnoreArg(), self.project, 123,
                                       self.project_addr, '*****@*****.**',
                                       111L, mock_auth_data.effective_ids,
                                       'test permissions', 'awesome!')

        self.mox.ReplayAll()

        ret = self.inbound.ProcessMail(self.msg, self.project_addr)
        self.mox.VerifyAll()
        self.assertIsNone(ret)
 def LookupLoggedInUserPerms(self, project):
   """Look up perms for user making a request in project (can be None)."""
   with self.profiler.Phase('looking up signed in user permissions'):
     self.perms = permissions.GetPermissions(
         self.auth.user_pb, self.auth.effective_ids, project)
Beispiel #20
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()
Beispiel #21
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
Beispiel #22
0
    def GatherPageData(self, mr):
        """Build up a dictionary of data values to use when rendering the page."""

        member_id = self.ValidateMemberID(mr.cnxn, mr.specified_user_id,
                                          mr.project)
        group_ids = self.services.usergroup.DetermineWhichUserIDsAreGroups(
            mr.cnxn, [member_id])
        users_by_id = framework_views.MakeAllUserViews(mr.cnxn,
                                                       self.services.user,
                                                       [member_id])
        framework_views.RevealAllEmailsToMembers(mr.auth, mr.project,
                                                 users_by_id)

        project_commitments = self.services.project.GetProjectCommitments(
            mr.cnxn, mr.project_id)
        (ac_exclusion_ids, no_expand_ids
         ) = self.services.project.GetProjectAutocompleteExclusion(
             mr.cnxn, mr.project_id)
        member_view = project_views.MemberView(
            mr.auth.user_id,
            member_id,
            users_by_id[member_id],
            mr.project,
            project_commitments,
            ac_exclusion=(member_id in ac_exclusion_ids),
            no_expand=(member_id in no_expand_ids),
            is_group=(member_id in group_ids))

        member_user = self.services.user.GetUser(mr.cnxn, member_id)
        # This ignores indirect memberships, which is ok because we are viewing
        # the page for a member directly involved in the project
        role_perms = permissions.GetPermissions(member_user, {member_id},
                                                mr.project)

        # TODO(jrobbins): clarify in the UI which permissions are built-in to
        # the user's direct role, vs. which are granted via a group membership,
        # vs. which ones are extra_perms that have been added specifically for
        # this user.
        member_perms = template_helpers.EZTItem()
        for perm in CHECKBOX_PERMS:
            setattr(
                member_perms, perm,
                ezt.boolean(role_perms.HasPerm(perm, member_id, mr.project)))

        displayed_extra_perms = [
            perm for perm in member_view.extra_perms
            if perm not in CHECKBOX_PERMS
        ]

        viewing_self = mr.auth.user_id == member_id
        warn_abandonment = (viewing_self
                            and permissions.ShouldCheckForAbandonment(mr))

        return {
            'subtab_mode':
            None,
            'member':
            member_view,
            'role_perms':
            role_perms,
            'member_perms':
            member_perms,
            'displayed_extra_perms':
            displayed_extra_perms,
            'offer_edit_perms':
            ezt.boolean(self.CanEditPerms(mr)),
            'offer_edit_member_notes':
            ezt.boolean(self.CanEditMemberNotes(mr, member_id)),
            'offer_remove_role':
            ezt.boolean(self.CanRemoveRole(mr, member_id)),
            'expand_perms':
            ezt.boolean(mr.auth.user_pb.keep_people_perms_open),
            'warn_abandonment':
            ezt.boolean(warn_abandonment),
            'total_num_owners':
            len(mr.project.owner_ids),
        }
Beispiel #23
0
  def HandleRequest(self, mr):
    """Process the task to notify users after an issue blocking change.

    Args:
      mr: common information parsed from the HTTP request.

    Returns:
      Results dictionary in JSON format which is useful just for debugging.
      The main goal is the side-effect of sending emails.
    """
    issue_ids = mr.GetIntListParam('issue_ids')
    hostport = mr.GetParam('hostport')
    if not issue_ids:
      return {
          'params': {},
          'notified': [],
          'message': 'Cannot proceed without a valid issue IDs.',
      }

    old_owner_ids = mr.GetIntListParam('old_owner_ids')
    comment_text = mr.GetParam('comment_text')
    commenter_id = mr.GetPositiveIntParam('commenter_id')
    amendments = mr.GetParam('amendments')
    send_email = bool(mr.GetIntParam('send_email'))
    params = dict(
        issue_ids=issue_ids, commenter_id=commenter_id, hostport=hostport,
        old_owner_ids=old_owner_ids, comment_text=comment_text,
        send_email=send_email, amendments=amendments)

    logging.info('bulk edit params are %r', params)
    issues = self.services.issue.GetIssues(mr.cnxn, issue_ids)
    # TODO(jrobbins): For cross-project bulk edits, prefetch all relevant
    # projects and configs and pass a dict of them to subroutines.  For
    # now, all issue must be in the same project.
    project_id = issues[0].project_id
    project = self.services.project.GetProject(mr.cnxn, project_id)
    config = self.services.config.GetProjectConfig(mr.cnxn, project_id)
    issues = [issue for issue in issues if not issue.is_spam]
    anon_perms = permissions.GetPermissions(None, set(), project)

    users_by_id = framework_views.MakeAllUserViews(
        mr.cnxn, self.services.user, [commenter_id])
    ids_in_issues = {}
    starrers = {}

    non_private_issues = []
    for issue, old_owner_id in zip(issues, old_owner_ids):
      # TODO(jrobbins): use issue_id consistently rather than local_id.
      starrers[issue.local_id] = self.services.issue_star.LookupItemStarrers(
          mr.cnxn, issue.issue_id)
      named_ids = set()  # users named in user-value fields that notify.
      for fd in config.field_defs:
        named_ids.update(notify_reasons.ComputeNamedUserIDsToNotify(
            issue.field_values, fd))
      direct, indirect = self.services.usergroup.ExpandAnyUserGroups(
          mr.cnxn, list(issue.cc_ids) + list(issue.derived_cc_ids) +
          [issue.owner_id, old_owner_id, issue.derived_owner_id] +
          list(named_ids))
      ids_in_issues[issue.local_id] = set(starrers[issue.local_id])
      ids_in_issues[issue.local_id].update(direct)
      ids_in_issues[issue.local_id].update(indirect)
      ids_in_issue_needing_views = (
          ids_in_issues[issue.local_id] |
          tracker_bizobj.UsersInvolvedInIssues([issue]))
      new_ids_in_issue = [user_id for user_id in ids_in_issue_needing_views
                          if user_id not in users_by_id]
      users_by_id.update(
          framework_views.MakeAllUserViews(
              mr.cnxn, self.services.user, new_ids_in_issue))

      anon_can_view = permissions.CanViewIssue(
          set(), anon_perms, project, issue)
      if anon_can_view:
        non_private_issues.append(issue)

    commenter_view = users_by_id[commenter_id]
    omit_addrs = {commenter_view.email}

    tasks = []
    if send_email:
      email_tasks = self._BulkEditEmailTasks(
          mr.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)
      tasks = email_tasks

    notified = notify_helpers.AddAllEmailTasks(tasks)
    return {
        'params': params,
        'notified': notified,
        }
Beispiel #24
0
  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
Beispiel #25
0
  def ProcessMail(self, msg, project_addr):
    """Process an inbound email message."""
    # TODO(jrobbins): If the message is HUGE, don't even try to parse
    # it. Silently give up.

    (from_addr, to_addrs, cc_addrs, references, incident_id, subject,
     body) = emailfmt.ParseEmailMessage(msg)

    logging.info('Proj addr:   %r', project_addr)
    logging.info('From addr:   %r', from_addr)
    logging.info('Subject:     %r', subject)
    logging.info('To:          %r', to_addrs)
    logging.info('Cc:          %r', cc_addrs)
    logging.info('References:  %r', references)
    logging.info('Incident Id: %r', incident_id)
    logging.info('Body:        %r', body)

    # If message body is very large, reject it and send an error email.
    if emailfmt.IsBodyTooBigToParse(body):
      return _MakeErrorMessageReplyTask(
          project_addr, from_addr, self._templates['body_too_long'])

    # Make sure that the project reply-to address is in the To: line.
    if not emailfmt.IsProjectAddressOnToLine(project_addr, to_addrs):
      return None

    project_name, verb = emailfmt.IdentifyProjectAndVerb(project_addr)

    is_alert = bool(verb and verb.lower() == 'alert')
    error_addr = from_addr
    local_id = None
    author_addr = from_addr

    if is_alert:
      error_addr = settings.alert_escalation_email
      author_addr = settings.alert_service_account
    else:
      local_id = emailfmt.IdentifyIssue(project_name, subject)
      if not local_id:
        logging.info('Could not identify issue: %s %s', project_addr, subject)
        # No error message, because message was probably not intended for us.
        return None

    cnxn = sql.MonorailConnection()
    if self.services.cache_manager:
      self.services.cache_manager.DoDistributedInvalidation(cnxn)

    project = self.services.project.GetProjectByName(cnxn, project_name)

    # TODO(zhangtiff): Add separate email templates for alert error cases.
    if not project or project.state != project_pb2.ProjectState.LIVE:
      return _MakeErrorMessageReplyTask(
          project_addr, error_addr, self._templates['project_not_found'])

    if not project.process_inbound_email:
      return _MakeErrorMessageReplyTask(
          project_addr, error_addr, self._templates['replies_disabled'],
          project_name=project_name)

    # Verify that this is a reply to a notification that we could have sent.
    is_development = os.environ['SERVER_SOFTWARE'].startswith('Development')
    if not (is_alert or is_development):
      for ref in references:
        if emailfmt.ValidateReferencesHeader(ref, project, from_addr, subject):
          break  # Found a message ID that we could have sent.
      else: # for-else: if loop completes with no valid reference found.
        return _MakeErrorMessageReplyTask(
            project_addr, from_addr, self._templates['not_a_reply'])

    # Authenticate the author_addr and perm check.
    # Note: If the issue summary line is changed, a new thread is created,
    # and replies to the old thread will no longer work because the subject
    # line hash will not match, which seems reasonable.
    try:
      auth = monorailrequest.AuthData.FromEmail(
          cnxn, author_addr, self.services, autocreate=is_alert)
      author_id = auth.user_id
    except user_svc.NoSuchUserException:
      author_id = None
    if not author_id:
      return _MakeErrorMessageReplyTask(
          project_addr, error_addr, self._templates['no_account'])

    if auth.user_pb.banned:
      logging.info('Banned user %s tried to post to %s',
                   from_addr, project_addr)
      return _MakeErrorMessageReplyTask(
          project_addr, error_addr, self._templates['banned'])

    perms = permissions.GetPermissions(
        auth.user_pb, auth.effective_ids, project)

    # If the email is an alert, switch to the alert handling path.
    if is_alert:
        self.ProcessAlert(cnxn, project, project_addr, from_addr, author_addr,
            author_id, subject, body, incident_id)
        return None

    # This email is a response to an email about a comment.
    self.ProcessIssueReply(
        cnxn, project, local_id, project_addr, from_addr, author_id,
        auth.effective_ids, perms, body)

    return None
Beispiel #26
0
  def HandleRequest(self, mr):
    """Provide the UI with info used in auto-completion.

    Args:
      mr: common information parsed from the HTTP request.

    Returns:
      Results dictionary in JSON format
    """
    # Issue options data can be cached separately in each user's browser.  When
    # the project changes, a new cached_content_timestamp is set and it will
    # cause new requests to use a new URL.
    self.SetCacheHeaders(self.response)

    member_data = project_helpers.BuildProjectMembers(
        mr.cnxn, mr.project, self.services.user)
    owner_views = member_data['owners']
    committer_views = member_data['committers']
    contributor_views = member_data['contributors']

    config = self.services.config.GetProjectConfig(mr.cnxn, mr.project_id)

    open_statuses = []
    closed_statuses = []
    for wks in config.well_known_statuses:
      if not wks.deprecated:
        item = dict(name=wks.status, doc=wks.status_docstring)
        if wks.means_open:
          open_statuses.append(item)
        else:
          closed_statuses.append(item)

    # TODO(jrobbins): restrictions on component definitions?
    components = [{'name': cd.path, 'doc': cd.docstring}
                  for cd in config.component_defs if not cd.deprecated]

    labels = []
    field_names = [
        fd.field_name for fd in config.field_defs if not fd.is_deleted]
    non_masked_labels = tracker_helpers.LabelsNotMaskedByFields(
        config, field_names)
    for wkl in non_masked_labels:
      if not wkl.commented:
        item = dict(name=wkl.name, doc=wkl.docstring)
        labels.append(item)

    # TODO(jrobbins): omit fields that they don't have permission to view.
    field_def_views = [
        tracker_views.FieldDefView(fd, config)
        for fd in config.field_defs
        if not fd.is_deleted]
    fields = [
        dict(field_name=fdv.field_name, field_type=fdv.field_type,
             field_id=fdv.field_id, needs_perm=fdv.needs_perm,
             is_required=fdv.is_required, is_multivalued=fdv.is_multivalued,
             choices=[dict(name=c.name, doc=c.docstring) for c in fdv.choices],
             docstring=fdv.docstring)
        for fdv in field_def_views]

    frequent_restrictions = _FREQUENT_ISSUE_RESTRICTIONS[:]
    custom_permissions = permissions.GetCustomPermissions(mr.project)
    if not custom_permissions:
      frequent_restrictions.extend(
          _EXAMPLE_ISSUE_RESTRICTIONS)

    labels.extend(_BuildRestrictionChoices(
        mr.project, frequent_restrictions,
        permissions.STANDARD_ISSUE_PERMISSIONS))

    group_ids = self.services.usergroup.DetermineWhichUserIDsAreGroups(
        mr.cnxn, [mem.user_id for mem in member_data['all_members']])
    logging.info('group_ids is %r', group_ids)

    acexclusion_ids = self.services.project.GetProjectAutocompleteExclusion(
        mr.cnxn, mr.project_id)

    # TODO(jrobbins): Normally, users will be allowed view the members
    # of any user group if the project From: email address is listed
    # as a group member, as well as any group that they are personally
    # members of.
    member_ids, owner_ids = self.services.usergroup.LookupVisibleMembers(
        mr.cnxn, group_ids, mr.perms, mr.auth.effective_ids, self.services)
    indirect_ids = set()
    for gid in group_ids:
      indirect_ids.update(member_ids.get(gid, []))
      indirect_ids.update(owner_ids.get(gid, []))
    indirect_user_ids = list(indirect_ids)
    indirect_member_views = framework_views.MakeAllUserViews(
        mr.cnxn, self.services.user, indirect_user_ids).values()

    visible_member_views = _FilterMemberData(
        mr, owner_views, committer_views, contributor_views,
        indirect_member_views)
    # Filter out servbice accounts
    visible_member_views = [m for m in visible_member_views
                            if not framework_helpers.IsServiceAccount(m.email)
                            and not m.user_id in acexclusion_ids]
    visible_member_email_list = list({
        uv.email for uv in visible_member_views})
    user_indexes = {email: idx
                    for idx, email in enumerate(visible_member_email_list)}
    visible_members_dict = {}
    for uv in visible_member_views:
      visible_members_dict[uv.email] = uv.user_id
    group_ids = self.services.usergroup.DetermineWhichUserIDsAreGroups(
        mr.cnxn, visible_members_dict.values())

    for field_dict in fields:
      needed_perm = field_dict['needs_perm']
      if needed_perm:
        qualified_user_indexes = []
        for uv in visible_member_views:
          # TODO(jrobbins): Similar code occurs in field_helpers.py.
          user = self.services.user.GetUser(mr.cnxn, uv.user_id)
          auth = monorailrequest.AuthData.FromUserID(
              mr.cnxn, uv.user_id, self.services)
          user_perms = permissions.GetPermissions(
              user, auth.effective_ids, mr.project)
          has_perm = user_perms.CanUsePerm(
              needed_perm, auth.effective_ids, mr.project, [])
          if has_perm:
            qualified_user_indexes.append(user_indexes[uv.email])

        field_dict['user_indexes'] = sorted(set(qualified_user_indexes))

    excl_prefixes = [prefix.lower() for prefix in
                     config.exclusive_label_prefixes]
    members_def_list = [dict(name=email, doc='')
                        for email in visible_member_email_list]
    members_def_list = sorted(
        members_def_list, key=lambda md: md['name'])
    for md in members_def_list:
      md_id = visible_members_dict[md['name']]
      if md_id in group_ids:
        md['is_group'] = True

    return {
        'open': open_statuses,
        'closed': closed_statuses,
        'statuses_offer_merge': config.statuses_offer_merge,
        'components': components,
        'labels': labels,
        'fields': fields,
        'excl_prefixes': excl_prefixes,
        'strict': ezt.boolean(config.restrict_to_known),
        'members': members_def_list,
        'custom_permissions': custom_permissions,
        }
Beispiel #27
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')
Beispiel #28
0
    def GatherPageData(self, mr):
        """Build up a dictionary of data values to use when rendering the page."""
        viewed_user = mr.viewed_user_auth.user_pb
        if self.services.usergroup.GetGroupSettings(
                mr.cnxn, mr.viewed_user_auth.user_id):
            url = framework_helpers.FormatAbsoluteURL(mr,
                                                      '/g/%s/' %
                                                      viewed_user.email,
                                                      include_project=False)
            self.redirect(url, abort=True)  # Show group page instead.

        with work_env.WorkEnv(mr, self.services) as we:
            project_lists = we.GetUserProjects(
                mr.viewed_user_auth.effective_ids)

            (visible_ownership, visible_archived, visible_membership,
             visible_contrib) = project_lists

        with mr.profiler.Phase('Getting user groups'):
            group_settings = self.services.usergroup.GetAllGroupSettings(
                mr.cnxn, mr.viewed_user_auth.effective_ids)
            member_ids, owner_ids = self.services.usergroup.LookupAllMembers(
                mr.cnxn, list(group_settings.keys()))
            friend_project_ids = []  # TODO(issue 4202): implement this.
            visible_group_ids = []
            for group_id in group_settings:
                if permissions.CanViewGroupMembers(mr.perms,
                                                   mr.auth.effective_ids,
                                                   group_settings[group_id],
                                                   member_ids[group_id],
                                                   owner_ids[group_id],
                                                   friend_project_ids):
                    visible_group_ids.append(group_id)

            user_group_views = framework_views.MakeAllUserViews(
                mr.cnxn, self.services.user, visible_group_ids)
            user_group_views = sorted(list(user_group_views.values()),
                                      key=lambda ugv: ugv.email)

        with mr.profiler.Phase('Getting linked accounts'):
            linked_parent = None
            linked_children = []
            linked_views = framework_views.MakeAllUserViews(
                mr.cnxn, self.services.user, [viewed_user.linked_parent_id],
                viewed_user.linked_child_ids)
            if viewed_user.linked_parent_id:
                linked_parent = linked_views[viewed_user.linked_parent_id]
            if viewed_user.linked_child_ids:
                linked_children = [
                    linked_views[child_id]
                    for child_id in viewed_user.linked_child_ids
                ]
            offer_unlink = (mr.auth.user_id == viewed_user.user_id
                            or mr.auth.user_id in linked_views)

        incoming_invite_users = []
        outgoing_invite_users = []
        possible_parent_accounts = []
        can_edit_invites = mr.auth.user_id == mr.viewed_user_auth.user_id
        display_link_invites = can_edit_invites or mr.auth.user_pb.is_site_admin
        # TODO(jrobbins): allow site admin to edit invites for other users.
        if display_link_invites:
            with work_env.WorkEnv(mr,
                                  self.services,
                                  phase='Getting link invites'):
                incoming_invite_ids, outgoing_invite_ids = we.GetPendingLinkedInvites(
                    user_id=viewed_user.user_id)
                invite_views = framework_views.MakeAllUserViews(
                    mr.cnxn, self.services.user, incoming_invite_ids,
                    outgoing_invite_ids)
                incoming_invite_users = [
                    invite_views[uid] for uid in incoming_invite_ids
                ]
                outgoing_invite_users = [
                    invite_views[uid] for uid in outgoing_invite_ids
                ]
                possible_parent_accounts = _ComputePossibleParentAccounts(
                    we, mr.viewed_user_auth.user_view, linked_parent,
                    linked_children)

        viewed_user_display_name = framework_views.GetViewedUserDisplayName(mr)

        with work_env.WorkEnv(mr, self.services) as we:
            starred_projects = we.ListStarredProjects(
                viewed_user_id=mr.viewed_user_auth.user_id)
            logged_in_starred = we.ListStarredProjects()
            logged_in_starred_pids = {p.project_id for p in logged_in_starred}

        starred_user_ids = self.services.user_star.LookupStarredItemIDs(
            mr.cnxn, mr.viewed_user_auth.user_id)
        starred_user_dict = framework_views.MakeAllUserViews(
            mr.cnxn, self.services.user, starred_user_ids)
        starred_users = list(starred_user_dict.values())
        starred_users_json = json.dumps(
            [uv.display_name for uv in starred_users])

        is_user_starred = self._IsUserStarred(mr.cnxn, mr.auth.user_id,
                                              mr.viewed_user_auth.user_id)

        if viewed_user.last_visit_timestamp:
            last_visit_str = timestr.FormatRelativeDate(
                viewed_user.last_visit_timestamp, days_only=True)
            last_visit_str = last_visit_str or 'Less than 2 days ago'
        else:
            last_visit_str = 'Never'

        if viewed_user.email_bounce_timestamp:
            last_bounce_str = timestr.FormatRelativeDate(
                viewed_user.email_bounce_timestamp, days_only=True)
            last_bounce_str = last_bounce_str or 'Less than 2 days ago'
        else:
            last_bounce_str = None

        can_ban = permissions.CanBan(mr, self.services)
        viewed_user_is_spammer = viewed_user.banned.lower() == 'spam'
        viewed_user_may_be_spammer = not viewed_user_is_spammer
        all_projects = self.services.project.GetAllProjects(mr.cnxn)
        for project_id in all_projects:
            project = all_projects[project_id]
            viewed_user_perms = permissions.GetPermissions(
                viewed_user, mr.viewed_user_auth.effective_ids, project)
            if (viewed_user_perms != permissions.EMPTY_PERMISSIONSET
                    and viewed_user_perms != permissions.USER_PERMISSIONSET):
                viewed_user_may_be_spammer = False

        ban_token = None
        ban_spammer_token = None
        if mr.auth.user_id and can_ban:
            form_token_path = mr.request.path + 'ban.do'
            ban_token = xsrf.GenerateToken(mr.auth.user_id, form_token_path)
            form_token_path = mr.request.path + 'banSpammer.do'
            ban_spammer_token = xsrf.GenerateToken(mr.auth.user_id,
                                                   form_token_path)

        page_data = {
            'user_tab_mode':
            'st2',
            'viewed_user_display_name':
            viewed_user_display_name,
            'viewed_user_may_be_spammer':
            ezt.boolean(viewed_user_may_be_spammer),
            'viewed_user_is_spammer':
            ezt.boolean(viewed_user_is_spammer),
            'viewed_user_is_banned':
            ezt.boolean(viewed_user.banned),
            'owner_of_projects': [
                project_views.ProjectView(p,
                                          starred=p.project_id
                                          in logged_in_starred_pids)
                for p in visible_ownership
            ],
            'committer_of_projects': [
                project_views.ProjectView(p,
                                          starred=p.project_id
                                          in logged_in_starred_pids)
                for p in visible_membership
            ],
            'contributor_to_projects': [
                project_views.ProjectView(p,
                                          starred=p.project_id
                                          in logged_in_starred_pids)
                for p in visible_contrib
            ],
            'owner_of_archived_projects':
            [project_views.ProjectView(p) for p in visible_archived],
            'starred_projects': [
                project_views.ProjectView(p,
                                          starred=p.project_id
                                          in logged_in_starred_pids)
                for p in starred_projects
            ],
            'starred_users':
            starred_users,
            'starred_users_json':
            starred_users_json,
            'is_user_starred':
            ezt.boolean(is_user_starred),
            'viewing_user_page':
            ezt.boolean(True),
            'last_visit_str':
            last_visit_str,
            'last_bounce_str':
            last_bounce_str,
            'vacation_message':
            viewed_user.vacation_message,
            'can_ban':
            ezt.boolean(can_ban),
            'ban_token':
            ban_token,
            'ban_spammer_token':
            ban_spammer_token,
            'user_groups':
            user_group_views,
            'linked_parent':
            linked_parent,
            'linked_children':
            linked_children,
            'incoming_invite_users':
            incoming_invite_users,
            'outgoing_invite_users':
            outgoing_invite_users,
            'possible_parent_accounts':
            possible_parent_accounts,
            'can_edit_invites':
            ezt.boolean(can_edit_invites),
            'offer_unlink':
            ezt.boolean(offer_unlink),
        }

        viewed_user_prefs = None
        if mr.perms.HasPerm(permissions.EDIT_OTHER_USERS, None, None):
            with work_env.WorkEnv(mr, self.services) as we:
                viewed_user_prefs = we.GetUserPrefs(
                    mr.viewed_user_auth.user_id)

        user_settings = (
            framework_helpers.UserSettings.GatherUnifiedSettingsPageData(
                mr.auth.user_id, mr.viewed_user_auth.user_view, viewed_user,
                viewed_user_prefs))
        page_data.update(user_settings)

        return page_data
Beispiel #29
0
    def GatherPageData(self, mr):
        """Build up a dictionary of data values to use when rendering the page."""
        viewed_user = mr.viewed_user_auth.user_pb
        if self.services.usergroup.GetGroupSettings(
                mr.cnxn, mr.viewed_user_auth.user_id):
            url = framework_helpers.FormatAbsoluteURL(mr,
                                                      '/g/%s/' %
                                                      viewed_user.email,
                                                      include_project=False)
            self.redirect(url, abort=True)  # Show group page instead.

        with self.profiler.Phase('GetUserProjects'):
            project_lists = sitewide_helpers.GetUserProjects(
                mr.cnxn, self.services, mr.auth.user_pb, mr.auth.effective_ids,
                mr.viewed_user_auth.effective_ids)

            (visible_ownership, visible_archived, visible_membership,
             visible_contrib) = project_lists

        viewed_user_display_name = framework_views.GetViewedUserDisplayName(mr)

        with self.profiler.Phase('GetStarredProjects'):
            starred_projects = sitewide_helpers.GetViewableStarredProjects(
                mr.cnxn, self.services, mr.viewed_user_auth.user_id,
                mr.auth.effective_ids, mr.auth.user_pb)

        logged_in_starred_pids = []
        if mr.auth.user_id:
            logged_in_starred_pids = self.services.project_star.LookupStarredItemIDs(
                mr.cnxn, mr.auth.user_id)

        starred_user_ids = self.services.user_star.LookupStarredItemIDs(
            mr.cnxn, mr.viewed_user_auth.user_id)
        starred_user_dict = framework_views.MakeAllUserViews(
            mr.cnxn, self.services.user, starred_user_ids)
        starred_users = starred_user_dict.values()

        is_user_starred = self._IsUserStarred(mr.cnxn, mr.auth.user_id,
                                              mr.viewed_user_auth.user_id)

        if viewed_user.last_visit_timestamp:
            last_visit_str = timestr.FormatRelativeDate(
                viewed_user.last_visit_timestamp, days_only=True)
            last_visit_str = last_visit_str or 'Less than 2 days ago'
        else:
            last_visit_str = 'Never'

        if viewed_user.email_bounce_timestamp:
            last_bounce_str = timestr.FormatRelativeDate(
                viewed_user.email_bounce_timestamp, days_only=True)
            last_bounce_str = last_bounce_str or 'Less than 2 days ago'
        else:
            last_bounce_str = None

        can_ban = permissions.CanBan(mr, self.services)
        viewed_user_is_spammer = viewed_user.banned.lower() == 'spam'
        viewed_user_may_be_spammer = not viewed_user_is_spammer
        all_projects = self.services.project.GetAllProjects(mr.cnxn)
        for project_id in all_projects:
            project = all_projects[project_id]
            viewed_user_perms = permissions.GetPermissions(
                viewed_user, mr.viewed_user_auth.effective_ids, project)
            if (viewed_user_perms != permissions.EMPTY_PERMISSIONSET
                    and viewed_user_perms != permissions.USER_PERMISSIONSET):
                viewed_user_may_be_spammer = False

        ban_token = None
        ban_spammer_token = None
        if mr.auth.user_id and can_ban:
            form_token_path = mr.request.path + 'ban.do'
            ban_token = xsrf.GenerateToken(mr.auth.user_id, form_token_path)
            form_token_path = mr.request.path + 'banSpammer.do'
            ban_spammer_token = xsrf.GenerateToken(mr.auth.user_id,
                                                   form_token_path)

        page_data = {
            'user_tab_mode':
            'st2',
            'viewed_user_display_name':
            viewed_user_display_name,
            'viewed_user_may_be_spammer':
            ezt.boolean(viewed_user_may_be_spammer),
            'viewed_user_is_spammer':
            ezt.boolean(viewed_user_is_spammer),
            'viewed_user_is_banned':
            ezt.boolean(viewed_user.banned),
            'viewed_user_ignore_action_limits':
            (ezt.boolean(viewed_user.ignore_action_limits)),
            'owner_of_projects': [
                project_views.ProjectView(p,
                                          starred=p.project_id
                                          in logged_in_starred_pids)
                for p in visible_ownership
            ],
            'committer_of_projects': [
                project_views.ProjectView(p,
                                          starred=p.project_id
                                          in logged_in_starred_pids)
                for p in visible_membership
            ],
            'contributor_to_projects': [
                project_views.ProjectView(p,
                                          starred=p.project_id
                                          in logged_in_starred_pids)
                for p in visible_contrib
            ],
            'owner_of_archived_projects':
            [project_views.ProjectView(p) for p in visible_archived],
            'starred_projects': [
                project_views.ProjectView(p,
                                          starred=p.project_id
                                          in logged_in_starred_pids)
                for p in starred_projects
            ],
            'starred_users':
            starred_users,
            'is_user_starred':
            ezt.boolean(is_user_starred),
            'viewing_user_page':
            ezt.boolean(True),
            'last_visit_str':
            last_visit_str,
            'last_bounce_str':
            last_bounce_str,
            'vacation_message':
            viewed_user.vacation_message,
            'can_ban':
            ezt.boolean(can_ban),
            'ban_token':
            ban_token,
            'ban_spammer_token':
            ban_spammer_token
        }

        settings = framework_helpers.UserSettings.GatherUnifiedSettingsPageData(
            mr.auth.user_id, mr.viewed_user_auth.user_view, viewed_user)
        page_data.update(settings)

        return page_data