Ejemplo n.º 1
0
  def has_role(self, principal, role, object=None):
    """
    True if `principal` has `role` (either globally, if `object` is None, or on
    the specific `object`).

    :param:role:  can be a list or tuple of strings or a :class:`Role` instance

    `object` can be an :class:`Entity`, a string, or `None`.

    Note: we're using a cache for efficiency here. TODO: check that we're not
    over-caching.

    Note2: caching could also be moved upfront to when the user is loaded.
    """
    if not principal:
      return False

    principal = noproxy(principal)
    if not self.running:
      return True

    if (principal is Anonymous
        or (hasattr(principal, 'is_anonymous') and principal.is_anonymous())):
      return False

    # root always have any role
    if isinstance(principal, User) and principal.id == 0:
      return True

    # admin & manager always have role
    if isinstance(role, (Role, basestring)):
      role = (role,)
    valid_roles = frozenset((Admin, Manager) + tuple(role))

    if object:
      assert isinstance(object, Entity)
      object_key = u"{}:{:d}".format(object.object_type, object.id)
    else:
      object_key = None

    if self.app_state.use_cache:
      cache = self._fill_role_cache(principal)

      if Admin in cache.get(None, ()):
        # user is a global admin
        return True

      if object_key in cache:
        roles = cache[object_key]
        return len(valid_roles & roles) > 0
      return False

    all_roles = self._all_roles(principal)

    if Admin in all_roles.get(None, ()):
      # user is a global admin
      return True

    roles = all_roles.get(object_key, set())
    return len(valid_roles & roles) > 0
Ejemplo n.º 2
0
  def has_permission(self, user, permission, obj=None, inherit=False):
    """ @param `inherit`: check with permission inheritance. By default, check
    only local roles.
    """
    assert permission in PERMISSION
    user = noproxy(user)

    # root always have any permission
    if isinstance(user, User) and user.id == 0:
      return True

    roles = [Manager, Admin]  # have 'manage' permission

    if permission in ('read', 'write'):
      roles.append(Role('writer'))

    if permission == 'read':
      roles.append(Role('reader'))

    checked_objs = [obj]

    if inherit and obj is not None:
      while (obj.inherit_security and obj.parent is not None):
        obj = obj.parent
        checked_objs.append(obj)

    principals = [user] + user.groups
    self._fill_role_cache_batch(principals)

    return any((self.has_role(principal, roles, item)
                for principal in principals
                for item in checked_objs))
Ejemplo n.º 3
0
 def filter_with_permission(self,
                            user,
                            permission,
                            obj_list,
                            inherit=False):
     user = noproxy(user)
     return [
         obj for obj in obj_list
         if self.has_permission(user, permission, obj, inherit)
     ]
Ejemplo n.º 4
0
    def grant_role(self, principal, role, obj=None):
        """
    Grants `role` to `user` (either globally, if `obj` is None, or on
    the specific `obj`).
    """
        assert principal
        principal = noproxy(principal)
        session = object_session(obj) if obj is not None else db.session
        manager = self._current_user_manager(session=session)
        args = dict(role=role,
                    object=obj,
                    anonymous=False,
                    user=None,
                    group=None)

        if (principal is AnonymousRole or
            (hasattr(principal, 'is_anonymous') and principal.is_anonymous())):
            args['anonymous'] = True
        elif isinstance(principal, User):
            args['user'] = principal
        else:
            args['group'] = principal

        q = session.query(RoleAssignment)
        if len(q.filter_by(**args).limit(1).all()) > 0:
            # role already granted, nothing to do
            return

        # same as above but in current, not yet flushed objects in session. We
        # cannot call flush() in grant_role() since this method may be called a
        # great number of times in the same transaction, and sqlalchemy limits to
        # 100 flushes before triggering a warning
        for ra in (o for models in (session.new, session.dirty) for o in models
                   if isinstance(o, RoleAssignment)):
            if all(getattr(ra, attr) == val for attr, val in args.items()):
                return

        ra = RoleAssignment(**args)
        session.add(ra)
        audit = SecurityAudit(manager=manager, op=SecurityAudit.GRANT, **args)
        if obj is not None:
            audit.object_id = obj.id
            audit.object_type = obj.entity_type
            object_name = u''
            for attr_name in ('name', 'path', '__path_before_delete'):
                if hasattr(obj, attr_name):
                    object_name = getattr(obj, attr_name)
            audit.object_name = object_name

        session.add(audit)
        self._needs_flush()

        if hasattr(principal, "__roles_cache__"):
            del principal.__roles_cache__
