Don't you speak portuguese? Translate this site with Google Translator

Pensamento do Dia

O mundo exterior poderá fazer - te sofrer, mas só tu poderás avinagrar - te a ti mesmo. (Georges Bernanos)

13.7 - Aplicativo com uma área restrita

13.7 - Aplicativo com uma área restrita/administrativa

 

Daqui a vinte anos, você não vai se arrepender das coisas que fez, mas das que deixou de fazer. Por isso, veleje longe do seu porto seguro. Pegue os ventos. Explore. Sonhe. Descubra. (Mark Twain)


Criar um aplicativo com uma área restrita/administrativa para o CakePHP 3

Adaptado do vídeo abaixo:

https://www.youtube.com/watch?v=eWu6r5aO1Jc&index=6&list=PL83b6tjXNFHe-OVRROawG3YdIN_-Kbc6j 

Criar um aplicativo chamado admin_area.

Banco admin_area

CREATE TABLE `customers` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `name` varchar(55) COLLATE utf8mb4_unicode_ci NOT NULL,
  `birthday` date DEFAULT NULL,
  `phone` varchar(14) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
  `observation` text COLLATE utf8mb4_unicode_ci,
  `created` datetime DEFAULT NULL,
  `modified` datetime DEFAULT NULL,
  PRIMARY KEY (`id`)
);

INSERT INTO `customers` (`id`, `name`, `birthday`, `phone`, `observation`, `created`, `modified`) VALUES
(1,	'Brennan G. Wilcox',	'2016-04-15',	'(851) 190-1314',	'ante. Maecenas mi felis, adipiscing',	NULL,	NULL),
(2,	'Chase Summers',	'2016-08-27',	'(846) 297-4733',	'Sed molestie. Sed id risus',	NULL,	NULL),
(3,	'Sonia L. Mckay',	'2015-12-02',	'(131) 453-1690',	'fermentum vel, mauris. Integer sem',	NULL,	NULL),
(4,	'Isadora L. Bowers',	'2015-10-24',	'(939) 798-4625',	'consequat, lectus sit amet luctus',	NULL,	NULL),
(5,	'Sophia Cochran',	'2017-06-15',	'(811) 687-0491',	'Aliquam tincidunt, nunc ac mattis',	NULL,	NULL),
(6,	'Maxwell T. Burton',	'2016-01-12',	'(147) 962-3265',	'at arcu. Vestibulum ante ipsum',	NULL,	NULL),
(7,	'Desiree Y. Henry',	'2017-07-21',	'(148) 711-3747',	'vitae dolor. Donec fringilla. Donec',	NULL,	NULL),
(8,	'Asher Key',	'2015-11-07',	'(355) 668-5871',	'a, aliquet vel, vulputate eu,',	NULL,	NULL),
(9,	'Tyler Castro',	'2016-08-31',	'(567) 793-5061',	'nec tempus mauris erat eget',	NULL,	NULL),
(10,	'Rudyard Weber',	'2015-10-26',	'(445) 457-4552',	'Morbi vehicula. Pellentesque tincidunt tempus',	NULL,	NULL),
(11,	'Allen Austin',	'2016-04-15',	'(758) 867-2179',	'ipsum. Phasellus vitae mauris sit',	NULL,	NULL),
(12,	'Octavius Cooper',	'2015-10-13',	'(101) 625-3985',	'ipsum non arcu. Vivamus sit',	NULL,	NULL),
(13,	'Dustin M. Oneill',	'2016-04-24',	'(276) 722-0976',	'magnis dis parturient montes, nascetur',	NULL,	NULL),
(14,	'Giacomo K. Horton',	'2016-07-03',	'(773) 532-7468',	'neque. Sed eget lacus. Mauris',	NULL,	NULL),
(15,	'Signe T. Weaver',	'2016-06-17',	'(210) 895-3664',	'dui nec urna suscipit nonummy.',	NULL,	NULL),
(16,	'Avram O. Delaney',	'2016-08-05',	'(609) 552-7572',	'Donec luctus aliquet odio. Etiam',	NULL,	NULL),
(17,	'Cara Parker',	'2016-07-24',	'(854) 169-4797',	'ornare lectus justo eu arcu.',	NULL,	NULL),
(18,	'Chelsea Mcclain',	'2016-08-06',	'(363) 636-1560',	'mollis lectus pede et risus.',	NULL,	NULL),
(19,	'Wesley Garner',	'2016-06-11',	'(578) 231-2389',	'Fusce feugiat. Lorem ipsum dolor',	NULL,	NULL),
(20,	'Irene P. Arnold',	'2016-02-12',	'(253) 631-9830',	'accumsan laoreet ipsum. Curabitur consequat,',	NULL,	NULL),
(21,	'Austin S. Wall',	'2016-01-21',	'(225) 694-9511',	'Sed eget lacus. Mauris non',	NULL,	NULL);

