I wanted to make a custom annotation to check security on some functions for my JSF web application. For security I use Tomcat security with JaaS, so I have no application managed security to my disposal.
What actually want to do is make an annotation for my methods in the Backing Beans like Spring Security (#Secured("role")). My security system is implemented so that every function is a role and you can dynamically make "user roles" these are stored in the DB and when somebody logs in all the (function)roles in that "user role" will be set in tomcat security as roles.
So now I have this piece of code to check if my user can access the function:
public static void checkSecurity(final String function) {
final FacesContext facesContext = FacesContext.getCurrentInstance();
try {
if (facesContext.getExternalContext().getRemoteUser() == null) {
facesContext.getExternalContext().redirect("login.xhtml");
return;
}
if (!facesContext.getExternalContext().isUserInRole(function)) {
facesContext.getExternalContext().redirect("restricted.xhtml");
return;
}
} catch (final Exception ex /* Mandatory "IOException e" will be caught + all other exceptions. */) {
facesContext.getExternalContext().setResponseStatus(403); // HTTP Status 403: Forbidden. Can also throw 401.
facesContext.responseComplete();
}
}
Now I have to call this SecurityUtil.checkSecurity("name_of_function"); in every method.
But I want to have an annotation like this #CustomSecurity("function_name_role").
#Target(ElementType.METHOD)
#Retention(RetentionPolicy.RUNTIME)
public #interface CustomSecurity {
// Single Element called value.
String value();
}
And when a method has this annotation the checkSecurity function automatically has to be performed. So I have to scan for this annotation at a point, or make some kind of actionlistener. JSF should have some options for this but all the forums I found on this don't really help.
Does somebody has some ideas?
EDIT:
I tried this blog it works but only on an action of a component (and components don't render when you don't have the role). So how secure is this when people try to hack into the JSF structure. And I rather have it running on every method.
public class SecurityActionListener extends ActionListenerImpl implements ActionListener {
private static final Logger LOGGER = FacesLogger.APPLICATION.getLogger();
#SuppressWarnings("unused")
#Override
public void processAction(final ActionEvent event) {
final FacesContext context = FacesContext.getCurrentInstance();
final Application application = context.getApplication();
final ConfigurableNavigationHandler navHandler = (ConfigurableNavigationHandler) application.getNavigationHandler();
// Action stuff
final UIComponent source = event.getComponent();
final ActionSource actionSource = (ActionSource) source;
MethodBinding binding;
binding = actionSource.getAction();
final String expr = binding.getExpressionString();
if (!expr.startsWith("#")) {
super.processAction(event);
return;
}
final int idx = expr.indexOf('.');
final String target = expr.substring(0, idx).substring(2);
final String t = expr.substring(idx + 1);
final String method = t.substring(0, (t.length() - 1));
final MethodExpression expression = new MethodExpressionMethodBindingAdapter(binding);
final ELContext elContext = context.getELContext();
final ExpressionFactory factory = context.getApplication().getExpressionFactory();
final ValueExpression ve = factory.createValueExpression(elContext, "#{" + target + '}', Object.class);
final Object result = ve.getValue(elContext);
// Check if the target method is a secured method
// and check security accordingly
final Method[] methods = result.getClass().getMethods();
for (final Method meth : methods) {
if (meth.getName().equals(method)) {
if (meth.isAnnotationPresent(CustomSecurity.class)) {
final CustomSecurity securityAnnotation = meth.getAnnotation(CustomSecurity.class);
System.out.println("Function to check security on: " + securityAnnotation.value()); // TODO TO LOG
SecurityUtil.checkSecurity(securityAnnotation.value());
} else {
super.processAction(event);
}
break;
}
}
}
}
And this in the faces-config.xml:
<action-listener>
com.nielsr.randompackagebecauseofnda.SecurityActionListener
</action-listener>
This blog could also be an answer, but I don't know how it will work with my JaaS Tomcat security because the security is in a separate project deployed as a standalone JAR in the tomcat lib folder.
But I actually don't know that I have to secure my Beans. Because I have configured all the functions (aka roles see above) that are on 1 page in the Web.xml as security constraints. And I render the components on the page only if you have to rights or "function_role" on that component. So is this secured enough? Or if somebody has a right to a function on a page can he render the components himself and so hack my site?
I'm not that familiar to JSF to know this, what is going on in that extra JSF abstraction layer between Controller and View? (I'm more of a Spring MVC developer, but because of requirements I have to use JSF but it's nice to broaden my knowledge.)
You can "scan for your Annotations" using
http://code.google.com/p/reflections/
Regards
Related
I am getting the below critical risk while scanning through Fortify tool.
method getUserLDAPNameFromAD() invokes a dynamically generated LDAP filter with unvalidated input,which could allow an attacker to modify the statement's meaning.
in class: ConsoleService
public class ConsoleService implements IConsoleService {
public char[] readPassword() throws IOError {
return System.console().readPassword();
}
and
public final class ConsoleUtils {
public static final String TERMINATION_CHARACTER = ".";
private static Scanner in = new Scanner(System.in);
being used in LDAPservice.java class i am getting LDAP injection error.
in below code lines:
public Attribute getUserLDAPNameFromAD(LDAPUserAttribute attribute) throws NamingException {
String accountName = attribute.constructName(Constants.DOT, true);
EqualsFilter equalsFilter = new EqualsFilter("sAMAccountName", accountName);
String FILTER = "(&(objectClass=user)(objectCategory=person)(" + equalsFilter.toString() + "))";
SearchControls ctls = new SearchControls();
ctls.setSearchScope(SearchControls.SUBTREE_SCOPE);
NamingEnumeration<SearchResult> answer = ctx.search(base, FILTER, ctls);
if (answer.hasMore()) {
SearchResult result = answer.next();
return result.getAttributes().get("distinguishedName");
}
return null;
}
Anybody having any idea how can i resolve this?
In order to fix the error you're getting in your static analysis tool you should use parameterized LDAP filters instead of doing string concatenation like you're doing. The risk with string concatenation is that a malicious user can potentially abuse the LDAP filter syntax to either always match, or always not match, which oftentimes has security implications in your application.
It looks like you're using Spring's EqualsFilter, which may properly escape the string. But the static analysis tool isn't acknowledging it.
To use parameterized LDAP filters you can do the following (as described in the JNDI tutorial):
ctx.search(base,
"(&(objectClass=user)(objectCategory=person)(sAMAccountName={0}))",
new Object[] { accountName },
ctls);
I'm working on a solution in Adobe Experience Manager (AEM) that receives an HTTP request containing a URL to a file, which I want to download and store in the JCR.
So, I have a servlet that receives the request. It spawns a thread so that I can do the download in the background, and then redirects to a confirmation page. This allows me to send the user on their way without waiting while I try to download the file.
I can download the file just fine, but I'm having trouble getting a usable ResourceResolver to store the file in the JCR from my thread.
At first, I simply referenced the request's ResourceResolver in the background thread:
Servlet:
public void doGet(SlingHttpServletRequest request, SlingHttpServletResponse response)
throws ServletException, IOException {
...
signingProvider.getDocFromService(params, request.getResourceResolver());
response.sendRedirect(confirmationPage);
}
And in the provider class:
public void getDocFromService(Map<String, String> params, ResourceResolver resolver) {
new Thread( new Runnable() {
public void run() {
Session session = null;
if (resolver != null) {
session = resolver.adaptTo(Session.class);
Node root = session.getRootNode();
...
}
}
}
}
but that didn't work. After reading up on resolvers vs threads, I thought I would be better off creating a new Resolver instance, so I tried to inject a ResourceResolverFactory:
Servlet:
signingProvider.getDocFromService(params);
Provider:
public void getDocFromService(Map<String, String> params) {
new Thread( new Runnable() {
#Reference
private ResourceResolverFactory resolverFactory;
// security hole, fix later
ResourceResolver resolver = resolverFactory.getAdministrativeResourceResolver(null);
Session session = null;
if (resolver != null) {
session = resolver.adaptTo(Session.class);
Node root = session.getRootNode();
...
}
}
}
but the ResourceResolverFactory is null, so I crash when asking it for a resolver. Apparently, no factory is getting injected into the #Reference
I would really rather not do the work on the main thread; after I download the file I'm going to turn around and read it from the JCR and copy it elsewhere. Both of these operations could be slow or fail. I have a copy of the file at the original URL, so the end-user needn't care if my download/uploads had trouble. I just want to send them a confirmation so they can get on with business.
Any advice on how to get a ResourceResolver in a separate thread?
For things like post\background processing you can use Sling Jobs. Please refer to the documentation to find out some details.
Note: #daniil-stelmakh brings a good point in his answer, sling jobs are much better suited for your purpose, to add to his answer, here is a sling tutorial that demonstrates sling jobs: https://sling.apache.org/documentation/tutorials-how-tos/how-to-manage-events-in-sling.html
To answer your question directly:
The issue, really is the placement of #Reference annotation.
That annotation is handled by Maven SCR Plugin and it should be placed on a private member of a '#Component' annotated class.
Basically move your ResourceResolverFactory declaration to become a private member of your class, not the Thread.
#Component(
label = "sample service",
description = "sample service"
)
#Service
public class ServiceImpl {
#Reference
private ResourceResolverFactory resolverFactory;
public void getDocFromService(Map<String, String> params) {
new Thread( new Runnable() {
// security hole, fix later
ResourceResolver resolver = resolverFactory.getAdministrativeResourceResolver(null);
Session session = null;
if (resolver != null) {
session = resolver.adaptTo(Session.class);
Node root = session.getRootNode();
...
}
}
}
}
Is it possible to have my app update the config settings at runtime? I can easily expose the settings I want in my UI but is there a way to allow the user to update settings and make them permanent ie save them to the config.yaml file? The only way I can see it to update the file by hand then restart the server which seems a bit limiting.
Yes. It is possible to reload the service classes at runtime.
Dropwizard by itself does not have the way to reload the app, but jersey has.
Jersey uses a container object internally to maintain the running application. Dropwizard uses the ServletContainer class of Jersey to run the application.
How to reload the app without restarting it -
Get a handle to the container used internally by jersey
You can do this by registering a AbstractContainerLifeCycleListener in Dropwizard Environment before starting the app. and implement its onStartup method as below -
In your main method where you start the app -
//getting the container instance
environment.jersey().register(new AbstractContainerLifecycleListener() {
#Override
public void onStartup(Container container) {
//initializing container - which will be used to reload the app
_container = container;
}
});
Add a method to your app to reload the app. It will take in the list of string which are the names of the service classes you want to reload. This method will call the reload method of the container with the new custom DropWizardConfiguration instance.
In your Application class
public static synchronized void reloadApp(List<String> reloadClasses) {
DropwizardResourceConfig dropwizardResourceConfig = new DropwizardResourceConfig();
for (String className : reloadClasses) {
try {
Class<?> serviceClass = Class.forName(className);
dropwizardResourceConfig.registerClasses(serviceClass);
System.out.printf(" + loaded class %s.\n", className);
} catch (ClassNotFoundException ex) {
System.out.printf(" ! class %s not found.\n", className);
}
}
_container.reload(dropwizardResourceConfig);
}
For more details see the example documentation of jersey - jersey example for reload
Consider going through the code and documentation of following files in Dropwizard/Jersey for a better understanding -
Container.java
ContainerLifeCycleListener.java
ServletContainer.java
AbstractContainerLifeCycleListener.java
DropWizardResourceConfig.java
ResourceConfig.java
No.
Yaml file is parsed at startup and given to the application as Configuration object once and for all. I believe you can change the file after that but it wouldn't affect your application until you restart it.
Possible follow up question: Can one restart the service programmatically?
AFAIK, no. I've researched and read the code somewhat for that but couldn't find a way to do that yet. If there is, I'd love to hear that :).
I made a task that reloads the main yaml file (it would be useful if something in the file changes). However, it is not reloading the environment. After researching this, Dropwizard uses a lot of final variables and it's quite hard to reload these on the go, without restarting the app.
class ReloadYAMLTask extends Task {
private String yamlFileName;
ReloadYAMLTask(String yamlFileName) {
super("reloadYaml");
this.yamlFileName = yamlFileName;
}
#Override
public void execute(ImmutableMultimap<String, String> parameters, PrintWriter output) throws Exception {
if (yamlFileName != null) {
ConfigurationFactoryFactory configurationFactoryFactory = new DefaultConfigurationFactoryFactory<ReportingServiceConfiguration>();
ValidatorFactory validatorFactory = Validation.buildDefaultValidatorFactory();
Validator validator = validatorFactory.getValidator();
ObjectMapper objectMapper = Jackson.newObjectMapper();
final ConfigurationFactory<ServiceConfiguration> configurationFactory = configurationFactoryFactory.create(ServiceConfiguration.class, validator, objectMapper, "dw");
File confFile = new File(yamlFileName);
configurationFactory.build(new File(confFile.toURI()));
}
}
}
You can change the configuration in the YAML and read it while your application is running. This will not however restart the server or change any server configurations. You will be able to read any changed custom configurations and use them. For example, you can change the logging level at runtime or reload other custom settings.
My solution -
Define a custom server command. You should use this command to start your application instead of the "server" command.
ArgsServerCommand.java
public class ArgsServerCommand<WC extends WebConfiguration> extends EnvironmentCommand<WC> {
private static final Logger LOGGER = LoggerFactory.getLogger(ArgsServerCommand.class);
private final Class<WC> configurationClass;
private Namespace _namespace;
public static String COMMAND_NAME = "args-server";
public ArgsServerCommand(Application<WC> application) {
super(application, "args-server", "Runs the Dropwizard application as an HTTP server specific to my settings");
this.configurationClass = application.getConfigurationClass();
}
/*
* Since we don't subclass ServerCommand, we need a concrete reference to the configuration
* class.
*/
#Override
protected Class<WC> getConfigurationClass() {
return configurationClass;
}
public Namespace getNamespace() {
return _namespace;
}
#Override
protected void run(Environment environment, Namespace namespace, WC configuration) throws Exception {
_namespace = namespace;
final Server server = configuration.getServerFactory().build(environment);
try {
server.addLifeCycleListener(new LifeCycleListener());
cleanupAsynchronously();
server.start();
} catch (Exception e) {
LOGGER.error("Unable to start server, shutting down", e);
server.stop();
cleanup();
throw e;
}
}
private class LifeCycleListener extends AbstractLifeCycle.AbstractLifeCycleListener {
#Override
public void lifeCycleStopped(LifeCycle event) {
cleanup();
}
}
}
Method to reload in your Application -
_ymlFilePath = null; //class variable
public static boolean reloadConfiguration() throws IOException, ConfigurationException {
boolean reloaded = false;
if (_ymlFilePath == null) {
List<Command> commands = _configurationBootstrap.getCommands();
for (Command command : commands) {
String commandName = command.getName();
if (commandName.equals(ArgsServerCommand.COMMAND_NAME)) {
Namespace namespace = ((ArgsServerCommand) command).getNamespace();
if (namespace != null) {
_ymlFilePath = namespace.getString("file");
}
}
}
}
ConfigurationFactoryFactory configurationFactoryFactory = _configurationBootstrap.getConfigurationFactoryFactory();
ValidatorFactory validatorFactory = _configurationBootstrap.getValidatorFactory();
Validator validator = validatorFactory.getValidator();
ObjectMapper objectMapper = _configurationBootstrap.getObjectMapper();
ConfigurationSourceProvider provider = _configurationBootstrap.getConfigurationSourceProvider();
final ConfigurationFactory<CustomWebConfiguration> configurationFactory = configurationFactoryFactory.create(CustomWebConfiguration.class, validator, objectMapper, "dw");
if (_ymlFilePath != null) {
// Refresh logging level.
CustomWebConfiguration webConfiguration = configurationFactory.build(provider, _ymlFilePath);
LoggingFactory loggingFactory = webConfiguration.getLoggingFactory();
loggingFactory.configure(_configurationBootstrap.getMetricRegistry(), _configurationBootstrap.getApplication().getName());
// Get my defined custom settings
CustomSettings customSettings = webConfiguration.getCustomSettings();
reloaded = true;
}
return reloaded;
}
Although this feature isn't supported out of the box by dropwizard, you're able to accomplish this fairly easy with the tools they give you.
Before I get started, note that this isn't a complete solution for the question asked as it doesn't persist the updated config values to the config.yml. However, this would be easy enough to implement yourself simply by writing to the config file from the application. If anyone would like to write this implementation feel free to open a PR on the example project I've linked below.
Code
Start off with a minimal config:
config.yml
myConfigValue: "hello"
And it's corresponding configuration file:
ExampleConfiguration.java
public class ExampleConfiguration extends Configuration {
private String myConfigValue;
public String getMyConfigValue() {
return myConfigValue;
}
public void setMyConfigValue(String value) {
myConfigValue = value;
}
}
Then create a task which updates the config:
UpdateConfigTask.java
public class UpdateConfigTask extends Task {
ExampleConfiguration config;
public UpdateConfigTask(ExampleConfiguration config) {
super("updateconfig");
this.config = config;
}
#Override
public void execute(Map<String, List<String>> parameters, PrintWriter output) {
config.setMyConfigValue("goodbye");
}
}
Also for demonstration purposes, create a resource which allows you to get the config value:
ConfigResource.java
#Path("/config")
public class ConfigResource {
private final ExampleConfiguration config;
public ConfigResource(ExampleConfiguration config) {
this.config = config;
}
#GET
public Response handleGet() {
return Response.ok().entity(config.getMyConfigValue()).build();
}
}
Finally wire everything up in your application:
ExampleApplication.java (exerpt)
environment.jersey().register(new ConfigResource(configuration));
environment.admin().addTask(new UpdateConfigTask(configuration));
Usage
Start up the application then run:
$ curl 'http://localhost:8080/config'
hello
$ curl -X POST 'http://localhost:8081/tasks/updateconfig'
$ curl 'http://localhost:8080/config'
goodbye
How it works
This works simply by passing the same reference to the constructor of ConfigResource.java and UpdateConfigTask.java. If you aren't familiar with the concept see here:
Is Java "pass-by-reference" or "pass-by-value"?
The linked classes above are to a project I've created which demonstrates this as a complete solution. Here's a link to the project:
scottg489/dropwizard-runtime-config-example
Footnote: I haven't verified this works with the built in configuration. However, the dropwizard Configuration class which you need to extend for your own configuration does have various "setters" for internal configuration, but it may not be safe to update those outside of run().
Disclaimer: The project I've linked here was created by me.
I have a filter which maps just before the Faces Servlet and populate some Attributes in the HttpSession before it proceeds with the request
if (_clientUser != null && !_clientUser.isEmpty()) {
session.setAttribute(CLIENT_USER_URI_PARAM_NAME, _clientUser);
filterChain.doFilter(servletRequest, servletResponse);
return;
}
Within the URI there are some informations for which Task-flow should be addressed
faces/adf.task-flow?adf.tfId=task-flow-definition-id&adf.tfDoc=/WEB-INF/task-flow-definition-id.xml
After I proceed the request there is a method-call within the bounded task flow which is called before the view (lets call the view MainPage), by making it default activity
<default-activity>doStuff</default-activity>
The Method itself is within a PageFlowScoped Managed Bean, which is added to the task-flow-definition-id.xml
Here is the doStuff Method which is called before the "MainPage"-View
public class Controller{
public void doStuff {
FacesContext ctx = FacesContext.getCurrentInstance();
Map sessionMap = ctx.getExternalContext().getSessionMap();
String clientUser = (String) sessionMap.get(CLIENT_USER_URI_PARAM_NAME);
AppModImpl am = getApplicationModule();
DBTransaction transaction = am.getDBTransaction();
//do more stuff
}
}
public AppModImpl getApplicationModule() {
FacesContext fc = FacesContext.getCurrentInstance();
Application app = fc.getApplication();
ExpressionFactory elFactory = app.getExpressionFactory();
ELContext elContext = fc.getELContext();
ValueExpression valueExp =
elFactory.createValueExpression(elContext, "#{data.AppModDataControl.dataProvider}", Object.class);
AppModImpl am = (AppModImpl) valueExp.getValue(elContext);
return am;
}
Everything works fine till here. But if I try to get the Application Module. There is non.
This application normally doesn't have any DataControl, because is just a root Application which holds different components which have been added by ADFLibrarys, but I added an DataControl to the DataBindings.cpx and a iterator to the MainPagePageDef (just to include a DataControl in the bindings) but still no result.
Do you have any advice for me how I can access the Application Module? FacesServlet should have been already created the necessary FacesContext by the time the Managed Bean is called or does it only create a new FacesContext the Time an actual Page (jspx/jsf) loads?
I've found myself an answer:
Here is the Block Post which helped me figure it out.
http://andrejusb.blogspot.de/2012/02/how-to-fix-data-control-initialization.html
I'd like to write an interceptor for the Apache CXF JAX-RS implementation that inspects the target service/method for a particular annotation and does some special processing for that annotation.
I can't seem to find anything in the interceptor documentation that describes how to do this. Does anyone have any ideas?
Thanks!
If the interceptor runs fairly late in the chain (like the USER_LOGICAL
phase), you should be able to do something like:
Exchange exchange = msg.getExchange();
BindingOperationInfo bop = exchange.get(BindingOperationInfo.class);
MethodDispatcher md = (MethodDispatcher)
exchange.get(Service.class).get(MethodDispatcher.class.getName());
Method meth = md.getMethod(bop);
That should give you the Method that was bound in so you can get the declared
class or the annotations, etc...
Ah. I didn't specify that I was using the JAX-RS part of CXF; not sure if that impacts Daniel Kulp's answer but his solution didn't actually work for me. I believe it is because CXF does things differently when handling JAX-RS.
I came across the source for CXF's [JAXRSInInterceptor][1] and I saw in that code that this interceptor is putting the method info into the Exchange object like so:
message.getExchange().put(OperationResourceInfo.class, ori);
...during the UNMARSHAL phase, which according to the CXF interceptor docs happens before the *_LOGICAL phase. So by writing an Interceptor that handles the USER_LOGICAL phase I can do:
message.getExchange().get(OperationResourceInfo.class)
...to get access in there to the Method and Class<?> of the Service handling the call!
Building off the original interrogator's answer, I came up with this
public UserContextInterceptor() {
super(Phase.USER_LOGICAL);
}
#Override
public void handleMessage(Message message) {
if(StringUtils.isEmpty(getHeader("some-header-name", message))) {
final Method method = getTargetMethod(message);
if(isAnnotated(method.getDeclaringClass().getAnnotations()) || isAnnotated(method.getAnnotations())) {
final Fault fault = new Fault(new LoginException("Missing user id"));
fault.setStatusCode(HttpServletResponse.SC_UNAUTHORIZED);
throw fault;
}
}
}
private static Method getTargetMethod(Message message) {
final Exchange exchange = message.getExchange();
final OperationResourceInfo resource = exchange.get(OperationResourceInfo.class);
if(resource == null || resource.getMethodToInvoke() == null) {
throw new AccessDeniedException("Method is not available");
}
return resource.getMethodToInvoke();
}
private static boolean isAnnotated(Annotation[] annotations) {
for(Annotation annotation : annotations) {
if(UserRequired.class.equals(annotation.annotationType())) {
return true;
}
}
return false;
}
It has been quite some time since the accepted answer. But there are a few supporting abstractions provided in the
cxf-rt-core-2.7.3.jar
One in there that is provided is org.apache.cxf.interceptor.security.AbstractAuthorizingInInterceptor
This sample excerpt from the source might be a good reference:
protected Method getTargetMethod(Message m) {
BindingOperationInfo bop = m.getExchange().get(BindingOperationInfo.class);
if (bop != null) {
MethodDispatcher md = (MethodDispatcher)
m.getExchange().get(Service.class).get(MethodDispatcher.class.getName());
return md.getMethod(bop);
}
Method method = (Method)m.get("org.apache.cxf.resource.method");
if (method != null) {
return method;
}
throw new AccessDeniedException("Method is not available : Unauthorized");
}