User permission check in Aggregate Root or Application Service? - domain-driven-design

In my project, there is a concept of user A sending a FriendRequest to user B. In a simplified version, the request looks like this:
class FriendRequest
{
long Id;
int UserId;
int OtherUserId;
string Message;
}
In the Accept method, I need to check whether the current authenticated user equals the OtherUserId in the FriendRequest. The currentAuthenticatedUserId is passed from the controller down to the application service. Now, the question comes that whether I should do the check in the application service or in the FriendRequest aggregate root.
//In application service code:
if(currentAuthenticatedUserId !=friendRequest.OtherUserId)
{
throw new FriendRequestException("Can only accept friend requests sent to you");
}
friendRequest.Accept();
vs.
//In application service, but we are not checking it here.
friendRequest.Accept(currentAuthenticatedUserId); //The check is done inside `FriendRequest` and the exception is also thrown there.

Access control is one of the main responsibilities of application services.
So check the user ID in the app service, not in the entity.

friendRequest.Accept(...)
What does it mean in the domain terms? The request accepts itself or what does it accept? I believe, you need to expand your ubiquitous language with more verbs that correlate to nouns.
As an example, I might think of "a person can accept a friend request that was sent by another person". In this case you would have a person.Accept(friendRequest). It will then be the service responsibility to fetch the Person based on the current authentication details.

Related

REST API - How to implement user specific authorisation?

So I'm currently learning/building a REST API backend server for my web application using NodeJS, ExpressJS, and MySQL as the database. My question is in regards to the best way to implement authorisation to ensure User A does not access or edit the data belonging to another User. Please note that I understand there are a lot of examples for implementation of role based authorisation (ie user groups vs admin groups, etc) but this is not what I'm asking. Instead, how do I authorise a user against the data they are accessing?
It is possible that I'm overthinking this and this is not even necessary; that I should just check whether the data belongs to the user in every SQL query, but I thought I'd ask if there's a middleware or policy architecture that takes care of this, or maybe even authorise through caching.
The only solution I can think of is that every SQL query returns the the user id with the result, then I just create a service that checks every result if the id matches or not. If yes, then proceed. If not rollback the query and return unauthorised error. Is this ok?
I very much appreciate your advice, help, and if you can point me in the right direction.
Many thanks in advance.
Save the userId (or ownerId) in every table, and create a middleware where each db access method requires the userId as a parameter, for example:
readOne(id, userId) {
// implements SELECT * FROM example WHERE id = id AND userId = userId
}
updateOne(id, data, userId) {
// implements UPDATE example SET data = data WHERE id = id AND userId = userId
}
...
For security reasons, never send as a response "Requested data exist by you aren't the owner".
The simplest things usually work best. You wouldn't have to have a special service for checking authorization rights for every entity and you can do it at data access level eg. SELECT * FROM foo WHERE user_id = :currentUser or UPDATE foo SET foo = bar WHERE user_id = :currentUser
It also depends whether you want to notify the user about unallowed access via HTTP401 or not to reveal that such a resource even exists for different user HTTP404.
For HTTP401 the scenario would be:
const entity = loadFromDB(id);
if(entity.userId !== currentUserId) {
res.send(401);
return;
}
... update entity logic ...

Is there an anonymous ID in Actions on Google with Dialogflow?

