Skip to content

Сборка Laravel 5.3 + Админка Sleeping Owl 4 + Управление правами и ролями пользователей + Авторизация через соц.сети + Заготовка под REST/RESTful API Dingo

License

MIT, MIT licenses found

Licenses found

MIT
LICENSE.md
MIT
LICENSE.txt
Notifications You must be signed in to change notification settings

evergreen-it-dev/laravel5.3_sowl4_sentinel_dingo

Repository files navigation

Админ-пакет для Laravel 5.3 + Sleeping Owl4 + Sentinel + Dingo

Пакет на базе SleepingOwlAdmin 4-ой версии и добавили к ней управление пользователями, ролями и правами через пакет Sentinel. А также добавили регистрацию и авторизацию через Facebook, Google+, VKontakte и реализовали заготовку под REST/RESTful API с помощью пакета Dingo.

Вопросы пишите на [email protected]

Инструкция по установке и настройке

Мы не будем рассматривать вопросы настройки окружения, установки и первоначальной настройки фреймворка. Начнем с той точки, когда у нас установлен laravel версии 5.3.* и подключен к БД. В .env указан URL приложения, например http://mysite.com и домен под API:

APP_URL=http://mysite.com
API_DOMAIN=api.mysite.com

На момент написания материала, последняя стабильная версия фреймворка - 5.3.16.

Для начала установим все необходимые пакеты, используемые в сборке, а после приступим к их настройке.

Склонировав и развернув проект, в .env добавляем следующие строки и задаем свои значения:

FACEBOOK_KEY=yourkeyfortheservice
FACEBOOK_SECRET=yoursecretfortheservice
FACEBOOK_REDIRECT_URI=http://mysite.com/callback?network=facebook 

GOOGLE_KEY=yourkeyfortheservice GOOGLE_SECRET=yoursecretfortheservice GOOGLE_REDIRECT_URI=http://mysite.com/callback?network=google

VKONTAKTE_KEY=yourkeyfortheservice VKONTAKTE_SECRET=yoursecretfortheservice VKONTAKTE_REDIRECT_URI=http://mysite.com/callback?network=vkontakte

APP_URL=http://mysite.com API_DOMAIN=api.mysite.com API_STANDARDS_TREE=vnd API_PREFIX=v1 API_NAME=mysite API_DEBUG=true

В терминале последовательно запускаем команды:

php artisan jwt:generate
php artisan migrate
php artisan db:seed
php artisan config:cache

Открываем для редактирования файл composer.json

В секцию "require" добавляем следующие строки:

"dingo/api": "1.0.x@dev",
"laravelrus/sleepingowl": "4.*@dev",
"cartalyst/sentinel": "2.0.*",
"laravelcollective/html": "^5.3.0",
"socialiteproviders/google": "^2.0",
"socialiteproviders/vkontakte": "^2.0",
"laravel/socialite": "^2.0",
"tymon/jwt-auth": "0.5.*",
"fzaninotto/faker": "^1.6",
"nesbot/carbon": "^1.21"

Запускаем в терминале команду

composer update

В config/app.php, в массив $providers добавляем (до Application Service Providers):

/*
 * Package Service Providers...
 */

SleepingOwl\Admin\Providers\SleepingOwlServiceProvider::class, Dingo\Api\Provider\LaravelServiceProvider::class, Cartalyst\Sentinel\Laravel\SentinelServiceProvider::class, Collective\Html\HtmlServiceProvider::class, \SocialiteProviders\Manager\ServiceProvider::class, Tymon\JWTAuth\Providers\JWTAuthServiceProvider::class,

в массив $aliases добавляем

'Activation' => Cartalyst\Sentinel\Laravel\Facades\Activation::class,
'Reminder'   => Cartalyst\Sentinel\Laravel\Facades\Reminder::class,
'Sentinel'   => Cartalyst\Sentinel\Laravel\Facades\Sentinel::class,
'Form' => Collective\Html\FormFacade::class,
'Html' => Collective\Html\HtmlFacade::class,
'Socialite' => Laravel\Socialite\Facades\Socialite::class,
'JWTAuth' => Tymon\JWTAuth\Facades\JWTAuth::class,
'JWTFactory' => Tymon\JWTAuth\Facades\JWTFactory::class,

Там же правим значение для локали приложения:

'locale' => 'ru',

Запускаем в терминале команду, устанавливаем административный интерфейс для Laravel - SleepingOwlAdmin:

php artisan sleepingowl:install

Последовательно запускаем в терминале команды:

php artisan vendor:publish --provider="Dingo\Api\Provider\LaravelServiceProvider"
php artisan vendor:publish --provider="Cartalyst\Sentinel\Laravel\SentinelServiceProvider"
php artisan vendor:publish --provider="Tymon\JWTAuth\Providers\JWTAuthServiceProvider"

Генерируем ключ для jwt:

php artisan jwt:generate

Во избежание коллизий, сразу удалим дефолтные миграции Laravel:
database/migrations/2014_10_12_000000_create_users_table.php
database/migrations/2014_10_12_100000_create_password_resets_table.php

Итак, все необходимые пакеты установлены. Приступаем к их настройке.

Настройка

Открываем для редактирования файл миграции database/migrations/2014_07_02_230147_migration_cartalyst_sentinel.php

В секцию с созданием таблицы users, добавляем поля:

$table->string('facebook_id')->nullable();
$table->string('google_id')->nullable();
$table->string('vkontakte_id')->nullable();

Создаем модель Post и миграцию под нее

php artisan make:model -m Post

Добавляем в миграцию 2 поля: title и content

public function up()
{
	Schema::create('posts', function (Blueprint $table) {
		$table->increments('id');
		$table->string('title');
		$table->text('content');
		$table->timestamps();
	});
}

Запускаем миграции:

php artisan migrate

Выполняем команду

php artisan make:seeder RoleSeeder

Редактируем файл database/seeds/RoleSeeder.php
Закладываем основу для 3-х ролей пользователей и создаем пользователя администратора.

<?php

use Illuminate\Database\Seeder;

