Exemple #1
0
    def _GetProjectIssueIDsAndConfig(self, mc, issue_refs, use_cache=True):
        """Get info from a single project for repeated issue_refs requests."""
        project_names = set()
        local_ids = []
        for issue_ref in issue_refs:
            if not issue_ref.local_id:
                raise exceptions.InputException('Param `local_id` required.')
            local_ids.append(issue_ref.local_id)
            if issue_ref.project_name:
                project_names.add(issue_ref.project_name)

        if not project_names:
            raise exceptions.InputException('Param `project_name` required.')
        if len(project_names) != 1:
            raise exceptions.InputException(
                'This method does not support cross-project issue_refs.')
        project_name = project_names.pop()
        with work_env.WorkEnv(mc, self.services,
                              phase='getting P, I ids, C') as we:
            project = we.GetProjectByName(project_name, use_cache=use_cache)
            mc.LookupLoggedInUserPerms(project)
            config = we.GetProjectConfig(project.project_id,
                                         use_cache=use_cache)
            project_local_id_pairs = [(project.project_id, local_id)
                                      for local_id in local_ids]
        issue_ids, _misses = self.services.issue.LookupIssueIDs(
            mc.cnxn, project_local_id_pairs)
        return project, issue_ids, config
Exemple #2
0
    def __init__(self,
                 main_clause,
                 insert_args=None,
                 update_args=None,
                 duplicate_update_cols=None,
                 or_where_conds=False):
        self.main_clause = main_clause  # E.g., SELECT or DELETE
        self.or_where_conds = or_where_conds
        self.insert_args = insert_args or []  # For INSERT statements
        for row_value in self.insert_args:
            if not all(_IsValidDBValue(val) for val in row_value):
                raise exceptions.InputException('Invalid DB value %r' %
                                                (row_value, ))
        self.update_args = update_args or []  # For UPDATEs
        for val in self.update_args:
            if not _IsValidDBValue(val):
                raise exceptions.InputException('Invalid DB value %r' % val)
        self.duplicate_update_cols = duplicate_update_cols or [
        ]  # For REPLACE-ish

        self.use_clauses = []
        self.join_clauses, self.join_args = [], []
        self.where_conds, self.where_args = [], []
        self.having_conds, self.having_args = [], []
        self.group_by_terms, self.group_by_args = [], []
        self.order_by_terms, self.order_by_args = [], []
        self.limit, self.offset = None, None
Exemple #3
0
    def GetRequester(self, metadata):
        """Return the email address of the signed in user or None."""
        # When running on localhost, allow request to specify test account.
        if TEST_ACCOUNT_HEADER in metadata:
            if not settings.local_mode:
                raise exceptions.InputException(
                    'x-test-account only accepted in local_mode')
            test_account = metadata[TEST_ACCOUNT_HEADER]
            if not test_account.endswith('@example.com'):
                raise exceptions.InputException(
                    'test_account must end with @example.com')
            logging.info('Using test_account: %r' % test_account)
            return test_account

        # Cookie-based auth
        user = users.get_current_user()
        if user:
            logging.info('Using cookie user: %r', user.email())
            return user.email()

        # Oauth
        try:
            user = oauth.get_current_user(framework_constants.OAUTH_SCOPE)
            if user:
                logging.info('Oauth requester %s', user.email())
                return user.email()
        except oauth.Error as ex:
            logging.info('Got oauth error: %r', ex)

        return None
