| Conditions | 21 | 
| Paths | 90 | 
| Total Lines | 142 | 
| Code Lines | 74 | 
| Lines | 0 | 
| Ratio | 0 % | 
| Changes | 0 | ||
Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.
For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.
Commonly applied refactorings include:
If many parameters/temporary variables are present:
| 1 | <?php | ||
| 63 | public function acs() | ||
| 64 |     { | ||
| 65 | /** @var Auth $auth */ | ||
| 66 | $auth = Injector::inst()->get(SAMLHelper::class)->getSAMLAuth(); | ||
| 67 | $caughtException = null; | ||
| 68 | |||
| 69 | // Log both errors (reported by php-saml and thrown as exception) with a common ID for later tracking | ||
| 70 |         $uniqueErrorId = uniqid('SAML-'); | ||
| 71 | |||
| 72 | // Force php-saml module to use the current absolute base URL (e.g. https://www.example.com/saml). This avoids | ||
| 73 | // errors that we otherwise get when having a multi-directory ACS URL like /saml/acs). | ||
| 74 | // See https://github.com/onelogin/php-saml/issues/249 | ||
| 75 | Utils::setBaseURL(Controller::join_links($auth->getSettings()->getSPData()['entityId'], 'saml')); | ||
| 76 | |||
| 77 | // Attempt to process the SAML response. If there are errors during this, log them and redirect to the generic | ||
| 78 | // error page. Note: This does not necessarily include all SAML errors (e.g. we still need to confirm if the | ||
| 79 | // user is authenticated after this block | ||
| 80 |         try { | ||
| 81 | $auth->processResponse(); | ||
| 82 | $error = $auth->getLastErrorReason(); | ||
| 83 |         } catch (Exception $e) { | ||
| 84 | $caughtException = $e; | ||
| 85 | } | ||
| 86 | |||
| 87 | // If there was an issue with the SAML response, if it was missing or if the SAML response indicates that they | ||
| 88 | // aren't authorised, then log the issue and provide a traceable error back to the user via the login form | ||
| 89 | if ( | ||
| 90 | $caughtException || | ||
| 91 | !empty($error) || | ||
| 92 | !$auth->isAuthenticated() || | ||
| 93 | $this->checkForReplayAttack($auth, $uniqueErrorId) | ||
| 94 |         ) { | ||
| 95 |             if ($caughtException instanceof Exception) { | ||
| 96 | $this->getLogger()->error(sprintf( | ||
| 97 | '[%s] [code: %s] %s (%s:%s)', | ||
| 98 | $uniqueErrorId, | ||
| 99 | $e->getCode(), | ||
|  | |||
| 100 | $e->getMessage(), | ||
| 101 | $e->getFile(), | ||
| 102 | $e->getLine() | ||
| 103 | )); | ||
| 104 | } | ||
| 105 | |||
| 106 |             if (!empty($error)) { | ||
| 107 |                 $this->getLogger()->error(sprintf('[%s] %s', $uniqueErrorId, $error)); | ||
| 108 | } | ||
| 109 | |||
| 110 | $this->getForm()->sessionMessage( | ||
| 111 | _t( | ||
| 112 | 'SilverStripe\\SAML\\Control\\SAMLController.ERR_SAML_ACS_FAILURE', | ||
| 113 | 'Unfortunately we couldn\'t log you in. If this continues, please contact your I.T. department' | ||
| 114 |                     . ' with the following reference: {ref}', | ||
| 115 | ['ref' => $uniqueErrorId] | ||
| 116 | ), | ||
| 117 | ValidationResult::TYPE_ERROR | ||
| 118 | ); | ||
| 119 | |||
| 120 | // Redirect the user back to the login form to display the generic error message and reference | ||
| 121 | $this->getRequest()->getSession()->save($this->getRequest()); | ||
| 122 |             return $this->redirect('Security/login'); | ||
| 123 | } | ||
| 124 | |||
| 125 | /** | ||
| 126 | * If processing reaches here, then the user is authenticated - the rest of this method is just processing their | ||
| 127 | * legitimate information and configuring their account. | ||
| 128 | */ | ||
| 129 | |||
| 130 | // If we expect the NameID to be a binary version of the GUID (ADFS), check that it actually is | ||
| 131 | // If we are configured not to expect a binary NameID, then we assume it is a direct GUID (Azure AD) | ||
| 132 |         if (Config::inst()->get(SAMLConfiguration::class, 'expect_binary_nameid')) { | ||
| 133 | $decodedNameId = base64_decode($auth->getNameId()); | ||
| 134 |             if (ctype_print($decodedNameId)) { | ||
| 135 |                 $this->getForm()->sessionMessage('NameID from IdP is not a binary GUID.', ValidationResult::TYPE_ERROR); | ||
| 136 | $this->getRequest()->getSession()->save($this->getRequest()); | ||
| 137 | return $this->getRedirect(); | ||
| 138 | } | ||
| 139 | |||
| 140 | // transform the NameId to guid | ||
| 141 | $helper = SAMLHelper::singleton(); | ||
| 142 | $guid = $helper->binToStrGuid($decodedNameId); | ||
| 143 |             if (!$helper->validGuid($guid)) { | ||
| 144 |                 $errorMessage = "Not a valid GUID '{$guid}' recieved from server."; | ||
| 145 | $this->getLogger()->error($errorMessage); | ||
| 146 | $this->getForm()->sessionMessage($errorMessage, ValidationResult::TYPE_ERROR); | ||
| 147 | $this->getRequest()->getSession()->save($this->getRequest()); | ||
| 148 | return $this->getRedirect(); | ||
| 149 | } | ||
| 150 |         } else { | ||
| 151 | $guid = $auth->getNameId(); | ||
| 152 | } | ||
| 153 | |||
| 154 | $attributes = $auth->getAttributes(); | ||
| 155 | |||
| 156 | $fieldToClaimMap = array_flip(Member::config()->claims_field_mappings); | ||
| 157 | |||
| 158 | // Write a rudimentary member with basic fields on every login, so that we at least have something | ||
| 159 | // if there is no further sync (e.g. via LDAP) | ||
| 160 |         $member = Member::get()->filter('GUID', $guid)->limit(1)->first(); | ||
| 161 |         if (!($member && $member->exists()) && Config::inst()->get(SAMLConfiguration::class, 'allow_insecure_email_linking') && isset($fieldToClaimMap['Email'])) { | ||
| 162 | // If there is no member found via GUID and we allow linking via email, search by email | ||
| 163 |             $member = Member::get()->filter('Email', $attributes[$fieldToClaimMap['Email']])->limit(1)->first(); | ||
| 164 | |||
| 165 |             if (!($member && $member->exists())) { | ||
| 166 | $member = new Member(); | ||
| 167 | } | ||
| 168 | |||
| 169 | $member->GUID = $guid; | ||
| 170 |         } else if(!($member && $member->exists())) { | ||
| 171 | // If the member doesn't exist and we don't allow linking via email, then create a new member | ||
| 172 | $member = new Member(); | ||
| 173 | $member->GUID = $guid; | ||
| 174 | } | ||
| 175 | |||
| 176 |         foreach ($member->config()->claims_field_mappings as $claim => $field) { | ||
| 177 |             if (!isset($attributes[$claim][0])) { | ||
| 178 | $this->getLogger()->warning( | ||
| 179 | sprintf( | ||
| 180 | 'Claim rule \'%s\' configured in SAMLMemberExtension.claims_field_mappings, ' . | ||
| 181 | 'but wasn\'t passed through. Please check IdP claim rules.', | ||
| 182 | $claim | ||
| 183 | ) | ||
| 184 | ); | ||
| 185 | |||
| 186 | continue; | ||
| 187 | } | ||
| 188 | |||
| 189 | $member->$field = $attributes[$claim][0]; | ||
| 190 | } | ||
| 191 | |||
| 192 | $member->SAMLSessionIndex = $auth->getSessionIndex(); | ||
| 193 | |||
| 194 | // This will trigger LDAP update through LDAPMemberExtension::memberLoggedIn, if the LDAP module is installed. | ||
| 195 | // The LDAP update will also write the Member record a second time, but the member *must* be written before | ||
| 196 | // IdentityStore->logIn() is called, otherwise the identity store throws an exception. | ||
| 197 | // Both SAML and LDAP identify Members by the same GUID field. | ||
| 198 | $member->write(); | ||
| 199 | |||
| 200 | /** @var IdentityStore $identityStore */ | ||
| 201 | $identityStore = Injector::inst()->get(IdentityStore::class); | ||
| 202 | $identityStore->logIn($member, false, $this->getRequest()); | ||
| 203 | |||
| 204 | return $this->getRedirect(); | ||
| 205 | } | ||
| 327 |