Ejemplo n.º 5
0
  def grant_role(self, principal, role, obj=None):
    """
    Grants `role` to `user` (either globally, if `obj` is None, or on
    the specific `obj`).
    """
    assert principal
    principal = noproxy(principal)
    session = object_session(obj) if obj is not None else db.session
    manager = self._current_user_manager(session=session)
    args = dict(role=role, object=obj,
                anonymous=False,
                user=None, group=None)

    if (principal is AnonymousRole
        or (hasattr(principal, 'is_anonymous') and principal.is_anonymous())):
      args['anonymous'] = True
    elif isinstance(principal, User):
      args['user'] = principal
    else:
      args['group'] = principal

    q = session.query(RoleAssignment)
    if len(q.filter_by(**args).limit(1).all()) > 0:
      # role already granted, nothing to do
      return

    # same as above but in current, not yet flushed objects in session. We
    # cannot call flush() in grant_role() since this method may be called a
    # great number of times in the same transaction, and sqlalchemy limits to
    # 100 flushes before triggering a warning
    for ra in (o for models in (session.new, session.dirty)
                for o in models if isinstance(o, RoleAssignment)):
      if all(getattr(ra, attr) == val for attr, val in args.items()):
        return

    ra = RoleAssignment(**args)
    session.add(ra)
    audit = SecurityAudit(manager=manager, op=SecurityAudit.GRANT, **args)
    if obj is not None:
      audit.object_id = obj.id
      audit.object_type = obj.entity_type
      object_name = u''
      for attr_name in ('name', 'path', '__path_before_delete'):
        if hasattr(obj, attr_name):
          object_name = getattr(obj, attr_name)
      audit.object_name = object_name

    session.add(audit)
    self._needs_flush()

    if hasattr(principal, "__roles_cache__"):
      del principal.__roles_cache__
Ejemplo n.º 6
0
  def ungrant_role(self, principal, role, object=None):
    """
    Ungrants `role` to `user` (either globally, if `object` is None, or on
    the specific `object`).
    """
    assert principal
    principal = noproxy(principal)
    session = object_session(object) if object is not None else db.session
    manager = self._current_user_manager()

    args = dict(role=role, object=object,
                anonymous=False, user=None, group=None)
    q = session.query(RoleAssignment)
    q = q.filter(RoleAssignment.role == role,
                 RoleAssignment.object == object)

    if (principal is Anonymous
        or (hasattr(principal, 'is_anonymous') and principal.is_anonymous())):
      args['anonymous'] = True
      q.filter(RoleAssignment.anonymous == False,
               RoleAssignment.user == None,
               RoleAssignment.group == None)

    elif isinstance(principal, User):
      args['user'] = principal
      q = q.filter(RoleAssignment.user == principal)
    else:
      args['group'] = principal
      q = q.filter(RoleAssignment.group == principal)

    ra = q.one()
    session.delete(ra)
    audit = SecurityAudit(manager=manager, op=SecurityAudit.REVOKE, **args)
    session.add(audit)
    self._needs_flush()

    if hasattr(principal, "__roles_cache__"):
      del principal.__roles_cache__
Ejemplo n.º 7
0
    def ungrant_role(self, principal, role, object=None):
        """Ungrant `role` to `user` (either globally, if `object` is None,
        or on the specific `object`).
        """
        assert principal
        principal = noproxy(principal)
        session = object_session(object) if object is not None else db.session
        manager = self._current_user_manager(session=session)

        args = dict(role=role,
                    object=object,
                    anonymous=False,
                    user=None,
                    group=None)
        query = session.query(RoleAssignment)
        query = query.filter(RoleAssignment.role == role,
                             RoleAssignment.object == object)

        if (principal is AnonymousRole or
            (hasattr(principal, 'is_anonymous') and principal.is_anonymous)):
            args['anonymous'] = True
            query.filter(RoleAssignment.anonymous == False,
                         RoleAssignment.user == None,
                         RoleAssignment.group == None)

        elif isinstance(principal, User):
            args['user'] = principal
            query = query.filter(RoleAssignment.user == principal)
        else:
            args['group'] = principal
            query = query.filter(RoleAssignment.group == principal)

        ra = query.one()
        session.delete(ra)
        audit = SecurityAudit(manager=manager, op=SecurityAudit.REVOKE, **args)
        session.add(audit)
        self._needs_flush()
        self._clear_role_cache(principal)