Exemple #4
0
    def ConvertIssueApprovalsTemplate(self, mc, request):
        """Update an issue's existing approvals structure to match the one of the
       given template."""

        if not request.issue_ref.local_id or not request.issue_ref.project_name:
            raise exceptions.InputException('Param `issue_ref.local_id` empty')
        if not request.template_name:
            raise exceptions.InputException('Param `template_name` empty')

        project, issue, config = self._GetProjectIssueAndConfig(
            mc, request.issue_ref, use_cache=False)

        with work_env.WorkEnv(mc, self.services) as we:
            we.ConvertIssueApprovalsTemplate(config,
                                             issue,
                                             request.template_name,
                                             request.comment_content,
                                             send_email=request.send_email)
            related_refs = we.GetRelatedIssueRefs([issue])

        with mc.profiler.Phase('making user views'):
            users_involved_in_issue = tracker_bizobj.UsersInvolvedInIssues(
                [issue])
            users_by_id = framework_views.MakeAllUserViews(
                mc.cnxn, self.services.user, users_involved_in_issue)
            framework_views.RevealAllEmailsToMembers(mc.auth, project,
                                                     users_by_id)

        with mc.profiler.Phase('converting to response objects'):
            response = issues_pb2.ConvertIssueApprovalsTemplateResponse()
            response.issue.CopyFrom(
                converters.ConvertIssue(issue, users_by_id, related_refs,
                                        config))
        return response
Exemple #5
0
 def InviteLinkedParent(self, cnxn, parent_id, child_id):
     """Child stores an invite for the proposed parent user to consider."""
     if not parent_id:
         raise exceptions.InputException('Parent account is missing')
     if not child_id:
         raise exceptions.InputException('Child account is missing')
     self._AssertNotAlreadyLinked(cnxn, parent_id, child_id)
     self.linkedaccountinvite_tbl.InsertRow(cnxn,
                                            parent_id=parent_id,
                                            child_id=child_id)
Exemple #6
0
 def UnlinkAccounts(self, cnxn, parent_id, child_id):
     """Delete a linked-account relationship."""
     if not parent_id:
         raise exceptions.InputException('Parent account is missing')
     if not child_id:
         raise exceptions.InputException('Child account is missing')
     self.linkedaccount_tbl.Delete(cnxn,
                                   parent_id=parent_id,
                                   child_id=child_id)
     self.user_2lc.InvalidateKeys(cnxn, [parent_id, child_id])
Exemple #7
0
    def IssueSnapshot(self, mc, request):
        """Fetch IssueSnapshot counts for charting."""
        warnings = []

        if not request.timestamp:
            raise exceptions.InputException('Param `timestamp` required.')

        if not request.project_name:
            raise exceptions.InputException('Param `project_name` required.')

        if request.group_by == 'label' and not request.label_prefix:
            raise exceptions.InputException('Param `label_prefix` required.')

        if request.canned_query:
            canned_query = savedqueries_helpers.SavedQueryIDToCond(
                mc.cnxn, self.services.features, request.canned_query)
            # TODO(jrobbins): support linked accounts me_user_ids.
            canned_query, warnings = searchpipeline.ReplaceKeywordsWithUserIDs(
                [mc.auth.user_id], canned_query)
        else:
            canned_query = None

        if request.query:
            query, warnings = searchpipeline.ReplaceKeywordsWithUserIDs(
                [mc.auth.user_id], request.query)
        else:
            query = None

        with work_env.WorkEnv(mc, self.services) as we:
            project = we.GetProjectByName(request.project_name)
            results, unsupported_fields, limit_reached = we.SnapshotCountsQuery(
                project,
                request.timestamp,
                request.group_by,
                label_prefix=request.label_prefix,
                query=query,
                canned_query=canned_query)
        if request.group_by == 'owner':
            # Map user ids to emails.
            snapshot_counts = [
                issues_pb2.IssueSnapshotCount(
                    dimension=self.services.user.GetUser(mc.cnxn, key).email,
                    count=result) for key, result in results.iteritems()
            ]
        else:
            snapshot_counts = [
                issues_pb2.IssueSnapshotCount(dimension=key, count=result)
                for key, result in results.items()
            ]
        response = issues_pb2.IssueSnapshotResponse()
        response.snapshot_count.extend(snapshot_counts)
        response.unsupported_field.extend(unsupported_fields)
        response.unsupported_field.extend(warnings)
        response.search_limit_reached = limit_reached
        return response