class RoleSeeder extends Seeder { /** * Run the database seeds. * * @return void */ public function run() { $admin = [ 'email' => '[email protected]', 'password' => 'adminadmin', ]; $adminUser = Sentinel::registerAndActivate($admin); $role = [ 'name' => 'Администратор', 'slug' => 'admin', 'permissions' => [ 'admin' => true, ] ]; $adminRole = Sentinel::getRoleRepository()->createModel()->fill($role)->save(); $adminUser->roles()->attach($adminRole); $role = [ 'name' => 'Пользователь', 'slug' => 'user', ]; $userRole = Sentinel::getRoleRepository()->createModel()->fill($role)->save(); $role = [ 'name' => 'Забанен', 'slug' => 'banned', ]; $banRole = Sentinel::getRoleRepository()->createModel()->fill($role)->save(); } }

Выполняем команду

php artisan make:seeder PostSeeder

Редактируем файл database/seeds/PostSeeder.php
Создаем болванки публикаций с фейковым наполнением:

<?php
use Illuminate\Database\Seeder;
use Carbon\Carbon;

class PostSeeder extends Seeder { /** * Run the database seeds. * * @return void */ public function run() { $faker = Faker\Factory::create(); for ($i=0; $i<10; $i++){ DB::table('posts')->insert([ 'title' => $faker->text(10), 'content' => $faker->text(200), 'created_at' => Carbon::now(), 'updated_at' => Carbon::now(), ]); } } }

Редактируем файл database/seeds/DatabaseSeeder.php

<?php
use Illuminate\Database\Seeder;

class DatabaseSeeder extends Seeder { /** * Run the database seeds. * * @return void */ public function run() { $this->call(RoleSeeder::class); $this->call(PostSeeder::class); } }

Запускаем команду:

php artisan db:seed

Удаляем директорию app/Http/Controllers/Auth за ненадобностью

Создаем AuthController

php artisan make:controller AuthController

Добавляем роуты в routes\web.php

/**
 * Route for auth system
 */
// Вызов страницы регистрации пользователя
Route::get('register', 'AuthController@register');
// Пользователь заполнил форму регистрации и отправил
Route::post('register', 'AuthController@registerProcess');
// Пользователь получил письмо для активации аккаунта со ссылкой сюда
Route::get('activate/{id}/{code}', 'AuthController@activate');
// Вызов страницы авторизации
Route::get('login', 'AuthController@login');
// Пользователь заполнил форму авторизации и отправил
Route::post('login', 'AuthController@loginProcess');
// Выход пользователя из системы
Route::get('logout', 'AuthController@logoutuser');
// Пользователь забыл пароль и запросил сброс пароля. Это начало процесса -
// Страница с запросом E-Mail пользователя
Route::get('reset', 'AuthController@resetOrder');
// Пользователь заполнил и отправил форму с E-Mail в запросе на сброс пароля
Route::post('reset', 'AuthController@resetOrderProcess');
// Пользователю пришло письмо со ссылкой на эту страницу для ввода нового пароля
Route::get('reset/{id}/{code}', 'AuthController@resetComplete');
// Пользователь ввел новый пароль и отправил.
Route::post('reset/{id}/{code}', 'AuthController@resetCompleteProcess');
// Сервисная страничка, показываем после заполнения рег формы, формы сброса и т.
// о том, что письмо отправлено и надо заглянуть в почтовый ящик.
Route::get('wait', 'AuthController@wait');
// Пользователь получил письмо после регистрации в приложении для активации аккаунта со ссылкой
Route::get('activate_app/{id}/{code}', 'AuthController@activateForAppUser');
//Авторизация через соцсети
Route::get('signin', 'AuthController@signin');
//Коллбэк после авторизации через соцсети
Route::get('callback', 'AuthController@callbackSignin');

В директории resources/views создаем следующие поддиректории: auth, footer, header, layouts, mail

В директории resources/views/layouts создаем файл master.blade.php

<!DOCTYPE html>
<!--[if lt IE 7]>      <html class="no-js lt-ie9 lt-ie8 lt-ie7"> <![endif]-->
<!--[if IE 7]>         <html class="no-js lt-ie9 lt-ie8"> <![endif]-->
<!--[if IE 8]>         <html class="no-js lt-ie9"> <![endif]-->
<!--[if gt IE 8]><!--> <html class="no-js"> <!--<![endif]-->
<head>
    @include('header.head')
</head>
<body>
<!--[if lt IE 7]>

Вы используете слишком старый браузер. Пожалуйста обновите ваш браузер для нормального серфинга по современным сайтам.


<![endif]-->
<header id="header" class="">
    @include('header.header')
</header>
<section>
    @yield('body')
</section>
@include('footer.footer')
@include('footer.foot_script')
</body>
</html>

Создаем пустые файлы:

resources/views/header/head.blade.php
resources/views/header/header.blade.php
resources/views/footer/footer.blade.php
resources/views/footer/foot_script.blade.php

В resources/views/header/head.blade.php добавляем

<link href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" rel="stylesheet">
<link href="http://maxcdn.bootstrapcdn.com/font-awesome/4.0.3/css/font-awesome.min.css" rel="stylesheet">
<link href="/css/auth.css" rel="stylesheet">

Создаем файл public/css/auth.css:

@media (min-width: 768px) {
    .omb_row-sm-offset-3 div:first-child[class*="col-"] {
        margin-left: 25%;
    }
}

.omb_login .omb_authTitle { text-align: center; line-height: 300%; }