Ejemplo n.º 8
0
def indexable_role(principal):
    """Return a string suitable for query against `allowed_roles_and_users`
    field.

    :param principal: It can be :data:`Anonymous`, :data:`Authenticated`,
      or an instance of :class:`User` or :class:`Group`.
    """
    principal = noproxy(principal)

    if hasattr(principal, 'is_anonymous') and principal.is_anonymous:
        # transform anonymous user to anonymous role
        principal = Anonymous

    if isinstance(principal, Role):
        return 'role:{}'.format(principal.name)
    elif isinstance(principal, User):
        fmt = 'user:{:d}'
    elif isinstance(principal, Group):
        fmt = 'group:{:d}'
    else:
        raise ValueError(repr(principal))

    return fmt.format(principal.id)
Ejemplo n.º 9
0
def indexable_role(principal):
  """
  Returns a string suitable for query against `allowed_roles_and_users`
  field.

  :param principal: It can be :data:`Anonymous`, :data:`Authenticated`,
      or an instance of :class:`User` or :class:`Group`.
  """
  principal = noproxy(principal)

  if (principal is Anonymous
      or (hasattr(principal, 'is_anonymous') and principal.is_anonymous())):
    return u'role:anonymous'
  elif isinstance(principal, Role):
    return u'role:{}'.format(unicode(principal))
  elif isinstance(principal, User):
    fmt = u'user:{:d}'
  elif isinstance(principal, Group):
    fmt = u'group:{:d}'
  else:
    raise ValueError(repr(principal))

  return fmt.format(principal.id)
Ejemplo n.º 10
0
 def filter_with_permission(self, user, permission, obj_list, inherit=False):
   user = noproxy(user)
   return [ obj for obj in obj_list
            if self.has_permission(user, permission, obj, inherit) ]
Ejemplo n.º 11
0
    def has_permission(self,
                       user,
                       permission,
                       obj=None,
                       inherit=False,
                       roles=None):
        """
        @param `obj`: target object to check permissions.
        @param `inherit`: check with permission inheritance. By default, check only
        local roles.
        @param `roles`: additional valid role or iterable of roles having
                        `permission`.
        """
        if not isinstance(permission, Permission):
            assert permission in PERMISSIONS
            permission = Permission(permission)
        user = noproxy(user)

        if not self.running:
            return True

        session = None
        if obj is not None:
            session = object_session(obj)

        if session is None:
            session = current_app.db.session()

        # root always have any permission
        if isinstance(user, User) and user.id == 0:
            return True

        # valid roles
        # 1: from database
        pa_filter = PermissionAssignment.object == None
        if obj is not None and obj.id is not None:
            pa_filter |= PermissionAssignment.object == obj

        pa_filter &= PermissionAssignment.permission == permission
        valid_roles = session \
            .query(PermissionAssignment.role) \
            .filter(pa_filter)
        valid_roles = {res[0] for res in valid_roles.yield_per(1000)}

        # complete with defaults
        valid_roles |= {Admin}  # always have all permissions
        valid_roles |= DEFAULT_PERMISSION_ROLE.get(permission, set())

        #FIXME: obj.__class__ could define default permisssion matrix too

        if roles is not None:
            if isinstance(roles, (Role, ) + string_types):
                roles = (roles, )

            for r in roles:
                valid_roles.add(Role(r))

        #FIXME: query permission_role: global and on object

        if AnonymousRole in valid_roles:
            return True

        if Authenticated in valid_roles and not user.is_anonymous:
            return True

        checked_objs = [None,
                        obj]  # first test global roles, then object local
        # roles

        if inherit and obj is not None:
            while (obj.inherit_security and obj.parent is not None):
                obj = obj.parent
                checked_objs.append(obj)

        principals = [user] + list(user.groups)
        self._fill_role_cache_batch(principals)

        return any((self.has_role(principal, valid_roles, item)
                    for principal in principals for item in checked_objs))
Ejemplo n.º 12
0
    def has_role(self, principal, role, object=None):
        """True if `principal` has `role` (either globally, if `object`
        is None, or on the specific `object`).

        :param:role:  can be a list or tuple of strings or a :class:`Role` instance

        `object` can be an :class:`Entity`, a string, or `None`.

        Note: we're using a cache for efficiency here. TODO: check that we're not
        over-caching.

        Note2: caching could also be moved upfront to when the user is loaded.
        """
        if not principal:
            return False

        principal = noproxy(principal)
        if not self.running:
            return True

        if isinstance(role, (Role, string_types)):
            role = (role, )

        # admin & manager always have role
        valid_roles = frozenset((Admin, Manager) + tuple(role))

        if AnonymousRole in valid_roles:
            # everybody has the role 'Anonymous'
            return True

        if (Authenticated in valid_roles and isinstance(principal, User)
                and not principal.is_anonymous):
            return True

        if (principal is AnonymousRole or
            (hasattr(principal, 'is_anonymous') and principal.is_anonymous)):
            # anonymous user, and anonymous role isn't in valid_roles
            return False

        # root always have any role
        if isinstance(principal, User) and principal.id == 0:
            return True

        if object:
            assert isinstance(object, Entity)
            object_key = u"{}:{}".format(object.object_type,
                                         text_type(object.id))
            if Creator in role:
                if object.creator == principal:
                    return True
            if Owner in role:
                if object.owner == principal:
                    return True

        else:
            object_key = None

        all_roles = (self._fill_role_cache(principal) if
                     self.app_state.use_cache else self._all_roles(principal))
        roles = set()
        roles |= all_roles.get(None, set())
        roles |= all_roles.get(object_key, set())
        return len(valid_roles & roles) > 0