def IngestAttachmentUploads(attachment_uploads):
    """Ingest protoc AttachmentUpload objects as tuples."""
    result = []
    for up in attachment_uploads:
        if not up.filename:
            raise exceptions.InputException('Missing attachment name')
        if not up.content:
            raise exceptions.InputException('Missing attachment content')
        mimetype = filecontent.GuessContentTypeFromFilename(up.filename)
        attachment_tuple = (up.filename, up.content, mimetype)
        result.append(attachment_tuple)
    return result
Exemple #9
0
    def ParsePersonData(self, mr, post_data):
        """Parse the POST data for a project member.

    Args:
      mr: common information parsed from the user's request.
      post_data: dictionary of lists of values for each HTML
          form field.

    Returns:
      A tuple with user_id, role, extra_perms, and notes.
    """
        if not mr.specified_user_id:
            raise exceptions.InputException('Field user_id is missing')

        role = post_data.get('role', '').lower()
        extra_perms = []
        for ep in post_data.getall('extra_perms'):
            perm = framework_bizobj.CanonicalizeLabel(ep)
            # Perms with leading underscores are reserved.
            perm = perm.strip('_')
            if perm:
                extra_perms.append(perm)

        notes = post_data.get('notes', '').strip()
        ac_exclusion = not post_data.get('ac_include', False)
        no_expand = not post_data.get('ac_expand', False)
        return (mr.specified_user_id, role, extra_perms, notes, ac_exclusion,
                no_expand)
Exemple #10
0
 def GatherPageData(self, mr):
   """Build up a dictionary of data values to use when rendering the page."""
   if not mr.project_name:
     raise exceptions.InputException('No project specified')
   return {
     'http_response_code': httplib.NOT_FOUND,
     }
Exemple #11
0
    def ListReferencedIssues(self, mc, request):
        """Return the specified issues in a response proto."""
        if not request.issue_refs:
            return issues_pb2.ListReferencedIssuesResponse()

        for issue_ref in request.issue_refs:
            if not issue_ref.project_name:
                raise exceptions.InputException(
                    'Param `project_name` required.')
            if not issue_ref.local_id:
                raise exceptions.InputException('Param `local_id` required.')

        default_project_name = request.issue_refs[0].project_name
        ref_tuples = [(ref.project_name, ref.local_id)
                      for ref in request.issue_refs]
        with work_env.WorkEnv(mc, self.services) as we:
            open_issues, closed_issues = we.ListReferencedIssues(
                ref_tuples, default_project_name)
            all_issues = open_issues + closed_issues
            all_project_ids = [issue.project_id for issue in all_issues]
            related_refs = we.GetRelatedIssueRefs(all_issues)
            configs = we.GetProjectConfigs(all_project_ids)

        with mc.profiler.Phase('making user views'):
            users_involved = tracker_bizobj.UsersInvolvedInIssues(all_issues)
            users_by_id = framework_views.MakeAllUserViews(
                mc.cnxn, self.services.user, users_involved)
            framework_views.RevealAllEmailsToMembers(mc.auth, None,
                                                     users_by_id)

        with mc.profiler.Phase('converting to response objects'):
            converted_open_issues = [
                converters.ConvertIssue(issue, users_by_id, related_refs,
                                        configs[issue.project_id])
                for issue in open_issues
            ]
            converted_closed_issues = [
                converters.ConvertIssue(issue, users_by_id, related_refs,
                                        configs[issue.project_id])
                for issue in closed_issues
            ]
            response = issues_pb2.ListReferencedIssuesResponse(
                open_refs=converted_open_issues,
                closed_refs=converted_closed_issues)

        return response
