예제 #1
0
def update_cycle_task_child_state(obj):
    """Update child attributes state of cycle task

  Args:
    obj: Cycle task instance
  """
    status_order = (None, 'Assigned', 'InProgress', 'Declined', 'Finished',
                    'Verified')
    status = obj.status
    children_attrs = _cycle_task_children_attr.get(type(obj), [])
    for children_attr in children_attrs:
        if children_attr:
            children = getattr(obj, children_attr, None)
            for child in children:
                if status == 'Declined' or \
                   status_order.index(status) > status_order.index(child.status):
                    if is_allowed_update(child.__class__.__name__, child.id,
                                         child.context.id):
                        old_status = child.status
                        child.status = status
                        db.session.add(child)
                        Signals.status_change.send(child.__class__,
                                                   obj=child,
                                                   new_status=child.status,
                                                   old_status=old_status)
                    update_cycle_task_child_state(child)
예제 #2
0
def update_cycle_task_child_state(obj):
  """Update child attributes state of cycle task

  Args:
    obj: Cycle task instance
  """
  status_order = (None, 'Assigned', obj.IN_PROGRESS,
                  'Declined', 'Finished', 'Verified')
  status = obj.status
  children_attrs = _cycle_task_children_attr.get(type(obj), [])
  for children_attr in children_attrs:
    if children_attr:
      children = getattr(obj, children_attr, None)
      for child in children:
        if status == 'Declined' or \
           status_order.index(status) > status_order.index(child.status):
          if is_allowed_update(child.__class__.__name__,
                               child.id, child.context.id):
            old_status = child.status
            child.status = status
            Signals.status_change.send(
                child.__class__,
                objs=[
                    Signals.StatusChangeSignalObjectContext(
                        instance=child,
                        new_status=child.status,
                        old_status=old_status,
                    )
                ]
            )
          update_cycle_task_child_state(child)
예제 #3
0
 def _check_post_permissions(self, objects):
     for obj in objects:
         if obj.type == "Snapshot" and\
            not permissions.is_allowed_update(obj.child_type,
                                              obj.child_id,
                                              obj.context_id):
             db.session.expunge_all()
             raise Forbidden()
     super(RelationshipResource, self)._check_post_permissions(objects)
예제 #4
0
def contribute_to_program_view(sender, obj=None, context=None):
  if obj.context_id != None and \
      permissions.is_allowed_read('Role', 1) and \
      permissions.is_allowed_read('UserRole', obj.context_id) and \
      permissions.is_allowed_create('UserRole', obj.context_id) and \
      permissions.is_allowed_update('UserRole', obj.context_id) and \
      permissions.is_allowed_delete('UserRole', obj.context_id):
    return 'permissions/programs/_role_assignments.haml'
  return None
예제 #5
0
def contribute_to_program_view(sender, obj=None, context=None):
    if obj.context_id is not None and \
       rbac_permissions.is_allowed_read('Role', None, 1) and \
       rbac_permissions.is_allowed_read('UserRole', None, obj.context_id) and \
       rbac_permissions.is_allowed_create('UserRole', None, obj.context_id) and \
       rbac_permissions.is_allowed_update('UserRole', None, obj.context_id) and \
       rbac_permissions.is_allowed_delete('UserRole', None, obj.context_id):
        return 'permissions/programs/_role_assignments.haml'
    return None
