/**
  * Method to authenticate an user
  *
  * @param $RAW_data Raw data to authenticate the user
  * @param Form $form Optional: If passed, better error messages can be
  *                             produced by using
  *                             {@link Form::sessionMessage()}
  * @return bool Returns FALSE if authentication fails, otherwise the
  *              member object    
  */
 public static function authenticate($RAW_data, Form $form = null)
 {
     $RAW_external_anchor = trim($RAW_data['External_Anchor']);
     $RAW_external_mailaddr = trim($RAW_data['External_MailAddr']);
     $RAW_external_source = trim($RAW_data['External_SourceID']);
     $RAW_external_passwd = $RAW_data['Password'];
     $userexists = false;
     //Does the user exist within SilverStripe?
     $userindbs = false;
     //Does the user already exist in the SStripe dbs?
     $authsuccess = false;
     //Initialization of variable
     //Set authentication message for failed authentication
     //Could be used by the individual drivers
     self::$authmessage = _t('ExternalAuthenticator.Failed', 'Authentication failed');
     self::AuthLog('Starting process for with alleged Anchor ' . $RAW_external_anchor . ' and alleged mail ' . $RAW_external_mailaddr . ' at ' . self::$timestamp);
     if ($memberquery = self::getHandleToUse($RAW_external_anchor, $RAW_external_mailaddr, $RAW_external_source, $form)) {
         if ($member = DataObject::get_one('Member', $memberquery)) {
             $Log_ID = $member->Email;
             // Before we continue we must check if the source is valid
             if (!self::validSource($member->External_SourceID, $Log_ID, $member)) {
                 self::failmessage($form, $member, $Log_ID, $RAW_external_source);
                 return false;
             }
             $userexists = true;
             $userindbs = true;
             self::AuthLog($Log_ID . ' - User with source ' . $member->External_SourceID . ' found in database');
             if (!self::getUseAnchor()) {
                 $RAW_external_source = stripslashes($member->External_SourceID);
                 $RAW_external_anchor = stripslashes($member->External_Anchor);
             }
             //Check if the user was behaving nicely
             if (self::accountLockedOut($member, $Log_ID)) {
                 self::failmessage($form, $member, $Log_ID, $RAW_external_source);
                 return false;
             }
         } else {
             $Log_ID = 'unknown';
             self::Authlog($Log_ID . ' - User with source NOT found in database');
         }
     } else {
         // Authentication form was not filled out properly
         return false;
     }
     if (!$userexists && self::getUseAnchor()) {
         if (self::validSource($RAW_external_source, $Log_ID)) {
             if (self::getAutoAdd($RAW_external_source)) {
                 $userexists = true;
             } else {
                 self::Authlog($Log_ID . ' - AutoAdd for source ' . $RAW_external_source . ' not enabled, aborting');
                 self::failmessage($form, $member, $Log_ID, $RAW_external_source);
                 return false;
             }
         } else {
             self::failmessage($form, $member, $Log_ID, $RAW_external_source);
             self::Authlog($Log_ID . ' - Illegal source ' . $RAW_external_source . ' or client not in valid IP range; aborting');
             return false;
         }
     }
     // Try to find our anchor, since we have none
     if (!$userexists && !self::getUseAnchor()) {
         foreach (self::getSources() as $source) {
             if (self::getAutoAdd($source)) {
                 $auth_type = strtoupper(self::getAuthType($source));
                 self::AuthLog($Log_ID . ' - loading driver ' . $auth_type);
                 //If we don't have a user yet and autoadd is on; try to find the anchor
                 if ($memberdata = self::locateAnchor($source, $RAW_external_mailaddr, $Log_ID)) {
                     extract($memberdata);
                     $userexists = true;
                     break;
                 }
             }
         }
     } else {
         // Load the correct driver
         if (!self::validSource($RAW_external_source, $Log_ID)) {
             $form->sessionMessage(_t('ExternalAuthenticator.Failed'), 'bad');
             self::Authlog($Log_ID . ' - Illegal source ' . $RAW_external_source . ' or client not in valid IP range; aborting');
             self::failmessage($form, $member, $Log_ID, $RAW_external_source);
             return false;
         }
         $auth_type = strtoupper(self::getAuthType($RAW_external_source));
         self::AuthLog($Log_ID . ' - loading driver ' . $auth_type);
     }
     if ($userexists) {
         $myauthenticator = $auth_type . '_Authenticator';
         $myauthenticator = new $myauthenticator();
         self::AuthLog($Log_ID . ' - executing authentication driver');
         $RAW_result = $myauthenticator->Authenticate($RAW_external_source, $RAW_external_anchor, $RAW_external_passwd);
         if ($RAW_result) {
             $authsuccess = true;
             self::AuthLog($Log_ID . ' - authentication success');
         } else {
             self::AuthLog($Log_ID . ' - authentication driver ' . $auth_type . ' failed');
             if ($member && self::getAuthSSLock($RAW_external_source)) {
                 self::AuthLog($Log_ID . ' - Registering failed login');
                 $member->registerFailedLogin();
             }
         }
     }
     // Check if we need to do something with the groups
     if ($authsuccess) {
         // We're an array, so we need to do auto-mapping
         // first determine which group we should be a member of
         $usergroup = self::getMyGroup($RAW_external_source, $RAW_result['group']);
         if (!$userindbs && !is_bool($usergroup)) {
             $SQL_memberdata = self::createMemberArray($RAW_result, $RAW_external_anchor, $RAW_external_source, self::getDefaultDomain($RAW_external_source));
             // First we check if the user's e-mail address has changed
             // we do this by checking if the anchor and source are already in the dbs
             // we do this only if the user used his mail address to authenticate
             // If the user does not exist we create a new member object
             if (!self::getUseAnchor()) {
                 // First we check if the user's e-mail address has changed
                 // we do this by checking if the anchor and source are already in the dbs
                 // we do this only if the user used his mail address to authenticate
                 // If the user does not exist we create a new member object
                 if (!($member = DataObject::get_one('Member', '"Member"."External_Anchor" = \'' . $SQL_memberdata['External_Anchor'] . '\' AND "Member"."External_SourceID" = \'' . $SQL_memberdata['External_SourceID'] . '\''))) {
                     $member = new Member();
                     self::AuthLog($Log_ID . ' - Anchor does not exist in database.');
                 } else {
                     self::AuthLog($Log_ID . ' - Anchor already present in the database but mail address is unknown. Changing mail address for this anchor');
                     $userindbs = true;
                     self::AuditLog($member, $Log_ID, 'modify', 'account exists', true, $RAW_external_source);
                 }
             } else {
                 // Now we check if the users e-mail address already exists. He
                 // did not authenticate himself with the mail address and we
                 // assume that if authentication was successful, he is owner
                 // of the address. This supports moving users from one source
                 // to another
                 if (!($member = DataObject::get_one('Member', '"Email" = \'' . $SQL_memberdata['Email'] . '\''))) {
                     $member = new Member();
                     self::AuthLog($Log_ID . ' - Mail address does not exist in the database');
                 } else {
                     self::Authlog($Log_ID . ' - Mail address already present in the database, modifying existing account');
                     $userindbs = true;
                     self::AuditLog($member, $Log_ID, 'modify', 'account exists', true, $RAW_external_source);
                 }
             }
             // But before we write ourselves to the database we must check if
             // the group we are subscribing to exists
             if (!is_bool($usergroup)) {
                 $member->update($SQL_memberdata);
                 if (!$userindbs) {
                     $member->ID = null;
                 }
                 self::AuthLog($Log_ID . ' - start adding or modifying user');
                 $member->write();
                 self::AuthLog($Log_ID . ' - finished adding user to database');
                 if (!$userindbs) {
                     self::AuthLog($Log_ID . ' - start setting group membership to group ' . $usergroup->Title);
                     $member->Groups()->add($usergroup->ID);
                     self::AuthLog($Log_ID . ' - finished setting group membership');
                 }
                 self::AuditLog($member, $Log_ID, 'creation', NULL, true, $RAW_external_source);
             } else {
                 self::AuthLog($Log_ID . ' - The group to add the user to did not exist');
                 $authsuccess = false;
             }
         }
         if ($userindbs && !is_bool($usergroup)) {
             self::AuthLog($Log_ID . ' - Group membership will be set to ID ' . $usergroup->ID . ' name ' . $usergroup->Title);
             // User exists. We should check current group against group from config
             $memberships = $member->Groups()->getIdList();
             if (array_key_exists($usergroup->ID, $memberships)) {
                 self::AuthLog($Log_ID . ' - User is already a member of ' . $usergroup->Title);
             } else {
                 foreach ($memberships as $membership) {
                     self::AuthLog($Log_ID . ' - Erasing membership of group ' . $membership);
                     $member->Groups()->remove(self::groupObj($membership));
                     self::AuthLog($Log_ID . ' - Done erasing membership of group ' . $membership);
                 }
                 self::AuthLog($Log_ID . ' - setting membership of ' . $usergroup->Title);
                 self::Auditlog($member, $Log_ID, 'modify', 'current group membership does not match configuration', true, $RAW_external_source);
                 $member->Groups()->add($usergroup->ID);
                 self::AuthLog($Log_ID . ' - Done setting membership of ' . $usergroup->Title);
             }
         }
     }
     self::AuthLog('Process for user ' . $Log_ID . ' ended');
     if ($authsuccess) {
         Session::clear('BackURL');
         self::$authmessage = '';
         // Set the security message here. Else it will be shown on logout
         Session::set('Security.Message.message', self::$authmessage);
         Session::set('Security.Message.type', 'good');
         self::AuditLog($member, $Log_ID, 'logon', NULL, true, $RAW_external_source);
         return $member;
     } else {
         self::failmessage($form, $member, $Log_ID, $RAW_external_source);
         return false;
     }
 }