Exemple #12
0
    def ParseRequest(self, request, services, do_user_lookups=True):
        """Parse tons of useful info from the given request object.

    Args:
      request: webapp2 Request object w/ path and query params.
      services: connections to backend servers including DB.
      do_user_lookups: Set to False to disable lookups during testing.
    """
        with self.profiler.Phase('basic parsing'):
            self.request = request
            self.current_page_url = request.url
            self.current_page_url_encoded = urllib.quote_plus(
                self.current_page_url)

            # Only accept a hostport from the request that looks valid.
            if not _HOSTPORT_RE.match(request.host):
                raise exceptions.InputException('request.host looks funny: %r',
                                                request.host)

            logging.info('Request: %s', self.current_page_url)

        with self.profiler.Phase('path parsing'):
            (viewed_user_val, self.project_name, self.hotlist_id,
             self.hotlist_name) = _ParsePathIdentifiers(self.request.path)
            self.viewed_username = _GetViewedEmail(viewed_user_val, self.cnxn,
                                                   services)
        with self.profiler.Phase('qs parsing'):
            self._ParseQueryParameters()
        with self.profiler.Phase('overrides parsing'):
            self._ParseFormOverrides()

        if not self.project:  # It can be already set in unit tests.
            self._LookupProject(services)
        if self.project_id and services.config:
            self.config = services.config.GetProjectConfig(
                self.cnxn, self.project_id)

        if do_user_lookups:
            if self.viewed_username:
                self._LookupViewedUser(services)
            self._LookupLoggedInUser(services)
            # TODO(jrobbins): re-implement HandleLurkerViewingSelf()

        if not self.hotlist:
            self._LookupHotlist(services)

        if self.query is None:
            self.query = self._CalcDefaultQuery()

        prod_debug_allowed = self.perms.HasPerm(permissions.VIEW_DEBUG,
                                                self.auth.user_id, None)
        self.debug_enabled = (request.params.get('debug')
                              and (settings.local_mode or prod_debug_allowed))
        # temporary option for perf testing on staging instance.
        if request.params.get('disable_cache'):
            if settings.local_mode or 'staging' in request.host:
                self.use_cached_searches = False
Exemple #13
0
    def _AssertNotAlreadyLinked(self, cnxn, parent_id, child_id):
        """Check constraints on our linked account graph."""
        # Our linked account graph should be no more than one level deep.
        parent_is_already_a_child = self.linkedaccount_tbl.Select(
            cnxn, cols=LINKEDACCOUNT_COLS, child_id=parent_id)
        if parent_is_already_a_child:
            raise exceptions.InputException(
                'Parent account is already a child')
        child_is_already_a_parent = self.linkedaccount_tbl.Select(
            cnxn, cols=LINKEDACCOUNT_COLS, parent_id=child_id)
        if child_is_already_a_parent:
            raise exceptions.InputException(
                'Child account is already a parent')

        # A child account can only be linked to one parent.
        child_is_already_a_child = self.linkedaccount_tbl.Select(
            cnxn, cols=LINKEDACCOUNT_COLS, child_id=child_id)
        if child_is_already_a_child:
            raise exceptions.InputException('Child account is already linked')
Exemple #14
0
    def GetIntListParam(self, query_param_name, default_value=None):
        """Get a list of ints from the URL or default."""
        param_list = self.GetListParam(query_param_name)
        if param_list is None:
            return default_value

        try:
            return [int(p) for p in param_list]
        except (TypeError, ValueError):
            raise exceptions.InputException(
                'Invalid value for integer list param')
Exemple #15
0
    def GetIntParam(self, query_param_name, default_value=None):
        """Get an integer param from the URL or default."""
        value = self.request.params.get(query_param_name)
        if value is None or value == '':
            return default_value

        try:
            return int(value)
        except (TypeError, ValueError):
            raise exceptions.InputException(
                'Invalid value for integer param: %r' % value)
