📜 ⬆️ ⬇️

Elegant login form to admin panel on Laravel and Sentry

When creating websites, you have to spend some time on the login and password entry form for accessing the control panel. To speed up the development process, I want to share a recipe for preparing a simple, convenient and elegant login form to the admin panel.

The article contains a description of some basic techniques for using Laravel in the development of sites and will be useful to those who are starting to master this framework. For example, using Ubuntu 12.04, PostgreSQL 9.3, Nginx 1.1.19, PHP 5.5.7, Composer and a fresh project created using Laravel 4.1. Under the control of PostgreSQL, the examples database is rotated, to which the user of examples with the same password has access. Nginx is configured in such a way that when contacting examples.loc examples.loc browser examples.loc opens the main stub page that comes with the Laravel bundled, with the words "You have arrived."

All paths to editable files are relative to the project directory.

Environment


First I set up the local environment in Laravel. To do this, create the app/config/local directory and add the database.php file to it:
 <?php /** * app/config/local/database.php */ return array( 'default' => 'pgsql', 'connections' => array( 'pgsql' => array( 'driver' => 'pgsql', 'host' => 'localhost', 'database' => 'examples', 'username' => 'examples', 'password' => 'examples', 'charset' => 'utf8', 'prefix' => '', 'schema' => 'public', ), ), ); 

I ask Laravel to use the local environment by default. To do this, edit the bootstrap / start.php file and replace the line 'your-machine-name' with '*' :
 //   bootstrap/start.php $env = $app->detectEnvironment(array( 'local' => array('*'), )); 

')

Sentry connection


A link to the instructions for connecting Sentry can be found at the end of the article. Briefly, I do the following.
I "cartalyst/sentry": "2.0.*" line "cartalyst/sentry": "2.0.*" to composer.json "cartalyst/sentry": "2.0.*" In the require block.
 //  composer.json { "name": "laravel/laravel", "description": "The Laravel Framework.", "keywords": ["framework", "laravel"], "license": "MIT", "require": { "laravel/framework": "4.1.*", "cartalyst/sentry": "2.0.*" }, ... 

I execute the command:
$ composer update
$ Character is not necessary to dial
The $ symbol means that you need to type composer update on the command line.

I add to the list of service providers in the file app / config / app.php:
'Cartalyst\Sentry\SentryServiceProvider',

I add to the list of aliases in the file app / config / app.php:
'Sentry' => 'Cartalyst\Sentry\Facades\Laravel\Sentry',

Perform Sentry migrations:
$ php artisan migrate --package=cartalyst/sentry

I publish the Sentry configuration file:
$ php artisan config:publish cartalyst/sentry

I open for editing app/config/packages/cartalyst/sentry/config.php . I find in it the string 'login_attribute' => 'email' and replace it with 'login_attribute' => 'username' , in order for Sentry to authenticate the user by his login, and not by e-mail.

During the Sentry migrations, a users table was created, in which there is an email field, but no username . Therefore, in order for Sentry to work, you must add the missing field. To do this, create a migration:
$ php artisan migrate:make alter_users_add_username

A file will appear in the app/database/migration directory, which will consist of the current date and time and ends with alter_users_add_username.php . I edit it as follows:
 <?php /** * app/database/migration/0000_00_00_000000_alter_users_add_username.php */ use Illuminate\Database\Migrations\Migration; class AlterUsersAddUsername extends Migration { /** * Run the migrations. * * @return void */ public function up() { Schema::table('users', function($table) { $table->string('username'); }); } /** * Reverse the migrations. * * @return void */ public function down() { Schema::table('users', function($table) { $table->dropColumn('username'); }); } } 

To verify the migration I perform:
$ php artisan migrate

To verify that the migration is successfully rolled back, I run:
$ php artisan migrate:rollback

I create another migration that adds a superuser entry to the users table:
$ php artisan migrate:make add_user_admin

I find a file in the app/database/migrate directory that ends with add_user_admin.php and edit it:
 <?php /** * app/database/migration/0000_00_00_000001_add_user_admin.php */ use Illuminate\Database\Migrations\Migration; class AddUserAdmin extends Migration { /** * Run the migrations. * * @return void */ public function up() { $user = Sentry::createUser(array( 'username' => 'admin', 'email' => 'admin@examples.loc', 'password' => 'password', 'activated' => 1, 'permissions' => array( 'superuser' => 1, ), )); } /** * Reverse the migrations. * * @return void */ public function down() { User::where('username', '=', 'admin')->firstOrFail()->delete(); } } 

