Passed
Pull Request — master (#73)
by Viktor
14:08
created

Authentication::withOptionalPatterns()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 5
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 4
CRAP Score 1

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 1
eloc 3
c 1
b 0
f 0
nc 1
nop 1
dl 0
loc 5
ccs 4
cts 4
cp 1
crap 1
rs 10
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Yiisoft\Auth\Middleware;
6
7
use Psr\Http\Message\ResponseFactoryInterface;
8
use Psr\Http\Message\ResponseInterface;
9
use Psr\Http\Message\ServerRequestInterface;
10
use Psr\Http\Server\MiddlewareInterface;
11
use Psr\Http\Server\RequestHandlerInterface;
12
use Yiisoft\Auth\AuthenticationMethodInterface;
13
use Yiisoft\Auth\Handler\AuthenticationFailureHandler;
14
use Yiisoft\Strings\WildcardPattern;
15
16
/**
17
 * Authentication middleware tries to authenticate and identity using request data.
18
 * If identity is found, it is set to request attribute allowing further middleware to obtain and use it.
19
 * If identity is not found failure handler is called. By default it is {@see AuthenticationFailureHandler}.
20
 */
21
final class Authentication implements MiddlewareInterface
22
{
23
    /**
24
     * @var RequestHandlerInterface A handler that is called when there is a failure authenticating an identity.
25
     */
26
    private RequestHandlerInterface $failureHandler;
27
28
    /**
29
     * @var array Patterns to match to consider the given request URI path optional.
30
     */
31
    private array $optionalPatterns = [];
32
33 5
    public function __construct(
34
        private AuthenticationMethodInterface $authenticationMethod,
35
        ResponseFactoryInterface $responseFactory,
36
        RequestHandlerInterface $authenticationFailureHandler = null,
37
        private bool $enableIdn = false,
38
    ) {
39 5
        $this->failureHandler = $authenticationFailureHandler ?? new AuthenticationFailureHandler(
40 5
            $responseFactory
41 5
        );
42
    }
43
44 4
    public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface
45
    {
46 4
        $identity = $this->authenticationMethod->authenticate($request);
47 4
        $request = $request->withAttribute(self::class, $identity);
48
49 4
        if ($identity === null && !$this->isOptional($request)) {
50 2
            return $this->authenticationMethod->challenge(
51 2
                $this->failureHandler->handle($request)
52 2
            );
53
        }
54
55 2
        return $handler->handle($request);
56
    }
57
58
    /**
59
     * @param array $optional Patterns to match to consider the given request URI path optional.
60
     *
61
     * @see WildcardPattern
62
     */
63 2
    public function withOptionalPatterns(array $optional): self
64
    {
65 2
        $new = clone $this;
66 2
        $new->optionalPatterns = $optional;
67 2
        return $new;
68
    }
69
70
    /**
71
     * Checks, whether authentication is optional for the given request URI path.
72
     */
73 3
    private function isOptional(ServerRequestInterface $request): bool
74
    {
75 3
        $path = $request
76 3
            ->getUri()
77 3
            ->getPath();
78 3
        if ($this->enableIdn) {
79
            if (!extension_loaded('ext-intl')) {
80
                throw new \RuntimeException('INTL php extension have to be load in order to have IDN support.');
81
            }
82
            $path = idn_to_ascii($path);
83
        }
84
85 3
        foreach ($this->optionalPatterns as $pattern) {
86 1
            $wildcardPattern = new WildcardPattern($pattern);
87 1
            if ($wildcardPattern->match($path)) {
88 1
                return true;
89
            }
90
        }
91 2
        return false;
92
    }
93
}
94