Exemple #16
0
    def FlagComment(self, mc, request):
        """Flag or unflag the given comment as spam."""
        _project, issue, _config = self._GetProjectIssueAndConfig(
            mc, request.issue_ref, use_cache=False)

        with work_env.WorkEnv(mc, self.services) as we:
            comments = we.ListIssueComments(issue)
            if request.sequence_num >= len(comments):
                raise exceptions.InputException('Invalid sequence number.')
            we.FlagComment(issue, comments[request.sequence_num], request.flag)

        result = issues_pb2.FlagCommentResponse()
        return result
Exemple #17
0
    def FlagIssues(self, mc, request):
        """Flag or unflag the given issues as spam."""
        if not request.issue_refs:
            raise exceptions.InputException('Param `issue_refs` empty.')

        _project, issue_ids, _config = self._GetProjectIssueIDsAndConfig(
            mc, request.issue_refs)
        with work_env.WorkEnv(mc, self.services) as we:
            issues_by_id = we.GetIssuesDict(issue_ids, use_cache=False)
            we.FlagIssues(list(issues_by_id.values()), request.flag)

        result = issues_pb2.FlagIssuesResponse()
        return result
Exemple #18
0
    def DeleteAttachment(self, mc, request):
        """Mark or unmark the given attachment as deleted."""
        _project, issue, _config = self._GetProjectIssueAndConfig(
            mc, request.issue_ref, use_cache=False)

        with work_env.WorkEnv(mc, self.services) as we:
            comments = we.ListIssueComments(issue)
            if request.sequence_num >= len(comments):
                raise exceptions.InputException('Invalid sequence number.')
            we.DeleteAttachment(issue, comments[request.sequence_num],
                                request.attachment_id, request.delete)

        result = issues_pb2.DeleteAttachmentResponse()
        return result
Exemple #19
0
    def ListProjectTemplates(self, mc, request):
        """Return the specific project's templates."""
        if not request.project_name:
            raise exceptions.InputException('Param `project_name` required.')
        project = self._GetProject(mc, request)

        with work_env.WorkEnv(mc, self.services) as we:
            templates = we.ListProjectTemplates(project)

        with mc.profiler.Phase('converting to response objects'):
            response = projects_pb2.ListProjectTemplatesResponse(
                templates=converters.ConvertTemplates(templates))

        return response
Exemple #20
0
    def AcceptLinkedChild(self, cnxn, parent_id, child_id):
        """Parent accepts an invite from a child account."""
        if not parent_id:
            raise exceptions.InputException('Parent account is missing')
        if not child_id:
            raise exceptions.InputException('Child account is missing')
        # Check that the child has previously created an invite for this parent.
        invite_rows = self.linkedaccountinvite_tbl.Select(
            cnxn,
            cols=LINKEDACCOUNTINVITE_COLS,
            parent_id=parent_id,
            child_id=child_id)
        if not invite_rows:
            raise exceptions.InputException('No such invite')

        self._AssertNotAlreadyLinked(cnxn, parent_id, child_id)

        self.linkedaccount_tbl.InsertRow(cnxn,
                                         parent_id=parent_id,
                                         child_id=child_id)
        self.linkedaccountinvite_tbl.Delete(cnxn,
                                            parent_id=parent_id,
                                            child_id=child_id)
        self.user_2lc.InvalidateKeys(cnxn, [parent_id, child_id])
Exemple #21
0
    def GatherPageData(self, mr):
        """Build up a dictionary of data values to use when rendering the page.

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

    Returns:
      A dict of values used by EZT for rendering the page.
    """
        artifact_name = mr.GetParam('name')
        if not artifact_name:
            raise exceptions.InputException()  # someone forged a link

        artifact_detail_url = '/p/%s/issues/detail?id=%s' % (
            mr.project_name, mr.continue_issue_id)

        return {
            'artifact_name': artifact_name,
            'artifact_detail_url': artifact_detail_url,
        }
