SAMLHelper   A
last analyzed

Complexity

Total Complexity 13

Size/Duplication

Total Lines 118
Duplicated Lines 0 %

Importance

Changes 0
Metric Value
eloc 41
c 0
b 0
f 0
dl 0
loc 118
rs 10
wmc 13

5 Methods

Rating   Name   Duplication   Size   Complexity  
A getSAMLauth() 0 4 1
A binToStrGuid() 0 18 4
A validGuid() 0 6 2
A redirect() 0 23 4
A getAdditionalGETQueryParameters() 0 8 2
1
<?php
2
3
namespace SilverStripe\SAML\Helpers;
4
5
use Exception;
6
use Psr\Log\LoggerInterface;
7
use SilverStripe\Control\Director;
8
use SilverStripe\Control\HTTPRequest;
9
use SilverStripe\Control\HTTPResponse_Exception;
10
use SilverStripe\Control\RequestHandler;
11
use SilverStripe\Core\Injector\Injectable;
12
use SilverStripe\Core\Injector\Injector;
13
use SilverStripe\SAML\Authenticators\SAMLLoginHandler;
14
use SilverStripe\SAML\Control\SAMLController;
15
use SilverStripe\SAML\Services\SAMLConfiguration;
16
use OneLogin\Saml2\Auth;
17
18
/**
19
 * Class SAMLHelper
20
 *
21
 * SAMLHelper acts as a simple wrapper for the OneLogin implementation, so that we can configure
22
 * and inject it via the config system.
23
 */
24
class SAMLHelper
25
{
26
    use Injectable;
27
28
    /**
29
     * @var array
30
     */
31
    private static $dependencies = [
0 ignored issues
show
introduced by
The private property $dependencies is not used, and could be removed.
Loading history...
32
        'SAMLConfService' => '%$' . SAMLConfiguration::class,
33
    ];
34
35
    /**
36
     * @var SAMLConfiguration
37
     */
38
    public $SAMLConfService;
39
40
    /**
41
     * @return Auth
42
     */
43
    public function getSAMLauth()
44
    {
45
        $samlConfig = $this->SAMLConfService->asArray();
46
        return new Auth($samlConfig);
47
    }
48
49
    /**
50
     * Create a SAML AuthN request and send the user off to the identity provider (IdP) to get authenticated. This
51
     * method does not check to see if the user is already authenticated, that is the responsibility of the caller.
52
     *
53
     * Note: This method will *never* return via normal control flow - instead one of two things will happen:
54
     * - The user will be forcefully & immediately redirected to the IdP to get authenticated, OR
55
     * - A HTTPResponse_Exception is thrown because php-saml encountered an error while generating a valid AuthN request
56
     *
57
     * @param RequestHandler $requestHandler In case of error, we require a RequestHandler to throw errors from
58
     * @param HTTPRequest $request The currently active request (used to retrieve session)
59
     * @param string|null $backURL The URL to return to after successful SAML authentication (@see SAMLController)
60
     * @throws HTTPResponse_Exception
61
     * @see SAMLLoginHandler::doLogin() How the SAML login form handles this
62
     * @see SAMLController::acs() How the response is processed after the user is returned from the IdP
63
     * @return void This function will never return via normal control flow (see above).
64
     */
65
    public function redirect(RequestHandler $requestHandler = null, HTTPRequest $request = null, $backURL = null)
66
    {
67
        // $data is not used - the form is just one button, with no fields.
68
        $auth = $this->getSAMLAuth();
69
70
        if ($request) {
71
            $request->getSession()->set('BackURL', $backURL);
72
            $request->getSession()->save($request);
73
        }
74
75
        $additionalGetQueryParams = $this->getAdditionalGETQueryParameters();
76
77
        try {
78
            $auth->login(Director::absoluteBaseURL() . 'saml/', $additionalGetQueryParams);
79
        } catch (Exception $e) {
80
            /** @var LoggerInterface $logger */
81
            $logger = Injector::inst()->get(LoggerInterface::class);
82
            $logger->error(sprintf('[code:%s] Error during SAMLHelper->redirect: %s', $e->getCode(), $e->getMessage()));
83
84
            if ($requestHandler) {
85
                $requestHandler->httpError(400);
86
            } else {
87
                throw new HTTPResponse_Exception(null, 400);
88
            }
89
        }
90
    }
91
92
    /**
93
     * Checks if the string is a valid guid in the format of A98C5A1E-A742-4808-96FA-6F409E799937
94
     * Case in-sensitive
95
     *
96
     * @param  string $guid
97
     * @return bool
98
     */
99
    public function validGuid($guid)
100
    {
101
        if (preg_match('/^[A-F0-9]{8}-[A-F0-9]{4}-[A-F0-9]{4}-[A-F0-9]{4}-[A-F0-9]{12}?$/i', $guid)) {
102
            return true;
103
        }
104
        return false;
105
    }
106
107
    /**
108
     * @param  string $object_guid
109
     * @return string
110
     */
111
    public function binToStrGuid($object_guid)
112
    {
113
        $hex_guid = bin2hex($object_guid);
114
        $hex_guid_to_guid_str = '';
115
        for ($k = 1; $k <= 4; ++$k) {
116
            $hex_guid_to_guid_str .= substr($hex_guid, 8 - 2 * $k, 2);
117
        }
118
        $hex_guid_to_guid_str .= '-';
119
        for ($k = 1; $k <= 2; ++$k) {
120
            $hex_guid_to_guid_str .= substr($hex_guid, 12 - 2 * $k, 2);
121
        }
122
        $hex_guid_to_guid_str .= '-';
123
        for ($k = 1; $k <= 2; ++$k) {
124
            $hex_guid_to_guid_str .= substr($hex_guid, 16 - 2 * $k, 2);
125
        }
126
        $hex_guid_to_guid_str .= '-' . substr($hex_guid, 16, 4);
127
        $hex_guid_to_guid_str .= '-' . substr($hex_guid, 20);
128
        return strtoupper($hex_guid_to_guid_str);
129
    }
130
131
    /**
132
     * @return string[]
133
     */
134
    private function getAdditionalGETQueryParameters()
135
    {
136
        $additionalGetQueryParams = $this->SAMLConfService->config()->get('additional_get_query_params');
137
        if (!is_array($additionalGetQueryParams)) {
138
            $additionalGetQueryParams = [];
139
        }
140
141
        return $additionalGetQueryParams;
142
    }
143
}
144