Ejemplo n.º 13
0
  def has_permission(self, user, permission, obj=None, inherit=False,
                     roles=None):
    """
    @param `obj`: target object to check permissions.
    @param `inherit`: check with permission inheritance. By default, check only
    local roles.
    @param `roles`: additional valid role or iterable of roles having
                    `permission`.
    """
    if not isinstance(permission, Permission):
      assert permission in PERMISSIONS
      permission = Permission(permission)
    user = noproxy(user)

    if not self.running:
      return True

    session = None
    if obj is not None:
      session = object_session(obj)

    if session is None:
      session = current_app.db.session()

    # root always have any permission
    if isinstance(user, User) and user.id == 0:
      return True

    # valid roles
    # 1: from database
    pa_filter = PermissionAssignment.object == None
    if obj is not None and obj.id is not None:
      pa_filter |= PermissionAssignment.object == obj

    pa_filter &= PermissionAssignment.permission == permission
    valid_roles = session.query(PermissionAssignment.role)\
                         .filter(pa_filter)
    valid_roles = { res[0] for res in valid_roles.yield_per(1000) }

    # complete with defaults
    valid_roles |= {Admin}  # always have all permissions
    valid_roles |= DEFAULT_PERMISSION_ROLE.get(permission, set())

    #FIXME: obj.__class__ could define default permisssion matrix too

    if roles is not None:
      if isinstance(roles, (Role, bytes, unicode)):
        roles = (roles,)

      for r in roles:
        valid_roles.add(Role(r))

    #FIXME: query permission_role: global and on object

    if AnonymousRole in valid_roles:
      return True

    if Authenticated in valid_roles and not user.is_anonymous():
        return True

    checked_objs = [None, obj] # first test global roles, then object local
                               # roles

    if inherit and obj is not None:
      while (obj.inherit_security and obj.parent is not None):
        obj = obj.parent
        checked_objs.append(obj)

    principals = [user] + list(user.groups)
    self._fill_role_cache_batch(principals)

    return any((self.has_role(principal, valid_roles, item)
                for principal in principals
                for item in checked_objs))
Ejemplo n.º 14
0
  def has_role(self, principal, role, object=None):
    """
    True if `principal` has `role` (either globally, if `object` is None, or on
    the specific `object`).

    :param:role:  can be a list or tuple of strings or a :class:`Role` instance

    `object` can be an :class:`Entity`, a string, or `None`.

    Note: we're using a cache for efficiency here. TODO: check that we're not
    over-caching.

    Note2: caching could also be moved upfront to when the user is loaded.
    """
    if not principal:
      return False

    principal = noproxy(principal)
    if not self.running:
      return True

    if isinstance(role, (Role, string_types)):
      role = (role,)

    # admin & manager always have role
    valid_roles = frozenset((Admin, Manager) + tuple(role))

    if AnonymousRole in valid_roles:
      # everybody has the role 'Anonymous'
      return True

    if (Authenticated in valid_roles
        and isinstance(principal, User) and not principal.is_anonymous()):
        return True

    if (principal is AnonymousRole
        or (hasattr(principal, 'is_anonymous') and principal.is_anonymous())):
      # anonymous user, and anonymous role isn't in valid_roles
      return False

    # root always have any role
    if isinstance(principal, User) and principal.id == 0:
      return True

    if object:
      assert isinstance(object, Entity)
      object_key = u"{}:{}".format(object.object_type, unicode(object.id))
      if Creator in role:
        if object.creator == principal:
          return True
      if Owner in role:
        if object.owner == principal:
          return True

    else:
      object_key = None

    all_roles = (self._fill_role_cache(principal)
                 if self.app_state.use_cache
                 else self._all_roles(principal))
    roles = set()
    roles |= all_roles.get(None, set())
    roles |= all_roles.get(object_key, set())
    return len(valid_roles & roles) > 0