docs: 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 = unwrap(user)
  
          if not self.running:
              return True
  
          session = None
          if obj is not None:
              session = object_session(obj)
  
          if session is None:
              session = 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,) + (str,)):
                  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
  
          # first test global roles, then object local roles
          checked_objs = [None, 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] + 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
          )