Is there an anonymous ID in Actions on Google with Dialogflow that I can access using DialogFlow in Node.js?
I don't need to know the Google account of who is using the Action, but I do want to have a unique identifier so that the Action can know when they come back.
Google no longer provides one for you. You will have to generate one when a new user interacts with your webhook and store the generated id in their user storage object.
To identify a new user your just check if they already have an id in their user storage object. For generating the id you can use an library like uuid. https://www.npmjs.com/package/uuid
Uuidv4 is probably the one that you need if you just need a unique id for simple identifications
The original idea from Google was to leverage a field called userStorage, but this feature seems to be borked ATM.
userStorage Documentation:
https://developers.google.com/actions/assistant/save-data
Reddit thread regarding issues:
https://www.reddit.com/r/GoogleAssistantDev/comments/d88z7e/userstorage_saga_continued/
Unless something has changed (I haven't checked on userStorage since I've been busy writing a fix around it) you may be out of luck without Account Linking. Feel free to try userStorage and keep me honest as they may have remedied the situation internally.
Alternatively, if all you need is an identifier or session for a single conversation you can leverage the conversationId which will be unique until the conversation ends.
I've found a possible option...
(When working in DialogFlow in Node.js, most code is in a handler and the parameter is usually called conv. The following assumes that it is inside such a handler.)
On every single call, check for an 'existing' id in the session data and the user storage:
var id = conv.data.MyId || conv.user.storage.MyId || '';
if(!id) {
id = /* make a new Id for this user... a GUID or some other unique id */
conv.user.storage.MyId = id;
}
Once I get the Id from storage or make a new one, it is critical to reassign it to conv.data, since conv.user.storage seems to be reliably provided only on the first call!
// IMPORTANT
conv.data.MyId = id;
/* use the Id as needed */
My code looks up the Id in a firebase database to get details from their last visit.
This seems to be working, but may not be reliable.

How to properly secure my rest endpoints in jhipster?

I would like to secure my rest endpoints in the backend. For example an author can query his books like this:
/books?authorId=5&login=username
#GetMapping("/books")
#Timed
public ResponseEntity<List<Book>> getAllBooks(
#RequestParam(value="authorId", required = false) String authorId,
#RequestParam(value="login", required = false) String login) {
if(!login.equals(SecurityUtils.getCurrentUserLogin().get())){
return ResponseEntity.status(401).build();
}
List<Book> result;
if(authorId!= null)
result = bookService.findByAuthorId(authorId);
else if("admin".equals(SecurityUtils.getCurrentUserLogin().get()))
result = bookService.findAll();
else return ResponseEntity.status(401).build();
return ResponseEntity.ok().body(result);
}
Preferable I would like to only pass the authorId in the params
/books?authorId=5
but since SecurityUtils only gives me the loginName I can't compare them and identify the user in the backend.
Also since it's a microservice I can't access the AccountService.java which is handled by the gateway.
It all works fine but it seems wrong? Is there a better way to allow certain querys only for certain users? Should I make another rest endpoint which handles specifally requests to get books for specific users?
Thank you
You are addressing 2 use cases: one for authors (list my books) and one for management (list all books) for security reasons but usually you may also want to return different data based on use case. It could be a good idea to have 2 different resources: /api/my_books for authors and /api/books for management, you could even use nested resources.
For returning different data (also for security reasons) you can use the DTO option of JHipster with a service layer to map them from entities rather than exposing entities in your REST controllers.
Also don't pass the user id as a request param, you should modify TokenProvider to add it to the token as a claim. If you don't want to add user id to the token, you should modify book entity in your service so that it references user login rather than internal id, as long as it is immutable it does not make a difference.

Entities and services

