PHP - Where to implement Session Logic in MVC?

Accessing my application (in order)

  • White ip address
    • redirect to 404 to invalid ip
  • Check if the last lesson was> 2 hours ago
    • redirect to login page and expiration
  • Check if the user is logged in by viewing the user details in $ _SESSION
    • redirect to login page if not valid

Index.php

(note that this is very similar to this question ):

/**
 * Set Timezone
 */
date_default_timezone_set('Zulu');

/**
 * Include globals and config files
 */
require_once('env.php');

/*
 * Closure for providing lazy initialization of DB connection
 */
$db = new Database();

/* 
 * Creates basic structures, which will be 
 * used for interaction with model layer.
 */
$serviceFactory = new ServiceFactory(new RepositoryFactory($db), new EntityFactory);
$serviceFactory->setDefaultNamespace('\\MyApp\\Service');

$request = new Request();
$session = new Session();
$session->start();
$router = new Router($request, $session);

/*
 * Whitelist IP addresses
 */
if (!$session->isValidIp()) {
    $router->import($whitelist_config);

/*
 * Check if Session is expired or invalid
 */
} elseif (!$session->isValid()) {
    $router->import($session_config);

/*
 * Check if user is logged in
 */
} elseif (!$session->loggedIn()) {
    $router->import($login_config);
} else {
    $router->import($routes_config);
}

/*
 * Find matched route, or throw 400 header.
 *
 * If matched route, add resource name 
 * and action to the request object.
 */
$router->route();

/* 
 * Initialization of View 
 */
$class = '\\MyApp\\View\\' . $request->getResourceName();
$view = new $class($serviceFactory);

/*
 * Initialization of Controller
 */
$class = '\\MyApp\\Controller\\' . $request->getResourceName();
$controller = new $class($serviceFactory, $view);

/*
 * Execute the necessary command on the controller
 */
$command = $request->getCommand();
$controller->{$command}($request);

/*
 * Produces the response
 */
echo $view->render();

      

The function $router->import()

takes a json file with route configuration and creates routes (didn't decide if I would continue to do this). My router is a modified version of Klein .

My question

Is this the correct implementation of session data validation?

I would rather check that the user data in the session can be found in the database, but I would need to use the Service to do this, and only the controller (?) Should be able to access the services. I would not know which controller will send the user as the route config will change if the user is logged in.

For example, if someone tried to navigate to www.myapp.com/orders/123, I would send them to the Orders controller if they are logged in, or the session controller (to display the login page) if they are not.

I read the ACL implementation from this question. But, if I'm not mistaken, this is about access control for users who are already logged in, not for users who are not logged in. If it is not, can someone explain how I would implement my ACL to verify this?

I really appreciate any help as looking for this answer gave me really mixed solutions and most of them don't like or seem like clean solutions. As a session manager, which is basically what I do, but pretend not to. = /

UPDATED index.php (my solution)

/**
 * Set Timezone
 */
date_default_timezone_set('Zulu');

/**
 * Include globals and config files
 */
require_once('env.php');

/*
 * Closure for providing lazy initialization of DB connection
 */
$db = new Database();

/* 
 * Creates basic structures, which will be 
 * used for interaction with model layer.
 */
$serviceFactory = new ServiceFactory(new MapperFactory($db), new DomainFactory);
$serviceFactory->setDefaultNamespace('\\MyApp\\Service');

include CONFIG_PATH.'routes.php';

$request = new Request();
$router = new Router($routes,$request);

/*
 * Find matched route.
 *
 * If matched route, add resource name 
 * and command to the request object.
 */
$router->route();

$session = $serviceFactory->create('Session');

/*
 * Whitelist Ip address, check if user is 
 * logged in and session hasn't expired.
 */
$session->authenticate();

/*
 * Access Control List
 */
include CONFIG_PATH.'acl_settings.php';

$aclFactory = new AclFactory($roles,$resources,$rules);
$acl = $aclFactory->build();

$user = $session->currentUser();
$role = $user->role();
$resource = $request->getResourceName();
$command = $request->getCommand();

// User trying to access unauthorized page
if (!$acl->isAllowed($role, $resource, $command) {
    $request->setResourceName('Session');
    $request->setCommand('index');
    if ($role === 'blocked') {
        $request->setResourceName('Error');
    }
}

/* 
 * Initialization of View 
 */
$class = '\\MyApp\\View\\' . $request->getResourceName();
$view = new $class($serviceFactory, $acl);

/*
 * Initialization of Controller
 */
$class = '\\MyApp\\Controller\\' . $request->getResourceName();
$controller = new $class($serviceFactory, $view, $acl);

/*
 * Execute the necessary command on the controller
 */
$command = $request->getCommand();
$controller->{$command}($request);

/*
 * Produces the response
 */
$view->{$command}
$view->render();

      

I start a session and authorize the user in the session model. The currentUser session will be "guest" if it is not logged in, or "blocked" if its IP address is not in the whitelist. I would like to implement a Controller wrapper as teresko's previous ACL post suggested, but I needed something to redirect the user. I send them to my home page (session index #) if they try to access a page they are not allowed to, or the Error # index if they are blocked. The Session # index allows the View to decide whether to show the home page for the registered user or the login page if they are not logged in (by checking the user's role). It might not be the best solution, but it doesn't seem too scary.

+3


source to share


1 answer


Single responsibility

Your session object is doing too many things. Sessions are more or less just for storing requests. The session does not have to execute authentication logic. You are storing the identity for the logged in user in the session, but the actual verification, logging in / out must be done in the authentication service.

Route management

Importing different routes based on the user authentication state won't scale well and will be painful to debug later when you have a lot more routes. It would be better to define all of your routes in one place and use an authenticator to redirect if they are not authorized. I'm not very familiar with this router, but looking at the documentation, you should have something like



$router->respond(function ($request, $response, $service, $app) { 
    $app->register('auth', function() {
        return new AuthService();
    }
});

      

Then on routes you need to be logged in so you can do something like

$router->respond('GET', '/resource', function ($request, $response, $service, $app) {
    if( ! $app->auth->authenticate() )
        return $response->redirect('/login', 401);
    // ...
});

      

+3


source







All Articles