.omb_login .omb_socialButtons a { color: white; // In yourUse @body-bg opacity:0.9; } .omb_login .omb_socialButtons a:hover { color: white; opacity:1; } .omb_login .omb_socialButtons .omb_btn-facebook {background: #3b5998;} .omb_login .omb_socialButtons .omb_btn-vkontakte {background: #00aced;} .omb_login .omb_socialButtons .omb_btn-google {background: #c32f10;}

.omb_login .omb_loginOr { position: relative; font-size: 1.5em; color: #aaa; margin-top: 1em; margin-bottom: 1em; padding-top: 0.5em; padding-bottom: 0.5em; } .omb_login .omb_loginOr .omb_hrOr { background-color: #cdcdcd; height: 1px; margin-top: 0px !important; margin-bottom: 0px !important; } .omb_login .omb_loginOr .omb_spanOr { display: block; position: absolute; left: 50%; top: -0.6em; margin-left: -1.5em; background-color: white; width: 3em; text-align: center; }

.omb_login .omb_loginForm .input-group.i { width: 2em; } .omb_login .omb_loginForm .help-block { color: red; }

@media (min-width: 768px) { .omb_login .omb_forgotPwd { text-align: right; margin-top:10px; } }

.btn-group-lg>.btn, .btn-lg { padding: 10px 7px; }

Создаем resources/views/errors/errmsg.blade.php :

@if ($errors->any())
{{-- --}} Ошибка: @if ($message = $errors->first(0, ':message')) {{ $message }} @else Пожалуйста проверьте правильность заполнения формы @endif

@endif

@if ($message = Session::get('success'))

Поздравляем! {{ $message }}

@endif

Создаем шаблоны элементов форм:
resources/views/widgets/form/_formitem_text.blade.php :

<?php
if(! isset($value)) $value = null;
$error_class = $errors->has($name) ? ' has-error' : '';
?>
{!! Form::text($name, $value, array('placeholder' => $placeholder, 'class' => 'form-control' . $error_class )) !!}

{!! $errors->first($name) !!}

resources/views/widgets/form/_formitem_password.blade.php :

<php $error_class = $errors->has($name) ? ' has-error' : ''; ?>
{!! Form::password($name, array('placeholder' => $placeholder, 'class' => 'form-control' . $error_class )) !!}

{!! $errors->first($name) !!}

resources/views/widgets/form/_formitem_btn_submit.blade.php :

{!! Form::submit($title, array('class' => 'btn btn-lg btn-primary btn-block')) !!}

Создаем resources/views/auth/login.blade.php :

@extends('layouts.master')
@section('body')

Войти или зарегистрироваться

@include('auth.social_buttons')

или
{!! Form::open(['class' => 'omb_loginForm']) !!} @include('errors.errmsg') @include('widgets.form._formitem_text', ['name' => 'email', 'placeholder' => 'Email', 'fa_icon_class' => 'fa-user' ]) @include('widgets.form._formitem_password', ['name' => 'password', 'placeholder' => 'Пароль', 'fa_icon_class' => 'fa-lock' ]) @include('widgets.form._formitem_btn_submit', ['title' => 'Войти']) {!! Form::close() !!}

Забыли пароль?


@stop

Создаем resources/views/auth/register.blade.php :

@extends('layouts.master')
@section('body')

Зарегистрироваться или войти

@include('auth.social_buttons')

или
{!! Form::open(['class' => 'omb_loginForm']) !!} @include('errors.errmsg') @include('widgets.form._formitem_text', ['value' => $email, 'name' => 'email', 'placeholder' => 'Email', 'fa_icon_class' => 'fa-user' ]) @include('widgets.form._formitem_password', ['name' => 'password', 'placeholder' => 'Пароль', 'fa_icon_class' => 'fa-lock' ]) @include('widgets.form._formitem_password', ['name' => 'password_confirm', 'placeholder' => 'Подтверждение пароля', 'fa_icon_class' => 'fa-lock' ]) @include('widgets.form._formitem_btn_submit', ['title' => 'Зарегистрироваться']) {!! Form::close() !!}

Забыли пароль?


@stop

Создаем resources/views/auth/reset_order.blade.php :

@extends('layouts.master')
@section('body')

Сброс пароля

{!! Form::open(['class' => 'omb_loginForm']) !!} @include('errors.errmsg') @include('widgets.form._formitem_text', ['name' => 'email', 'placeholder' => 'Email', 'fa_icon_class' => 'fa-user' ]) @include('widgets.form._formitem_btn_submit', ['title' => 'Сбросить пароль']) {!! Form::close() !!}

@stop

Создаем resources/views/auth/reset_complete.blade.php :

@extends('layouts.master')
@section('body')

Обновление пароля

{!! Form::open(['class' => 'omb_loginForm']) !!} @include('errors.errmsg') @include('widgets.form._formitem_password', ['name' => 'password', 'placeholder' => 'Новый пароль', 'fa_icon_class' => 'fa-lock' ]) @include('widgets.form._formitem_password', ['name' => 'password_confirm', 'placeholder' => 'Подтверждение нового пароля', 'fa_icon_class' => 'fa-lock' ]) @include('widgets.form._formitem_btn_submit', ['title' => 'Подтвердить обновление пароля']) {!! Form::close() !!}

@stop

Создаем resources/views/auth/wait.blade.php :

@extends('layouts.master')
@section('body')

Через несколько минут, вам на почту придет письмо с дальнейшими инструкциями.


@stop

Создаем resources/views/auth/social_buttons.blade.php :

Facebook
Google+
VKontakte

Готовим представления E-Mail:

resources/views/mail/account_activate.blade.php :

Для активации аккаунта пройдите по getUserId()}/{$code}") }}">ссылке

resources/views/mail/account_reminder.blade :

Для активации аккаунта пройдите по getUserId()}/{$code}") }}">ссылке

resources/views/mail/account_activate_app.blade.php :

Для создания нового пароля пройдите по getUserId()}/{$code}") }}">ссылке

В resources/views/welcome.blade.php произвльно, в любом месте добавим временную болванку:

<?php
if ($user = Sentinel::getUser())
{
    echo 'Привет, ' . $user->email;
}else{
    echo 'Привет, гость';
}
?>

Наполняем app/Http/Controllers/AuthController.php :

<?php

namespace App\Http\Controllers;

use Cartalyst\Sentinel\Checkpoints\NotActivatedException; use Cartalyst\Sentinel\Checkpoints\ThrottlingException;

use Illuminate\Http\Request; use App\Http\Requests; use Redirect; use Sentinel; use Activation; use Reminder; use Validator; use Mail; use Storage; use CurlHttp; use Illuminate\Support\Facades\Input; use Socialite; use Session;

