public function authorize(
        $class,
        string $method = null,
        array $attr = []
    ) : bool {
    
        $auth = $this->extractAuth($class, $method);

        // Class / method has no rules - allow all
        if (empty($auth)) {
            return true;
        }

        // Store mode, remove from auth array
        $mode = $auth['mode'] ?? $this->defaultMode;
        $e = new AuthException($mode);
        unset($auth['mode']);

        // Handle bans, remove them from auth
        $this->handleBans($auth, $attr);

        switch ($mode) {
            case self::MODE_AND:
                // All values in all arrays must match
                foreach ($auth as $set => $values) {
                    if (!isset($attr[$set])) {
                        $e->addReason(new Reason(
                            $set,
                            [],
                            $values
                        ));
                    } else {
                        $attr[$set] = (array)$attr[$set];
                        sort($values);
                        sort($attr[$set]);
                        if ($values != $attr[$set]) {
                            $e->addReason(new Reason(
                                $set,
                                $attr[$set],
                                $values
                            ));
                        }
                    }
                }

                if ($e->hasReasons()) {
                    throw $e;
                }
                return true;
            case self::MODE_NONE:
                // There must be no overlap between any of the array values

                foreach ($auth as $set => $values) {
                    if (isset($attr[$set]) && count(
                        array_intersect(
                            (array)$attr[$set],
                            $values
                        )
                    )
                    ) {
                        $e->addReason(new Reason(
                            $set,
                            (array)($attr[$set] ?? []),
                            $values
                        ));
                    }
                }

                if ($e->hasReasons()) {
                    throw $e;
                }
                return true;
            case self::MODE_OR:
                // At least one match must be present
                foreach ($auth as $set => $values) {
                    if (isset($attr[$set]) && count(
                        array_intersect(
                            (array)$attr[$set],
                            $values
                        )
                    )
                    ) {
                        return true;
                    }
                    $e->addReason(new Reason(
                        $set,
                        (array)($attr[$set] ?? []),
                        $values
                    ));
                }

                throw $e;
            default:
                throw new \InvalidArgumentException('Durrrr');
        }

    }