def can_access(self, user, allow_admin=True): """Checks if the user can access the object. :param user: The :class:`.User` to check. May be None if the user is not logged in. :param allow_admin: If admin users should always have access """ override = self._check_can_access_override(user, allow_admin=allow_admin) if override is not None: return override # Usually admins can access everything, so no need for checks if allow_admin and user and user.is_admin: rv = True # If there's a valid access key we can skip all other ACL checks elif self.allow_access_key and self.check_access_key(): rv = True elif self.protection_mode == ProtectionMode.public: # if it's public we completely ignore the parent protection # this is quite ugly which is why it should only be allowed # in rare cases (e.g. events which might be in a protected # category but should be public nonetheless) rv = True elif self.protection_mode == ProtectionMode.protected: # if it's protected, we also ignore the parent protection # and only check our own ACL if any(user in entry.principal for entry in iter_acl(self.acl_entries)): rv = True elif isinstance(self, ProtectionManagersMixin): rv = self.can_manage(user, allow_admin=allow_admin) else: rv = False elif self.protection_mode == ProtectionMode.inheriting: # if it's inheriting, we only check the parent protection # unless `inheriting_have_acl` is set, in which case we # might not need to check the parents at all if self.inheriting_have_acl and any(user in entry.principal for entry in iter_acl(self.acl_entries)): rv = True else: # the parent can be either an object inheriting from this # mixin or a legacy object with an AccessController parent = self.protection_parent if parent is None: # This should be the case for the top-level object, # i.e. the root category, which shouldn't allow # ProtectionMode.inheriting as it makes no sense. raise TypeError('protection_parent of {} is None'.format(self)) elif hasattr(parent, 'can_access'): rv = parent.can_access(user, allow_admin=allow_admin) else: raise TypeError('protection_parent of {} is of invalid type {} ({})'.format(self, type(parent), parent)) else: # should never happen, but since this is a sensitive area # we better fail loudly if we have garbage raise ValueError('Invalid protection mode: {}'.format(self.protection_mode)) override = self._check_can_access_override(user, allow_admin=allow_admin, authorized=rv) return override if override is not None else rv
def can_access(self, user, allow_admin=True): """Checks if the user can access the object. :param user: The :class:`.User` to check. May be None if the user is not logged in. :param allow_admin: If admin users should always have access """ # Trigger signals for protection overrides rv = values_from_signal( signals.acl.can_access.send(type(self), obj=self, user=user, allow_admin=allow_admin), single_value=True ) if rv: # in case of contradictory results (shouldn't happen at all) # we stay on the safe side and deny access return all(rv) # Usually admins can access everything, so no need for checks if allow_admin and user and user.is_admin: return True if self.protection_mode == ProtectionMode.public: # if it's public we completely ignore the parent protection # this is quite ugly which is why it should only be allowed # in rare cases (e.g. events which might be in a protected # category but should be public nonetheless) return True elif self.protection_mode == ProtectionMode.protected: # if it's protected, we also ignore the parent protection # and only check our own ACL return user is not None and any(user in entry.principal for entry in iter_acl(self.acl_entries)) elif self.protection_mode == ProtectionMode.inheriting: # if it's inheriting, we only check the parent protection # unless `inheriting_have_acl` is set, in which case we # might not need to check the parents at all if ( self.inheriting_have_acl and user is not None and any(user in entry.principal for entry in iter_acl(self.acl_entries)) ): return True # the parent can be either an object inheriting from this # mixin or a legacy object with an AccessController parent = self.protection_parent if parent is None: # This should be the case for the top-level object, # i.e. the root category, which shouldn't allow # ProtectionMode.inheriting as it makes no sense. raise TypeError("protection_parent of {} is None".format(self)) elif hasattr(parent, "can_access"): return parent.can_access(user, allow_admin=allow_admin) elif hasattr(parent, "canAccess"): return parent.canAccess(AccessWrapper(user.as_avatar if user else None)) else: raise TypeError("protection_parent of {} is of invalid type {} ({})".format(self, type(parent), parent)) else: # should never happen, but since this is a sensitive area # we better fail loudly if we have garbage raise ValueError("Invalid protection mode: {}".format(self.protection_mode))
def get_contributions_with_user_as_submitter(event, user): """Get a list of contributions in which the `user` has submission rights""" contribs = (Contribution.query.with_parent(event) .options(joinedload('acl_entries')) .filter(Contribution.acl_entries.any(ContributionPrincipal.has_management_role('submit'))) .all()) return {c for c in contribs if any(user in entry.principal for entry in iter_acl(c.acl_entries))}
def get_sessions_for_user(event, user): sessions = (Session.query.with_parent(event) .options(joinedload('acl_entries')) .filter(Session.acl_entries.any(SessionPrincipal.has_management_role('coordinate')), ~Session.is_deleted) .all()) return {sess for sess in sessions if any(user in entry.principal for entry in iter_acl(sess.acl_entries))}
def can_access(self, user, acl_attr='acl', legacy_method='canAccess', allow_admin=True): """Checks if the user can access the object. When using a custom `acl_attr` on an object that supports inherited proection, ALL possible `protection_parent` objects need to have an ACL with the same name too! :param user: The :class:`.User` to check. May be None if the user is not logged in. :param acl_attr: The name of the property that contains the set of authorized principals. :param legacy_method: The method name to use when inheriting the protection from a legacy object. :param allow_admin: If admin users should always have access """ # Trigger signals for protection overrides rv = values_from_signal(signals.acl.can_access.send(type(self), obj=self, user=user, acl_attr=acl_attr, legacy_method=legacy_method, allow_admin=allow_admin), single_value=True) if rv: # in case of contradictory results (shouldn't happen at all) # we stay on the safe side and deny access return all(rv) # Usually admins can access everything, so no need for checks if allow_admin and user and user.is_admin: return True if self.protection_mode == ProtectionMode.public: # if it's public we completely ignore the parent protection # this is quite ugly which is why it should only be allowed # in rare cases (e.g. events which might be in a protected # category but should be public nonetheless) return True elif self.protection_mode == ProtectionMode.protected: # if it's protected, we also ignore the parent protection # and only check our own ACL return any(user in principal for principal in iter_acl(getattr(self, acl_attr))) elif self.protection_mode == ProtectionMode.inheriting: # if it's inheriting, we only check the parent protection. # the parent can be either an object inheriting from this # mixin or a legacy object with an AccessController parent = self.protection_parent if parent is None: # This should be the case for the top-level object, # i.e. the root category, which shouldn't allow # ProtectionMode.inheriting as it makes no sense. raise TypeError('protection_parent of {} is None'.format(self)) elif hasattr(parent, 'can_access'): return parent.can_access(user, acl_attr=acl_attr) elif legacy_method is not None and hasattr(parent, legacy_method): return getattr(parent, legacy_method)(AccessWrapper(user.as_avatar if user else None)) else: raise TypeError('protection_parent of {} is of invalid type {} ({})'.format(self, type(parent), parent)) else: # should never happen, but since this is a sensitive area # we better fail loudly if we have garbage raise ValueError('Invalid protection mode: {}'.format(self.protection_mode))
def test_iter_acl(): user = MagicMock(is_group=False, spec=['is_group']) user_p = MagicMock(principal=user, spec=['principal']) local_group = MagicMock(is_group=True, is_local=True, spec=['is_local']) local_group_p = MagicMock(principal=local_group, spec=['principal']) remote_group = MagicMock(is_group=True, is_local=False, spec=['is_local']) remote_group_p = MagicMock(principal=remote_group, spec=['principal']) acl = [user_p, remote_group, local_group_p, user, local_group, remote_group_p] assert list(iter_acl(iter(acl))) == [user_p, user, local_group_p, local_group, remote_group, remote_group_p]
def contains_user(self, name, user): """Checks if a user is in an ACL. To pass this check, the user can either be in the ACL itself or in a group in the ACL. :param name: Setting name :param user: A :class:`.User` """ return any(user in principal for principal in iter_acl(self.get(name)))
def test_iter_acl(): user = User() user_p = MagicMock(principal=user, spec=['principal']) ipn = IPNetworkGroup() ipn_p = MagicMock(principal=ipn, spec=['principal']) local_group = GroupProxy(123, _group=MagicMock()) local_group_p = MagicMock(principal=local_group, spec=['principal']) remote_group = GroupProxy('foo', 'bar') remote_group_p = MagicMock(principal=remote_group, spec=['principal']) acl = [ipn, user_p, remote_group, local_group_p, user, local_group, remote_group_p, ipn_p] assert list(iter_acl(iter(acl))) == [user_p, user, ipn, ipn_p, local_group_p, local_group, remote_group, remote_group_p]
def can_be_overridden(self, user, room=None, explicit_only=False): """Determines if a user can override the blocking The following persons are authorized to override a blocking: - owner (the one who created the blocking) - any users on the blocking's ACL - unless explicitOnly is set: admins and room owners (if a room is given) """ if not user: return False if self.created_by_user == user: return True if not explicit_only: if rb_is_admin(user): return True elif room and room.is_owned_by(user): return True return any(user in principal for principal in iter_acl(self.allowed))
def can_override(self, user, room=None, explicit_only=False, allow_admin=True): """Check if a user can override the blocking The following persons are authorized to override a blocking: - the creator of the blocking - anyone on the blocking's ACL - unless explicit_only is set: rb admins and room managers (if a room is given) """ if not user: return False if self.created_by_user == user: return True if not explicit_only: if allow_admin and rb_is_admin(user): return True if room and room.can_manage(user): return True return any(user in principal for principal in iter_acl(self.allowed))
def test_iter_acl(): user = MagicMock(is_group=False, spec=['is_group']) local_group = MagicMock(is_group=True, is_local=True) remote_group = MagicMock(is_group=True, is_local=False) acl = [remote_group, user, local_group] assert list(iter_acl(iter(acl))) == [user, local_group, remote_group]
def can_manage(self, user, role=None, allow_admin=True, check_parent=True, explicit_role=False): """Checks if the user can manage the object. :param user: The :class:`.User` to check. May be None if the user is not logged in. :param: role: The management role that is needed for the check to succeed. If not specified, full management privs are required. May be set to the string ``'ANY'`` to check if the user has any management privileges. If the user has `full_access` privileges, he's assumed to have all possible roles. :param allow_admin: If admin users should always have access :param check_parent: If the parent object should be checked. In this case the role is ignored; only full management access is inherited to children. :param explicit_role: If the specified role should be checked explicitly instead of short-circuiting the check for Indico admins or managers. When this option is set to ``True``, the values of `allow_admin` and `check_parent` are ignored. This also applies if `role` is None in which case this argument being set to ``True`` is equivalent to `allow_admin` and `check_parent` being set to ``False``. """ if role is not None and role != 'ANY' and role not in get_available_roles(type(self)): raise ValueError("role '{}' is not valid for '{}' objects".format(role, type(self).__name__)) if user is None: # An unauthorized user is never allowed to perform management operations. # Not even signals may override this since management code generally # expects session.user to be not None. # XXX: Legacy modification keys are checked outside return False # Trigger signals for protection overrides rv = values_from_signal(signals.acl.can_manage.send(type(self), obj=self, user=user, role=role, allow_admin=allow_admin, check_parent=check_parent, explicit_role=explicit_role), single_value=True) if rv: # in case of contradictory results (shouldn't happen at all) # we stay on the safe side and deny access return all(rv) # Usually admins can access everything, so no need for checks if not explicit_role and allow_admin and user.is_admin: return True if any(user in entry.principal for entry in iter_acl(self.acl_entries) if entry.has_management_role(role, explicit=(explicit_role and role is not None))): return True if not check_parent or explicit_role: return False # the parent can be either an object inheriting from this # mixin or a legacy object with an AccessController parent = self.protection_parent if parent is None: # This should be the case for the top-level object, # i.e. the root category return False elif hasattr(parent, 'can_manage'): return parent.can_manage(user, allow_admin=allow_admin) elif hasattr(parent, 'canUserModify'): return parent.canUserModify(user.as_avatar) else: raise TypeError('protection_parent of {} is of invalid type {} ({})'.format(self, type(parent), parent))
def can_manage(self, user, permission=None, allow_admin=True, check_parent=True, explicit_permission=False): """Checks if the user can manage the object. :param user: The :class:`.User` to check. May be None if the user is not logged in. :param: permission: The management permission that is needed for the check to succeed. If not specified, full management privs are required. May be set to the string ``'ANY'`` to check if the user has any management privileges. If the user has `full_access` privileges, he's assumed to have all possible permissions. :param allow_admin: If admin users should always have access :param check_parent: If the parent object should be checked. In this case the permission is ignored; only full management access is inherited to children. :param explicit_permission: If the specified permission should be checked explicitly instead of short-circuiting the check for Indico admins or managers. When this option is set to ``True``, the values of `allow_admin` and `check_parent` are ignored. This also applies if `permission` is None in which case this argument being set to ``True`` is equivalent to `allow_admin` and `check_parent` being set to ``False``. """ if permission is not None and permission != 'ANY' and permission not in get_available_permissions( type(self)): raise ValueError( "permission '{}' is not valid for '{}' objects".format( permission, type(self).__name__)) if user is None: # An unauthorized user is never allowed to perform management operations. # Not even signals may override this since management code generally # expects session.user to be not None. return False # Trigger signals for protection overrides rv = values_from_signal(signals.acl.can_manage.send( type(self), obj=self, user=user, permission=permission, allow_admin=allow_admin, check_parent=check_parent, explicit_permission=explicit_permission), single_value=True) if rv: # in case of contradictory results (shouldn't happen at all) # we stay on the safe side and deny access return all(rv) # Usually admins can access everything, so no need for checks if not explicit_permission and allow_admin and user.is_admin: return True if any(user in entry.principal for entry in iter_acl(self.acl_entries) if entry.has_management_permission( permission, explicit=(explicit_permission and permission is not None))): return True if not check_parent or explicit_permission: return False # the parent can be either an object inheriting from this # mixin or a legacy object with an AccessController parent = self.protection_parent if parent is None: # This should be the case for the top-level object, # i.e. the root category return False elif hasattr(parent, 'can_manage'): return parent.can_manage(user, allow_admin=allow_admin) else: raise TypeError( 'protection_parent of {} is of invalid type {} ({})'.format( self, type(parent), parent))
def can_access(self, user, allow_admin=True): """Checks if the user can access the object. :param user: The :class:`.User` to check. May be None if the user is not logged in. :param allow_admin: If admin users should always have access """ override = self._check_can_access_override(user, allow_admin=allow_admin) if override is not None: return override # Usually admins can access everything, so no need for checks if allow_admin and user and user.is_admin: rv = True # If there's a valid access key we can skip all other ACL checks elif self.allow_access_key and self.check_access_key(): rv = True elif self.protection_mode == ProtectionMode.public: # if it's public we completely ignore the parent protection # this is quite ugly which is why it should only be allowed # in rare cases (e.g. events which might be in a protected # category but should be public nonetheless) rv = True elif self.protection_mode == ProtectionMode.protected: # if it's protected, we also ignore the parent protection # and only check our own ACL if any(user in entry.principal for entry in iter_acl(self.acl_entries)): rv = True elif isinstance(self, ProtectionManagersMixin): rv = self.can_manage(user, allow_admin=allow_admin) else: rv = False elif self.protection_mode == ProtectionMode.inheriting: # if it's inheriting, we only check the parent protection # unless `inheriting_have_acl` is set, in which case we # might not need to check the parents at all if self.inheriting_have_acl and any( user in entry.principal for entry in iter_acl(self.acl_entries)): rv = True else: # the parent can be either an object inheriting from this # mixin or a legacy object with an AccessController parent = self.protection_parent if parent is None: # This should be the case for the top-level object, # i.e. the root category, which shouldn't allow # ProtectionMode.inheriting as it makes no sense. raise TypeError( 'protection_parent of {} is None'.format(self)) elif hasattr(parent, 'can_access'): rv = parent.can_access(user, allow_admin=allow_admin) else: raise TypeError( 'protection_parent of {} is of invalid type {} ({})'. format(self, type(parent), parent)) else: # should never happen, but since this is a sensitive area # we better fail loudly if we have garbage raise ValueError('Invalid protection mode: {}'.format( self.protection_mode)) override = self._check_can_access_override(user, allow_admin=allow_admin, authorized=rv) return override if override is not None else rv
def can_access(self, user, allow_admin=True): """Checks if the user can access the object. :param user: The :class:`.User` to check. May be None if the user is not logged in. :param allow_admin: If admin users should always have access """ # Trigger signals for protection overrides rv = values_from_signal(signals.acl.can_access.send( type(self), obj=self, user=user, allow_admin=allow_admin), single_value=True) if rv: # in case of contradictory results (shouldn't happen at all) # we stay on the safe side and deny access return all(rv) # Usually admins can access everything, so no need for checks if allow_admin and user and user.is_admin: return True if self.protection_mode == ProtectionMode.public: # if it's public we completely ignore the parent protection # this is quite ugly which is why it should only be allowed # in rare cases (e.g. events which might be in a protected # category but should be public nonetheless) return True elif self.protection_mode == ProtectionMode.protected: # if it's protected, we also ignore the parent protection # and only check our own ACL if user is None: return False elif any(user in entry.principal for entry in iter_acl(self.acl_entries)): return True elif isinstance(self, ProtectionManagersMixin): return self.can_manage(user, allow_admin=allow_admin) else: return False elif self.protection_mode == ProtectionMode.inheriting: # if it's inheriting, we only check the parent protection # unless `inheriting_have_acl` is set, in which case we # might not need to check the parents at all if (self.inheriting_have_acl and user is not None and any(user in entry.principal for entry in iter_acl(self.acl_entries))): return True # the parent can be either an object inheriting from this # mixin or a legacy object with an AccessController parent = self.protection_parent if parent is None: # This should be the case for the top-level object, # i.e. the root category, which shouldn't allow # ProtectionMode.inheriting as it makes no sense. raise TypeError('protection_parent of {} is None'.format(self)) elif hasattr(parent, 'can_access'): return parent.can_access(user, allow_admin=allow_admin) elif hasattr(parent, 'canAccess'): return parent.canAccess( AccessWrapper(user.as_avatar if user else None)) else: raise TypeError( 'protection_parent of {} is of invalid type {} ({})'. format(self, type(parent), parent)) else: # should never happen, but since this is a sensitive area # we better fail loudly if we have garbage raise ValueError('Invalid protection mode: {}'.format( self.protection_mode))