class AuthController extends Controller {

public $networks = ['vkontakte', 'facebook', 'google'];

/**
 * Show login page
 *
 * @return \Illuminate\Contracts\View\Factory|\Illuminate\View\View
 */
public function login()
{
    return view('auth.login');
}

/**
 * Show Register page
 *
 * @return \Illuminate\Contracts\View\Factory|\Illuminate\View\View
 */
public function register()
{
    $email = Session::get('network_user') != '' ? Session::get('network_user')-&gt;getEmail() : '';
    return view('auth.register', ['email' =&gt; $email]);
}


/**
 * Show wait page
 *
 * @return \Illuminate\Contracts\View\Factory|\Illuminate\View\View
 */
public function wait()
{
    return view('auth.wait');
}


/**
 * Process login users
 *
 * @param Request $request
 * @return $this|\Illuminate\Http\RedirectResponse
 */
public function loginProcess(Request $request)
{
    try
    {
        $messages = [
            'email.required'            =&gt; 'Введите email',
            'email.email'               =&gt; 'Похоже, что email введен с ошибкой',
            'password.required'         =&gt; 'Введидите пароль',
            'password.between'          =&gt; 'Минимальная длина пароля - 8 символов',
        ];
        $this-&gt;validate($request, [
            'email' =&gt; 'required|email',
            'password' =&gt; 'required|between:8,100',
        ], $messages);
        if (Sentinel::authenticate($request-&gt;all()))
        {
            if(Sentinel::inRole('admin')) return redirect('admin');
            return Redirect::intended('/');
        }
        $errors = 'Неправильный логин или пароль.';
        return Redirect::back()
            -&gt;withInput()
            -&gt;withErrors($errors);
    }
    catch (NotActivatedException $e)
    {
        $sentuser= $e-&gt;getUser();
        $activation = Activation::create($sentuser);
        $code = $activation-&gt;code;
        $sent = Mail::send('mail.account_activate', compact('sentuser', 'code'), function($m) use ($sentuser)
        {
            $m-&gt;from('[email protected]', 'MySite');
            $m-&gt;to($sentuser-&gt;email)-&gt;subject('Активация аккаунта');
        });

        if ($sent === 0)
        {
            return Redirect::to('login')
                -&gt;withErrors('Ошибка отправки письма активации.');
        }
        $errors = 'Ваш аккаунт не ативирован! Поищите в своем почтовом ящике письмо со ссылкой для активации (Вам отправлено повторное письмо). ';
        return view('auth.login')-&gt;withErrors($errors);
    }
    catch (ThrottlingException $e)
    {
        $delay = $e-&gt;getDelay();
        $errors = "Ваш аккаунт блокирован на {$delay} секунд.";
    }
    return Redirect::back()
        -&gt;withInput()
        -&gt;withErrors($errors);
}


/**
 * Process register user from site
 *
 * @param Request $request
 * @return $this
 */
public function registerProcess(Request $request)
{
    $messages = [
        'email.required'            =&gt; 'Введите email',
        'email.email'               =&gt; 'Похоже, что email введен с ошибкой',
        'password.required'         =&gt; 'Введидите пароль',
        'password_confirm.required' =&gt; 'Введидите подтверждение пароля',
        'password_confirm.same'     =&gt; 'Введенные пароли не одинаковы',
        'password.between'          =&gt; 'Минимальная длина пароля - 8 символов',
    ];
    $this-&gt;validate($request, [
        'email' =&gt; 'required|email',
        'password' =&gt; 'required|between:8,100',
        'password_confirm' =&gt; 'required|same:password',
    ], $messages);
    $input = $request-&gt;all();

    if (Session::get('network_user') &amp;&amp; Session::get('network_user')-&gt;id) {

        $input[Session::get('network') . '_id'] = Session::get('network_user')-&gt;id;

    }

    $credentials = [ 'email' =&gt; $request-&gt;email ];
    if($user = Sentinel::findByCredentials($credentials))
    {
        return Redirect::to('register')
            -&gt;withErrors('Такой Email уже зарегистрирован.');
    }

    if ($sentuser = Sentinel::register($input))
    {
        $activation = Activation::create($sentuser);
        $code = $activation-&gt;code;
        $sent = Mail::send('mail.account_activate', compact('sentuser', 'code'), function($m) use ($sentuser)
        {
            $m-&gt;from('[email protected]', 'MySite');
            $m-&gt;to($sentuser-&gt;email)-&gt;subject('Активация аккаунта');
        });
        if ($sent === 0)
        {
            return Redirect::to('register')
                -&gt;withErrors('Ошибка отправки письма активации.');
        }

        $role = Sentinel::findRoleBySlug('user');
        $role-&gt;users()-&gt;attach($sentuser);

        Session::forget('network_user');
        Session::forget('network');

        return Redirect::to('login')
            -&gt;withSuccess('Ваш аккаунт создан. Проверьте Email для активации.')
            -&gt;with('userId', $sentuser-&gt;getUserId());
    }
    return Redirect::to('register')
        -&gt;withInput()
        -&gt;withErrors('Failed to register.');
}


/**
 *  Activate user account by user id and activation code
 *
 * @param $id
 * @param $code
 * @return $this
 */
public function activate($id, $code)
{
    $sentuser = Sentinel::findById($id);

    if ( ! Activation::complete($sentuser, $code))
    {
        return Redirect::to("login")
            -&gt;withErrors('Неверный или просроченный код активации.');
    }

    return Redirect::to('login')
        -&gt;withSuccess('Аккаунт активирован.');
}

/**
 *  Activate user account by user id and activation code
 *
 * @param $id
 * @param $code
 * @return $this
 */
public function activateForAppUser($id, $code)
{
    $sentuser = Sentinel::findById($id);

    if ( ! Activation::complete($sentuser, $code))
    {
        return view('auth.activate_result', ['result' =&gt; 'Invalid or expired activation code.']);
    }

    Sentinel::update($sentuser, ['email_confirmed' =&gt; 1]);

    return view('auth.activate_result', ['result' =&gt; 'Account activated.']);
}


/**
 * Show form for begin process reset password
 *
 * @return \Illuminate\Contracts\View\Factory|\Illuminate\View\View
 */
public function resetOrder()
{
    return view('auth.reset_order');
}


/**
 * Begin process reset password by email
 *
 * @param Request $request
 * @return $this|\Illuminate\Http\RedirectResponse
 */
public function resetOrderProcess(Request $request)
{
    $messages = [
        'email.required'            =&gt; 'Введите email',
        'email.email'               =&gt; 'Похоже, что email введен с ошибкой',
    ];
    $this-&gt;validate($request, [
        'email' =&gt; 'required|email',
    ], $messages);
    $email = $request-&gt;email;
    $sentuser = Sentinel::findByCredentials(compact('email'));
    if ( ! $sentuser)
    {
        return Redirect::back()
            -&gt;withInput()
            -&gt;withErrors('Пользователь с таким E-Mail в системе не найден.');
    }
    $reminder = Reminder::exists($sentuser) ?: Reminder::create($sentuser);
    $code = $reminder-&gt;code;

    $sent = Mail::send('mail.account_reminder', compact('sentuser', 'code'), function($m) use ($sentuser)
    {
        $m-&gt;from('[email protected]', 'MySite');
        $m-&gt;to($sentuser-&gt;email)-&gt;subject('Сброс пароля');
    });
    if ($sent === 0)
    {
        return Redirect::to('reset')
            -&gt;withErrors('Ошибка отправки email.');
    }
    return Redirect::to('wait');
}

/**
 * Show form for complete reset password
 *
 * @param $id
 * @param $code
 * @return mixed
 */
public function resetComplete($id, $code)
{
    $user = Sentinel::findById($id);
    return view('auth.reset_complete');
}


/**
 * Complete reset password
 *
 * @param Request $request
 * @param $id
 * @param $code
 * @return $this
 */
public function resetCompleteProcess(Request $request, $id, $code)
{
    $messages = [
        'password.required'         =&gt; 'Введидите новый пароль',
        'password_confirm.required' =&gt; 'Введидите подтверждение нового пароля',
        'password_confirm.same'     =&gt; 'Введенные пароли не одинаковы',
        'password.between'          =&gt; 'Минимальная длина пароля - 8 символов',
    ];
    $this-&gt;validate($request, [
        'password' =&gt; 'required|between:8,100',
        'password_confirm' =&gt; 'required|same:password',
    ], $messages);
    $user = Sentinel::findById($id);
    if ( ! $user)
    {
        return Redirect::back()
            -&gt;withInput()
            -&gt;withErrors('Такого пользователя не существует.');
    }
    if ( ! Reminder::complete($user, $code, $request-&gt;password))
    {
        return Redirect::to('login')
            -&gt;withErrors('Неверный или просроченный код сброса пароля.');
    }
    return Redirect::to('login')
        -&gt;withSuccess("Пароль сброшен.");
}

/**
 * @return mixed
 */
public function logoutuser()
{
    Sentinel::logout();
    return Redirect::intended('/');
}

/**
 * @return mixed
 */
public function signin()
{
    if (Input::get('network') &amp;&amp; in_array(Input::get('network'), $this-&gt;networks)) {
        return Socialite::with(Input::get('network'))-&gt;redirect();
    }
}

public function callbackSignin()
{
    //проверяем, есть ли параметр network, и присутствует ли он в массиве доступных соц. сетей
    if (Input::get('network') &amp;&amp; in_array(Input::get('network'), $this-&gt;networks)) {
        //получили данные пользователя из соц. сети
        $network_user = Socialite::with(Input::get('network'))-&gt;stateless()-&gt;user();

        //проверяем, есть ли в полученных данных значение email
        if ($network_user-&gt;getEmail() != '') {
            //если email получен - проверяем, есть ли в системе пользователь с таким email

            $credentials = ['email' =&gt; $network_user-&gt;getEmail()];
            $user = Sentinel::findByCredentials($credentials);

            if (!$user) {
                //если нет в системе пользователя с таким email,
                //проверяем есть ли в системе пользователь с таким id в конкретной соцсети

                $credentials = [Input::get('network') . '_id' =&gt; $network_user-&gt;getId()];

                $user = Sentinel::findByCredentials($credentials);

            } else {
                //если есть в системе пользователь с таким email,
                //проверяем заполнен ли id сети

                if ($user[Input::get('network') . '_id'] == '') {

                    $credentials = [Input::get('network') . '_id' =&gt; $network_user-&gt;getId()];
                    $user = Sentinel::update($user, $credentials);

                }
            }
        } else {
            //в полученных данных нет email
            //проверяем есть ли в системе пользователь с таким id в конкретной соцсети

            $credentials = [Input::get('network') . '_id' =&gt; $network_user-&gt;getId()];
            $user = Sentinel::findByCredentials($credentials);

        }

        if (!$user) {
            //пользователь в системе не найден -
            //отправляем на страницу регистрации со всеми полученными из соц. сети данными

            Session::put('network_user', $network_user);
            Session::put('network', Input::get('network'));

            return Redirect::to('/register');

        } else {
            //если найден - логиним в систему и редиректим на главную

            if ($activation = Activation::completed($user)) {

                Sentinel::login($user);
                return Redirect::intended('/');

            } else {
                return Redirect::to('login')
                    -&gt;withErrors('Ранее, на Вашу почту было отправлено письмо с дальнейшими инструкциями по активации аккаунта.');
            }

        }

    }

}

}