I check how migrations are rolling and rolling back. And I turn to creating a controller that will be responsible for authenticating users.

Login form


In the app/controllers directory app/controllers create the file AuthController.php :
 <?php /** * app/controllers/AuthController.php */ class AuthController extends BaseController { /** *    * * @return Illuminate\View\View */ public function getLogin() { $title = ''; return View::make('auth.login', compact('title')); } } 

The getLogin() method passes the page title via the $title variable to the auth.login , which serves to display the login page to the admin panel.

I create representation auth.login . To do this, add the auth directory to the app/views directory and create the login.blade.php file in it:
 /** * app/views/auth/login.blade.php */ @extends('layout') @section('main') <div class="container"> {{ Form::open(array('class' => 'form-signin')) }} @if (!$errors->isEmpty()) <div class="alert alert-danger"> @foreach ($errors->all() as $error) <p>{{ $error }}</p> @endforeach </div> @endif <h2 class="form-signin-heading">{{ $title }}</h2> {{ Form::text('username', null, array('class' => 'form-control', 'placeholder' => '')) }} {{ Form::password('password', array('class' => 'form-control', 'placeholde' => '')) }} <label class="checkbox"> {{ Form::checkbox('remember-me', 1) }}   </label> {{ Form::submit('', array('class' => 'btn btn-lg btn-primary btn-block')) }} {{ Form::close() }} </div> @stop 

The login.blade.php template extends the layout.blade.php template. Therefore, I create it in the app/views directory:
 /** * app/views/layout.blade.php */ <!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>{{ $title }}</title> @section('styles') {{ HTML::style('//netdna.bootstrapcdn.com/bootstrap/3.0.3/css/bootstrap.min.css') }} {{ HTML::style(URL::asset('styles/base.css')) }} @show </head> <body> @yield('main') @section('scripts') {{ HTML::script('//netdna.bootstrapcdn.com/bootstrap/3.0.3/js/bootstrap.min.js') }} @show <body> </html> 

After there is a controller method and the representation returned to it, it is necessary to set up routing so that GET requests sent to examples.loc/login examples.loc/login , processed by the getLogin() method. To do this, add the following code to the file app/routes.php :
 //  app/routes.php ... Route::group(array('before' => 'guest'), function () { Route::get('login', array( 'as' => 'auth.login', 'uses' => 'AuthController@getLogin' )); }); 

Using Route::get() define a route called auth.login , which sends GET requests to the /login method of getLogin() AuthController .
Also put the auth.login auth.login into groups of routes. Before any route included in this group, the guest filter will be executed. This filter means that GET requests to the /login address will only be processed if the user is a guest, i.e. not authorized

I will rewrite the guest filter so that it uses Sentry. To do this, in the file app/filters.php will change the guest filter code:
 //  app/filters.php ... Route::filter('guest', function() { if (Sentry::check()) return Redirect::to('/'); }); ... 

Now you can see what the login form in the browser looks like. To do this, go to examples.loc/login examples.loc/login and ...

I see the error message Call to undefined method Illuminate\Cookie\CookieJar::get() .

With a little google google, I’m finding out that Sentry 2.0 is compatible with Laravel 4.0, but not compatible with 4.1. It's good that Sentry 2.1 has already been released. To get rid of the error, change the version of Sentry in composer.json to 2.1:
 //  composer.json { "name": "laravel/laravel", "description": "The Laravel Framework.", "keywords": ["framework", "laravel"], "license": "MIT", "require": { "laravel/framework": "4.1.*", "cartalyst/sentry": "2.1.*" }, ... 

I execute the command:
$ composer update

