I am considering using Spring Security annotations for my application, with the EL (expression language) feature. For example:
#PreAuthorize("hasPermission(#contact, 'admin')")
public void deletePermission(Contact contact, Sid recipient, Permission permission);
I need the EL capability because I have built my own ACL implementation. However, to use this capability with the "#contact" type arguments, the Spring documentation says this:
You can access any of the method
arguments by name as expression
variables, provided your code has
debug information compiled in.
This begs two questions:
It is acceptable to have a
production application commercially
distributed with debug info in it?
If not, is there any way around
this?
Thanks for any guidance on this!
As a workaround you can implement a custom ParameterNameDiscoverer with your own strategy. Here is an example which produces simple numbered names (arg0, etc):
public class SimpleParameterNameDiscoverer implements
ParameterNameDiscoverer {
public String[] getParameterNames(Method m) {
return getParameterNames(m.getParameterTypes().length);
}
public String[] getParameterNames(Constructor c) {
return getParameterNames(c.getParameterTypes().length);
}
protected String[] getParameterNames(int length) {
String[] names = new String[length];
for (int i = 0; i < length; i++)
names[i] = "arg" + i;
return names;
}
}
And configuration:
<global-method-security ...>
<expression-handler ref = "methodSecurityExpressionHandler" />
</global-method-security>
<beans:bean id = "methodSecurityExpressionHandler"
class = "org.springframework.security.access.expression.method.DefaultMethodSecurityExpressionHandler">
<beans:property name = "parameterNameDiscoverer">
<beans:bean class = "foo.bar.SimpleParameterNameDiscoverer" />
</beans:property>
</beans:bean>
I guess this wasn´t an option when you approached the problem the first time, but now you can do this
#PreAuthorize("hasPermission(#contact, 'admin')")
public void deletePermission(#P("contact") Contact contact, Sid recipient, Permission permission);
http://docs.spring.io/spring-security/site/docs/current/reference/html/el-access.html#access-control-using-preauthorize-and-postauthorize
I can't find the reference now, but you might be interested to know that Java 8 will include parameter names at all times, even when I believe Java 8 will include parameter names at all times, even in debug mode.
Related
I am new for spring security. I've seen many posts on how to inject values via annotation from external property file. I've tried many ways, but I always end up with java.lang.IllegalArgumentException: Could not resolve placeholder 'val.id' exception.
Can you provide me some tips how to handle this exception please?
My java class is the following one:
#Controller
public class Employee {
#Value("${val.id}")
public String valId;
public String getValId() {
return valId;
}
public void setValId(String valId) {
this.valId = valId;
}
My property file is called val.properties which is located under WEB-INF, and its content is
val.id=xyz
I put the following in my main context bean.
<context:property-placeholder location="/WEB-INF/*.properties" />
<bean id="valProp" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer"
p:location="/WEB-INF/val.properties"/>
A continuous question:
The injecting values from properties file to annotated beans works fine as I accepted the answer above. However, I cannot able to inject it to #PreAuthorize(...) annotation by following the same procedure.
Assume I want to secure a method called 'update'. This method is allowed if and only if valId is equal to empId. values of valId and empId are initialized in the val.properties file.
my java bean is:
public class Employee {
public String valId;
public String empId;
public String getValId() {
return valId;
}
public void setValId(String valId) {
this.valId = valId;
}
public String getEmpId() {
return empId;
}
public void setEmpId(String empId) {
this.empId = empId;
}
}
my property file contains:
val.id=nn
emp.id=nn
I have the place holder configuration in my main context file:
<context:property-placeholder location="/WEB-INF/*.properties" />
<bean id="valProp" class="org.springframework.beans.factory.config.PropertiesFactoryBean"
p:location="/WEB-INF/val.properties"/>
My PreAuthorize annotation (method security) is:
#PreAuthorize("(#{valProp['val.id']} == #{valProp['emp.id']})")
public boolean update(){
//if accessable
return true;
}
But the expression #{valProp['val.id']} == #{valProp['emp.id']} is not evaluated.
Did I do any mistake to inject values? It was worked when I annotate member variables, but it doesn't work here. Any idea please? Thanks in advance.
try to consider the following
1). change your annotation to:
#Value("#{valProp['val.id']}")
2). Replace PropertyPlaceholderConfigurer by PropertiesFactoryBean.
Hope this will resolve the exception.
The reason why the exception is thrown is, because the property placeholder by default throws an exception when a values cannot be resolved.
Furthermore you have two property placeholders, via which probably not all values can be resolved.
You can change this behaviour via setting the ignore-unresolvable property:
<context:property-placeholder location="/WEB-INF/*.properties" ignore-unresolvable="true" />
<bean id="valProp" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer"
p:location="/WEB-INF/val.properties" p:ignoreUnresolvablePlaceholders="true" />
Note however that b< turning off this feature typos in a property file will not be detected.
(Sorry if this is a duplicate, but the question is very search-engine-unfriendly.)
I want to know how to evaluate Spring EL inside EL (with all the functions, variables, context, etc. passed through).
Specifically, I want to dynamically evaluate a Spring Security expression (which is just EL plus some functions and contexts) loaded from a database entity inside a hard-coded EL in #PreAuthorize.
I.e. something like #PreAuthorize("eval(argument.securityExpr)").
You can extend Springs MethodSecurityExpressionRoot (and create it in your own MethodSecurityExpressionHandler) and add an eval method which excepts a String and let the SpringExpressionParser evaluate the String. Should work...
Edit:
A little code:
public class MySpringSecurityRoot extends MethodSecurityExpressionRoot {
private MyMethodSecurityExpressionHandler handler; // to be injected in the handler
public boolean eval(String expression) {
Expression expression = handler.getExpressionParser().parseExpression(expression);
return ExpressionUtils.evaluateAsBoolean(
handler.getExpressionParser().parseExpression(expression),
handler.createEvaluationContext(authentification, methodInvocation));
}
}
your handler must be set as the default method security expression handler:
<security:global-method-security pre-post-annotations="enabled">
<security:expression-handler ref="myHandler"/>
</security:global-method-security>
now your eval function is accessible in every method security expression
BUT: You must be aware of the fact that the person who describes your security rule, can access all beans inside the current spring context! Might be a security leak.
If you use a simpleparametername discover with # to evaluate permissions you can do virtually anything you want, without enabling debug mode.
#PreAuthorize("#mySecurityService.hasPermission(#arg0)")
public String getSpecial(final String special) {
return "authorized";
}
mySecurityService can be any bean/method returning a boolean, with this wired up for arg1
public class SimpleParameterNameDiscoverer implements ParameterNameDiscoverer {
public String[] getParameterNames(Method m) {
return getParameterNames(m.getParameterTypes().length);
}
public String[] getParameterNames(Constructor c) {
return getParameterNames(c.getParameterTypes().length);
}
protected String[] getParameterNames(int length) {
String[] names = new String[length];
for (int i = 0; i < length; i++)
names[i] = "arg" + i;
return names;
}
}
and context :
<bean id="methodSecurityExpressionHandler"
class="org.springframework.security.access.expression.method.DefaultMethodSecurityExpressionHandler">
<property name="parameterNameDiscoverer">
<bean class="your.path.SimpleParameterNameDiscoverer"/>
</property>
I want to escape my Spring propeties file in order to get in my bean property: ${ROOTPATH}/relativePath
I have a simple Spring config file that contains:
<context:property-placeholder location="classpath:myprops.properties" />
<bean id="myBean" class="spring.MyBean">
<property name="myProperty" value="${myproperty}" />
</bean>
The myprops.properties contains:
myproperty=\${ROOTPATH}/relativePath
The above setup returns: Could not resolve placeholder 'ROOTPATH'. I tried a lot of possible syntaxes but was not able to find the right one.
Instead of ${myproperty} use #{'$'}{myproperty}. Simply replace $ with #{'$'}.
Seems so far, that is no way to escape the ${}, however you can try below configuration to solve the problem
dollar=$
myproperty=${dollar}{myproperty}
Result for myproperty will be ${myproperty} after evaluation.
Here is a Spring ticket which asks for escaping support (still unresolved at the time of writing).
The workaround of using
$=$
myproperty=${$}{ROOTPATH}/relativePath
does provide a solution, but looks quite dirty.
Using SPEL expressions like #{'$'} did not work for me with Spring Boot 1.5.7.
Although it works, escaping the placeholder is super-ugly.
I achieved this my overriding PropertySourcesPlaceholderConfigurer.doProcessProperties and using a custom StringValueResolver
public static class CustomPropertySourcesPlaceholderConfigurer extends PropertySourcesPlaceholderConfigurer {
#Override
protected void doProcessProperties(ConfigurableListableBeanFactory beanFactoryToProcess, StringValueResolver valueResolver) {
StringValueResolver customValueResolver = strVal -> {
if(strVal.startsWith("${something.")) {
PropertySourcesPropertyResolver customPropertySourcesPropertyResolver = new PropertySourcesPropertyResolver(this.getAppliedPropertySources());
String resolvedText = customPropertySourcesPropertyResolver.resolvePlaceholders(strVal);
//remove the below check if you are okay with the property not being present (i.e remove if the property is optional)
if(resolvedText.equals(strVal)) {
throw new RuntimeException("placeholder " + strVal + " not found");
}
return resolvedText;
}
else {
//default behaviour
return valueResolver.resolveStringValue(strVal);
}
};
super.doProcessProperties(beanFactoryToProcess, customValueResolver);
}
}
plugging it into the app
#Configuration
public class PlaceHolderResolverConfig
{
#Bean
public static PropertySourcesPlaceholderConfigurer placeHolderConfigurer() {
PropertySourcesPlaceholderConfigurer placeHolderConfigurer = new CustomPropertySourcesPlaceholderConfigurer();
placeHolderConfigurer.setLocation(new ClassPathResource("application.properties"));
return placeHolderConfigurer;
}
}
In the above example, for all properties starting with something.* nested placeholders wont be resolved..
remove the if(strVal.startsWith("${something.")) check if you want the behaviour for all properties
I am trying to access multiple resource bundles from a JSF page. I have two resource bundles:
general_messages.properties
module_message.properties
I want to access both these resource bundles in a JSF file. One way I can do this is to define specific properties for each of these bundles:
<f:loadBundle basename="com.sample.general_messages" var="general"/>
<f:loadBundle basename="com.sample.module_message" var="module"/>
Is there a way I can access both these resource bundles using the same variable name.
Something like:
<f:loadBundle basename="com.sample.general_messages, com.sample.module_message" var="general"/>
Or any other best way to access multiple resource bundles?
You tagged your question with Spring, so I recommend you using Spring MessageSource. Spring MessageSource can aggregate many property files even hierarchically. It gives you many advantages over old java ResourceBundle.
You can define spring MessageSource in you spring-config.xml like this:
<!--
Application messages configuration.
-->
<bean id="messageSource" name="resourceBundle"
class="org.springframework.context.support.ReloadableResourceBundleMessageSource"
p:fallbackToSystemLocale="false"
p:cacheSeconds="0">
<property name="basenames">
<list>
<value>/messages/Messages</value>
<!-- <value>${application.messages}</value>-->
</list>
</property>
</bean>
Than you can define your Class which extends ResourceBundle like this (Needs some cleaning and refactoring):
public class SpringResourceBundle extends ResourceBundle
{
private MessageSource messages;
private FacesContext fc;
private Locale locale = null;
public SpringResourceBundle()
{
fc = FacesContext.getCurrentInstance();
WebApplicationContext webAppCtx = (WebApplicationContext) fc.getExternalContext().getApplicationMap().get(
WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE);
messages = (MessageSource) webAppCtx.getBean("messageSource");
}
#Override
public Locale getLocale()
{
Locale loc = fc.getELContext().getLocale();
if (fc.getExternalContext() != null) {
loc = fc.getExternalContext().getRequestLocale();
}
try {
UIViewRoot viewRoot = fc.getViewRoot();
if (viewRoot != null) {
loc = viewRoot.getLocale();
}
if (loc == null) {
loc = fc.getApplication().getDefaultLocale();
}
} catch (Throwable th) {
System.out.println(th.getMessage());
loc = locale;
}
locale = loc;
return loc;
}
#Override
protected Object handleGetObject(String key)
{
try {
return messages.getMessage(key, null, getLocale());
} catch (NoSuchMessageException e) {
return "???" + key + "???";
}
}
#Override
public Enumeration<String> getKeys()
{
return Collections.enumeration(Collections.EMPTY_LIST);
}
}
Finnaly in faces-config.xml declare your resource bundle with Class above. Something like this:
<application>
<locale-config>
<default-locale>en</default-locale>
<supported-locale>cs</supported-locale>
<supported-locale>de</supported-locale>
<supported-locale>en</supported-locale>
</locale-config>
<message-bundle>your.package.SpringResourceBundle</message-bundle>
</application>
Here you go Spring MessageSource in JSF. Hope it's understandable.
If two resource bundles contain the same key , then which resource bundles should be used to resolve this key ? So ,IMO ,I don't think the same variable name can be assigned to multiple resource bundles.
Perhaps , you can merge all the .properties into a single .properties in your build process (make sure all keys in the merged properties file is unique , for example , by adding some prefix in each key.) . Then you use this single merged .properties throughout the application.
The only situation (that I know of) in which JSF checks multiple files for the same bundle is if you are providing bundles for multiple locales (see Providing Localized Messages and Labels).
You might be able to point the f:loadBundle tag to a class that extends ResourceBundle instead of a properties file and use that class to reference multiple properties files. I haven't tried that before though.
Also, if you are using Seam, it provides the ability to register multiple 'global' bundles as well as bundles that can be associated with one of more views (facelets), all of which can be referenced using messages e.g. #{messages.my_message} as described here (that's for Seam 2, it may be a little different in Seam 3). I think that's what you're after though.
I'm wondering that if there is a way for binding a spring bean's property to another bean's property so if any change on binded property occurs in runtime, what i expect is referencing bean's property also changes. I'll explain more with a little code snippet.
<bean id="johnHome" class="example.Contact">
<property name="phone" value="5551333" />
</bean>
<bean id="johnWork" class="example.Contact">
<property name="phone">
<util:property-path path="johnHome.phone" />
</property>
</bean>
OK. This works at initial bean wiring but what i exactly want is to bind property so if the property changes at runtime the referencing bean also changes. If i should like to show with a metaphor it will seem like this.
<bean id="johnHome" class="example.Contact">
<property name="phone" value="5551333" />
</bean>
<bean id="johnWork" class="example.Contact">
<property name="phone">
<util:bind path="johnHome.phone" />
</property>
</bean>
Am i overloading the spring's concept too much or is this possible without a lot of tricks?
Thanks..
Simplest way - make that property a bean which is referenced by the two other beans, e.g. for a String value have a StringHolder class:
public class StringHolder {
private String value;
// setter and getter elided due to author's lazyness
}
The whole idea behind Spring is (was?) to keep a clean object-oriented design consisting of plain old java objects and use the spring framework to handle the tedious object creation. As for AOP, this should only handle cross-cutting concerns. I'm not at all convinced that this is one of those cases where AOP is a good idea. Your application relies on the behaviour of these phone numbers getting synced to each other, it's one of the main functionalities. As such, your design should reflect this.
Probably the most logical way to handle this specific problem is to make phone numbers their own class (which is also handy if you ever want to distinguish different types of phone numbers).
If you have a PhoneNumber object which takes the number as a constructor argument the mapping becomes trivial:
<bean id="johnFirstPhone" class="example.PhoneNumber">
<constructor-arg value="5551333" />
</bean>
<bean id="johnHome" class="example.Contact">
<property name="phone" ref="johnFirstPhone" />
</bean>
<bean id="johnWork" class="example.Contact">
<property name="phone" ref="johnFirstPhone" />
</bean>
Of course whether you'd map it like this in a static file is another matter, but the thing is in this situation you pretty clearly just need a reference/pointer.
I don't think what you're doing is possible in Spring 2.5. It may be possible in Spring 3, using the new expression syntax, but I don't think so.
Even if it were, it'd be confusing, I think. Better to stick your shared value into its own class and inject an instance of that class into the other beans that need to share it.
I can think of two possibilities.
One is (it is kind of a hack), if you don't have very many beans that need to be linked like the ones in your example, you could inject johnWork into the johnHome bean, and in johnHome.setPhone you could update the johnWork phone property, something like:
public class Contact {
private Contact myWorkContact;
private String phone;
public void setPhone(String phone) {
this.phone = phone;
if (this.myWorkContact != null) {
this.myWorkContact.setPhone(phone);
}
}
public void setWorkContact(Contact c) {
this.myWorkContact = c;
}
}
Or you could have HomeContact and WorkContact both extend a class Contact and do the same injection with that.
If you have tons and tons of beans that will need this (like if your application actually IS dealing with contact information), with AOP (you'll need AspectJ for the example given) I think you could do something like this (it will be a bit memory intensive if you get a ton of objects, but you can see how something like it would work):
Warning: this actually got complicated fast, but I'm pretty sure it would work after you worked out a few kinks
public class Contact {
...
private String phone;
private String name;
private Integer id;
public Contact(Integer id, String name, String phone) {
this.phone = phone;
this.name = name;
this.id = id;
}
public void setPhone(String phone) {
this.phone = phone.
}
//Other getters, setters, etc
...
}
#Aspect
public class ContactPhoneSynchronizer {
//there is probably a more efficient way to keep track of contact objects
//but right now i can't think of one, because for things like a tree, we need to
//be able to identify objects with the same name (John Smith), but that
//have different unique ids, since we only want one of each Contact object
//in this cache.
private List<Contact> contacts = Collections.synchronizedList(new ArrayList<Contact>());
/**
This method will execute every time someone makes a new Contact object.
If it already exists, return it from the cache in this.contacts. Otherwise,
proceed with the object construction and put that object in the cache.
**/
#Around("call(public Contact.new(Integer,String,String)) && args(id,name,phone)")
public Object cacheNewContact(ProceedingJoinPoint joinPoint, Integer id, String name, String phone) {
Contact contact = null;
for (Contact c : contacts) {
if (id.equals(c.getId()) {
contact = c;
break;
}
}
if (contact == null) {
contact = (Contact) joinPoint.proceed();
this.contacts.add(contact);
}
return contact;
}
/**This should execute every time a setPhone() method is executed on
a contact object. The method looks for all Contacts of the same
name in the cache and then sets their phone number to the one being passed
into the original target class.
Because objects are passed by reference until you do a reassociation,
calling c.setPhone on the object in the cache should update the actual
instance of the object in memory, so whoever has that reference will
get the updated information.
**/
#After("execution(example.Contact.setPhone(String) && args(phone)")
public void syncContact(JoinPoint joinPoint, String phone) {
Contact contact = joinPoint.getTarget();
for (Contact c : this.contacts) {
if (c.getName().equals(contact.getName()) {
c.setPhone(phone);
}
}
}
}
Again, there is probably 100 ways you could optimize this, since I'm typing it off the top of my head; that is, if you wanted to go this route in the first place. In theory it should work but I haven't tested it at all.
Anyway, Happy Springing!