예제 #6
0
    def generate_automappings(self, relationship):
        """Generate Automappings for a given relationship"""
        # pylint: disable=protected-access
        self.auto_mappings = set()
        with benchmark("Automapping generate_automappings"):
            # initial relationship is special since it is already created and
            # processing it would abort the loop so we manually enqueue the
            # neighborhood
            src = Stub.from_source(relationship)
            dst = Stub.from_destination(relationship)
            self._step(src, dst)
            self._step(dst, src)
            while self.queue:
                if len(self.auto_mappings) > self.COUNT_LIMIT:
                    break
                src, dst = entry = self.queue.pop()

                if {src.type, dst.type} != {"Audit", "Issue"}:
                    # Auditor doesn't have edit (+map) permission on the Audit,
                    # but the Auditor should be allowed to Raise an Issue.
                    # Since Issue-Assessment-Audit is the only rule that
                    # triggers Issue to Audit mapping, we should skip the
                    # permission check for it
                    if not (permissions.is_allowed_update(
                            src.type, src.id, None)
                            and permissions.is_allowed_update(
                                dst.type, dst.id, None)):
                        continue

                created = self._ensure_relationship(src, dst)
                self.processed.add(entry)
                if not created:
                    # If the edge already exists it means that auto mappings for it have
                    # already been processed and it is safe to cut here.
                    continue
                self._step(src, dst)
                self._step(dst, src)

            if len(self.auto_mappings) <= self.COUNT_LIMIT:
                self._flush(relationship)
            else:
                relationship._json_extras = {  # pylint: disable=protected-access
                    'automapping_limit_exceeded': True
                }
예제 #7
0
    def generate_automappings(self, relationship):
        """Generate Automappings for a given relationship"""
        self.auto_mappings = set()

        # initial relationship is special since it is already created and
        # processing it would abort the loop so we manually enqueue the
        # neighborhood
        src = Stub.from_source(relationship)
        dst = Stub.from_destination(relationship)
        self._step(src, dst)
        self._step(dst, src)
        while self.queue:
            if len(self.auto_mappings) > self.COUNT_LIMIT:
                break
            src, dst = entry = self.queue.pop()

            if {src.type, dst.type} not in self._AUTOMAP_WITHOUT_PERMISSION:
                # Mapping between some objects should be created even if there is no
                # permission to edit (+map) this objects. Thus permissions check for
                # them should be skipped.
                if not (permissions.is_allowed_update(src.type, src.id, None)
                        and permissions.is_allowed_update(
                            dst.type, dst.id, None)):
                    continue

            created = self._ensure_relationship(src, dst)
            self.processed.add(entry)
            if not created:
                # If the edge already exists it means that auto mappings for it have
                # already been processed and it is safe to cut here.
                continue
            self._step(src, dst)
            self._step(dst, src)

        if len(self.auto_mappings) <= self.COUNT_LIMIT:
            if self.auto_mappings:
                logger.info("Automapping count: count=%s",
                            len(self.auto_mappings))
            self._flush(relationship)
        else:
            logger.error("Automapping limit exceeded: limit=%s, count=%s",
                         self.COUNT_LIMIT, len(self.auto_mappings))
예제 #8
0
def update_cycle_task_parent_state(obj):  # noqa
    """Propagate changes to obj's parents"""

    if not is_allowed_update(obj.__class__.__name__, obj.id, obj.context.id):
        return

    def update_parent(parent, old_status, new_status):
        """Update a parent element and emit a signal about the change"""
        parent.status = new_status
        db.session.add(parent)
        Signals.status_change.send(
            parent.__class__,
            obj=parent,
            old_status=old_status,
            new_status=new_status,
        )
        update_cycle_task_parent_state(parent)

    parent_attrs = _cycle_task_parent_attr.get(type(obj), [])
    for parent_attr in parent_attrs:
        if not parent_attr:
            continue

        parent = getattr(obj, parent_attr, None)
        old_status = parent.status
        if not parent:
            continue

        # Don't propagate changes to CycleTaskGroup if it's a part of backlog wf
        if isinstance(parent, models.CycleTaskGroup) \
           and parent.cycle.workflow.kind == "Backlog":
            continue

        # If any child is `InProgress`, then parent should be `InProgress`
        if obj.status in {"InProgress", "Declined"
                          } and old_status != "InProgress":
            new_status = "InProgress"
            update_parent(parent, old_status, new_status)
        # If all children are `Finished` or `Verified`, then parent should be same
        elif obj.status in {"Finished", "Verified", "Assigned"}:
            children_attrs = _cycle_task_children_attr.get(type(parent), [])
            for children_attr in children_attrs:
                children = getattr(parent, children_attr, None)
                if not children:
                    continue

                children_statues = [c.status for c in children]
                unique_statuses = set(children_statues)
                for status in ["Verified", "Finished", "Assigned"]:
                    # Check if all elements match a certain state
                    if children_statues[0] == status and len(
                            unique_statuses) == 1:
                        new_status = status
                        update_parent(parent, old_status, new_status)
