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
        )