Lets take a scenario where an Order is dispatched and optionally a mail can be sent to the customer depending on an attribute in the Order class. The functionality to send an email is in a service "EmailService".
My question is should the Order.Dispatch method call the EmailService to send the email depending on the attribute in the Order class or should the application layer after calling the Order.Dispatch method call the EmailService? Which is the correct way to do this in DDD?
Thank you.
NN
Domain events allow you to decouple concerns. As soon as an order is dispatched, it could raise a domain event informing interested subscribers. This allows you to make the decision to send an email somwewhere else, so that the order aggregate can remain pure. It often also helps to capture the language better; when an order is dispatched, send an email.
public class Order
{
public Order(string id)
{
Guard.ForEmpty(id, "id");
Id = id;
}
public string Id { get; private set; }
public void Dispatch()
{
DomainEvents.Raise(new OrderDispatchedEvent());
}
}
public class MailService : IHandle<OrderDispatchedEvents>
{
private readonly IMailSender _mailSender:
public MailService(IMailSender mailSender)
{
_mailSender = mailSender;
}
public void Handle(OrderDispatchedEvent #event)
{
_mailSender.Send(...);
}
}
I would keep that on the application service layer. The sending bit may be simple enough in the Order.Dispatch as you could pass in an instance of the service but how about first of all composing the e-mail? That is probably somewhat more tricky.
Placing it in the operation script in some application service layer task is one way. You could also respond to a domain event (OrderDispatched for instance) and then send. Another option (when using a service bus) is to publish the OrderDispatchedEvent and have you Order endpoint subscribe to that and then send a SendEMailCommand to the E-Mail endpoint.
But I would not try to handle the sending in the domain itself as it feels more like output generation as opposed to some business operation/calculation that the domain is typically involved with. It is a business requirement but just on another level in the same way reporting works. Although business-related, or process related, it does not necessarily belong in the domain.
Just my ZAR 0.02 :)
DDD is not a recipe so there's no correct way to do something.
If by Dispatch you mean that the Order is on the way to the customer (hint: perhaps more accurate semantics are needed here) then the Application layer which sets the Order status as Dispatched can also ask a NotificationService to notify the customer.
I believe that a Domain Event approach is the best though, so once the Order has been dispatched, the OrderDispatched event will be published and a notification service could subscribe to it and will notify the customer using the EmailService, SMSService etc
But the Order itself has nothing to do with this. Some other domain object decides when an Order should be dispatched, it sends a DispatchOrder command to a Domain service which will do the dispatching and then it will generate the OrderDispatched event. This means that your Order shouldn't have a Dispatch method as it's not the Order that does the dispatching. A simple Status property is enough.

Domain driven design: allowed logic in application services

I have a question of how to better organize the implementation of the following functionality.
Suppose a user needs to be registered into the system by unique email and password (first step) and then he should confirm registration (second step). I have several choices of structuring implementation of first step (registration) between application services/domain services/user entity and I'm not sure which one is better.
First option:
AppService:
var existingUser = UserRepository.GetUserByEmail(email);
if (existingUser != null)
{
throw new ValidationException(...);
}
var newUser = UserFactory.CreateUser();
newUser.Register(email, password);
UserRepository.Save(newUser);
// commit
So here, we do not use any domain service. The thing which I personally don't feel confortable is that Email uniqueness business rule is checked in the Application Service, this being a business rule.
Second option:
AppService:
var user = UserRegistrationDomainService.RegisterUser(email, password);
UserRepository.Save(user);
// commit
UserRegistrationDomainService:
User RegisterUser(email, password)
{
var existingUser = UserRepository.GetUserByEmail(email);
if (existingUser != null)
{
throw new ValidationException(...);
}
var newUser = UserFactory.CreateUser();
newUser.Register(email, password);
return newUser;
}
What I don't like here, is that this solution is not quite symmetric with the implementation of second step, where we just get the user from repository and call User.ConfirmRegistration(). So for registration confirmation we do not need any domain service whereas for registration, in second option, we use such service.
Which option is better? Can the application service from first option contain email uniqueness validation?
Personally I think the Validation for that lives in the Domain (either the Entity of the service). The rule after all, is required due to a business rule.
It would be preferable in option 2 for the application services not to be responsible for saving the user, this is blurring the lines of responsibilities and it would be nicer if the domain service handled it. And the application service would simply call UserRegistrationDomainService.RegisterUser(email, password)
Option 1 means that the unique email rule is application-specific. In other words, if you take the Domain dll (or jar, module, etc.) to reuse it in another application, the rule won't be there any more.
Since we can reasonably consider that rule to be application-agnostic, I'd choose option 2.
Another solution could be to implement it in the Factory instead. After all, this is where you'll typically put the validation logic upon creation of your User (null/empty name checking, email format verification, and so on) so why not centralize all creation rules in the same place ?

Resources