HybridAuth для UMI.CMS. Авторизация через социальные сети на UMI.CMS

Я давно хотел написать для UMI скрип отличный от loginza, так как считаю, что он не совсем удачный, по ряду причин (чисто субъективных). Могу ошибаться, если так и есть не закидывайте комнями 🙂
В данной статье будет рассмотрено как реализовать авторизацию на UMI.CMS через популярную библиотеку(не сзаю как правильно назвать) HybridAuth.

Авторизация для UMI.CMS будет реализована через провайдеров:

  • Facebook
  • Twitter
  • Linkedin
  • Google+
  • Вконтакте

Статья по HybridAuth для UMI.CMS будет разделена на несколько статей:
1) Настройка полей пользователя
2) Установка и настройка HybridAuth
3) Установка скрипта HybridAuth + UMI.CMS
4) Корректировка шаблона

Настройка полей пользователя

Для начала следует зайти в шаблоны данных(модуль) и найти там тип данных «Пользователь»:
hybridauth-dlya-umi-cms-3

Добавить новое поле «Аватар» и «ID пользователя из соц. сети»:

hybridauth-dlya-umi-cms-4

hybridauth-dlya-umi-cms-5

hybridauth-dlya-umi-cms-6

Настройка пользователей закончена.

Установка и настройка HybridAuth

Скачать библиотеку можно здесь (это оригинальные файлы), а здесь доп. провадеров. По этой ссылке можно скачать с уже добавленным vk.com (предлагаю использовать этот архив).

Для начала нужно создать приложения для своего сайта. Я не буду расписывать процесс создания приложения, уверен, вы самостоятельно в этом разберетесь. Для того, чтобы Вы быстрее разобрались привожу ссылки на создания приложения:

Далее, нужно сконфигурировать HybridAuth. Для этого откройте http://ваш_сайт/hybridauth/install.php и выберите нужные сервисы. Хочу обратить внимание на то, что ребята разработавшие hybridauth сразу привели ссылки на создание приложений.

После того, как Вы внесли все настройки и сохранили, должны увидеть что-то вроде этого:

hybridauth-dlya-umi-cms-2

Теперь нужно добавить нового провайдера.

Если Вы воспользовались оригинальными файлами из репозитория, то нужно установить провайдера для vk.com (если взяли готовый архив, то пропустите настройку vk.com). Следует перейти по этой ссылке и скачать файл. Его нужно разместить в директории \hybridauth\Hybrid\Providers. В файле hybridauth\config.php добавить участок кода и вписать id и secret, который предоставить вконтакте.

"Vkontakte" => array (
	"enabled" => true,
	"keys"    => array ( "id" => "", "secret" => "" )
),

Установка скрипта HybridAuth + UMI.CMS

Далее, нужно разместить в корне файл auth_with_social.php со следующим содержанием:

session_start();

require_once('standalone.php');

$config = CURRENT_WORKING_DIR . '/hybridauth/config.php';
require CURRENT_WORKING_DIR . '/hybridauth/Hybrid/Auth.php';

$domain = cmsController::getInstance()->getCurrentDomain()->getHost(); // Получение домена
$users = cmsController::getInstance()->getModule("users"); // "Подключение" к модулю users
$data = cmsController::getInstance()->getModule("data"); // "Подключение" к модулю data

/*
 * /auth_with_social.php?login_with=_fb
 * /auth_with_social.php?login_with=_vk
 * /auth_with_social.php?login_with=_in
 * /auth_with_social.php?login_with=_gp
 * /auth_with_social.php?login_with=_tw
 * */

