I have a custom MVC framework that is in a constant state of evolution. There's a long standing debate with a co-worker how the routing should work. Considering the following directory structure:

/mvc/Controllers/{Public controllers}
/mvc/Controllers/Private/{Controllers requiring valid user}
/mvc/Controllers/CMS/{Controllers requiring valid user and specific roles}

The question is: "Where should the current User's authentication be established: in the Router, when choosing which controller/directory to load, or in each Controller?"

My argument is that when authenticating in the Router, an Error Controller is created instead of the requested Controller, informing you of your mishap; And the directory structure clearly indicates the authentication required.

His argument is that a router should do routing and only routing. Leave it to the Controller to handle it on a case by case basis. This is more modular and allows more flexibility should changes need to be made by the router.

https://codereview.stackexchange.com/questions/26345/php-mvc-custom-routing-mechanism alluded to it, but the topic was of a different nature.

Alternative suggestions would be welcomed as well.

  • 1
    I would agree with your co-worker. Keep the router simple and let it do just routing. You can still have any conventions you want, as per your path structure. in regards to authentication, you can have a parent controller called public_controller/private_controller and implement your authentication in there, depending on which controller you inherit from – bumperbox Oct 9 '13 at 0:37
  • i agree that the preference is the controller. but this assumes that your controllers are clearly broken out by role. whereas if you have controllers in your CMS that are available to different Roles - then consider what pinetree is suggesting. – cartalot Oct 14 '13 at 0:32
  • Is it safe to say that a CMS controller "is a" private controller? – Anthony Rutledge Jul 14 '18 at 5:30

I'd have another component in between that determines the access rights. This component can have a set of mappings of user roles to resources, where controllers and/or actions are the resources. The execution would then go like this:

Router -> Authorization -> Controller

Router: determines which controller and action to execute

Auth: find out which user role is logged in and check
if the user has access to the controller/action,
if not, throw an exception that will get caught by the error handler,
or redirect to login page for guests, or whatever

Controller: if the authorization component didn't complain, do work as usual

If you want to get fancy, you can use php doc comments and reflection to annotate your methods and controllers on the spot instead of having a big list of allowed resources for user roles. Something like:

class Controller {
     * @allow({'admin','user'})
     * @prevent({'guest'})
    public function someAction(){}

The auth component can then read the annotations to determine access rights, instead of having a big list of role->resource mappings

| improve this answer | |

Here is an idea. Technically, maybe both of you are not correct. Maybe the best place for the user's login status and role to be validated in is a concrete instance of a Validator class.

enter image description here

This begs the question, what would a ControllerValidator do in your scenario beyond checking to see if a user is logged in? It could test five fundamental things about an HTTP request to derive if it is a reasonably good request, or probably a malicious hack.

1) Minimum inputs (Were enough HTML controls submitted?)

2) Maximum inputs (Were too many HTML controls submitted?)

3) Correct inputs (Were the HTML controls submitted the right ones?)

4) Correct encoding (Typically, is the encoding UTF-8. But, is it what you want / need?). This check should have its own specialty validator class. Inject an instance of this object into all ControllerValidator objects.

5) Max input sizes (Is any input beyond the acceptable size that should have made it this far?).

So, the answer is that controllers should be checking user and role stuff (because it is relevant to the HTTP request, which is the carrier of the ultimate command). Remember, the definition of an HTTP URL allows for user authentication. So, checking the user as part of the request is a legitimate task, even if you are not using HTTP authentication. Role verification is simply an extension of user authentication.

Now, this UML diagram is just an abstract. However, it does capture the essence of checking the intent of an HTTP request (good faith request, or clearly malicious). Either all the tests of a ControllerValidator pass, or the request is rejected and terminated.

Notice though, going by your requirements,that there would be a difference between a PrivateControllerValidator and a CMSControllerValidator. The former would not be allowed to do role checking. Is that how you really want it to be? It seems to me that a CMSControllerValidator is a PrivateControllerValidator. Are you certain that a PrivateControllerValidator instance will never need to utilize roll checking?

Update: If you have a URL rewriting web server and a Router class, you would only need to do the five tests above for things related to the HTTP request headers, initially. Once you have tested enough to send data to the Router, you can do page specific validations in the model (in PHP, this would include validating $_POST, $_FILES, $_GET, $_COOKIE, or whatever).

| improve this answer | |

I would go with your colleagues suggestion of leaving the Router to do its duty and not take others responsibility. I think its a good programming practice and would help others to understand the same as well.

Now, for the controllers, suggest you go ahead and use inheritance. Have a parent controller, Base_Controller, which can do all the chores which is common across all controllers. From Base, derive Secure_Controller and NonSecure_Controller, which does what the name states.

You can always throw an exception from the controller, which can well be handled by hooks to display the view gracefully.

| improve this answer | |

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.