예제 #9
0
  def generate_automappings(self, relationship):
    """Generate Automappings for a given relationship"""
    # pylint: disable=protected-access
    self.auto_mappings = set()
    with benchmark("Automapping generate_automappings"):
      # initial relationship is special since it is already created and
      # processing it would abort the loop so we manually enqueue the
      # neighborhood
      src = Stub.from_source(relationship)
      dst = Stub.from_destination(relationship)
      self._step(src, dst)
      self._step(dst, src)
      while self.queue:
        if len(self.auto_mappings) > self.COUNT_LIMIT:
          break
        src, dst = entry = self.queue.pop()

        if {src.type, dst.type} != {"Audit", "Issue"}:
          # Auditor doesn't have edit (+map) permission on the Audit,
          # but the Auditor should be allowed to Raise an Issue.
          # Since Issue-Assessment-Audit is the only rule that
          # triggers Issue to Audit mapping, we should skip the
          # permission check for it
          if not (permissions.is_allowed_update(src.type, src.id, None) and
                  permissions.is_allowed_update(dst.type, dst.id, None)):
            continue

        created = self._ensure_relationship(src, dst)
        self.processed.add(entry)
        if not created:
          # If the edge already exists it means that auto mappings for it have
          # already been processed and it is safe to cut here.
          continue
        self._step(src, dst)
        self._step(dst, src)

      if len(self.auto_mappings) <= self.COUNT_LIMIT:
        self._flush(relationship)
      else:
        logger.error("Automapping limit exceeded: limit=%s, count=%s",
                     self.COUNT_LIMIT, len(self.auto_mappings))
예제 #10
0
def update_cycle_task_parent_state(obj):  # noqa
  """Propagate changes to obj's parents"""

  if not is_allowed_update(obj.__class__.__name__,
                           obj.id, obj.context.id):
    return

  def update_parent(parent, old_status, new_status):
    """Update a parent element and emit a signal about the change"""
    parent.status = new_status
    db.session.add(parent)
    Signals.status_change.send(
        parent.__class__,
        obj=parent,
        old_status=old_status,
        new_status=new_status,
    )
    update_cycle_task_parent_state(parent)

  parent_attrs = _cycle_task_parent_attr.get(type(obj), [])
  for parent_attr in parent_attrs:
    if not parent_attr:
      continue

    parent = getattr(obj, parent_attr, None)
    old_status = parent.status
    if not parent:
      continue

    # Don't propagate changes to CycleTaskGroup if it's a part of backlog wf
    if isinstance(parent, models.CycleTaskGroup) \
       and parent.cycle.workflow.kind == "Backlog":
      continue

    # If any child is `InProgress`, then parent should be `InProgress`
    if obj.status in {"InProgress", "Declined"} and old_status != "InProgress":
      new_status = "InProgress"
      update_parent(parent, old_status, new_status)
    # If all children are `Finished` or `Verified`, then parent should be same
    elif obj.status in {"Finished", "Verified", "Assigned"}:
      children_attrs = _cycle_task_children_attr.get(type(parent), [])
      for children_attr in children_attrs:
        children = getattr(parent, children_attr, None)
        if not children:
          continue

        children_statues = [c.status for c in children]
        unique_statuses = set(children_statues)
        for status in ["Verified", "Finished", "Assigned"]:
          # Check if all elements match a certain state
          if children_statues[0] == status and len(unique_statuses) == 1:
            new_status = status
            update_parent(parent, old_status, new_status)