if( isset( $_GET["login_with"] ) ) {
    $login_with = $_GET["login_with"];

    try  {
        $hybridauth = new Hybrid_Auth($config);
        $adapter = false;

        switch ($login_with) {
            case "_fb":
                $adapter = $hybridauth->authenticate( "facebook" );
                break;
            case "_vk":
                $adapter = $hybridauth->authenticate( "vkontakte" );
                break;
            case "_in":
                $adapter = $hybridauth->authenticate( "linkedin" );
                break;
            case "_gp":
                $adapter = $hybridauth->authenticate( "google" );
                break;
            case "_tw":
                $adapter = $hybridauth->authenticate( "twitter" );
                break;
        }

        // Если в параметр передана "чушь" выдаем ошибку
        if(!$adapter) die( "<b>Ошибка про авторизации! Попробуйте еще раз.</b> ");

        $profile = $adapter->getUserProfile();

        // Если пользователь не залогинен - редирект
        if( !isset( $profile ) ){
            /* Редирект $profile случае запроса auth_with_social.php без параметров */
            $domain = cmsController::getInstance()->getCurrentDomain()->getHost();
            header ("Location: http://" . $domain);

        // Если пользователь залогинен, продолжаем работу :)
        } else {
            echo "<h1>Отладочная информация</h1>";
            echo "<pre>";
            print_r($profile);
            echo "</pre>";

            $objectTypes = umiObjectTypesCollection::getInstance();
            $objectTypeId	= $objectTypes->getBaseType("users", "user");
            $objectType = $objectTypes->getType($objectTypeId);

            $provider = $login_with; // Провайдер через который авторизовался пользователь
            $identifier = $profile->identifier;

            $login = $profile->email;
            $email = $profile->email;
            if(iconv_strlen($login) == 0) {
                $login = transliterate($profile->firstName);
                $login .= $profile->identifier;

                $email = $login . '@'. $domain;
            }
            $fname = $profile->firstName;
            $lname = $profile->lastName;
            $password = md5(generate_password(10));

            $sel = new selector('objects');
            $sel->types('object-type')->name('users', 'user');
            $sel->where('e-mail')->equals($email);
            $user = $sel->first;

            if( $user instanceof iUmiObject ) {
                permissionsCollection::getInstance()->loginAsUser($user);
                session_commit();

                header ("Location: http://" . $domain);
            } else {
                if(!preg_match("/.+@.+\..+/", $email)) {
                    while(true) {
                        $email = $login.rand(1,100)."@".getServer('HTTP_HOST');
                        if($this->checkIsUniqueEmail($email)) {
                            break;
                        }
                    }
                }

                $object_id = umiObjectsCollection::getInstance()->addObject($login, $objectTypeId);
                $object = umiObjectsCollection::getInstance()->getObject($object_id);

                $object->setValue("login", $login);
                $object->setValue("password", md5($password));
                $object->setValue("e-mail", $email);
                $object->setValue("fname", ($fname));
                $object->setValue("lname", $lname);
                $object->setValue("loginza", $provider);
                $object->setValue("social_identifier", $identifier);
                $object->setValue("register_date", time());
                $object->setValue("is_activated", '1');
                $object->setValue("activate_code", md5(uniqid(rand(), true)));

                /* Создане аватара пользователя */
                $user_pic = "./files/users_upload/social_avatars/";
                $user_pic .= $identifier . $provider;

                $png_pos = strpos($profile->photoURL , '.png');
                $gif_pos = strpos($profile->photoURL , '.gif');

                if ( $png_pos !== false )
                    $user_pic .= ".png";
                elseif ( $gif_pos !== false )
                    $user_pic .= ".gif";
                else
                    $user_pic .= ".jpeg";

                // How to save facebook profile picture using php graph Api
                // http://goo.gl/zgL3iR

                // Сохранение изображения с заданного URL-адреса
                // http://goo.gl/VHeXk1
                $data = file_get_contents($profile->photoURL);
                $file = fopen($user_pic, 'wb');
                fputs($file, $data);
                fclose($file);

                if( file_exists("$user_pic") ) {
                    $oFile = new umiFile($user_pic);

                    if (!$oFile->getIsBroken()) {
                        $object->setValue("avatar", $oFile);
                    }

                }

                /* // Создане аватара пользователя */

                $_SESSION['cms_login'] = $login;
                $_SESSION['cms_pass'] = md5($password);
                $_SESSION['user_id'] = $object_id;

                session_commit();

                $group_id = regedit::getInstance()->getVal("//modules/users/def_group");
                $object->setValue("groups", Array($group_id));

                $data_module = cmsController::getInstance()->getModule('data');
                $data_module->saveEditedObject($object_id, true);

                $object->commit();

                header ("Location: http://" . $domain);
            }

        }

    } catch( Exception $e )  {
        /*$html = "Ошибка при авторизации. Информация об ошибки ниже: <br>";
        $html .= "<b>" . $e->getMessage() . "</b>";

        die( $html );*/

        header ("Location: http://" . $domain . '/authorization/?error=' . $e->getMessage());
    }
} else {
    /* Редирект в случае запроса auth_with_social.php без параметров */
    header ("Location: http://" . $domain);
}

/*
 * Генератор пароля
 * */
