Creare un Plugin con autenticazione utente in CakePHP 3.x

In questo breve tutorial è illustrato come creare un Plugin con autenticazione utente, in una applicazione CakePHP 3.x. che utilizza il plugin AOBuilder. Utilizzando il BAKE realizzeremo lo skeleton del plugin, modificando l’appController definiremo i metodi di login e logout,  attraverso un PagesController gestiremo le richieste dei contenuti statici, ed infine attraverso il file delle routes, ruoteremo le richieste le dei contenuti opportunamente. Alla fine verrà presentato un file template, per creare pagina di login.

Installazione del plugin

Come primo passo installiamo il plugin utilizzando il BAKE, lanciando il comando:

bin/cake bake plugin MyArea

A questo punto il plugin è stato creato in /plugins/MyArea, assicurarsi che il plugin sia stato caricato nel bootstrap dell’applicazione principale /config/bootstrap.php e che l’impostazione di autoload sia stata aggiunta in composer.json nella root dell’applicazione. Queste due operazioni in genere sono automatiche durante l’installazione ed i messaggi dovrebbero comparire nella shell. Qualora l’autoload del plugin non è stato eseguito in fase di installazione, è possibile intervenire manualmente in /config/bootstrap.php come con il seguente comando:

Plugin::load('MyArea', ['autoload' => true, 'routes' => true]);

Come modificare i controllers

A questo punto modifichiamo l’AppController, contenuto in /plugins/src/Controller, per integrare una versione modificata dell’AuthComponent nativo in CakePHP.

<?php

namespace MyArea\Controller;

use Cake\Controller\Controller as BaseController;
use Cake\I18n\Number;
use Cake\I18n\Time;
use Cake\Filesystem\File;
use Cake\Filesystem\Folder;
use Cake\ORM\TableRegistry;
use Cake\Core\Configure;

/**
 * 
 */
class AppController extends BaseController {

    /**
     * 
     */
    public function initialize() {
        parent::initialize();

        /**
         * 
         */
        Time::setToStringFormat('YYYY-MM-dd');
        $this->loadComponent('Flash');

        /**
         * Define $AuthConf configuration array
         */
        $AuthConf = [
            'table' => 'Operatore',
            'username' => 'email',
            'password' => 'password',
            'md5_hashing' => FALSE,
            'login_redirect' => '/my-area',
            'logout_redirect' => '/my-area',
        ];

        /**
         * Configure::write('User', $user);
         */
        if ($this->request->session()->check('User.id')):
            $this->User = TableRegistry::get($AuthConf['table']);
        endif;

        /**
         * 
         */
        if (isset($this->request->pass[0])):
            if (trim($this->request->pass[0]) == 'login')
                $this->login($AuthConf);
            if (trim($this->request->pass[0]) == 'logout')
                $this->logout($AuthConf);
        endif;

        /**
         * Redirect to login page
         */
        if (!$this->isAuthorized())
            return $this->redirect(['login']);
    }

    /**
     * App Login
     * 
     * @param type $options
     * @return type
     */
    private function login($options = []) {


        extract($options);

        /**
         * Login Utente
         */
        if ($this->request->is('post') && isset($this->request->data[$username])) {
            $this->User = TableRegistry::get($table);
            $query = $this->User->find('all');
            $query->where([$username => trim($this->request->data[$username])]);
            if ($md5_hashing)
                $query->where([$password => md5(trim($this->request->data['password']))]);
            else
                $query->where([$password => trim($this->request->data['password'])]);
            $user = $query->first();

            if ($user) {
                $this->request->session()->write('User.id', $user->id);
                return $this->redirect($login_redirect);
            } else {
                $this->Flash->set(__('Username or password is incorrect!'), ['element' => 'error']);
            }
        }
    }

    /**
     * App Logout
     */
    private function logout($options = []) {
        extract($options);
        $this->request->session()->destroy();
        $this->Flash->set(__('You are now logged out!'), ['element' => 'success']);
        return $this->redirect($logout_redirect);
    }

    /**
     * Authorized rules
     * 
     * @param type $user
     * @return boolean
     */
    private function isAuthorized() {

        /**
         * Use $permitted to insert name of pages that do not require authentication
         */
        $permitted = ['login', 'logout'];
        if (isset($this->request->pass[0]))
            if (in_array($this->request->pass[0], $permitted))
                return TRUE;

        /**
         * Check if the user is authenticated
         */
        if ($this->request->session()->check('User.id'))
            return TRUE;

        /**
         * Default rule
         */
        return FALSE;
    }

}