Exemple #22
0
def _ParsePathIdentifiers(path):
    """Parse out the workspace being requested (if any).

  Args:
    path: A string beginning with the request's path info.

  Returns:
    (viewed_user_val, project_name).
  """
    viewed_user_val = None
    project_name = None
    hotlist_id = None
    hotlist_name = None

    # Strip off any query params
    split_path = path.lstrip('/').split('?')[0].split('/')
    if len(split_path) >= 2:
        if split_path[0] == 'p':
            project_name = split_path[1]
        if split_path[0] == 'u':
            viewed_user_val = urllib.unquote(split_path[1])
            if len(split_path) >= 4 and split_path[2] == 'hotlists':
                try:
                    hotlist_id = int(
                        urllib.unquote(split_path[3].split('.')[0]))
                except ValueError:
                    raw_last_path = (split_path[3][:-3]
                                     if split_path[3].endswith('.do') else
                                     split_path[3])
                    last_path = urllib.unquote(raw_last_path)
                    match = framework_bizobj.RE_HOTLIST_NAME.match(last_path)
                    if not match:
                        raise exceptions.InputException(
                            'Could not parse hotlist id or name')
                    else:
                        hotlist_name = last_path.lower()

        if split_path[0] == 'g':
            viewed_user_val = urllib.unquote(split_path[1])

    return viewed_user_val, project_name, hotlist_id, hotlist_name
Exemple #23
0
    def GatherPageData(self, mr):
        """Build up a dictionary of data values to use when rendering the page."""

        # We are not actually in /p/PROJECTNAME, so mr.project_name is None.
        # Putting the ProjectMoved page inside a moved project would make
        # the redirect logic much more complicated.
        if not mr.specified_project:
            raise exceptions.InputException('No project specified')

        project = self.services.project.GetProjectByName(
            mr.cnxn, mr.specified_project)
        if not project:
            self.abort(404, 'project not found')

        if not project.moved_to:
            # Only show this page for projects that are actually moved.
            # Don't allow hackers to construct misleading links to this servlet.
            logging.info(
                'attempt to view ProjectMoved for non-moved project: %s',
                mr.specified_project)
            self.abort(400, 'This project has not been moved')

        if framework_bizobj.RE_PROJECT_NAME.match(project.moved_to):
            moved_to_url = framework_helpers.FormatAbsoluteURL(
                mr,
                urls.SUMMARY,
                include_project=True,
                project_name=project.moved_to)
        elif (project.moved_to.startswith('https://')
              or project.moved_to.startswith('http://')):
            moved_to_url = project.moved_to
        else:
            # Prevent users from using javascript: or any other tricky URL scheme.
            moved_to_url = '#invalid-destination-url'

        return {
            'project_name': mr.specified_project,
            'moved_to_url': moved_to_url,
        }
Exemple #24
0
    def GetParam(self,
                 query_param_name,
                 default_value=None,
                 antitamper_re=None):
        """Get a query parameter from the URL as a utf8 string."""
        value = self.request.params.get(query_param_name)
        assert value is None or isinstance(value, six.text_type)
        using_default = value is None
        if using_default:
            value = default_value

        if antitamper_re and not antitamper_re.match(value):
            if using_default:
                logging.error(
                    'Default value fails antitamper for %s field: %s',
                    query_param_name, value)
            else:
                logging.info('User seems to have tampered with %s field: %s',
                             query_param_name, value)
            raise exceptions.InputException()

        return value