Once again I try to open examples.loc/login examples.loc/login and see the login form and password. In order for the form to look elegant, you need to add a little CSS. To do this, in the public directory, create a styles directory and add the base.css file base.css :
 /** * public/style/base.css */ body { padding-top: 40px; padding-bottom: 40px; background-color: #eee; } .form-signin { max-width: 330px; padding: 15px; margin: 0 auto; } .form-signin .form-signin-heading, .form-signin .checkbox { margin-bottom: 10px; } .form-signin .form-signin-heading { text-align: center; } .form-signin .checkbox { font-weight: normal; } .form-signin .form-control { position: relative; font-size: 16px; height: auto; padding: 10px; -webkit-box-sizing: border-box; -moz-box-sizing: border-box; box-sizing: border-box; } .form-signin .form-control:focus { z-index: 2; } .form-signin input[type="text"] { margin-bottom: -1px; border-bottom-left-radius: 0; border-bottom-right-radius: 0; } .form-signin input[type="password"] { margin-bottom: 10px; border-top-left-radius: 0; border-top-right-radius: 0; } 


Controllers


Now the form is displayed beautifully, but when you click on the "Login" button, an error 404 is returned. So you need to add to the AuthController a POST request handler sent to the /login address.
 <?php /** * app/controllers/AuthController.php */ class AuthController extends BaseController { /** *    * * @return Illuminate\View\View */ public function getLogin() { $title = ''; return View::make('auth.login', compact('title')); } /** *      * * @return Illuminate\Http\RedirectResponse */ public function postLogin() { Input::flash(); try { $credentials = array( 'username' => Input::get('username'), 'password' => Input::get('password') ); $user = Sentry::authenticate($credentials, Input::get('remember-me')); } catch (Exception $e) { return Redirect::to(route('auth.login')) ->withErrors(array($e->getMessage())); } return Redirect::intended(route('admin')); } /** *   * * @return Illuminate\Http\RedirectResponse */ public function getLogout() { Sentry::logout(); return Redirect::route('auth.login'); } } 

In the postLogin() method, the data passed through the form is stored in the session using Input::flash() . In the try block, the Sentry attempts to authenticate the user. If the user sets the “Remember me” checkbox in the login and password entry form, then the second parameter in Sentry::authenticate() will be passed to true and the Sentry will remember the user in case of successful authentication. If authentication for any reason is not successful, then in the catch the Redirect::to() method will send us to the login and password entry page in the place with an error message. In the case of successful authentication, the Redirect::intended() method will send the user to the page he intended to enter when he was redirected to the login and password entry form. If such a page is not set, then the page at the address /admin is opened, with which a route is named admin .

The getLogin() method is the getLogin() implement as you can log off the user. After logging out, the user will be redirected to the login and password entry page.

Before proceeding to setting up routing, you need to add a method that will handle requests sent to /admin . To do this, edit the file app/controllers/HomeController.php as follows:
 <?php /** * app/controllers/HomeController.php */ class HomeController extends BaseController { public function showWelcome() { return View::make('hello'); } public function getAdmin() { return link_to(route('auth.logout'), ''); } } 

The getAdmin() method simply displays the “Exit” link on the page, when clicked, the user will be logged out.

Routing


Now, in order for the new methods to process the request, app/routes.php edit it in the app/routes.php :
 /** * app/routes.php */ Route::get('/', function() { return View::make('hello'); }); Route::group(array('before' => 'guest'), function () { Route::get('login', array( 'as' => 'auth.login', 'uses' => 'AuthController@getLogin' )); Route::post('login', array( 'before' => 'csrf', 'uses' => 'AuthController@postLogin' )); }); Route::group(array('before' => 'auth'), function () { Route::get('admin', array( 'as' => 'admin', 'uses' => 'HomeController@getAdmin', )); Route::get('logout', array( 'as' => 'auth.logout', 'uses' => 'AuthController@getLogout' )); }); 

The first group of routes, before which the guest filter is executed, contains the routing for the /login address for GET and POST requests. The guest filter means that requests from only guests will be processed at the /login address, i.e. unauthorized users.

The second group of routes, before which the auth filter is executed, contains routing for the /admin and /logout addresses. The auth filter means that only addresses from authorized users will be processed at these addresses.

You also need to edit the auth filter so that it uses Sentry. To do this, open the file app/filters.php change the auth filter as follows:
 //  app/filters.php ... Route::filter('auth', function() { if (!Sentry::check()) return Redirect::guest(route('auth.login')); }); ... 


Now you can try to login to admin panel using admin username and password password. An unauthorized user will not be able to access the /admin page, it will be available only after successful authentication. Also, after authentication, it will be impossible to enter the /login page, as there will be a redirect to the main page.

I hope this example will help novice developers to better understand some elements of Laravel.

Related Links


getcomposer.org/doc/00-intro.md#installation-nix
laravel.com/docs/installation#install-laravel
cartalyst.com/manual/sentry/installation/laravel-4

UPD
Sample code github.com/bskton/examples/tree/eform

Source: https://habr.com/ru/post/206908/


All Articles