DROP TABLE IF EXISTS `groups`;
CREATE TABLE `groups` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `name` varchar(100) COLLATE utf8mb4_unicode_ci NOT NULL,
  `created` datetime DEFAULT NULL,
  `modified` datetime DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;

INSERT INTO `groups` (`id`, `name`, `created`, `modified`) VALUES
(1,	'Supers',	'2016-08-30 21:15:01',	'2016-08-30 21:15:01'),
(2,	'Admins',	'2016-08-30 21:15:01',	'2016-08-30 21:15:01'),
(3,	'Managers',	'2016-08-30 21:15:01',	'2016-08-30 21:15:01'),
(4,	'Users',	'2016-08-30 21:15:01',	'2016-08-30 21:15:01');

CREATE TABLE `users` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `username` varchar(55) COLLATE utf8mb4_unicode_ci NOT NULL,
  `password` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
  `group_id` int(11) NOT NULL,
  `created` datetime DEFAULT NULL,
  `modified` datetime DEFAULT NULL,
  PRIMARY KEY (`id`),
  UNIQUE KEY `username` (`username`),
  KEY `group_id` (`group_id`),
  CONSTRAINT `users_ibfk_1` FOREIGN KEY (`group_id`) REFERENCES `groups` (`id`) ON DELETE CASCADE ON UPDATE CASCADE
);


INSERT INTO `users` (`id`, `username`, `password`, `group_id`, `created`, `modified`) VALUES
(1,	'super',	'$2y$10$zlIAsTubGNqvhn3pS76jN.QeF6msHGYGcTIJ7KgzS757vDsioA3xa',	1,	'2016-09-15 15:57:16',	'2019-06-12 19:03:49'),
(2, 'admin', '$2y$10$f26.qAgF5Jnl7b3zIdRlQuKTBkrxM2d1xtrLlIee0EKULfSKgejqm', 2, '2016-09-15 15:57:16',	'2019-06-12 19:03:49'),
(3, 'manager', '$2y$10$fx0/o/XU3WPO5.nnP7cnCeSuFsFjxCMkk72DciLqABzHp50cOFnre', 3, '2016-09-15 15:57:16',	'2019-06-12 19:03:49');

Três usuários usando o hash bcrypt:

Login - super
Senha - abc123S@

Login - admin
Senha - abc123A@

Login - manager
Senha - abc123M@


Configurar o banco em config/app.php

Configurar a rota default em config/routes.php para Customers/index

Gerar o código com o bake

bin/cake bake all customers
bin/cake bake model Users
bin/cake bake model Groups

bin/cake bake controller Users --prefix admin
bin/cake bake template Users --prefix admin

bin/cake bake controller Groups --prefix admin
bin/cake bake template Groups --prefix admin

Adicionar ao webroot/css/style.css

.emlinha{
    display: inline;
    padding: 10px;
    text-align: center;
}
li a{
    color: #fff;
}


Crie um arquivo src/Template/Element/menu.ctp contendo:

<div>
    <ul>
        <li class="emlinha"><?= $this->Html->link(__('Customers'), '/customers/index') ?></li>
        <li class="emlinha"><?= $this->Html->link(__('Groups'), '/admin/groups/index') ?></li>
        <li class="emlinha"><?= $this->Html->link(__('Users'), '/admin/users/index') ?></li>
        <li class="emlinha"><?= $this->Html->link(__('Login'), '/admin/users/login') ?></li>
        <li class="emlinha"><?= $this->Html->link(__('Sair'), '/admin/users/logout') ?></li>
    </ul>
</div>


Edite o src/Template/Layout/default.ctp

E sobrescreva o código entre
<nav>
e
</nav>

Com este abaixo, logo abaixo de <body>:

    <nav class="top-bar expanded" data-topbar role="navigation">
<?php
    if($loguser){
?>
        <h2 align="center"><?= $titulo ?><?php echo $this->element('menu') ?></h2>
        <div align="center"><?= $this->fetch('title') ?></div>
        <div class="right">Logado como: <strong><?=__($loguser) ?></strong>&nbsp;&nbsp;&nbsp;</div> 
<?php
    }else{
        echo '<h3 class="titulo" align="center">Acesso ao Sistema</h3>';
    }
?>
    </nav>

Somente customers poderá ser acessado livremente, groups e users serão restritos

Criar a rota admin em config/routes.php

Router::prefix('admin', function ($routes) {
    $routes->fallbacks(DashedRoute::class);
});

Obs.:
Veja que na pasta src/Controller/Admin os controllers tem o namespace para Admin

namespace App\Controller\Admin;

Adicionar os actions login e logout para src/Controller/Admin/UsersController.php:

    public function login()
    {
        if ($this->request->is('post')) {
            $user = $this->Auth->identify();
            if ($user) {
                $this->Auth->setUser($user);
                return $this->redirect($this->Auth->redirectUrl());
            }
            $this->Flash->error(__('Usuário ou senha ínvalido, tente novamente'));
        }
    }

    public function logout()
    {
        return $this->redirect($this->Auth->logout());
    }

Adicionar o login.ctp para src/Template/Admin/Users:

<div class="users form">
<?= $this->Flash->render('auth') ?>
<?= $this->Form->create() ?>
    <fieldset>
        <legend><?= __('Por favor informe seu usuário e senha') ?></legend>
        <?= $this->Form->input('username', ['autofocus' => true]) ?>
        <?= $this->Form->input('password') ?>
    </fieldset>
<?= $this->Form->button(__('Login')); ?>
<?= $this->Form->end() ?>
</div>

Para acessar customers:
http://localhost/admin_area

Ainda não podemos acessar users e groups e o acesso integral de customers é permitido.

Implementar a Auenticação
Para controlar o acesso ao aplicativo

Adicionar suporte para bcrypt ao login:

Adicionar ao início do src/Model/Entity/User.php:

use Cake\Auth\DefaultPasswordHasher;

Ao final:

    protected function _setPassword($password)
    {
        if (strlen($password) > 0) {
            return (new DefaultPasswordHasher)->hash($password);
        }
    }


Implementar a autenticação

Adicione ao initialize() do AppController, logo abaixo do carregamento do componente Flash::

        $this->loadComponent('Auth', [
            'authorize' => ['Controller'],
            'logoutRedirect' => [
                'controller' => 'Users',
                'action' => 'login',
                'home'
            ],
            //'unauthorizedRedirect' => $this->referer()
		    'unauthorizedRedirect' => [
		    'controller' => 'Users',
		    'action' => 'login',
		    'prefix' => false
	       ],
	        'authError' => 'Você não tem permissão para acessar esta área!',
                  'flash' => [
                      'element' => 'error'
           ]
        ]);

        $this->set('titulo', 'Aplicativo para área Admin Restrita');	   
		$user = $this->request->getSession()->read('Auth.User');
		$loguser = $user['username'];
		$this->set('loguser',$loguser);

Restringindo acesso com o Authorization através do callback isAuthorized().