예제 #11
0
 def _can_map_to(obj, parent_relationship):
     """True if the current user can edit obj in parent_relationship.context."""
     context_id = None
     if parent_relationship.context:
         context_id = parent_relationship.context.id
     elif parent_relationship.context_id:
         logger.warning(
             "context is unset but context_id is set on a "
             "relationship %r: context=%r, context_id=%r",
             parent_relationship, parent_relationship.context,
             parent_relationship.context_id)
         context_id = parent_relationship.context_id
     return is_allowed_update(obj.type, obj.id, context_id)
예제 #12
0
    def _ensure_has_permissions(obj):
        """Ensure user has permissions, otherwise raise error"""

        model_name = obj.__class__.__name__

        if permissions.is_allowed_update(model_name, obj.id, obj.context_id):
            return

        if permissions.has_conditions('update', model_name):
            return

        if permissions.is_allowed_update_for(obj):
            return

        raise exceptions.Forbidden()
예제 #13
0
파일: folder.py 프로젝트: google/ggrc-core
  def _ensure_has_permissions(obj):
    """Ensure user has permissions, otherwise raise error"""

    model_name = obj.__class__.__name__

    if permissions.is_allowed_update(model_name, obj.id, obj.context_id):
      return

    if permissions.has_conditions('update', model_name):
      return

    if permissions.is_allowed_update_for(obj):
      return

    raise exceptions.Forbidden()
예제 #14
0
def _update_parent_state(obj, parent, child_statuses):
    """Util function, update status of sent parent, if it's allowed.

  New status based on sent object status and sent child_statuses"""
    if not is_allowed_update(obj.__class__.__name__, obj.id, obj.context.id):
        return
    old_status = parent.status
    if obj.status in {"InProgress", "Declined"}:
        new_status = "InProgress"
    elif obj.status in {"Finished", "Verified", "Assigned"}:
        in_same_status = len(child_statuses) == 1
        new_status = child_statuses.pop() if in_same_status else old_status
    if old_status == new_status:
        return
    parent.status = new_status
    db.session.add(parent)
    Signals.status_change.send(
        parent.__class__,
        obj=parent,
        old_status=old_status,
        new_status=new_status,
    )
예제 #15
0
def _update_parent_state(obj, parent, child_statuses):
  """Util function, update status of sent parent, if it's allowed.

  New status based on sent object status and sent child_statuses"""
  if not is_allowed_update(obj.__class__.__name__, obj.id, obj.context.id):
    return
  old_status = parent.status
  if obj.status in {"InProgress", "Declined"}:
    new_status = "InProgress"
  elif obj.status in {"Finished", "Verified", "Assigned"}:
    in_same_status = len(child_statuses) == 1
    new_status = child_statuses.pop() if in_same_status else old_status
  if old_status == new_status:
    return
  parent.status = new_status
  db.session.add(parent)
  Signals.status_change.send(
      parent.__class__,
      obj=parent,
      old_status=old_status,
      new_status=new_status,
  )
예제 #16
0
def update_cycle_task_child_state(obj):

  status_order = (None, 'Assigned', 'InProgress',
                  'Declined', 'Finished', 'Verified')
  status = obj.status
  children_attrs = _cycle_task_children_attr.get(type(obj), [])
  for children_attr in children_attrs:
    if children_attr:
      children = getattr(obj, children_attr, None)
      for child in children:
        if status == 'Declined' or \
           status_order.index(status) > status_order.index(child.status):
          if is_allowed_update(child.__class__.__name__,
                               child.id, child.context.id):
            old_status = child.status
            child.status = status
            db.session.add(child)
            Signals.status_change.send(
                child.__class__,
                obj=child,
                new_status=child.status,
                old_status=old_status
            )
          update_cycle_task_child_state(child)
예제 #17
0
 def _can_map_to(self, obj, parent_relationship):
   return is_allowed_update(obj.type, obj.id, parent_relationship.context)