function generate_password($number) {
    $arr = array('a','b','c','d','e','f',
        'g','h','i','j','k','l',
        'm','n','o','p','r','s',
        't','u','v','x','y','z',
        'A','B','C','D','E','F',
        'G','H','I','J','K','L',
        'M','N','O','P','R','S',
        'T','U','V','X','Y','Z',
        '1','2','3','4','5','6',
        '7','8','9','0','-');
    // Генерируем пароль
    $pass = "";
    for($i = 0; $i < $number; $i++)
    {
        // Вычисляем случайный индекс массива
        $index = rand(0, count($arr) - 1);
        $pass .= $arr[$index];
    }
    return $pass;
}

/*
 * Транслит руского языка
 * */
function transliterate($input) {
    $gost = array(
        "Є"=>"YE","І"=>"I","Ѓ"=>"G","і"=>"i","№"=>"-","є"=>"ye","ѓ"=>"g",
        "А"=>"A","Б"=>"B","В"=>"V","Г"=>"G","Д"=>"D",
        "Е"=>"E","Ё"=>"YO","Ж"=>"ZH",
        "З"=>"Z","И"=>"I","Й"=>"J","К"=>"K","Л"=>"L",
        "М"=>"M","Н"=>"N","О"=>"O","П"=>"P","Р"=>"R",
        "С"=>"S","Т"=>"T","У"=>"U","Ф"=>"F","Х"=>"Kh",
        "Ц"=>"C","Ч"=>"CH","Ш"=>"SH","Щ"=>"SHH","Ъ"=>"'",
        "Ы"=>"Y","Ь"=>"","Э"=>"E","Ю"=>"YU","Я"=>"YA",
        "а"=>"a","б"=>"b","в"=>"v","г"=>"g","д"=>"d",
        "е"=>"e","ё"=>"yo","ж"=>"zh",
        "з"=>"z","и"=>"i","й"=>"j","к"=>"k","л"=>"l",
        "м"=>"m","н"=>"n","о"=>"o","п"=>"p","р"=>"r",
        "с"=>"s","т"=>"t","у"=>"u","ф"=>"f","х"=>"kh",
        "ц"=>"c","ч"=>"ch","ш"=>"sh","щ"=>"shh","ъ"=>"",
        "ы"=>"y","ь"=>"","э"=>"e","ю"=>"yu","я"=>"ya",
        " "=>"-","—"=>"-",","=>"-","!"=>"-","@"=>"-",
        "#"=>"-","$"=>"","%"=>"","^"=>"","&"=>"","*"=>"",
        "("=>"",")"=>"","+"=>"","="=>"",";"=>"",":"=>"",
        "'"=>"","\""=>"","~"=>"","`"=>"","?"=>"","/"=>"",
        "\\"=>"","["=>"","]"=>"","{"=>"","}"=>"","|"=>"", "."=>"-"
    );

    $string = strtr($input, $gost);
    return strtolower($string);
}

Далее в файл .htaccess добавить строку:

###### SOCIAL #####
RewriteRule ^hybridauth/(.*)*$ hybridauth/index.php?%{QUERY_STRING} [L]
###### // SOCIAL #####

Корректировка шаблона

Далее нужно добавить в шаблон ссылки, для авторизации через соц. сети. Нужно взять вот этот код:

<ul class="b-social-links">
	<li class="b-social-links__item">
		<a href="/auth_with_social.php?login_with=_fb" class="fb" title="Войти используя Facebook">Войти используя Facebook</a>
	</li>

	<li class="b-social-links__item">
		<a href="/auth_with_social.php?login_with=_vk" class="vk" title="Войти используя ВКонтакте">Войти используя ВКонтакте</a>
	</li>

	<li class="b-social-links__item">
		<a href="/auth_with_social.php?login_with=_in" class="in" title="Войти используя LinkedIn">Войти используя LinkedIn</a>
	</li>

	<li class="b-social-links__item">
		<a href="/auth_with_social.php?login_with=_gp" class="gp" title="Войти используя Google+">Войти используя Google+</a>
	</li>
	<li class="b-social-links__item">
		<a href="/auth_with_social.php?login_with=_tw" class="tw" title="Войти используя Twitter">Войти используя Twitter</a>
	</li>
</ul>

На этом с настройкой все.

Теперь хочу обратить внимание на то, если произошла ошибка при авторизации. Если такое произошло, то скрипт производит редирект на страницу /authorization/ с get параметром error.

Для того, чтобы показать пользователю ошибки, нужно создать страницу в структуре сайта и ее урл сделать /authorization/. Далее при помощи xslt обратотать параметр error. TPL-шаблоназатор не расматриваю.

Ссылки:

P.S. если есть вопросы и непонятные моменты пишите комментарии 🙂
P.S.S. если Вы нашли ошибку, сообщите, я сразу исправлю. Старался побыстрее поделится опытом, может кому и пригодится.