How to treat the logged-in user in an MVC system?

3

I'm creating a MVC framework a couple of weeks (developing it and using it at the same time), I got to the part of creating the authentication system.

So I would like to create a way for me to configure which routes the user can access when he is not authenticated in the applications that are created on top of Framework , without having to check each method if the user is authenticated or not.

I already have the code snippet where I call the controller to get an idea of what I'm developing.

class Application {

   public static function RUN() {

      $request = New Request();

      $class = '\Controller\'.$request->class.'Controller';

      if (!empty($request->post['mvc:model'])){
         $model = '\Model\' . array_remove($request->post, 'mvc:model') . 'Model';
         $param = New $model($request->post);
      } else if (!empty($request->lost))
         $param = $request->lost;
      else {
         $param = NULL;
      }

      try {
         $app = New $class();
         $action = $request->action;

         if (!is_null($param) && !empty($param))
            $app->$action($param);
         else 
            $app->$action();

      } catch (SystemException $e) {
         if ( strpos(Exceptions::E_FILENOTFOUND.'|'.Exceptions::E_CLASSNOTEXIST, $e->getCode()) !== FALSE){
            $app = New \Controller\FileNotFound();
            $app->file = $class;
            $app->index();
         } else {
            $app = New \Controller\ErrorController();
            $app->message = $e->getMessage();
            $app->error = $class;
            $app->index();
         }

      }


      $app->output();
   }
}

The point is that I'm not in the mood to call a method to verify that the user is authenticated to every method I create. So I would need a way to set up in Application::Run() to check if the user has permission to access the requested route.

Is there any default in MVC templates?

    
asked by anonymous 14.11.2015 / 15:40

3 answers

2

I suggest implementing as a plugin or hook.

I've developed PHP frameworks for many years. In the penultimate version, built in 2009, I implemented user login / login permissions within the Core of the framework because I fell into this same dilemma. As I was in a hurry to see results, I built the login and authentication feature inside Core .

Later, with years of use and several cases, I realized that it would be better separated, as if it were a plugin, even because implementing such a feature in Core itself, was already hurting the concept > MVC . The logic is that login / authentication belongs to the Model .

In a clearer example, I had to build a small site several times where I did not need a login or user registration. So that native feature of user permissions became completely useless.

Let's see a practical example of how to implement logic with plugins / hooks?

class Core
{
    public function MainProcesses()
    {
        /**
        Aqui excecuta as rotinas comuns e nativas do framework, normalmente
        */

        /**
        Em algum ponto, vc deve decidir onde carregar os plugins/hooks
        */
        $this->MainProcessesHooks();
    }
    private function MainProcessesHooks()
    {
        /**
        Uma rotina que vai procurar por hooks registrados e configurados adequadamente para ser executado no método MainProcesses()

        Exemplo, aqui vc pode ter um plugin para verificar se um usuário está autenticado e setar suas permissões.

        A ideia aqui é fazer leitura de algum arquivo php, json, txt, enfim. Algo que seja rápido de carregar e processar. 
        Nesse arquivo, conterá informações sobre quais os hooks/plugins devem ser carregados aqui.
        */
         $p = '/path/absolute/of/app/Plugins/Core/MainProcesses.php';
         if (file_exists($p))
             include $p;
    }
}

Within "MainProcesses.php"

<?php
include '/path/absolute/of/app/Model/Users/Plugins/Permissions.php';
include '/path/absolute/of/app/Model/OutroModelQualquer/Plugins/qualquer-outra-coisa.php';

That way, whenever you need to add functionality in Core and native functions of the framework, specific to business models, you can do without appealing to gambiarras and destroying the MVC concept of the application.

The idea of a framework is that you do not need to tinker with your native routines even when you need to implement a very specific feature of a business model.

Roughly, this logic with hooks / plugins loading, is an elegant gambiarra. What will make the process more elegant is still what OOP concept you will be able to implement under this scheme. At this point we will start talking about programming paradigms and very complex things. Try not to think too much about things that complicate. Keep the standard KISS (Keep It Simple, Stupid).

* The above examples are purely didactic. Nomenclatures, file system structures, semantics, code pattern, method visibility and the logic itself are merely illustrative.

Security

In this scheme of running plugins / hooks, you should be aware of security. It should be fairly restrictive and organized about what a plugin can perform.

So, never allow an ordinary (unauthorized) user to be able to include a plugin, since it obviously opens up loopholes for malicious people or addictive actions.

    
28.12.2015 / 06:19
1

I have a file next to the application folders that is called appsconfig.php , in this file I added the following data:

'authentication' => [
   'controller' => 'Auth',
   'action' => 'logged',
   'redirect' => [
      'controller' => 'Login',
      'action' => 'index'
   ],
   'routes' => [ // Routes allowed
      'login' => ['index', 'authenticate']
   ]
]

There, I put the controller and action responsible for verifying the Auth::logged() authentication that returns a Boolean (this method is implemented by the application and not by the framework. the data for the redirect redirect and the allowed routes when it is not authenticated.

I created the checkPermission method on the main Controller:

private function checkPermission() {

    if ($this->config->app->authentication) {

        $authentication = $this->config->app->authentication;

        $auth = "\Controller\{$authentication->controller}Controller";
        $check = $authentication->action;

        if (!($auth::$check())){
            if (!property_exists($authentication->routes, $this->request->controller)){
                $this->request->redirect(Route::href("{$authentication->redirect->controller}/{$authentication->redirect->action}"));
            }
            else if (
                property_exists($authentication->routes, $this->request->controller) && 
                !in_array($this->request->action, (array)$authentication->routes->{$this->request->controller})
            ){
                $this->request->redirect(Route::href("{$authentication->redirect->controller}/{$authentication->redirect->action}"));
            }
        }
    }

    return TRUE;
}

I know there's a better way to do this, but for now you're solving it. I'll leave the question open, waiting for other possible solutions.

    
20.11.2015 / 10:54
0

In this case there is a table in the database (permissions) where the access permissions of the profiles are (Admin, Editor, Common), in this table the addresses that the user can be transited are placed .. when the user is logged the permissions are placed in $ _SESSION and whenever it is to access any address this routine defines whether the user has permission or not. In the second part, if there is no permission in the session, it checks the visitor's permissions ( $ routes = Routes :: guest () )

   if(isset($_SESSION['permissions']) && sizeof($_SESSION['permissions']) > 0){
        foreach ($_SESSION['permissions'] as $i => $value) {
            if(@preg_match($value['route'], $url) || $value['route'] == $url){
                return $value['allow'];
            }
        }
    }
    $routes = Routes::guest();
    foreach ($routes as $route => $array) {
        if(@preg_match($route, $url) || $route == $url){
            return $routes[$route];
        }
    }
    return false;

I just wanted to give you an idea of how it can be done, you need to make some adjustments and make it as modular as possible, in this strategy I have three working files (index.php? url, Router :: mapping (), Permissions :: check ());

    
16.11.2015 / 15:10