7
gitpush
3y

Domain Drive Design question:

I am working on a simple case to teach how to apply DDD, my case is as follows:
Simple forum with Author, Moderator and Users.

I am using Dotnet core for this. I am not sure how and where I should implement authorization:
1. Author can edit his posts only
2. Moderator edits any post

In dotnet core, we handle roles, policies in the api layer, and its per endpoint, I have an identity layer which handles accounts, registering roles and policies in database.

But I'm not sure if I should or how to handle authorization based on permissions in application layer.

Comments
  • 1
    Throw an exception if role != Admin when editing something
  • 3
    *grabs popcorn*
  • 2
    If you're already using claims principal/claims identity just use that. It's really easy.
  • 2
    +1

    I have heard of this DDD thing for a while and coincidentally just started through Eric Evans DDD book. This also crossed my mind as I read Robert Martin's Clean Architecture the other day and in it he talks about how the web is a detail.

    As you say though authorisation happens at that detail level in asp .net core... Well authorisation is not just a detail.
  • 1
    Also, not that helpful, but you can authorise in the application level by DIing IAuthorizationService.

    Personally I wrote a small extension around this service to easily hook it into my own permission claim based authorisation pipeline, which ultimately is just a wrapper around the IAuthorizationService to hide away the details of my extension.
  • 0
    @alexbrooklyn The thing is, details of roles and policies are in identity, and since I also use Entity Framework core, this means i use IdentityContext so if I'm going to deal with it from Application layer then I'll be depending on EF core for this one
  • 0
    @spongessuck So you are suggesting that I pass claims down to Application layer and let it handle decision making?
  • 0
    @craig939393 True web is a detail, but in web I use UserManager, RoleManager and adding them to Application layer means that layer is depended on some sort of Framework to work, and shouldn't it be not tightly coupled to a Framework?
  • 1
    @gitpush whatever anyone says is just an opinion. However my opinion is that a framework or library is a tool. There is risk in strongly coupling yourself to it on your side, and no risk on theirs. It shouldn't impact your system design.

    I use of core in my system too, although I am not a big fan. I don't use their identity stuff though personally.
  • 1
    @gitpush there are some things I think it's ok to depend on. Things that are very unlikely to change or cause breakage - low risk.

    Extending the .net core framework is acceptable to me, we are pretty much married to it anyway.

    I still wrapped it in an abstraction though, so truthfully I can change it any time I want.
  • 0
    @craig939393 What about passing claims with my calls, for example Moderator requests to edit a post, I do this: PostService.EditPost(post, userClaims);

    But this means that every call I do requires me to pass down user claims
  • 0
    @craig939393 Yes it is unlikely to change since I'm using Dotnet Core and if anything is going to change is from an API to a windows forms application, and since EF is part of dotnet and managed by Microsoft there is almost zero risk in relying on it as it will less likely to go away anytime soon
  • 0
    @gitpush I can't tell you very much about how the .net team implemented IAuthorizationService service or the rest of the frameworks pipeline, but if you take a look at their policy based authorisation, particularly their requirement and requirement handler documentation, you will see access to a request context. On that you can get hold of the user's claims. This is without passing anything about a user around as an argument.

    Even if not using that specifically you could also write a separate abstraction for this:
    https://docs.microsoft.com/en-us/...

    The concrete of the abstraction would use it to easily get the user's claims and perhaps return true false they have permission. For other GUI than web you could override that default concrete at the DI config.

    Hope that helps.
  • 1
    If you're using jwt the claims should be baked in to the hydrated user principal; you don't need to pass them up each time.

    You should be able to use AuthorizeAttribute in conjunction with auth policies to verify claims for particular actions.
  • 1
    @gitpush there is risk on relying directly on ef, and if memory serves user manager directly used is direct reliance on ef.

    I work on a project where we started on ef core 1. 15 devs wrote code with poor abstraction for 1.5 years.

    Ef core 3.0 comes out and has breaking changes. They're related to in memory query processing that must now be explicit.

    Well we are fucked. The company will not give time dedicated to fix such a cluster fuck. There are no tests or separation of concerns that compound the risk greatly. The code still compiles so we can't even determine which areas break without manual testing.

    My point is my colleagues never expected a very popular tool to introduce breaking changes. They were wrong, it's just extremely unlikely. Made likely when practicing this approach with every tool.

    Now we are locked at ef core 2.0 and we keep just writing new features for a long time now, completely coupled to the tool, digging a deeper grave.

    This is an extreme case of course.
  • 0
    @spongessuck Thanks man and that is what I'm doing at the API level, but my question was when I want to update a Post, I need to check if

    1. User is owner of post

    2. Or user is a moderator

    Else I need to fail the command, my question was how do I check role of each one in Application Layer
  • 0
    @craig939393 wow I totally forgot about that, yes I faced that issue too but it was a mid sized project for a client that I was lucky enough to be able to fix it in a month.

    Thanks for the link in your previous comment, I'm thinking of creating an interface IUser which has:

    string Name

    ILIst<Claim> GetClaimsIdentity

    And from there I continue, but then another question came to my mind, though its unlikely to happen:

    Claims are from the token presented by the user, what if the user role changed, this means user will need a new token?
  • 1
    @gitpush I'm not too confident in my answer being the best solutions, but it might help you get started.

    you could do some kind of messy stuff like invalidating the token by adding it to a persisted temporary blacklist. And then storing the blacklist in cache for performance, I doubt a list of identifiers should be a scale problem.

    Alternatively what I do without ef identity is generate claims based on my database setup when a request comes in. Those are then cached either distributed or in memory, can't remember, and attached to the request through some aspect of the request pipeline.

    That way if you drop a role off a user, it's not baked into their jwt, so it's just a cache you have to invalidate and the pipeline takes over as normal to rebuild their cached claims and attach them to the request instead.

    Sorry if that isn't helpful.
  • 1
    I don't see how you could verify the owner of a post without a db call. Whether or not a person is an moderator should be a claim, though.
  • 1
    @spongessuck you can't. IAurhorization Service allows you to pass it data as well as the policy name. Depending on design then you can check some aspect of the data structure for its owner and compare it against claims within a requirement class.

    As you say to find out if someone is allowed to edit something, you actually have to have the details of that something in memory.

    Edit: again just opinion but I don't recommend tackling this problem by shifting Auth to the database level if it crosses anyone's minds. dB is detail and therefore high risk, and I've seen it done before anyway and it's too restrictive.

    I've also seen authorisation applied against URLs stored in a database. That was also a clusterfuck but off topic :D
  • 0
    @craig939393 I think I found the solution, I'm thinking of writing a middleware which replaces role identity of the token for the calling user, this way I can keep Claims list up to date, though the bad thing about it is, user token might hold invalid claims, but token ttl is no more than 2 hours not sure if that is ok or I'm doing something wrong here
  • 0
    @gitpush couldn't say of its correct or not.

    Would say I think the framework doesn't make guarantees about the order of middleware execution, of that's true then best be careful some other middleware in the future doesn't look at claims before they're overwritten to make some decision.

    For what's it's worth I'd avoid persistent invalid state like this. Gut feeling.
  • 1
    @craig939393 Good point but from checking further I found that they are sequential, so if I add my middle as the last app.Use() then it should be the last one to be called and modifying claims. But thanks for pointing it out I'll double check and see how safe it is to do so
  • 0
    We never did learn where authorisation code should live according to DDD here.

    I've been reading more of the book tonight and I have decided that it certainly would not be infrastructure layer, which leaves application and domain layers.

    Now the question is, is it supporting code or domain logic? It's not specific to how a business person would describe their business... At least not at it's core.

    But it is business logic to me, from a business perspective I need to stop people doing some things.

    So I will say it should live in the domain layer, considering the application layer is supposed to be supporting and orchestral as I understand.

    That said I would not litter my domain models with Auth checks. Perhaps I would use the decorator pattern to protect my domain logic from multiple responsibilities. Could be messy though if every domain model has a corresponding decorator class.. you also often need access to the entity details to determine Auth...

    Interested to be corrected.
  • 1
    Domain Vs application distinction matters I think as different GUI concerns for instance produce different solutions at the application layer. Eg authorisation decorators Vs action filters...
  • 1
    @craig939393 what I ended up doing creating an interface for defining authorization functions in domain, implementing it in application layer. And implementing class gets an instance of IUser which includes userclaims

    Frome there I check role and act based on it. I don't know if this is a good way of solving it، but at least services now do know who is and who isn't allowed to be calling this function and no one is tightly depended on a lib or framework
Add Comment