Pages Controller

Creiamo il PagesController in /plugins/MyArea/src/Controller per la gestione di tutte le pagine statiche che vogliamo creare e proteggere da login utente. Per come definito nell’AppController del plugin, nella funzione isAuthorized(), è possibile definire le pagine che è possibile invocare senza autenticazione, aggiungendo il nome della pagina all’array $permitted.

<?php

namespace MyArea\Controller;

use Cake\Core\Configure;
use Cake\Network\Exception\NotFoundException;
use Cake\View\Exception\MissingTemplateException;

/**
 * Static content controller
 *
 * This controller will render views from MyArea/Template/Pages/
 */
class PagesController extends AppController {

    /**
     * 
     */
    public function initialize() {
        parent::initialize();
        $this->viewBuilder()->layout('MyArea.default');
    }

    /**
     * Displays a view
     *
     * @return void|\Cake\Network\Response
     * @throws \Cake\Network\Exception\NotFoundException When the view file could not
     *   be found or \Cake\View\Exception\MissingTemplateException in debug mode.
     */
    public function display() {
        $path = func_get_args();
        $count = count($path);
        if (!$count) {
            return $this->redirect('/');
        }
        $page = $subpage = null;
        if (!empty($path[0])) {
            $page = $path[0];
        }
        if (!empty($path[1])) {
            $subpage = $path[1];
        }
        $this->set(compact('page', 'subpage'));
        try {
            $this->render(implode('/', $path));
        } catch (MissingTemplateException $e) {
            if (Configure::read('debug')) {
                throw $e;
            }
            throw new NotFoundException();
        }
    }

}

Il PagesController sopra definito esegue il render dei templates creati e salvati in /plugins/MyArea/src/Template/Pages.

Configurazioni

A questo punto si rende necessario modificare le routes in /plugins/MyArea/config/routes.php per la rotazione dei contenuti statici utilizzando il PagesController sopra descritto.

<?php

use Cake\Routing\Router;

/**
 * 
 */
Router::plugin('MyArea', function ($routes) {
    $routes->fallbacks('InflectedRoute');
});

/**
 * Display pages/home by default
 */
Router::scope('/my-area', function ($routes) {
    $routes->connect('/', ['plugin' => 'MyArea', 'controller' => 'Pages', 'action' => 'display', 'home']);
    $routes->connect('/pages/*', ['plugin' => 'MyArea', 'controller' => 'Pages', 'action' => 'display']);
});

Creazione il template di Login

Viene riportato come esempio un template di login, che usa i settaggi ed i file del plugin AOBuilder.

<?php
$this->layout = null;
?>
<html>
    <head>
        <?= $this->Html->charset() ?>
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <title><?= $this->fetch('title') ?></title>
        <?= $this->Html->meta('icon') ?>
        <!-- Builder and app styles -->
        <?= $this->fetch('builder-css') ?>
        <?= $this->Html->css('Builder.signin') ?>
        <?= $this->fetch('css') ?>
    </head>
    <body>
        <div class="container clearfix">            
            <div class="form-signin">
                <?= $this->Flash->render() ?>
                <?= $this->Form->create() ?>
                <h1 class="form-signin-heading text-center"><i class="fa fa-sign-in"></i> <?= __('My Area - Operatore') ?></h1>
                <?= $this->Form->input('email', ['placeholder' => 'Please insert email...']) ?>
                <?= $this->Form->input('password', ['placeholder' => 'Please insert password...']) ?>
                <?= $this->Form->button(__('Sign In'), ['class' => 'btn btn-primary btn-lg btn-block']); ?>
                <?= $this->Form->end() ?>
            </div>
            <?= $this->fetch('content') ?>
        </div>
        <!-- Builder default element -->
        <?= $this->fetch('builder-element') ?>
        <!-- Builder and app scripts -->
        <?= $this->fetch('builder-script') ?>
        <?= $this->Html->script('Builder.signin') ?>
        <?= $this->fetch('script') ?>
    </body>
</html>

Conclusioni

A questo e possibile testare il funzionamento collegandosi all’url (es. http://mio-progetto/my-area) e dovrebbe comparire la schermata di login come nell’immagine di seguito riportata.

My Area operatore in plugin con autenticazione utente
My Area operatore in plugin con autenticazione utente

 

Lascia un commento