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
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))
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) ]
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__
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__
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)
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)
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)
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))
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
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))
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