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 ?
Related
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.
Dear community and vitaly-t hopefully,
I am building a website / server with pg-promise.
I use postgre role/group login for authentification.
I don't know if I am doing the things correctly but I would like that each user use their own postgres connection to query the database.
So in practice, I create a connection for each user when they connect (if it is not already existing).
To do so, I have created a Pool object with an ugly 'fake promise' and a pgUser object:
var pgPool = function(pg){
var _this=this;
var fakePromise = function(err){
var _this=this;
_this.err=err
_this.then=function(cb){if(!err){cb();return _this}else{return _this};};
_this.catch=function(cb){if(err){cb(_this.err)}else{return _this};};
return _this;
};
_this.check= function(user){
if (_this[user]){
return _this[user].check();
}else{
return new fakePromise({error:'Echec de connection à la base de
données'})
}
}
_this.add = function(user,password){
var c={};
c.host = 'localhost';
c.port = 5432;
c.database = 'pfe';
c.poolSize = 10;
c.poolIdleTimeout = 30000;
c.user=user;
c.password=password
if (!_this[user]){
_this[user] = new pgUser(c,pg);
return _this[user].check();
}else{
_this[user].config.password=password;
return _this[user].check();
};
};
return _this;
};
var pgUser = function(c,pg){
var _this=this
_this.config = c
_this.db = new pg(_this.config)
_this.check = function(){
return _this.db.connect();
};
return _this;
};
And here is how I add a user to the 'pool' during the login POST handling
pool.add(req.body.user,req.body.password).then(function(obj){
obj.done();
req.session.user = req.body.user;
req.session.password = req.body.password;
res.redirect("/");
return;
}).catch(function(err){
options.error='incorect password/login';
res.render('login', options);
return;
});
I am sure it could irritate pro developpers and you would be kind if you could explain me the best way :
is that a good idea to have one connection to the database per user
(it seems legit to have a good security)?
how can I use the pg-promise library better to avoid this ugly custom 'pool' object?
Sincerly thank you.
I have contacted the security responsible of my project, doing research as associate profressor in security (CITI lab)...here is his comment :
====================
Since it is my fault, I will try to explain ;-). First, to be clear, I
work on the security side (notably access control and RDBMS security)
but am not very familiar with JS or promises.
Our aim is to implement the principle of least privilege with a defense
in depth approach. In this particular case, this means that a query sent
by an unprivileged user should not have admin rights on the database
side. RDBMS such as PostgreSQL provide very powerful, expressive and
well-tested access control mechanisms : RBAC, row-level security,
parametrized views, etc. These controls, indeed, are usually totally
ignored in web applications which use the paradigm "1 application == 1
user", this user has thus admin role. But heavy clients often use
several different users on the database side (either one per final user
or one per specific role) and thus benefit from the access control of
the database.
Access control from the DB is an addition to access control in the web
application. AC in the webapp will be more precise but may probably
suffer from some bugs ; AC in the DB will be a bit more laxist but
better enforced, limiting damages in case of an application bug.
So in our case, we want to create a DB user for every application user.
Then, the connection to the database belongs to this specific user and
the database can thus enforce that a simple user cannot execute admin
operations. An intermediate possibility would be to drop some privileges
before executing a query, but our preferred way is to connect to the
database as the currently logged-in user. The login-password is sent by
the user when he authenticates and we just pass it to the DBMS.
Scalability is not (yet) an issue for our application, we can sacrifice
some scalability for this type of security.
Would you have any hints to help us achieve this ?
==================
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.
So I have made a meteor app and I have the autopublish and insecure packages removed, now in order to receive data from my collections I have to subscribe to them in the client. I also have a python program that communicates with my meteor server over ddp using the python-meteor package, in it I simply subscribe to my collections and have complete access to all my data, I can also make Meteor.calls to call functions on the server. This is nice but I can't help but feel like this is a major security hole, anyone can write a client and subscribe to my collections and grab all my data on a whim, if they guess the collection names right.
Is there a way to only let certain clients subscribe to collections and perform server calls?
Yes, you should add security checks to all publishers and methods.
Here's an example publisher that ensures the user is logged in and is a member of the group before receiving any posts related to the group:
Meteor.publish('postsForGroup', function(groupId) {
check(groupId, String);
// make sure the user is a member of the group
var group = Groups.findOne(groupId);
if (!_.contains(group.members, this.userId))
throw new Meteor.Error(403, 'You must be a member of the group!');
return Posts.find({groupId: groupId});
});
Here's an example method that ensures the user is logged in and an admin of the group before being allowed to change the group's name:
Meteor.methods({
'groups.update.name': function(groupId, name) {
check(groupId, String);
check(name, String);
// make sure the user is an admin of the group
var group = Groups.findOne(groupId);
if (!_.contains(group.admins, this.userId))
throw new Meteor.Error(403, 'You must be an admin of the group!');
// make sure the name isn't empty
if (!name.length)
throw new Meteor.Error(403, 'Name can not be empty!');
return Groups.update(groupId, {$set: {name: name}});
}
});
One detail to watch out for: If you are using iron router, be careful not to cause any errors in your publishers. Doing so, will cause waitOn to never return. If you think that throwing an error is possible under normal operation, then I'd recommend return this.ready() instead of throw new Meteor.Error in your publisher.
I posted this question Access Control with a multi database application
So I tried putting it into application. Here is the case. I have a mainDB that has an ACL with no roles defined. The User clicks a button and it opens a control for CRUD with a datasource that has a computed filepath to a different database call it appDB. In appDB the ACL has several roles defined, and I have added myself to the ACL and assigned me the roles [Admin] and [Finance]. In this control I have added an After Page Load event that does the following:
var roles = context.getUser().getRoles();
viewScope.put("vsRoles", roles);
Upon opening the page the viewScope vsRoles is [] so it has not recognized that I have an additional set of roles in the appDB. So it would appear that context.getUser().getRoles() only gets my roles at authentication time when I log into the mainDB.nsf, and is not picking up the roles when I open appDB. I need to use the roles to configure what actions a person can perform, plus which documents a user can read and/or edit.
To complicate the issue the user may switch between multiple target application databases and will no doubt have different roles and access to each one.
thanks for the response to my previous question,but I might not have explained it in enough detail.
So, as far as I understood, what you need is to learn what specific roles the user has for the appDb.
context.getUser().getRoles() provides information about the current application (mainDB.nsf in your case). You are accessing appDB.nsf at a data source level. You can use a java method to learn the roles of a specific user in a target database:
public static List<String> getRoles(Database targetDb, String userName) {
ACL acl=null;
List<String> roles=new ArrayList<String>();
try {
acl=targetDb.getACL();
roles.addAll(targetDb.queryAccessRoles(userName));
} catch (NotesException e) {
// failed, nothing to do...
} finally {
if(acl!=null) acl.recycle();
}
return roles;
}
As an example:
Session session=ExtLibUtil.getCurrentSession();
Database appDb=session.getDatabase("", "appdb.nsf");
// Make sure appDb is not null...
List<String> roleList=getRoles(appDb, session.getEffectiveUserName());
ExtLibUtil.getViewScope().put("vsRoles", roleList);