예제 #18
0
class Resource(ModelView):
    """View base class for Views handling.  Will typically be registered with an
  application following a collection style for routes. Collection `GET` and
  `POST` will have a route like `/resources` while collection member
  resource routes will have routes likej `/resources/<pk:pk_type>`.

  To register a Resource subclass FooCollection with a Flask application:

  ..

     FooCollection.add_to(app, '/foos')

  By default will only support the `application/json` content-type.
  """
    def dispatch_request(self, *args, **kwargs):
        method = request.method.lower()

        if method == 'get':
            if self.pk in kwargs and kwargs[self.pk] is not None:
                return self.get(*args, **kwargs)
            else:
                return self.collection_get()
        elif method == 'post':
            if self.pk in kwargs and kwargs[self.pk] is not None:
                return self.post(*args, **kwargs)
            else:
                return self.collection_post()
        elif method == 'put':
            return self.put(*args, **kwargs)
        elif method == 'delete':
            return self.delete(*args, **kwargs)
        else:
            raise NotImplementedError()

    def post(*args, **kwargs):
        raise NotImplementedError()

    # Default JSON request handlers
    def get(self, id):
        obj = self.get_object(id)
        if obj is None:
            return self.not_found_response()
        if 'Accept' in self.request.headers and \
           'application/json' not in self.request.headers['Accept']:
            return current_app.make_response(
                ('application/json', 406, [('Content-Type', 'text/plain')]))
        if not permissions.is_allowed_read(self.model.__name__,
                                           obj.context_id):
            raise Forbidden()
        object_for_json = self.object_for_json(obj)
        if 'If-None-Match' in self.request.headers and \
            self.request.headers['If-None-Match'] == self.etag(object_for_json):
            return current_app.make_response(
                ('', 304, [('Etag', self.etag(object_for_json))]))
        return self.json_success_response(self.object_for_json(obj),
                                          self.modified_at(obj))

    def validate_headers_for_put_or_delete(self, obj):
        missing_headers = []
        if 'If-Match' not in self.request.headers:
            missing_headers.append('If-Match')
        if 'If-Unmodified-Since' not in self.request.headers:
            missing_headers.append('If-Unmodified-Since')
        if missing_headers:
            # rfc 6585 defines a new status code for missing required headers
            return current_app.make_response(('If-Match is required.', 428, [
                ('Content-Type', 'text/plain')
            ]))
        if request.headers['If-Match'] != self.etag(self.object_for_json(obj)) or \
           request.headers['If-Unmodified-Since'] != \
              self.http_timestamp(self.modified_at(obj)):
            return current_app.make_response((
                'The resource has been changed. The conflict must be resolved and '
                'the request resubmitted with an up to date Etag for If-Match '
                'header.', 409, [('Content-Type', 'text/plain')]))
        return None

    def put(self, id):
        obj = self.get_object(id)
        if obj is None:
            return self.not_found_response()
        if self.request.headers['Content-Type'] != 'application/json':
            return current_app.make_response(
                ('Content-Type must be application/json', 415, []))
        header_error = self.validate_headers_for_put_or_delete(obj)
        if header_error:
            return header_error
        src = UnicodeSafeJsonWrapper(self.request.json)
        root_attribute = self.model._inflector.table_singular
        try:
            src = src[root_attribute]
        except KeyError, e:
            return current_app.make_response(
                ('Required attribute "{0}" not found'.format(root_attribute),
                 400, []))
        if not permissions.is_allowed_update(self.model.__name__,
                                             obj.context_id):
            raise Forbidden()
        ggrc.builder.json.update(obj, src)
        #FIXME Fake the modified_by_id until we have that information in session.
        obj.modified_by_id = get_current_user_id()
        db.session.add(obj)
        db.session.commit()
        obj = self.get_object(id)
        get_indexer().update_record(fts_record_for(obj))
        return self.json_success_response(self.object_for_json(obj),
                                          self.modified_at(obj))
예제 #19
0
 def _can_map_to(obj, parent_relationship):
   return is_allowed_update(obj.type, obj.id, parent_relationship.context)