Приводим модель app/User.php к виду:

<?php
namespace App;

use Cartalyst\Sentinel\Users\EloquentUser as CartalystUser; use Hash;

class User extends CartalystUser { /** * The attributes that are mass assignable. * * @var array */ protected $fillable = [ 'email', 'password', 'first_name', 'last_name', 'facebook_id', 'google_id', 'vkontakte_id' ];

/**
 * The attributes excluded from the model's JSON form.
 *
 * @var array
 */
protected $hidden = [
    'password', 'remember_token', 'created_at', 'updated_at'
];

protected $loginNames = ['email', 'facebook_id', 'google_id', 'vkontakte_id'];

public function roles()
{
    return $this-&gt;belongsToMany('App\Role', 'role_users', 'user_id', 'role_id');
}

public function theroles()
{
    return $this-&gt;belongsToMany('App\Role', 'role_users', 'user_id', 'role_id');
}

public function setTherolesAttribute($roles)
{
    $this-&gt;theroles()-&gt;detach();
    if ( ! $roles) return;
    if ( ! $this-&gt;exists) $this-&gt;save();
    $this-&gt;theroles()-&gt;attach($roles);
}

public function getTherolesAttribute($roles)
{
    return array_pluck($this-&gt;theroles()-&gt;get(['id'])-&gt;toArray(), 'id');
}

}

В конфиге config/cartalyst.sentinel.php

'users' => [
    'model' =&gt; 'Cartalyst\Sentinel\Users\EloquentUser',

],</code></pre>