Agora que os usuários podem se conectar, nós vamos querer limitar os bookmarks que podem ver para aqueles que fizeram. Nós vamos fazer isso usando um adaptador de ‘autorização’. Sendo os nossos requisitos bastante simples, podemos escrever um código em nossa BookmarksController. Mas antes de fazer isso, vamos querer dizer ao AuthComponent como nossa aplicação vai autorizar ações. Em seu AppController adicione o seguinte:

Ao final, abaixo do initialize():

    public function isAuthorized($user = null)
    {
        // Any registered user can access public functions
        if (!$this->request->getParam('prefix')) {
            return true;
        }

        // Only admins can access admin functions
        if ($this->request->getParam('prefix') === 'admin') {
            return (bool)($user['group_id'] === 1 || $user['group_id'] === 2);
        }

        // Default deny
        return false;
    }

Observe que os usuários dos grupos 1 (super) e 2 (admin), têm direito a acessar tudo. Aqui podemos customizar mais este comportamento. Mas caso precise de algo mais flexível recomendo o uso de um dos plugins sugeridos ao final.

Abaixo do isAuthorized() adicione:

    public function beforeFilter(Event $event)
    {
        $this->Auth->allow(['customers'=>'index', 'customers'=>'view']);
        // Negar acesso para todos os actions do controller Customers para usuários comuns
        //$this->Auth->deny('customers');
    }

Também podemos negar acesso:
Obs.: Não se aplica para os usuários dos grupos 1 e 2, que podem tudo e já foram previamente autorizados.

As regras abaixo aplicam-se somente para os usuários dos grupos 3 em diante.

// Negar acesso a qualquer parte do aplicativo:
$this->Auth->deny();

// Negar apenas para o action add
$this->Auth->deny('add');

// Negar para um grupo de actions
$this->Auth->deny(['add', 'edit']);

// Negar para um controller
$this->Auth->deny('customers');

Observe que são públicos apenas os actions index e view do controller Customers.


Isto indica a que estamos garantindo acesso completo em todos os controllers:
        $this->Auth->allow(['index', 'view']);

Podemos controlar o acesso por este método acima.

Para que um controller permita o acesso completo adicione ao seu início:

use Cake\Event\Event;

public function beforeFilter(Event $event)
{
    $this->Auth->allow();
}


Testar o acesso para áreas restritas:

http://localhost/admin_area/admin/users/add

ou

http://localhost/admin_area/customers/add

Veja que ele pede o controller Users no src. Então lhe daremos:

E crie o src/Controller/UsersController.php com apenas:

<?php
namespace App\Controller;
use App\Controller\AppController;

class UsersController extends AppController
{

    public function login()
    {
        return $this->redirect('/admin/users/login');
    }
}

Agora experimente acessar

http://localhost/admin_area/customers/add

Será redirecionado para o login.

Acesse com os logins citados no início. Veja que super e admin têm acesso completo e manager tem acesso somente a customers.

Sugestão de Plugins

Para um maior controle e mais recursos use um dos plugins abaixo:

Acl da equipe do Cake, via terminal/prompt:
https://github.com/cakephp/acl 
Um ótimo exemplo de uso
https://github.com/mattmemmesheimer/cakephp-3-acl-example

Plugim com administração via interface web
https://github.com/ribafs/admin-br

Mais detalhes em:


https://book.cakephp.org/3.0/en/controllers/components/authentication.html 
https://book.cakephp.org/3.0/en/tutorials-and-examples/cms/authentication.html 
https://book.cakephp.org/3.0/pt/tutorials-and-examples/blog-auth-example/auth.html 

Comments fornecido por CComment

Novo Testamento

Ora, os principais dos sacerdotes e os fariseus tinham dado ordem para que, se alguém soubesse onde ele estava, o denunciasse, para o prenderem.
(Jo, 11:57)

Rotas no Mapa do Google

© 2015 Ribamar FS. All Rights Reserved. Designed By JoomShaper