I'm wondering what is good approach to secure handler method in Spring MVC controller. Now i use #Secured annotation, that ensure that some method may be accessed by logged user only. But how to ensure that one logged user doesn't do something bad for other users ? For example i have method that delete item with given id. To ensure that someone can't remove other than his items i check item owner. Is better way to do something like that ?
#Secured("ROLE_USER")
#RequestMapping("/deleteitem.html")
public String delete(#RequestParam(value="id") Long id) {
Item b = itemDAO.get(id);
if(b.getOwner().getId().equals(((UserDetails) SecurityContextHolder.getContext().getAuthentication().getPrincipal()).getUser().getId())) {
itemDAO.delete(id);
}
return "redirect:/user/items.html";
}
Perhaps you can look at #Preauthorize annotation. You can do something like
#PreAuthorize("#item.id == authentication.id")
public void doSomething(Item item);
You would need to rewrite your current code suitably.
Look into Spring Security ACL (Access control list) you can create a list of permissions that users have for this object. Permissions include read, write, delete...
You need to implement role base system, base on privileges user can perform delete operation.
If specific user having delete access then he/she do the delete stub.
Related
My goal is to achieve something like the following with flowbuilder:
Trigger: order placed (achievable with the flowbuilder)
If : if order.customFields.customtextField not empty (trying to implement this)
Action : send email with (achievable with the flowbuilder)
For this, I am trying to add a custom rule for order following this : https://developer.shopware.com/docs/guides/plugins/plugins/framework/rule/add-custom-rules#create-custom-rule
But, I see that the last created order is not easily accessible in the rule class. Is there a better/recommended way to do check for an order custom field after order is placed?
Within the flow the order is converted back to a cart with the order line items becoming cart line items once again. This is done to make existing rules work for both evaluating carts and orders. This also means the custom fields of a placed order are not available in the context of the cart as they wouldn't be available yet during checkout. Unfortunately within a rule condition there is no ideal way to identify if the cart was converted from an existing order, no less which order it is.
What you could do however is add a new flow action. Within the scope of the action you could have access to the order entity and then decide within the action what to do with it. The triggers for order placed or enter/leave states will dispatch CheckoutOrderPlacedEvent or OrderStateMachineStateChangeEvent respectively. Both of these have a getter for the order entity which you will be able to access from within your action.
public function handle(FlowEvent $event): void
{
$baseEvent = $event->getEvent();
if (!$baseEvent instanceof CheckoutOrderPlacedEvent && !$baseEvent instanceof OrderStateMachineStateChangeEvent) {
return;
}
$order = $baseEvent->getOrder();
// evaluate the order and decide what to do with it
}
I see that another approach that worked for me was with adding a custom trigger(event) which is dispatched when checkout.order.placed event is fired and if that event's order has the custom field that I am looking for.
I'm looking to extend the base Singleton "AccessInfo" to include additional information pertaining to the current user (such as roles.)
Is there a most favorable path to do this? I can see how to extend a Graph, Cache, but not seeing any documentation how to extend this area.
Unfortunately due to the way it has been implemented, there is not a way to add fields to that DAC and have them populated on instantiation, and since it's not selected from the DB like a normal DAC, I do not think events would fire for it.
If you would like to access Roles related to the current Users, this should suffice.
PXSelect<UsersInRoles,
Where<UsersInRoles.userName, Equal<Current<AccessInfo.userName>>>>.Select(this /*Or Base if it's a Graph Extension*/);
As there will likely be multiple Roles per user, you will need to loop.
foreach (UsersInRoles role in PXSelect<UsersInRoles, Where<UsersInRoles.userName, Equal<Current<AccessInfo.userName>>>>.Select(this /*Or Base if it's a Graph Extension*/))
{
//Some thing here
}
Suppose I have a command that saves an application Role along with some application Permissions. My roles and permissions have (or will have) business rules, so I'll use domain objects:
class Role {
...
IEnumerable<Permission> Permissions { ... }
AddPermission(...)
...
}
class Permission {
...
int ID { ... }
string Foo { ... }
string Bar { ... }
string Baz { ... }
}
When I'm saving a Role, I need a full Role object and will probably receive everything I need from the presentation layer. I do not need full Permission objects, though, because I only need to associate their ID with the Role. I don't need the Foo, Bar, and Baz properties.
When I'm saving a Permission, I obviously need the full Permission object.
My Question: What is the right way to handle that Permission object? If I only have one Permission class, then, when I'm saving a Role, I will either:
Have to query/hydrate full Permission objects from the database so the Role has legitimate Permission objects in its collection, or
Attach incomplete Permission objects (IDs only) to avoid the trip to the database.
Option #1 sounds like the kind of command/query complexity CQRS aims to avoid, and #2 sounds like an invalid object floating around--I don't even want to be able to create invalid objects, much less use them.
I could also create a PermissionSummary class and the full Permission class derives from it. I've done this before, and it inevitably leads to a creep of properties from the "full" class up to the "summary" class.
Batavia's response to CQRS is great and as I have no experience with that pattern Ill try and answer the 'what do I save' question.
The answer to this one depends strongly on your Model design and the proposed behavior of the Permission entities. DDD does not work without a strong knowledge of the business domain.
Having said that I can think of 3 scenarios:
1) Permissions are immutable and Roles get changed. In this scenario Role becomes the aggregate root and the collection of Permissions in re-hydrated on each fetch. The properties and methods of each Permissions entity is available to Role entity enabling operations like
partial class role() {
public enumAccessType AccessToAction(enumAction action) {
foreach(var p in Permissions)
if p.HasFullAccess(action) return enumAccesssType.Full;
foreach(var p in Permissions)
if p.HasLimitedAccess(action) return enumAccesssType.Restricted;
return enumAccesssType.None;
}
}
There is a Permission repository for saving new Permissions and a Role Repository for maintaining the roles and the role_Permission tables
NOTE: Just because the domain object has the full PERMISSION objects doesn't mean that the persistence layer needs to update the PERMISSION table for each of the permissions added. The RoleRepository should only update ROLE (roleId, roleName) and ROLE_PERMISSION (roleId, permissonId) tables.
2) Permissions are mutable But roles are static. In this situation is may make more sense for your model to have Permission as the Aggregate root and a collection of Roles as role is just a bucket for grouping Permissions:
class Permission {
Ienumerable<RoleId> Roles {get;private set;}
PermissionId ID { ... }
string Foo { ... }
string Bar { ... }
}
Again there is Permission repository for saving new Permissions and maintaining the Permission-Role relations. The Role Repository just handles roles.
3) Roles and Permissions change all the time - your security requirement is complex and will difficult for most end users to comprehend. Bring in the Domain Expert to explain how Role and Permissions relate and affect the rest of the system and WHY the have to be so flexible. Look at the why and try and identify processes or behavior that may be incorrectly being forced into a roles-permissions pattern. This will probably require its own bounded context and services to work.
It helps to remembers 'Database Tables' != 'DDD Entities' != 'User Interface'. At first blush it looks like your coming at this from a database design point of view rather than the domain behaviour point of view.
A big 'Am I Doing This Right' is this check: If I save changes to one entity , do I need to reload any other entities that reference that original entity in order for those other entities to work? If the answer is yes then you need to revisit your model.
so when using CQRS i think the answer is neither.
I think it's important to differentiate between DDD and CQRS.
When saving a role (In the CQRS pattern) you would be sending a "RoleChangeRequest" command to your backend. This would then raise a RoleChangeRequested event. (this could be done by a domain service, possibly even by the domain layer itself). This event would be handled by your database layer but this event would look more closely to this
class RoleChangeRequested {
IEnumerable<int> PermissionIds {....}
string name {....}
}
The key here is that in raising the event that would save your data you only need to know about the Id's of the permissions. There is no need to either query permissions (ok, maybe you want some check that they actually exist. but a foreign key relation could handle this). or to attach incomplete objects.
NOTE: In just this example CQRS is going to make your application a lot more complex. CQRS is weapons-grade architecture and should be handled with extreme caution. Now why would you want to use CQRS, that's because your next requirement is to make a full and guaranteed audit trail of all your role and or permission changes. This is just another event handler to the same events. And you could even have an event handler to the generic ievent interface and then you are guaranteed that you audit every event being raised in the application. That you get (almost) for free and is why CQRS can be a benifit.
I'm trying to get my head around DDDD in Greg Young's style.
There is much talk about how to implement DDDD with CQRS+EventSourcing and there are some sample implementations ... and altogether it can be quite confusing...
In Gregs view aggregates don't have getters or setters - just state-changing methods which emit an corresponding event.
Basicly a event decribes a state-transitions that happend in the past. It's data describes what changed.
Some say, that this data can be "enriched" by additional data.
Where can this additional data come from?
i.e. I have User and Usergroup - both aggregate roots (can exist independently, have identity). User has a method called AddToUsergroup.
public class User : AggregateRoot
{
// ...
public void AddToUsergroup(Usergroup usergroup)
{
// validate state
RaiseEvent(new UserJoinedUsergroup(this.Id, usergroup.Id));
}
// ...
}
public class Usergroup : AggregateRoot
{
private string _displayName;
// ...
public void ChangeDisplayName(string displayName)
{
// validate state
RaiseEvent(new DisplayNameChanged(this.Id, displayName));
}
public void Apply(DisplayNameChanged e)
{
this._displayName = e.DisplayName;
}
// ...
}
If I would like to "enrich" the event with the name of the Usergroup (for debug reasons or something like this), how would I do that?
getters are nonexistent
internal state of usergroup is inaccessible
Injecting such things as repositories into User is not allowed (am I right here?!?), like
Read-side repository
Event-store repository
Bottomline questions:
Can Should something like repositories be injected to aggregate roots?
Should a event only use data available through parameters and internal state of the aggregate?
Should events only contain the minimum data describing the state-change?
And (little off-topic but the sample is here)
Should AddToUsergroup take an Guid instead of an full aggregate root?
Looking forward to your answers!
Lg
warappa
Should something like repositories be injected to aggregate roots?
No and there is no need to in this case. It can be appropriate to pass a domain service to a behavioral method on an aggregate, but again, not needed in this case.
Should a event only use data available through parameters and internal
state of the aggregate?
Yes, the original domain event should be such that it can be easily built by the aggregate and can be replayed in a deterministic fashion.
Should events only contain the minimum data describing the
state-change?
Yes. However, to address requirements of external subscribers, this is where content enricher comes into play. To dispatch a domain event externally, it is first committed to the event store then you can dispatch in the same tx or have an out of process mechanism which publishes events externally. At the point of publishing externally, you will normally use a different contract for the message since subscribers may need more than what is on the domain event itself. In this case, you need the user group name. The publisher, then, can pull up the user group name and place that data into the enriched event. This publisher can have access to a read-model repository for user groups which would allow it to retrieve the name.
Should AddToUsergroup take an Guid instead of an full aggregate root?
Yes. Passing the entire aggregate may not be possible and also creates the illusion that something other than the ID may be used, which should not be the case.
I'm trying to figure out how to accomplish the following:
User can have many Websites
What I need to do before adding a new website to a user, is to take the website URL and pass it to a method which will check whether the Website already exist in the database (another User has the same website associated), or whether to create a new record. <= The reason for this is whether to create a new thumbnail or use an existing.
The problem is that the repository should be per aggregate root, which means I Cant do what I've Explained above? - I could first get ALL users in the database and then foreach look with if statement that checks where the user has a website record with same URL, but that would result in an endless and slow process.
Whatever repository approach you're using, you should be able to specify criteria in some fashion. Therefore, search for a user associated with the website in question - if the search returns no users, the website is not in use.
For example, you might add a method with the following signature (or you'd pass a query object as described in this article):
User GetUser(string hasUrl);
That method should generate SQL more or less like this:
select u.userId
from User u
join Website w
on w.UserId = u.UserId
where w.Url = #url
This should be nearly as efficient as querying the Website table directly; there's no need to load all the users and website records into memory. Let your relational database do the heavy lifting and let your repository implementation (or object-relational mapper) handle the translation.
I think there is a fundamental problem with your model. Websites are part of a User aggregate group if I understand correctly. Which means a website instance does not have global scope, it is meaningful only in the context of belonging to a user.
But now when a user wants to add a new website, you first want to check to see if the "website exists in the database" before you create a new one. Which means websites in fact do have a global scope. Otherwise anytime a user requested a new website, you would create a new website for that specific user with that website being meaningful in the scope of that user. Here you have websites which are shared and therefore meaningful in the scope of many users and therefore not part of the user aggregate.
Fix your model and you will fix your query difficulties.
One strategy is to implement a service that can verify the constraint.
public interface IWebsiteUniquenessValidator
{
bool IsWebsiteUnique(string websiteUrl);
}
You will then have to implement it, how you do that will depend on factors I don't know, but I suggest not worrying about going through the domain. Make it simple, it's just a query (* - I'll add to this at the bottom).
public class WebsiteUniquenessValidator : IWebsiteUniquenessValidator
{
//.....
}
Then, "inject" it into the method where it is needed. I say "inject" because we will provide it to the domain object from outside the domain, but .. we will do so with a method parameter rather than a constructor parameter (in order to avoid requiring our entities to be instantiated by our IoC container).
public class User
{
public void AddWebsite(string websiteUrl, IWebsiteUniquenessValidator uniquenessValidator)
{
if (!uniquenessValidator.IsWebsiteUnique(websiteUrl) {
throw new ValidationException(...);
}
//....
}
}
Whatever the consumer of your User and its Repository is - if that's a Service class or a CommandHandler - can provide that uniqueness validator dependency. This consumer should already by wired up through IoC, since it will be consuming the UserRepository:
public class UserService
{
private readonly IUserRepository _repo;
private readonly IWebsiteUniquenessValidator _validator;
public UserService(IUserRepository repo, IWebsiteUniquenessValidator validator)
{
_repo = repo;
_validator = validator;
}
public Result AddWebsiteToUser(Guid userId, string websiteUrl)
{
try {
var user = _repo.Get(userId);
user.AddWebsite(websiteUrl, _validator);
}
catch (AggregateNotFoundException ex) {
//....
}
catch (ValidationException ex) {
//....
}
}
}
*I mentioned making the validation simple and avoiding the Domain.
We build Domains to encapsulate the often complex behavior that is involved with modifying data.
What experience shows, is that the requirements around changing data are very different from those around querying data.
This seems like a pain point you are experiencing because you are trying to force a read to go through a write system.
It is possible to separate the reading of data from the Domain, from the write side, in order to alleviate these pain points.
CQRS is the name given to this technique. I'll just say that a whole bunch of lightbulbs went click once I viewed DDD in the context of CQRS. I highly recommend trying to understand the concepts of CQRS.