Passed
Pull Request — master (#37)
by
unknown
08:26
created

SAMLHelper::validateNameID()   A

Complexity

Conditions 5
Paths 4

Size

Total Lines 20
Code Lines 9

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 5
eloc 9
c 0
b 0
f 0
nc 4
nop 2
dl 0
loc 20
rs 9.6111
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\Extensible;
12
use SilverStripe\Core\Config\Config;
13
use SilverStripe\Core\Injector\Injectable;
14
use SilverStripe\Core\Injector\Injector;
15
use SilverStripe\SAML\Authenticators\SAMLLoginHandler;
16
use SilverStripe\SAML\Control\SAMLController;
17
use SilverStripe\SAML\Services\SAMLConfiguration;
18
use OneLogin\Saml2\Auth;
19
use SilverStripe\Security\Member;
20
21
/**
22
 * Class SAMLHelper
23
 *
24
 * SAMLHelper acts as a simple wrapper for the OneLogin implementation, so that we can configure
25
 * and inject it via the config system.
26
 */
27
class SAMLHelper
28
{
29
    use Injectable;
30
    use Extensible;
31
32
    /**
33
     * @var array
34
     */
35
    private static $dependencies = [
0 ignored issues
show
introduced by
The private property $dependencies is not used, and could be removed.
Loading history...
36
        'SAMLConfService' => '%$' . SAMLConfiguration::class,
37
    ];
38
39
    /**
40
     * @var SAMLConfiguration
41
     */
42
    public $SAMLConfService;
43
44
    /**
45
     * @return Auth
46
     */
47
    public function getSAMLauth()
48
    {
49
        $samlConfig = $this->SAMLConfService->asArray();
50
        return new Auth($samlConfig);
51
    }
52
53
    /**
54
     * Create a SAML AuthN request and send the user off to the identity provider (IdP) to get authenticated. This
55
     * method does not check to see if the user is already authenticated, that is the responsibility of the caller.
56
     *
57
     * Note: This method will *never* return via normal control flow - instead one of two things will happen:
58
     * - The user will be forcefully & immediately redirected to the IdP to get authenticated, OR
59
     * - A HTTPResponse_Exception is thrown because php-saml encountered an error while generating a valid AuthN request
60
     *
61
     * @param RequestHandler $requestHandler In case of error, we require a RequestHandler to throw errors from
62
     * @param HTTPRequest $request The currently active request (used to retrieve session)
63
     * @param string|null $backURL The URL to return to after successful SAML authentication (@see SAMLController)
64
     * @throws HTTPResponse_Exception
65
     * @see SAMLLoginHandler::doLogin() How the SAML login form handles this
66
     * @see SAMLController::acs() How the response is processed after the user is returned from the IdP
67
     * @return void This function will never return via normal control flow (see above).
68
     */
69
    public function redirect(RequestHandler $requestHandler = null, HTTPRequest $request = null, $backURL = null)
70
    {
71
        // $data is not used - the form is just one button, with no fields.
72
        $auth = $this->getSAMLAuth();
73
74
        if ($request) {
75
            $request->getSession()->set('BackURL', $backURL);
76
            $request->getSession()->save($request);
77
        }
78
79
        try {
80
            $auth->login(Director::absoluteBaseURL() . 'saml/');
81
        } catch (Exception $e) {
82
            /** @var LoggerInterface $logger */
83
            $logger = Injector::inst()->get(LoggerInterface::class);
84
            $logger->error(sprintf('[code:%s] Error during SAMLHelper->redirect: %s', $e->getCode(), $e->getMessage()));
85
86
            if ($requestHandler) {
87
                $requestHandler->httpError(400);
88
            } else {
89
                throw new HTTPResponse_Exception(null, 400);
90
            }
91
        }
92
    }
93
94
    /**
95
     * Validates a NameID.
96
     *
97
     * @param  string $nameID
98
     * @param  string $nameIDFormat
99
     * @return bool
100
     */
101
    public function validateNameID($nameID, $nameIDFormat): bool
102
    {
103
        // Validate: Must fit within maximum size of DBVarchar.
104
        $nameIDMaxSize = singleton(Member::class)->dbObject('GUID')->getSize();
105
        if (strlen($nameID) > $nameIDMaxSize) {
106
            return false;
107
        }
108
109
        // OK IF: We don't wish to do any further validation.
110
        if (!Config::inst()->get(SAMLConfiguration::class, 'validate_nameid')) {
111
            return true;
112
        }
113
114
        // OK IF: One of the registered extensions returns true.
115
        $validationResults = $this->extend('updateNameIDValidation', $nameID, $nameIDFormat);
116
        if ($validationResults && is_array($validationResults)) {
117
            return (bool) max($validationResults);
118
        }
119
120
        return false;
121
    }
122
123
    /**
124
     * @param  string $object_guid
125
     * @return string
126
     */
127
    public function binToStrGuid($object_guid)
128
    {
129
        $hex_guid = bin2hex($object_guid);
130
        $hex_guid_to_guid_str = '';
131
        for ($k = 1; $k <= 4; ++$k) {
132
            $hex_guid_to_guid_str .= substr($hex_guid, 8 - 2 * $k, 2);
133
        }
134
        $hex_guid_to_guid_str .= '-';
135
        for ($k = 1; $k <= 2; ++$k) {
136
            $hex_guid_to_guid_str .= substr($hex_guid, 12 - 2 * $k, 2);
137
        }
138
        $hex_guid_to_guid_str .= '-';
139
        for ($k = 1; $k <= 2; ++$k) {
140
            $hex_guid_to_guid_str .= substr($hex_guid, 16 - 2 * $k, 2);
141
        }
142
        $hex_guid_to_guid_str .= '-' . substr($hex_guid, 16, 4);
143
        $hex_guid_to_guid_str .= '-' . substr($hex_guid, 20);
144
        return strtoupper($hex_guid_to_guid_str);
145
    }
146
}
147