заменяем на

'users' => [
'model' =&gt; 'App\User',

],

Выполняем команду:

php artisan config:cache

Правим app/Providers/EventServiceProvider.php

protected $listen = [
    \SocialiteProviders\Manager\SocialiteWasCalled::class => [
        'SocialiteProviders\VKontakte\VKontakteExtendSocialite@handle',
        'SocialiteProviders\Google\GoogleExtendSocialite@handle',
    ],
];

Заводим приложения в Facebook, Google, VKontakte

Добавляем в config/services.php :

'facebook' => [
        'client_id'  => env('FACEBOOK_KEY'),
        'client_secret'  =>  env('FACEBOOK_SECRET'),
        'redirect' => env('FACEBOOK_REDIRECT_URI'),
    ],
'google' =&gt; [
    'client_id' =&gt; env('GOOGLE_KEY'),
    'client_secret' =&gt; env('GOOGLE_SECRET'),
    'redirect' =&gt; env('GOOGLE_REDIRECT_URI'),  
], 

'vkontakte' =&gt; [
    'client_id' =&gt; env('VKONTAKTE_KEY'),
    'client_secret' =&gt; env('VKONTAKTE_SECRET'),
    'redirect' =&gt; env('VKONTAKTE_REDIRECT_URI'),  
],</code></pre>

В .env добавляем:

FACEBOOK_KEY=yourkeyfortheservice
FACEBOOK_SECRET=yoursecretfortheservice
FACEBOOK_REDIRECT_URI=http://mysite.com/callback?network=facebook 

GOOGLE_KEY=yourkeyfortheservice GOOGLE_SECRET=yoursecretfortheservice GOOGLE_REDIRECT_URI=http://mysite.com/callback?network=google

VKONTAKTE_KEY=yourkeyfortheservice VKONTAKTE_SECRET=yoursecretfortheservice VKONTAKTE_REDIRECT_URI=http://mysite.com/callback?network=vkontakte

Значения yourkeyfortheservice и yoursecretfortheservice заменяем на значения из настроек приложений в указанных соц. сетях

С регистацией, логином, авторизацией закончили. Приступаем к настройке панели администратора.

В файле app\Http\Kernel.php изменяем значение $routeMiddleware :

protected $routeMiddleware = [
     //   'auth' => \Illuminate\Auth\Middleware\Authenticate::class,
     //   'auth.basic' => \Illuminate\Auth\Middleware\AuthenticateWithBasicAuth::class,
     //   'bindings' => \Illuminate\Routing\Middleware\SubstituteBindings::class,
     //   'can' => \Illuminate\Auth\Middleware\Authorize::class,
     //   'guest' => \App\Http\Middleware\RedirectIfAuthenticated::class,
        'throttle' => \Illuminate\Routing\Middleware\ThrottleRequests::class,
        'api.throttle' => \Illuminate\Routing\Middleware\ThrottleRequests::class,
        'isadmin' => \App\Http\Middleware\AdminPanel::class,
        'jwt.auth' => 'Tymon\JWTAuth\Middleware\GetUserFromToken',
        'jwt.refresh' => 'Tymon\JWTAuth\Middleware\RefreshToken',
    ];

В config\sleeping_owl.php :

'middleware' => ['web'],

заменяем на

'middleware' => ['web', 'isadmin'],

Создаем файл app/Http/Middleware/AdminPanel.php :

<?php

namespace App\Http\Middleware;

use Closure; use Cartalyst\Sentinel\Laravel\Facades\Sentinel; use Redirect; use Request; use Response; use AdminSection;

class AdminPanel { /** * Handle an incoming request. * * @param \Illuminate\Http\Request $request * @param \Closure $next * @return mixed */ public function handle($request, Closure $next) { if (Sentinel::guest()) return redirect('login'); if(Sentinel::inRole('admin')) return $next($request);

    $arrSlugs=str_getcsv($request-&gt;path(), '/');
    $method=Request::method();
    $user = Sentinel::check();
    if($method == 'DELETE')
    {
        $permit = $arrSlugs[0] . '.' . $arrSlugs[1] . '.delete';
        if ($user-&gt;hasAccess([$permit])) return $next($request);
        if ($user-&gt;hasAccess(['admin']))
        {
            $content = 'Для удаления объекта необходимы установленные права <b>'.  $permit .'</b> Для получения прав обратитесь к администратору';
            return Response::make(AdminSection::view($content, 'Попытка несанкционированного доступа'), 403);
        }
        return Redirect::back();
    }
    $permit=$arrSlugs[0];
    if(isset($arrSlugs[1])) $permit = $permit . '.' . $arrSlugs[1];
    if(isset($arrSlugs[2]) &amp;&amp; ($arrSlugs[2] == "create")) $permit = $permit . '.' . $arrSlugs[2];
    if(isset($arrSlugs[3])) $permit = $permit . '.' . $arrSlugs[3];
    if ($user-&gt;hasAccess([$permit])) return $next($request);
    if ($user-&gt;hasAccess(['admin']))
    {
        $content = 'Для данного действия необходимы установленные права <b>'. $permit .'</b> Для получения прав обратитесь к администратору';
        return Response::make(AdminSection::view($content, 'Попытка несанкционированного доступа'), 403);

    }
    return Redirect::back();
}

}

Создаем модель app/Role.php :

php artisan make:model Role
<?php
namespace App;

use Cartalyst\Sentinel\Roles\EloquentRole;

class Role extends EloquentRole {

public function permits()
{
    return $this-&gt;belongsToMany('App\Permit');
}

}

Создаем модель и миграцию для сущности Permit :

php artisan make:model -m Permit

в файле миграции:

public function up()
    {
        Schema::create('permits', function (Blueprint $table) {
            $table->increments('id');
            $table->string('name');
            $table->string('slug');
            $table->timestamps();
        });
    }

Далее:

php artisan make:migration --create="permit_role" create_pivot_permit_role

в файле миграции:

Schema::create('permit_role', function (Blueprint $table) {
            $table->increments('id');
            $table->integer('permit_id');
            $table->integer('role_id');
            $table->timestamps();
        });

В модели Permit.php:

public function roles()
    {
        return $this->belongsToMany('App\Role');
    }

В терминале:

php artisan migrate