Exemple #25
0
    def BulkUpdateApprovals(self, mc, request):
        """Update multiple issues' approval and return the updated issue_refs."""
        if not request.issue_refs:
            raise exceptions.InputException('Param `issue_refs` empty.')

        project, issue_ids, config = self._GetProjectIssueIDsAndConfig(
            mc, request.issue_refs)

        approval_fd = tracker_bizobj.FindFieldDef(request.field_ref.field_name,
                                                  config)
        if not approval_fd:
            raise exceptions.NoSuchFieldDefException()
        if request.HasField('approval_delta'):
            approval_delta = converters.IngestApprovalDelta(
                mc.cnxn, self.services.user, request.approval_delta,
                mc.auth.user_id, config)
        else:
            approval_delta = tracker_pb2.ApprovalDelta()
        # No bulk adding approval attachments for now.

        with work_env.WorkEnv(mc, self.services,
                              phase='updating approvals') as we:
            updated_issue_ids = we.BulkUpdateIssueApprovals(
                issue_ids,
                approval_fd.field_id,
                project,
                approval_delta,
                request.comment_content,
                send_email=request.send_email)
            with mc.profiler.Phase('converting to response objects'):
                issue_ref_pairs = we.GetIssueRefs(updated_issue_ids)
                issue_refs = [
                    converters.ConvertIssueRef(pair)
                    for pair in issue_ref_pairs.values()
                ]
                response = issues_pb2.BulkUpdateApprovalsResponse(
                    issue_refs=issue_refs)

        return response
Exemple #26
0
def IngestFieldValues(cnxn, user_service, field_values, config, phases=None):
    """Ingest a list of protoc FieldValues and create protorpc FieldValues.

  Args:
    cnxn: connection to the DB.
    user_service: interface to user data storage.
    field_values: a list of protoc FieldValue used by the API.
    config: ProjectIssueConfig for this field_value's project.
    phases: a list of the issue's protorpc Phases.


  Returns: A protorpc FieldValue object.
  """
    fds_by_name = {fd.field_name.lower(): fd for fd in config.field_defs}
    phases_by_name = {phase.name: phase.phase_id for phase in phases or []}

    ingested_fvs = []
    for fv in field_values:
        fd = fds_by_name.get(fv.field_ref.field_name.lower())
        if fd:
            if not fv.value:
                logging.info('Ignoring blank field value: %r', fv)
                continue
            ingested_fv = field_helpers.ParseOneFieldValue(
                cnxn, user_service, fd, fv.value)
            if not ingested_fv:
                raise exceptions.InputException(
                    'Unparsable value for field %s' % fv.field_ref.field_name)
            if ingested_fv.user_id == field_helpers.INVALID_USER_ID:
                raise exceptions.NoSuchUserException()
            if fd.is_phase_field:
                ingested_fv.phase_id = phases_by_name.get(
                    fv.phase_ref.phase_name)
            ingested_fvs.append(ingested_fv)

    return ingested_fvs
Exemple #27
0
    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')
Exemple #28
0
def _ParseOneRule(
    cnxn, predicate, action_type, action_value, user_service,
    rule_num, error_list):
  """Parse one FilterRule based on the action type."""

  if action_type == 'default_status':
    status = framework_bizobj.CanonicalizeLabel(action_value)
    rule = MakeRule(predicate, default_status=status)

  elif action_type == 'default_owner':
    if action_value:
      try:
        user_id = user_service.LookupUserID(cnxn, action_value)
      except exceptions.NoSuchUserException:
        user_id = framework_constants.NO_USER_SPECIFIED
        error_list.append(
            'Rule %d: No such user: %s' % (rule_num, action_value))
    else:
      user_id = framework_constants.NO_USER_SPECIFIED
    rule = MakeRule(predicate, default_owner_id=user_id)

  elif action_type == 'add_ccs':
    cc_ids = []
    for email in re.split('[,;\s]+', action_value):
      if not email.strip():
        continue
      try:
        user_id = user_service.LookupUserID(
            cnxn, email.strip(), autocreate=True)
        cc_ids.append(user_id)
      except exceptions.NoSuchUserException:
        error_list.append(
            'Rule %d: No such user: %s' % (rule_num, email.strip()))

    rule = MakeRule(predicate, add_cc_ids=cc_ids)

  elif action_type == 'add_labels':
    add_labels = framework_constants.IDENTIFIER_RE.findall(action_value)
    rule = MakeRule(predicate, add_labels=add_labels)

  elif action_type == 'also_notify':
    add_notify = []
    for addr in re.split('[,;\s]+', action_value):
      if validate.IsValidEmail(addr.strip()):
        add_notify.append(addr.strip())
      else:
        error_list.append(
            'Rule %d: Invalid email address: %s' % (rule_num, addr.strip()))

    rule = MakeRule(predicate, add_notify=add_notify)

  elif action_type == 'warning':
    rule = MakeRule(predicate, warning=action_value)

  elif action_type == 'error':
    rule = MakeRule(predicate, error=action_value)

  else:
    logging.info('unexpected action type, probably tampering:%r', action_type)
    raise exceptions.InputException()

  return rule
