سؤال

I'm building a four layer simple architecture with the following layers:

  • Controller
  • Actions and Events
  • Repositories, that returns single entities or collections
  • Models, where the SQL code resides

Say the ADMIN wants to see the profile of a specific product. In this case the code flow would go through the Controller layer, then the Action layer (LoadProductProfileAction), then the repository layer, finally the model and then all the way back. We don't check permissions since admin roles can see all product profiles.

But now SUPPLIER#1 wants to do the same action. I need to check whether the product belongs to said supplier (aka supplier has permission to access this product profile), and since loading the entire profile takes resources, it makes sense to check permissions beforehand, and to me it makes sense to do it in the Controller layer before going any deeper. However, I still need to access the Database to check permissions.

Ideas so far:

  • Have quick specific static methods in the Repositories that can conveniently be called from the Controller to make quick permission checks.
  • Have a new class that interacts directly with DB for permissions, and call it from Controller. I think this method is suggested here: What is a good approach for permissions of this application's users?

What's a simple way to do it?

Thanks

هل كانت مفيدة؟

المحلول

and since loading the entire profile takes resources, it makes sense to check permissions beforehand

I'm unsure if you mean to load the product information before checking the user's permissions. If that is the case, then you are right that you are potentially wasting effort (by fetching data that you end up not needing) and you should do the permission check before the actual data check.

Always try to bar unwanted requests (invalid requests, unauthenticated users, unauthorized users) at the earliest possible point. Doing it later just means that you're wasting effort, which is going to cost performance for a sufficiently saturated service.

to me it makes sense to do it in the Controller layer before going any deeper

Phrasing is very important here. While the decision to check the permissions may originate from the Controller, the actual check itself should still follow the BLL/DAL route just like any other call.

In essence, you will be "digging deep" twice: once for the permissions check, once for the actual query.

to me it makes sense to do it in the Controller layer before going any deeper

Suppose your application is succesful and you start distributing it on multiple platforms (WPF, Android, iOS, hosted website, ...). It makes sense that you want the same permissions checks to be executed in all cases.

But if you make a non-web app (i.e. an application that does not call the web API), then this non-web app will rely directly on the BLL layer and it won't care about the Web project. This means that the permissions checks (which would be implemented in the Web project's controllers) would be ignored.

This is a potential source of problems. However, if you determine that the web application is going to be your single source of information (e.g. any application would always call this API), then it's a non-issue.

Regardless, I disagree that this logic belongs in the controller. The web project's responsibility is to route web request between the external client and your business logic, its reponsibility is not to decide the application's behavior. By pure definition, permissions checks are business logic and business logic belongs in the BLL.

Note that redirecting unauthenticated users is part of the web application's responsibility (if you wish to enforce authentication), but the question focuses on authorization, not authentication.

There is also nothing wrong with having additional permissions checks on the controller level when appropriate, e.g. if access to the REST API is different from access to the MVC controller methods. But this is again not the issue at hand in your question so I'm not elaborating on that point.

Have quick specific static methods in the Repositories that can conveniently be called from the Controller to make quick permission checks.

No, the web project should not be interacting with the DAL directly. The web project interacts with the BLL, which in turn interacts with the DAL.

Supposing your database data is quite convoluted and requires complex calculations to figure out the permissions of a user, your proposed solution would mean that either the DAL or the web project needs to perform these calculations, and that simply doesn't fit with the responsibilities of the DAL (data storage) or web project (routing web requests).

The BLL handles the computation logic, so the permissions checks should be handled by the BLL, even if no computation is currently required. It's a matter of principle. Should the permission logic become more complex in the future, having the existing logic in the BLL means it's already in the appropriate location, which minimizes the impact of the change.

Have a new class that interacts directly with DB for permissions, and call it from Controller. I think this method is suggested here: O good approach for permissions of an application' users

From the link, it seems you've missed the fact that the OP's permission checks originate from the BLL (as they are handling business objects in the example code).

The DAL's only job is to present the data from the database. At best, it entails a mapping to a DTO.

However, the decision logic, i.e. "does this data (from the DB) mean that the user has the appropriate permissions?" is business logic and not part of the DAL's responsibilities.

What's a simple way to do it?

There are some general tips here:

  • Don't optimize prematurely. First build it and see if there are notable performance issues before deciding to tackle the performance issues.
  • You can use caching to reduce the amount of permissions checks that hit the DB. This has pros and cons:
    • Pro less DB hits, better overall performance (except the first time when the cache is empty)
    • Con If a user's permissions change, your cached response needs to be updated. If you don't manually clear the cache, the user is going to have to wait until the cache expires if they want to use their new permissions.
    • Con Caching seems simple at first but often ends up as a srouce for bugs and frustrated developers. A bad cache can be a bigger issue than not having a cache. Beware.
  • If you use encrypted bearer tokens (JWT, OAuth), it's possible to store the user's permissions into the encrypted token. This means that you don't need to actually keep hitting the DB. During login, the DB is hit, and the user is given a token that they can use (possibly for a limited time). Subsequent requests will require this token, and your application can then read the user's permissions directly from the token, it doesn't need to hit the DB since it already received the information. However:
    • This authentication model requires significant changes if your current setup is basic or Windows authentication
    • Obviously only do this for encrypted tokens or else end user can add whatever permissions they want to the token.
    • If the user's permissions change, you must invalidate the token and refresh it (whether automatically or by forcing the user to login again)

نصائح أخرى

Before you make decisions about how expensive it is to load a profile, you might want to time it first to see if you really take a performance penalty by doing so.

You asked for a simple way? The simple way is to get the permissions from the database when the user makes a request. If you're worried about performance, and your application has a notion of "sessions," then consider caching the permissions for each session.

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى softwareengineering.stackexchange
scroll top