Создаем файлы конфигураций моделей для админки:

app/Admin/User.php :

<?php
use App\User;
use App\Role;
use SleepingOwl\Admin\Model\ModelConfiguration;

AdminSection::registerModel(User::class, function (ModelConfiguration $model) { $model->setTitle('Пользователи'); $model->onDisplay(function () { $display = AdminDisplay::table()->setColumns([ AdminColumn::link('email')->setLabel('email')->setWidth('400px'), AdminColumn::text('first_name')->setLabel('Имя'), AdminColumn::text('last_name')->setLabel('Фамилия'), ]); $display->paginate(15); return $display; }); $model->onEdit(function () { $form = AdminForm::panel()->addBody( AdminFormElement::text('first_name', 'Имя'), AdminFormElement::text('last_name', 'Фамилия'), AdminFormElement::text('facebook_id', 'Facebook аккаунт'), AdminFormElement::text('google_id', 'Google+ аккаунт'), AdminFormElement::text('vkontakte_id', 'VKontakte аккаунт'), AdminFormElement::text('email', 'Email')->unique()->required()->addValidationRule('email'), AdminFormElement::multiselect('theroles', 'Роли')->setModelForOptions(new Role())->setDisplay('name') ); return $form; }); });

app/Admin/Role.php :

<?php
use App\Role;
use App\Permit;
use SleepingOwl\Admin\Model\ModelConfiguration;

AdminSection::registerModel(Role::class, function (ModelConfiguration $model) { $model->setTitle('Роли'); // Display $model->onDisplay(function () { $display = AdminDisplay::table()->setColumns([ AdminColumn::text('name')->setLabel('Название роли'), AdminColumn::text('slug')->setLabel('Роль'), ]); $display->paginate(15); return $display; }); // Create And Edit

$model-&gt;onCreate(function () {
    $form = AdminForm::panel()-&gt;addBody(
        AdminFormElement::text('name', 'Название роли')-&gt;required()-&gt;unique(),
        AdminFormElement::text('slug', 'Роль')-&gt;required()-&gt;unique(),
        AdminFormElement::multiselect('permits', 'Права доступа')-&gt;setModelForOptions(new Permit())-&gt;setDisplay('name')
    );
    return $form;
});

$model-&gt;onEdit(function () {
    $form = AdminForm::panel()-&gt;addBody(
        AdminFormElement::text('name', 'Название роли')-&gt;required()-&gt;unique(),//-&gt;setReadOnly(true)
        AdminFormElement::text('slug', 'Роль')-&gt;required()-&gt;unique(),//-&gt;setReadOnly(true)
        //AdminFormElement::multiselect('permissions', 'permissions')-&gt;setModelForOptions('App\Permit')-&gt;setDisplay('name')
       //AdminFormElement::multiselect('permissions', 'permissi', Role::getPermitsOptions())-&gt;setDefaultValue(array(0 =&gt; ''))-&gt;nullable()
        AdminFormElement::multiselect('permits', 'Права доступа')-&gt;setModelForOptions(new Permit())-&gt;setDisplay('name')
    );
    return $form;
});
$model-&gt;disableDeleting();

});

app/Admin/Permit.php :

<?php
use App\Role;
use App\Permit;
use SleepingOwl\Admin\Model\ModelConfiguration;

AdminSection::registerModel(Permit::class, function (ModelConfiguration $model) { $model->setTitle('Права'); // Display $model->onDisplay(function () { $display = AdminDisplay::table()->setColumns([ AdminColumn::text('name')->setLabel('Название права'), AdminColumn::text('slug')->setLabel('Slug'), ]); $display->paginate(15); return $display; }); // Create And Edit

$model-&gt;onCreateAndEdit(function () {
    $form = AdminForm::panel()-&gt;addBody(
        AdminFormElement::text('name', 'Название права')-&gt;required()-&gt;unique(),
        AdminFormElement::text('slug', 'Slug')-&gt;required()-&gt;unique()
    );
    return $form;
});

});

app/Admin/Post.php :

<?php
use App\Post;
use SleepingOwl\Admin\Model\ModelConfiguration;

AdminSection::registerModel(Post::class, function (ModelConfiguration $model) { $model->setTitle('Публикации'); // Display $model->onDisplay(function () { $display = AdminDisplay::table()->setColumns([ AdminColumn::text('title')->setLabel('Заголовок'), ]); $display->paginate(15); return $display; }); // Create And Edit

$model-&gt;onCreateAndEdit(function () {
    $form = AdminForm::panel()-&gt;addBody(
        AdminFormElement::text('title', 'Заголовок')-&gt;required(),
        AdminFormElement::textarea('content', 'Содержимое')-&gt;required()
    );
    return $form;
});

}) ->addMenuPage(Post::class, 0) ->setIcon('fa fa-pencil');

Вносим изменения в app\Providers\AppServiceProvider.php :

<?php
namespace App\Providers;

use Illuminate\Support\ServiceProvider; use Illuminate\Support\Facades\Request; use App\Role; use App\Permit;

class AppServiceProvider extends ServiceProvider { /** * Bootstrap any application services. * * @return void */ public function boot() { Role::saving(function ($role) { $permits = Request::get('permits'); $role->setPermissions([]); if (isset($permits)){ foreach($permits as $permitid) { $permit = Permit::find($permitid); $role->addPermission($permit->slug); } } if ( ! $permits) return; }); }

/**
 * Register any application services.
 *
 * @return void
 */
public function register()
{
    //
}

}

Поправим навигацию app/Admin/navigation.php:

<?php	
use SleepingOwl\Admin\Navigation\Page;

return [ [ 'title' => 'Dashboard', 'icon' => 'fa fa-dashboard', 'url' => route('admin.dashboard'), ],

[
    'title' =&gt; 'Пользователи',
    'icon' =&gt; 'fa fa-group',
    'pages' =&gt; [
        [
            'title' =&gt; 'Пользователи',
            'icon'  =&gt; 'fa fa-group',
            'url'   =&gt; 'admin/users',
        ],
        [
            'title' =&gt; 'Роли',
            'icon'  =&gt; 'fa fa-graduation-cap',
            'url'   =&gt; 'admin/roles',
        ],
        [
            'title' =&gt; 'Права',
            'icon'  =&gt; 'fa fa-key',
            'url'   =&gt; 'admin/permits',
        ],
    ]
],

[
    'title' =&gt; 'Выйти',
    'icon'  =&gt; 'fa fa-sign-out',
    'url'   =&gt; '/logout',
],

];