Exemple #29
0
    def GatherPageData(self, mr):
        """Build up a dictionary of data values to use when rendering the page.

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

    Returns:
      Dict of values used by EZT for rendering the page.
    """
        with mr.profiler.Phase('getting issues'):
            if not mr.local_id_list:
                raise exceptions.InputException()
            requested_issues = self.services.issue.GetIssuesByLocalIDs(
                mr.cnxn, mr.project_id, sorted(mr.local_id_list))

        with mr.profiler.Phase('filtering issues'):
            # TODO(jrobbins): filter out issues that the user cannot edit and
            # provide that as feedback rather than just siliently ignoring them.
            open_issues, closed_issues = (
                tracker_helpers.GetAllowedOpenedAndClosedIssues(
                    mr, [issue.issue_id for issue in requested_issues],
                    self.services))
            issues = open_issues + closed_issues

        if not issues:
            self.abort(404, 'no issues found')

        config = self.services.config.GetProjectConfig(mr.cnxn, mr.project_id)
        type_label_set = {
            lab.lower()
            for lab in issues[0].labels if lab.lower().startswith('type-')
        }
        for issue in issues[1:]:
            new_type_set = {
                lab.lower()
                for lab in issue.labels if lab.lower().startswith('type-')
            }
            type_label_set &= new_type_set

        issue_phases = list(
            itertools.chain.from_iterable(issue.phases for issue in issues))

        field_views = tracker_views.MakeAllFieldValueViews(config,
                                                           type_label_set, [],
                                                           [], {},
                                                           phases=issue_phases)
        # Explicitly set all field views to not required. We do not want to force
        # users to have to set it for issues missing required fields.
        # See https://bugs.chromium.org/p/monorail/issues/detail?id=500 for more
        # details.
        for fv in field_views:
            fv.field_def.is_required_bool = None

        with mr.profiler.Phase('making issue proxies'):
            issue_views = [
                template_helpers.EZTItem(
                    local_id=issue.local_id,
                    summary=issue.summary,
                    closed=ezt.boolean(issue in closed_issues))
                for issue in issues
            ]

        num_seconds = (int(len(issue_views) * self._SECONDS_PER_UPDATE) +
                       self._SECONDS_OVERHEAD)

        page_perms = self.MakePagePerms(mr, None, permissions.CREATE_ISSUE,
                                        permissions.DELETE_ISSUE)

        return {
            'issue_tab_mode':
            'issueBulkEdit',
            'issues':
            issue_views,
            'local_ids_str':
            ','.join([str(issue.local_id) for issue in issues]),
            'num_issues':
            len(issue_views),
            'show_progress':
            ezt.boolean(num_seconds > self._SLOWNESS_THRESHOLD),
            'num_seconds':
            num_seconds,
            'initial_blocked_on':
            '',
            'initial_blocking':
            '',
            'initial_comment':
            '',
            'initial_status':
            '',
            'initial_owner':
            '',
            'initial_merge_into':
            '',
            'initial_cc':
            '',
            'initial_components':
            '',
            'labels': [],
            'fields':
            field_views,
            'restrict_to_known':
            ezt.boolean(config.restrict_to_known),
            'page_perms':
            page_perms,
            'statuses_offer_merge':
            config.statuses_offer_merge,
            'issue_phase_names':
            list({phase.name.lower()
                  for phase in issue_phases}),
        }