1<?php
2
3/**
4 * SAML 2 Authentication Request
5 *
6 */
7class OneLogin_Saml2_AuthnRequest
8{
9
10    /**
11     * Object that represents the setting info
12     * @var OneLogin_Saml2_Settings
13     */
14    protected $_settings;
15
16    /**
17     * SAML AuthNRequest string
18     * @var string
19     */
20    private $_authnRequest;
21
22    /**
23     * SAML AuthNRequest ID.
24     * @var string
25     */
26    private $_id;
27
28    /**
29     * Constructs the AuthnRequest object.
30     *
31     * @param OneLogin_Saml2_Settings $settings Settings
32     * @param bool   $forceAuthn When true the AuthNReuqest will set the ForceAuthn='true'
33     * @param bool   $isPassive When true the AuthNReuqest will set the Ispassive='true'
34     * @param bool   $setNameIdPolicy When true the AuthNReuqest will set a nameIdPolicy
35     * @param string $nameIdValueReq Indicates to the IdP the subject that should be authenticated
36     */
37    public function __construct(OneLogin_Saml2_Settings $settings, $forceAuthn = false, $isPassive = false, $setNameIdPolicy = true, $nameIdValueReq = null)
38    {
39        $this->_settings = $settings;
40
41        $spData = $this->_settings->getSPData();
42        $idpData = $this->_settings->getIdPData();
43        $security = $this->_settings->getSecurityData();
44
45        $id = OneLogin_Saml2_Utils::generateUniqueID();
46        $issueInstant = OneLogin_Saml2_Utils::parseTime2SAML(time());
47
48        $subjectStr = "";
49        if (isset($nameIdValueReq)) {
50            $subjectStr = <<<SUBJECT
51
52    <saml:Subject>
53        <saml:NameID Format="{$spData['NameIDFormat']}">{$nameIdValueReq}</saml:NameID>
54        <saml:SubjectConfirmation Method="urn:oasis:names:tc:SAML:2.0:cm:bearer"></saml:SubjectConfirmation>
55    </saml:Subject>
56SUBJECT;
57        }
58
59        $nameIdPolicyStr = '';
60        if ($setNameIdPolicy) {
61            $nameIDPolicyFormat = $spData['NameIDFormat'];
62            if (isset($security['wantNameIdEncrypted']) && $security['wantNameIdEncrypted']) {
63                $nameIDPolicyFormat = OneLogin_Saml2_Constants::NAMEID_ENCRYPTED;
64            }
65
66            $nameIdPolicyStr = <<<NAMEIDPOLICY
67
68    <samlp:NameIDPolicy
69        Format="{$nameIDPolicyFormat}"
70        AllowCreate="true" />
71NAMEIDPOLICY;
72        }
73
74
75        $providerNameStr = '';
76        $organizationData = $settings->getOrganization();
77        if (!empty($organizationData)) {
78            $langs = array_keys($organizationData);
79            if (in_array('en-US', $langs)) {
80                $lang = 'en-US';
81            } else {
82                $lang = $langs[0];
83            }
84            if (isset($organizationData[$lang]['displayname']) && !empty($organizationData[$lang]['displayname'])) {
85                $providerNameStr = <<<PROVIDERNAME
86    ProviderName="{$organizationData[$lang]['displayname']}"
87PROVIDERNAME;
88            }
89        }
90
91        $forceAuthnStr = '';
92        if ($forceAuthn) {
93            $forceAuthnStr = <<<FORCEAUTHN
94
95    ForceAuthn="true"
96FORCEAUTHN;
97        }
98
99        $isPassiveStr = '';
100        if ($isPassive) {
101            $isPassiveStr = <<<ISPASSIVE
102
103    IsPassive="true"
104ISPASSIVE;
105        }
106
107        $requestedAuthnStr = '';
108        if (isset($security['requestedAuthnContext']) && $security['requestedAuthnContext'] !== false) {
109            $authnComparison = 'exact';
110            if (isset($security['requestedAuthnContextComparison'])) {
111                $authnComparison = $security['requestedAuthnContextComparison'];
112            }
113
114            $authnComparisonAttr = '';
115            if (!empty($authnComparison)) {
116                $authnComparisonAttr = sprintf('Comparison="%s"', $authnComparison);
117            }
118
119            if ($security['requestedAuthnContext'] === true) {
120                $requestedAuthnStr = <<<REQUESTEDAUTHN
121
122    <samlp:RequestedAuthnContext $authnComparisonAttr>
123        <saml:AuthnContextClassRef>urn:oasis:names:tc:SAML:2.0:ac:classes:PasswordProtectedTransport</saml:AuthnContextClassRef>
124    </samlp:RequestedAuthnContext>
125REQUESTEDAUTHN;
126            } else {
127                $requestedAuthnStr .= "    <samlp:RequestedAuthnContext $authnComparisonAttr>\n";
128                foreach ($security['requestedAuthnContext'] as $contextValue) {
129                    $requestedAuthnStr .= "        <saml:AuthnContextClassRef>".$contextValue."</saml:AuthnContextClassRef>\n";
130                }
131                $requestedAuthnStr .= '    </samlp:RequestedAuthnContext>';
132            }
133        }
134
135        $spEntityId = htmlspecialchars($spData['entityId'], ENT_QUOTES);
136        $acsUrl = htmlspecialchars($spData['assertionConsumerService']['url'], ENT_QUOTES);
137        $request = <<<AUTHNREQUEST
138<samlp:AuthnRequest
139    xmlns:samlp="urn:oasis:names:tc:SAML:2.0:protocol"
140    xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion"
141    ID="$id"
142    Version="2.0"
143{$providerNameStr}{$forceAuthnStr}{$isPassiveStr}
144    IssueInstant="$issueInstant"
145    Destination="{$idpData['singleSignOnService']['url']}"
146    ProtocolBinding="{$spData['assertionConsumerService']['binding']}"
147    AssertionConsumerServiceURL="{$acsUrl}">
148    <saml:Issuer>{$spEntityId}</saml:Issuer>{$subjectStr}{$nameIdPolicyStr}{$requestedAuthnStr}
149</samlp:AuthnRequest>
150AUTHNREQUEST;
151
152        $this->_id = $id;
153        $this->_authnRequest = $request;
154    }
155
156    /**
157     * Returns deflated, base64 encoded, unsigned AuthnRequest.
158     *
159     * @param bool|null $deflate Whether or not we should 'gzdeflate' the request body before we return it.
160     *
161     * @return string
162     */
163    public function getRequest($deflate = null)
164    {
165        $subject = $this->_authnRequest;
166
167        if (is_null($deflate)) {
168            $deflate = $this->_settings->shouldCompressRequests();
169        }
170
171        if ($deflate) {
172            $subject = gzdeflate($this->_authnRequest);
173        }
174
175        $base64Request = base64_encode($subject);
176        return $base64Request;
177    }
178
179    /**
180     * Returns the AuthNRequest ID.
181     *
182     * @return string
183     */
184    public function getId()
185    {
186        return $this->_id;
187    }
188
189    /**
190     * Returns the XML that will be sent as part of the request
191     *
192     * @return string
193     */
194    public function getXML()
195    {
196        return $this->_authnRequest;
197    }
198}
199