Создаем через админку новую роль "Модератор" со slug`ом moderator mysite.com/admin/roles/create

После этого создаем права для работы с публикациями:

Просмотр админки admin
Просмотр списка публикаций admin.posts
Просмотр публикации admin.posts.edit
Создание публикации admin.posts.create
Удаление публикации admin.posts.delete

В итоге, список прав у нас выглядит так:

Добавим для роли "Модератор" созданные права:

Выполним в терминале

php artisan config:cache

В значениях slug'ов прав, с помощью точечной нотации указаны роуты, в отдельных случаях - последним элементом действие - create, edit, delete. Таким образом, пользователь с ролью "Модератор", сможет выполнять указанные действия в админке, в соответствии с роутами, указанными в правах. Если пользователь с ролью "Модератор" перейдет на страницу, без назначенных прав на этот роут, от увидит примерно следующее:

С первоначальной настройкой админки закончили.

Приступаем к настройке заготовки под API.

Создадим контроллер app\Http\Controllers\ApiAuthController.php :

php artisan make:controller ApiAuthController
<?php
namespace App\Http\Controllers;

use Illuminate\Http\Request; use App\Http\Requests; use Redirect; use Sentinel; use Activation; use Reminder; use Tymon\JWTAuth\Facades\JWTAuth; use Tymon\JWTAuth\Exceptions\JWTException; use Validator; use Mail; use Storage; use CurlHttp; use Dingo\Api\Routing\Helpers;

class ApiAuthController extends Controller { use Helpers;

public function authenticate(Request $request)
{
    $credentials = $request-&gt;only('email', 'password');
    try {
        if (!$token = JWTAuth::attempt($credentials)) {
            return response()-&gt;json(['error' =&gt; 'invalid_credentials'], 401);
        }
    } catch (JWTException $e) {
        return response()-&gt;json(['error' =&gt; 'could_not_create_token'], 500);
    }
    return response()-&gt;json(compact('token'), 200);
}

public function refreshToken(){
    $token = JWTAuth::getToken();

    if(!$token){
        return $this-&gt;response-&gt;error("Token is invalid")-&gt;setStatusCode(401);
    }

    try {
        $refreshed_token = JWTAuth::refresh($token);
    } catch (JWTException $ex) {
        return $this-&gt;response-&gt;error("Something went wrong with token")-&gt;setStatusCode(401);
    }

    return $this-&gt;response-&gt;array(compact('refreshed_token'))-&gt;setStatusCode(200);
}

public function register(Request $request)
{
    $input = $request-&gt;all();
    $messages = [
        'email.required'            =&gt; 'Email required',
        'email.email'               =&gt; 'It appears that the email entered with error',
        'password.required'         =&gt; 'Enter password',
        'password.between'          =&gt; 'Minimum password length of 8 characters',
        'password_confirm.required' =&gt; 'Enter password',
        'password_confirm.same'     =&gt; 'The entered passwords do not match',
        'password_confirm.between'  =&gt; 'Minimum password length of 8 characters',
    ];
    $validator = Validator::make($input, [
        'email' =&gt; 'required|email',
        'password' =&gt; 'required|between:8,20',
        'password_confirm' =&gt; 'required|same:password|between:8,20',
    ], $messages);

    if(count($validator-&gt;errors()-&gt;all()) &gt; 0){
        return $this-&gt;response-&gt;array(['validation_error' =&gt; 1, 'error_msgs' =&gt; $validator-&gt;errors()-&gt;all()])-&gt;setStatusCode(422);
    }

    $credentials = [ 'email' =&gt; $request-&gt;email ];
    if($user = Sentinel::findByCredentials($credentials))
    {
        return $this-&gt;response-&gt;error("This Email is already registered", 200);
    }

    if ($sentuser = Sentinel::register($input))
    {
        $activation = Activation::create($sentuser);
        $code = $activation-&gt;code;
        $sent = Mail::send('mail.account_activate_app', compact('sentuser', 'code'), function($m) use ($sentuser)
        {
            $m-&gt;from('[email protected]', 'MySite');
            $m-&gt;to($sentuser-&gt;email)-&gt;subject('Account activation');
        });
        if ($sent === 0)
        {
            return $this-&gt;response-&gt;error("Activation email is not sending", 200);
        }

        $role = Sentinel::findRoleBySlug('user');
        $role-&gt;users()-&gt;attach($sentuser);

        return $this-&gt;response-&gt;array(['success' =&gt; 1, 'msg' =&gt; 'Your account has been created. Check Email to activate.'])-&gt;setStatusCode(200);
    }
    return $this-&gt;response-&gt;error("Failed to register", 200);
}

}

Создаём контроллер app\Http\Controllers\PostController.php :

php artisan make:controller PostController
<?php
namespace App\Http\Controllers;

use Illuminate\Http\Request; use App\Http\Requests; use Tymon\JWTAuth\Facades\JWTAuth; use Tymon\JWTAuth\Exceptions\JWTException; use App\Post; use Dingo\Api\Routing\Helpers;

class PostController extends Controller { use Helpers;

public function getPosts(){
    $user = JWTAuth::parseToken()-&gt;authenticate();

    if(!$user){
        return $this-&gt;response-&gt;errorNotFound("authenticate");
    }

    $data = Post::all();

    return $this-&gt;response-&gt;array(compact('data'))-&gt;setStatusCode(200);
}

}

Наполняем routes\api.php :


$api = app('Dingo\Api\Routing\Router');

$api->version('v1', function ($api){ $api->post('authenticate', 'App\Http\Controllers\ApiAuthController@authenticate'); $api->post('register', 'App\Http\Controllers\ApiAuthController@register'); });

$api->version('v1',

About

Сборка Laravel 5.3 + Админка Sleeping Owl 4 + Управление правами и ролями пользователей + Авторизация через соц.сети + Заготовка под REST/RESTful API Dingo

Resources

License

MIT, MIT licenses found

Licenses found

MIT
LICENSE.md
MIT
LICENSE.txt

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages