I can use Spring Security ACL with permissions on entity but I'd like to know how to test if a user has access to the "create" (bit 2) permission on a class.
Something like :
aclPermissionEvaluator.hasPermission(auth, clazz, "create")
Could someone help me?
Thanks in advance
You can use Spring's SpEL annotations, e.g. #PreAuthorize, and override the hasPermission method of the PermissionEvaluator interface. If you're using bitwise permission masks, and the user's permissions (as an int) evaluate to '15' (1111), and the required permissions for the object are '6' (0110), you can do something like the following:
public boolean hasPermission(Authentication auth, Object targetObject, Object requiredPermissions) {
int permissionMask = MyUserClass.getMask();
int permissionsRequired = Integer.valueOf(requiredPermissions.toString());
return ((permissionMask | requiredPermissions) == permissionMask);
}
This will return true whenever the bits active in the object's permissions mask are active on the user's permissions. Then, you'd need to declare this custom permission evaluator in your security.xml file:
<security:global-method-security pre-post-annotations="enabled">
<security:expression-handler ref="expressionHandler"/>
</security:global-method-security>
<bean id="expressionHandler" class="org.springframework.security.access.expression.method.DefaultMethodSecurityExpressionHandler">
<property name="permissionEvaluator" ref="permissionEvaluator"/>
</bean>
<bean id="permissionEvaluator" class="my.project.package.CustomPermissionEvaluator"/>
Now, whenever you call hasPermission(), your custom evaluator will handle the request. Obviously, you can use whatever logic you like to evaluate permissions -- just make sure the return type is boolean, and the parameters to be passed match what you're sending (or evaluating against; be careful of format exceptions).
Note that your custom parameter must be passed as an Object to override hasPermission(); you could also overload the method by changing the signature to handle whatever parameter type you prefer (e.g. String or int), and the compiler should select the most specific signature. Since you're implementing the PermissionEvaluator interface, however, you'll have to include the given signature (Authentication, Object, Object) anyway, so unless you have some specific need to write an overload method, you may as well just override.
I have exactly the same problem, and sadly, there is no "out of the box" solutions.
One way to do it, if your domain model allow for it, is to add the create permission to the parent object you want to create
For example, imagine you want to create an user for a client. You can add the create permission to the client for the user allowed to create user for this specific client. That the path I choose.
If your domain object doesn't allow for that, the only way I found to do it is:
to create a new table acl_class_entry, wich list acl_entry equivalent but linked to an acl_class and not to an acl_object_identity
Then you create your own permission evaluator, surcharging the method boolean hasPermission(Authentication authentication, Serializable targetId, String targetType, Object permission) to check the permission against the acl_class_entry if target_id is null. This will allow you to check permission against a class by using the SPel expression hasPermission(nulll, 'className', 'permission')
Of course, you will also need to create your own version of AclService for creating such permission.
Related
I want to create an (one) instance of a Domain class (which, as expected, has a GORM interface to my database) and only use it as a container to pass data around, like a Map object. I want to make absolutely sure that my instance is never going to get persisted in the database. I'm afraid that GORM, with all its cleverness, will somehow manage to save it in the database behind the scene even without an explicit call to save(). Is there a way to specify a "do not persist this" clause when instantiating my object? I know how to prevent persistence on a domain class, what I want is to prevent persistence on a particular instance of the class only.
The solution I have now is to create a class in groovy/src/ that carries the same properties and methods, and use it as my data container, and do type casts as required. It feels wrong, fails DRY, and hacky.
Of course you may also tell me that I should stop being so paranoid and that Grails is never going to persist an domain class instance without an explicit save.
Assume that, you already know how to prevent persistence(table creation) on a domain class. Furthermore, you also know that w/o explicit .save() object won't be persisted.
So, what do you want actually? Is it like.. even if someone accidentally call obj.save(), it will never persist.
Although that doesn't make any sense, but according to your query ,
Is there a way to specify a "do not persist this" clause when
instantiating my object?
Yes, there is a way :
class MyFishyDomain {
String pwd
// properties
// constraints
def beforeInsert() {
if (!this.pwd.equals("drago")) return false
}
def beforeUpdate () {
if (!this.pwd.equals("drago")) return false
}
}
Now..
new MyFishyDomain(pwd:"drago").save() // success
new MyFishyDomain(pwd:"rambo").save() // fail
By the way, if you want to permanently disable Create+Update+Delete But at the same time want to issue query against domain then solution is:
static mapping = {
cache usage: "read-only"
}
def beforeInsert() {
return false
}
Grails will not save an instance of your domain class without an explicit call to save() on the instance. You can create an instance and pass it around, and it will not be persisted.
I want to build a wrapper around log4j2 to do the below:
1) There are around 6 mandatory fields like event_name, action, desc etc
2) Some fields, i want to make them use only certain values, like enum
3) log should be created in key value pairs for Splunk.
Below is my approach:
1) Created a class called CustomLogger accepeting the mandatory fields, logger and variable fields as key value
2) Users can call methods like below:
CustomLogger.info(logger, transactionId, app_name, event_name,
"inside the loop", "inside the loop of the sample app",
CustomLogger.Result.success, "looped in", "loop_count",
String.valueOf(i));
Method definition:
public static String log(LogLevel logLevel, Logger logger,
String transactionId, String app_name, String event_name,
String action, String desc, Result result, String reason,
String... addtnlFields)
Issues with the approach:
1) Not extending the log4j, not sure if this is the right way
2) need to pass the logger from every class. If that can be avoided
3) method and line number is lost since it is getting called from a different method
This will be widely used across my internal applications, so want to do it right. Is this approach ok or is there a better approach?
Take a look at the code generator attached to this Jira: https://issues.apache.org/jira/browse/LOG4J2-519
Perhaps you can use that as a base class? Gives you a slightly nicer API.
(I still need to update this to reflect some API changes in log4j-2.0-rc2...)
UPDATE
A different approach is to have a custom implementation of the Message interface defined in the log4j2 api module. Your custom message would have a constructor with all fields you define as required, and the toString method (and perhaps some other methods too) would format these fields as you require into key-value pairs.
At runtime, my app will be provided receive a String that may be a valid URL that my app serves (per UrlMappings.groovy), or it may not. I'd like to determine which is the case.
Is there a way for me to apply Grails routing logic manually to a URL, and see what controller/params (if any) it maps to?
Just to be clear, let's say my UrlMappings are:
"/path/one" (controller:"api", action:"one")
"/path/two" (controller:"api", action:"two")
"/variable/$segment/1" (controller:"api", action:"var")
Now my app receives a string at runtime with the value "/path/fake" -- can I test "/path/fake" against the list of all known routes to determine that it's not a match? (Or alternatively, can I test "/variable/segA/1" and determine that it's associated with the "var" action of my API controller?"
Note: I'm looking for a solution that will work for paths with variables, etc -- and will populate a params array with those variables.
Check whether the mapping is present from the list of mappings available in UrlMappings using grailsApplication which can be injected easily in artefacts like controller/service.
def allMappings = grailsApplication.allClasses
.find{it.name == "UrlMappings"}.mappings.urlMappings
assert '/path/one' == allMappings.find{it.toString() == "/path/one"}?.toString()
assert !allMappings.find{it.toString() == "/path/fake"}?.toString()
I am looking for a simple way to hydrate a related object. A Note belongs to a Document and only owners of a Document can add Notes so when a user tries to edit a Note, I need to hydrate the related Document in order to find out if the user has access to it. In my Service layer I have the following:
public void editNote(Note note)
{
// Get the associated Document object (required for validation) and validate.
int docID = noteRepository.Find(note.NoteID).DocumentID;
note.Document = documentRepository.Find(docID);
IDictionary<string, string> errors = note.validate();
if (errors.Count > 0)
{
throw new ValidationException(errors);
}
// Update Repository and save.
noteRepository.InsertOrUpdate(note);
noteRepository.Save();
}
Trouble is, noteRepository.InsertOrUpdate(note) throws an exception with "An object with the same key already exists in the ObjectStateManager." when the repository sets EntityState.Modified. So a number of questions arise:
Am I approaching this correctly and if so, how do I get around the exception?
Currently, the controller edit action takes in a NoteCreateEditViewModel. Now this does have a DocumentID field as this is required when creating a new Note as we need to know which Document to attach it to. But for edit, I cannot use it as a malicious user could provide a DocumentID to which they do have access and thus edit a Note they don't own. So should there be seperate viewmodels for create and edit or can I just exclude the DocumentID somehow on edit? Or is there a better way to go about viewmodels such that an ID is not required?
Is there a better way to approach this? I have read that I should just have a Document repository as an aggregate and lose the Note repository but am not sure if/how this helps.
I asked a similar question related to this but it wasn't very clear so hoping this version will allow someone to understand and thus point me in the right direction.
EDIT
Based on the information provided by Ladislav Mrnka and the answer detailed here: An object with the same key already exists in the ObjectStateManager. The ObjectStateManager cannot track multiple objects with the same key, it seems that my repository method need to be like the following:
public void InsertOrUpdate(Note note)
{
if (note.NoteID == default(int)) {
// New entity
context.Notes.Add(note);
} else {
// Existing entity
//context.Entry(note).State = EntityState.Modified;
context.Entry(oldNote).CurrentValues.SetValues(note);
}
}
But how do I get the oldNote from the context? I could call context.Entry(Find(note.NoteID)).CurrentValues.SetValues(note) but am I introducing potential problems here?
Am I approaching this correctly and if so, how do I get around the exception?
I guess this part of your code loads the whole Node from the database to find DocumentID:
int docID = noteRepository.Find(note.NoteID).DocumentID;
In such case your InsertOrUpdate cannot take your node and attach it to context with Modified state because you already have note with the same key in the context. Common solution is to use this:
objectContext.NoteSet.ApplyCurrentValues(note);
objectContext.SaveChanges();
But for edit, I cannot use it as a malicious user could provide a DocumentID to which they do have access and thus edit a Note they don't own.
In such case you must add some security. You can add any data into hidden fields in your page but those data which mustn't be changed by the client must contain some additional security. For example second hidden field with either signature computed on server or hash of salted value computed on server. When the data return in the next request to the server, it must recompute and compare signature / hash with same salt and validate that the passed value and computed value are same. Sure the client mustn't know the secret you are using to compute signature or salt used in hash.
I have read that I should just have a Document repository as an aggregate and lose the Note repository but am not sure if/how this helps.
This is cleaner way to use repositories but it will not help you with your particular error because you will still need Note and DocumentId.
I am using valueUnbound method of HttpSessionBindingListener to release lock(an entry from the database), before session is about to expire:
#Override
public void valueUnbound(HttpSessionBindingEvent event) {
String user = (String) event.getSession().getAttribute("currentUsr");
removeLock(user);
}
When the lock is set, I am setting up the username as a session variable.
I need this "username" in my remove lock method. But the getAttribute is throwing an exception:
java.lang.IllegalStateException: getAttribute: Session already invalidated
I need help in getting the session variable?? or is there any other way to get the username?
No, since session has been invalidated.
Although, I figured out the solution, I am setting the attribute via servlet context in
valueBound method and getting it through the : event.getSession().getServletContext().getAttribute("cUser");
it works fine. Thank You EJP
I got your point EJP, you are right , I am making it complex, I can get it from event.getValue() . +1 to your answer, Thank You.
Although, I figured out the solution, I am setting the attribute via servlet context in valueBound method and getting it through the : event.getSession().getServletContext().getAttribute("cUser");
So.. You are storing session scoped data in the application scope. Do you realize that this way the data is shared among all visitors of the webapp? Visitor X would then see the attribute set by visitor Y which has visited the website at a later moment. It makes the problem only worse.
Anyway, as to the concrete problem, as the exception message is trying to tell you, the session has already been invalidated at that point. There are two ways to solve this:
Make currentUsr a property of the class which is implementing HttpSessionBindingListener, so that you don't need to grab it as a distinct session attribute.
Use a HttpSessionListener instead. The sessionDestroyed() method is called right before invalidation, so you should still have access to all attributes.