I'm working on a large Grails 1.3.7 project and I want to access the flow state name from a filter for logging purposes. I've been googling a lot and the closest answer I could find was: Grails WebFlow State Name, but it only works from within the flow itself.
Is there any way to obtain the state name of the flow that is being executed in current session from outside the flow (the filter)?
Thanks in advance,
Guillermo
After a lot of googling and debugging I managed to produce the following code. It works in a simple application. I'll integrate it with the main application when I come back from holidays and then I'll update this question.
package org.glalejos
import org.springframework.context.ApplicationContext
import org.springframework.webflow.context.ExternalContext
import org.springframework.webflow.context.ExternalContextHolder
import org.springframework.webflow.context.servlet.ServletExternalContext
import org.springframework.webflow.execution.FlowExecution
import org.springframework.webflow.execution.FlowExecutionKey
import org.springframework.webflow.execution.repository.FlowExecutionRepository
class LoggingFilters {
def grailsApplication
String getFlowStateName(def grailsApplication, def servletContext, def request, def response) {
String stateName
if (grailsApplication && servletContext && request && request.queryString && response) {
try {
String strKey = null
String[] keys = request.queryString.split("&")
keys.each{ if (it.startsWith("execution=")) strKey = it.substring(10)}
if (strKey != null) {
ApplicationContext ctx = grailsApplication.mainContext
FlowExecutionRepository fer = ctx.getBean("flowExecutionRepository")
FlowExecutionKey fek = fer.parseFlowExecutionKey(strKey)
ExternalContext previousContext = ExternalContextHolder.getExternalContext()
try {
// You have to set an external context before invoking "fer.getFlowExecution()" or it'll throw a NPE
ExternalContextHolder.setExternalContext(new ServletExternalContext(servletContext, request, response));
FlowExecution fe = fer.getFlowExecution(fek)
stateName = fe.getActiveSession().getState().getId()
} finally {
ExternalContextHolder.setExternalContext(previousContext);
}
} else {
stateName = null
}
} catch(Exception e) {
stateName = null
}
} else {
stateName = null
}
return stateName
}
def filters = {
logData(controller:"*", action:"*") {
before = {
println("Incoming request. Current flow state name is: ${getFlowStateName(grailsApplication, servletContext, request, response)}")
}
after = {
println("Dispatched request. Current flow state name is: ${getFlowStateName(grailsApplication, servletContext, request, response)}")
}
}
}
}
EDIT: The code above is OK to determine the name of the current flow state at a given point in time, but it won't update the Mapped Diagnostic Context of the logging framework as the flow execution evolves. For this purpose it is necessary to implement a org.springframework.webflow.execution.FlowExecutionListener and register it in conf/spring/resources.groovy:
beans = {
myLoggingFlowExecutionListener(org.example.MyLoggingFlowExecutionListener)
}
You have to register this listener bean and the hibernateConversationListener bean in a executionListenerLoader bean, but, for some reason, Spring DSL doesn't work in this case (see EDIT2 below). So here is the resources.xml you can place in the same folder as resources.groovy in order to properly declare your resources:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd">
<bean id="executionListenerLoader" class="org.springframework.webflow.execution.factory.StaticFlowExecutionListenerLoader">
<constructor-arg>
<list>
<ref bean="hibernateConversationListener" />
<ref bean="myLoggingFlowExecutionListener" />
</list>
</constructor-arg>
</bean>
</beans>
Each FlowExecutionListener method receives a lot of context information that can be used for logging purposes (I'm ommiting the implementation of this class for clarity).
EDIT2: Failing to add the hibernateConversationListener bean to the executionListenerLoader results in Hibernate exceptions when manipulating domain objects during the lifecycle of the flow. However, Spring DSL doesn't work in this specific case, so I had to declare the required beans using XML format. See http://grails.1312388.n4.nabble.com/Registering-custom-flow-execution-listener-td2279764.html. I've updated the code above to the final, working, version.
Related
I have searched for a good example and cannopt find one. I want to take the username and password from the SOAP header, and set the spring security context after I authenticate using our exisiting service methods. I have implemented the Wss4jSecurityInterceptor and it validates the header element. WHat I need to do in the callback, or some other mechanism, is create an uthetication context so I can access it later in our endpoint.
However, I dont think that the callback is the correct place to do it, as I keep getting password supplied no password errors. I am new to spring security and integration.
Config:
<bean id="SOAPSecurityInterceptor" class="com.ps.snt.ws.interceptor.SOAPSecurityInterceptor">
<property name="validationActions" value="UsernameToken"/>
<property name="validationCallbackHandler" ref="callbackHandler"/>
</bean>
<bean id="callbackHandler" class="com.ps.snt.ws.interceptor.SOAPSecurityValidationCallbackHandler">
</bean>
callback:
public class SOAPSecurityValidationCallbackHandler extends SimplePasswordValidationCallbackHandler {
#Override
protected void handleUsernameToken(WSPasswordCallback callback) throws IOException, UnsupportedCallbackException {
System.out.println("In security callback " + callback.getPassword());
boolean valid = true;
String token = callback.getIdentifier();
String password = callback.getPassword();
Integer zoneID = null;
String username = null;
StringBuffer errorMessages = new StringBuffer();
if(StringUtils.isEmpty(token)) {
errorMessages.append("Username token cannot be empty");
valid = false;
} else {
Pattern pattern = Pattern.compile("^[\\w]+\\d\\d\\d\\d\\d");
Matcher matcher = pattern.matcher(token);
if(!matcher.matches()) {
valid = false;
errorMessages.append("Username token must be in the format 'user#zone'.");
}
else {
String[] parts = token.split("#");
username = parts[0];
zoneID = Integer.parseInt(parts[1]);
}
}
if(StringUtils.isEmpty(password)) {
errorMessages.append("Password cannot be empty.");
valid = false;
}
if(valid && username != null && zoneID != null) {
LoginService loginService = new LoginService();
LoginContextDO loginContextDO = loginService.getAuthenticatedLoginContext(username, password, zoneID);
AbstractAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(username, password);
authentication.setDetails(loginContextDO);
authentication.setAuthenticated(true);
SecurityContextHolder.getContext().setAuthentication(authentication);
} else {
System.out.println("Authetnication failed!");
}
}
}
My requirements are simple:
- validate the SOAP header (works)
- retrieve the username and password
- call our legacy service to create our login context
- set the spring security context (with logincontext as details) so I can use later in an endpoint
What mechanism can I use to validate the soap header and set a security context from that header?
SpringSecurityPasswordValidationCallbackHandler is for you. From Spring WS docs:
The SpringSecurityPasswordValidationCallbackHandler validates plain text and digest passwords using a Spring Security UserDetailService to operate. It uses this service to retrieve the (digest of ) the password of the user specified in the token. The (digest of) the password contained in this details object is then compared with the digest in the message. If they are equal, the user has successfully authenticated, and a UsernamePasswordAuthenticationToken is stored in theSecurityContextHolder. You can set the service using the userDetailsService. Additionally, you can set a userCache property, to cache loaded user details.
<beans>
<bean class="org.springframework.ws.soap.security.wss4j.callback.SpringDigestPasswordValidationCallbackHandler">
<property name="userDetailsService" ref="userDetailsService"/>
</bean>
<bean id="userDetailsService" class="com.mycompany.app.dao.UserDetailService" />
...
</beans>
It's fairly easy to use custom JSP login page in Spring Security. Our application is based on Vaadin though and I don't want to have JSP login page. What I want is custom fancy login window created as Vaadin widget.
Well technically I can use Vaadin's FormLayout and name fields like j_username and j_password ... but this is Java class and not JSP file, so what do I specify in http Spring Security element? I mean:
<http auto-config='true'>
<intercept-url pattern="/**" access="ROLE_USER" />
<form-login login-page='MyLoginWindow.java or what?' />
</http>
I'm totally unfamiliar with Spring Security, but one of our guys at Vaadin made a demo application a couple of years ago https://github.com/peholmst/SpringSecurityDemo. I don't know if it's still up-to-date or if it even answers your question, but perhaps you could have a look and see for yourself if you can get any answers from there. Otherwise you could perhaps ping Petter personally and see if he has any fresh ideas about the topic.
Use LoginForm and in LoginListener use something like this
try {
val authentication = new UsernamePasswordAuthenticationToken(name, pass)
SecurityContextHolder.getContext.setAuthentication(authenticationManager.authenticate(authentication))
} catch {
case e: AuthenticationException => {
SecurityContextHolder.clearContext()
}
}
Please see below my approach. The code shows the functionality that occurs when clicking the login button.
loginBtn.addListener(new Button.ClickListener() {
#Override
public void buttonClick(ClickEvent event) {
// Getting the helper for working with spring context
// found here https://vaadin.com/wiki/-/wiki/Main/Spring%20Integration
SpringContextHelper helper = new SpringContextHelper(getApplication());
// Get the providerManagerBean
ProviderManager authenticationManager = (ProviderManager)helper
.getBean("authenticationManager");
// Get entered data for name and password
String name = usernameEntered;
String password = passwordEntered;
// Validation
if (StringUtils.isBlank(name) || StringUtils.isBlank(password)) {
getWindow().showNotification("Username or password cannot be empty",
Notification.TYPE_ERROR_MESSAGE);
} else {
try {
// Security functionality goes here
UsernamePasswordAuthenticationToken token =
new UsernamePasswordAuthenticationToken(name, password);
Authentication authentication = authenticationManager.authenticate(token);
// Set the authentication info to context
SecurityContextHolder.getContext().setAuthentication(authentication);
// During the authentification the AppUser instance was set as
// details, for more info about the user
AppUser user = (AppUser) authentication.getDetails();
if (user != null) {
// Switch the view after succesfull login
getApplication().getMainWindow().setContent(new ComboBoxUserStartsWith());
}
} catch (AuthenticationException e) {
// Display error occured during logining
getWindow().showNotification(e.getMessage(), Notification.TYPE_ERROR_MESSAGE);
}
}
}
});
I have a struts2 application which uses the struts2-rest-plugin v.2.2.3.
Everything is working great when it comes to the routing of the requests to the actions and its methods and I'm also using ModelDriven to specify the object to serialise when using extensions like JSON and XML.
The problem I'm having is that when I send a POST or PUT request to the struts layer I just get an empty response.
I am sending a POST request to the action like so: http://localhost:8080/alert-settings!update.json. I have a breakpoint in that method and it gets called and the code runs and completes. I have a feeling the issue might be that I am trying to use the ModelDriven interface to send me back the response and for some reason the rest-plugin doesn't like this but I don't know why it would behave like that.
Is there a known issue with receiving responses from POST requests while using the rest plugin? I have looked everywhere and can't find anything about it really.
Any help appreciated and I can provide any more details on request.
I encountered the same issue. Have you tried to set in the struts.xml file:
struts.rest.content.restrictToGET = false
See the last setting on the rest plugin docs
I actually figured out that it was a line in the rest plugin causing this:
// don't return any content for PUT, DELETE, and POST where there are no errors
if (!hasErrors && !"get".equalsIgnoreCase(ServletActionContext.getRequest().getMethod())) {
target = null;
}
This is in org.apache.struts2.rest.RestActionInvocation in the selectTarget() method. I find this to be quite annoying as it doesn't really follow the REST architecture, id like the option to be able to return response objects for POST, DELETE and PUT requests in some cases.
I worked around this by extending RestActionProxyFactory and RestActionInvocation and specifying the use of this in my struts xml like so:
<bean type="com.opensymphony.xwork2.ActionProxyFactory" name="restOverride" class="uk.co.ratedpeople.tp.rest.RPRestActionProxyFactory" />
<constant name="struts.actionProxyFactory" value="restOverride" />
This allows me to use the struts plugin throughout while returning object on POST requests.
RestActionProxyFactory
public class RPRestActionProxyFactory extends RestActionProxyFactory {
#Override
public ActionProxy createActionProxy(String namespace, String actionName, String methodName, Map extraContext, boolean executeResult, boolean cleanupContext) {
if (namespace.startsWith(this.namespace)) {
ActionInvocation inv = new RPRestActionInvocation(extraContext, true);
container.inject(inv);
return createActionProxy(inv, namespace, actionName, methodName, executeResult, cleanupContext);
} else {
return super.createActionProxy(namespace, actionName, methodName, extraContext, executeResult, cleanupContext);
}
}
}
RestActionInvocation
public class RPRestActionInvocation extends RestActionInvocation {
public RPRestActionInvocation(Map extraContext, boolean pushAction) {
super(extraContext, pushAction);
}
#SuppressWarnings("unchecked")
#Override
protected void selectTarget() {
// Select target (content to return)
Throwable e = (Throwable)stack.findValue("exception");
if (e != null) {
// Exception
target = e;
hasErrors = true;
} else if (action instanceof ValidationAware && ((ValidationAware)action).hasErrors()) {
// Error messages
ValidationAware validationAwareAction = ((ValidationAware)action);
Map errors = new HashMap();
if (validationAwareAction.getActionErrors().size() > 0) {
errors.put("actionErrors", validationAwareAction.getActionErrors());
}
if (validationAwareAction.getFieldErrors().size() > 0) {
errors.put("fieldErrors", validationAwareAction.getFieldErrors());
}
target = errors;
hasErrors = true;
} else if (action instanceof ModelDriven) {
// Model
target = ((ModelDriven)action).getModel();
} else {
target = action;
}
// don't return any content for PUT, DELETE, and POST where there are no errors
// if (!hasErrors && !"get".equalsIgnoreCase(ServletActionContext.getRequest().getMethod())) {
// target = null;
// }
}
}
I've used struts actions with mixed result types in the past, returning json, xml, and tiles for instance. I'm not sure if it's the recommended way to do it but it requires some configuration using struts.xml even though conventions are being used. Maybe you've already done this, not sure there isn't enough info provided to tell.
Struts.xml settings:
<constant name="struts.convention.default.parent.package" value="restful"/>
<package name="restful" extends="rest-default, struts-default, json-default">
<result-types>
<result-type name="tiles" class="org.apache.struts2.views.tiles.TilesResult" />
<result-type name="json" class="com.googlecode.jsonplugin.JSONResult"/>
</result-types>
....
</package>
I have setup the extra result types to be used on specific actions later. In the action class you can then setup your result types by action or method.
Action Class:
#Results({
#Result(name = "JsonSuccess", type = "json"),
#Result(name = "success", type = "tiles", location = "/tickets.tiles")
})
public class EventController extends RestActionSupport implements ModelDriven<EventBean>{
...
}
Something else to note about json results, I've noticed that when I have a serializable object being returned as a result, if that object contains other complex objects with a getter/setter that returns the embedded object, I will often receive an empty result or no result. I often end up writing json wrapper objects to use for my json results with getters/setters that only return java types (String, int, Boolean, etc) and not the embedded objects. I think that've solved this using delegate getters/setters but I'll have to go back and look at some old code.
We have an application where users with proxy rights need to be able to see links in an application.
For example, we might have:
<s:intercept-url pattern="/resourceManager.htm" access=" ROLE_ADMIN_GROUP, ROLE_PROXY"/>
If the user has the role of proxy, but not the admin role, I need to present them with a page telling them that they need to be in proxy mode to see this page. Additionally, I need to check the permissions of the user they proxy to, to verify they have the correct role.
We have multiple pages, so I'd like to like to do this logic in a filter, so we can apply the logic across the board.
I'm mocking this up in pseudo code while I continue to research.
class Filter
{
protected void doFilterHttp()
{
//proxy summary is session based object
if(proxySummary.isProxyMode())
{
user = proxySummary.getProxiedUser()
//here load user's authorities
//will have to look at ldap authorities populator, but I should be able to work this part out
}
if(user.getGrantedAuthorities.contains("Role_Proxy"))
{
//Is there any way to tell possible valid roles for a url?
if(url.getPossibleRoles() intersect user.getGrantedAuthorities().size == 1 &&
intersection.contains(Role_Proxy))
{ redirectToProxyPage(); }
}
}
What's the best way to get any metadata for the url I'm attempting to access?
If there is no way to get information on all allowable roles for a url, then I imagine I would have to do it at the page.
Would upgrading to Spring Security 3 give me any more flexibility?
I ended up creating a runAsManager implementation that ran as the proxy user if in proxy mode. Otherwise, if the user only had a proxy role for the link, they were redirected. The runAsManager only modified the authentication object when in proxy mode.
I've included snippets of each class, so as not to make the post too long.
RunAsProxy Snippet
public Authentication buildRunAs(Authentication authentication, Object object,
ConfigAttributeDefinition config) {
//probably need to do something to cache the proxied user's roles
if(proxySummary.isProxyMode())
{
SpringSecurityLdapTemplate template = new SpringSecurityLdapTemplate(contextSource);
String dn = proxySummary.getLoggedInUser();
String [] tmp = { "uid", "cn" };
DirContextOperations user = template.retrieveEntry(dn, tmp);
GrantedAuthority[] proxiedAuthorities = authoritiesPopulator.getGrantedAuthorities(user, user.getStringAttribute("cn").toString());
return new RunAsUserToken(this.key, authentication.getPrincipal(), authentication.getCredentials(),
proxiedAuthorities, authentication.getClass());
}
return null;
}
Interceptor Code -> extends AbstractSecurityInterceptor implements Filter, Ordered
public void invoke(FilterInvocation fi) throws IOException, ServletException {
//same code as from proxy security interceptor here ...
//config attributes are the roles assigned to a link
ConfigAttributeDefinition cad = ((DefaultFilterInvocationDefinitionSource)objectDefinitionSource).lookupAttributes(fi.getRequestUrl());
if(cad != null)
{
HashSet<String> configAttributes = new HashSet<String>();
for(Object ca: cad.getConfigAttributes())
{
configAttributes.add(((ConfigAttribute)ca).getAttribute());
}
SecurityContext sc = SecurityContextHolder.getContext();
HashSet<String> authorities = new HashSet<String>();
for(GrantedAuthority ga: sc.getAuthentication().getAuthorities())
{
authorities.add(ga.getAuthority());
}
//intersection and remaining available roles to determine
//if they just have the proxy role
authorities.retainAll(configAttributes);
if(authorities.size() == 1 && authorities.contains("ROLE_PROXY"))
{
//redirect to page telling them to proxy
((HttpServletResponse)fi.getResponse()).sendRedirect("jsp/doProxy.jsp");
}
//System.out.println(cad);
fi.getRequest().setAttribute(FILTER_APPLIED, Boolean.TRUE);
//other boilderplate code
}
Spring Setup
<bean id="proxySecurityInterceptor" class="org.springframework.security.intercept.web.ProxySecurityInterceptor">
<property name="authenticationManager" ref="authenticationManager"/>
<property name="accessDecisionManager" ref="_accessManager"/>
<property name="proxySummary" ref="proxySummary" />
<property name="runAsManager" ref="runAsProxy" />
<property name="objectDefinitionSource">
<s:filter-invocation-definition-source>
<s:intercept-url pattern="/groupManager.htm*" access="ROLE_GLOBAL_ADMIN, ROLE_ADMIN_GROUP, ROLE_PROXY"/>
</s:filter-invocation-definition-source>
</property>
<s:custom-filter after="FILTER_SECURITY_INTERCEPTOR" />
</bean>
I've developed a web site using Struts2 as a controller and integrated it with Spring and Hibernate to do the business logic and DB stuff. The website's URIs are http://my.domian.com/URI; which {URI} is dynamically generated thorough the admin cms. The mapping of each uri to the servlet are done with help of Apache mod_rewrite, as follow:
RewriteCond %{HTTP_HOST} ^www\.domain\.com
RewriteRule ^([a-zA-Z0-9_-]+)$ /dynamic\.action?f=$1 [QSA,L]
(Before any further information, is this a good and suitable approach?)
The struts configuration is just a typically-academic one as:
<package name="Default" extends="struts-default" namespace="/">
...
<action name="dynamic" class="DynamicContentAction">
<result name="index">/content/web/dynamic/index.jsp</result>
</action>
</package>
DynamicContentAction is extending ActionSupport and implementing ServletRequestAware, ServletContextAware. I'm checking a couple of things (such as a current visiting language which is identified as a subdomain), looking up in the database that the requested uri is valid or not, generating that uri's content and setting a couple of runtime global variables (such as current visiting page id, layout config due to current visiting language ...) and put it on a Request object in this servlet.
Everything looks good and even works perfectly ok, unless too many dynamic pages being requested by a single user concurrently. "Too Many" in my case is at least 9-10 pages. In this case it throws exceptions, different ones! Sometimes the HttpServletRequest request is null, sometimes ServletContext servletContext is null, some other times these are ok, but the runtime variables are null which is used in business logic or db querying.
I've googled about it and found out that this action is being instantiated "Per Request". Isn't this so? If there is an action per request, what's wrong with this conflict or "nullability thing". Should I do some thread-like thing in that action, beyond the threading of struts?
I'd be so appreciated if you could help me out or point me a direction.
Here is simplified version of DynamicContentAction.java
public class DynamicContentAction extends ActionSupport implements ServletRequestAware, ServletContextAware {
private HttpServletRequest request;
private ServletContext servletContext;
private ResourceSelectorService resourceSelectorService;
private String f = null;
public String execute() {
if ( f != null ) {
HashMap<String, Object> resolvedURI = resourceSelectorService.resolveURI(f);
if ( resolvedURI.get("ERROR").equals(true) ) {
//Generating nice 404 error page content
} else {
//Generating Content
//and put it on request object as:
//request.setAttribute("attrName", resourceContent);
}
}
else {
//Generating nice 404 error page content
}
request = null;
servletContext = null;
f = null;
return "index";
}
#Override
public void setServletRequest(HttpServletRequest request) {
this.request = request;
}
#Override
public void setServletContext(ServletContext servletContext) {
this.servletContext = servletContext;
}
public void setF(String f) {
this.f = f;
}
public String getF() {
return f;
}
}
as I'm writing this post, I have came to the knowledge that this class is NOT thread-safe. Is it? So I've changed it a little bit as follow:
Here is newer version of DynamicContentAction.java
public class DynamicContentAction extends ActionSupport {
private ResourceSelectorService resourceSelectorService;
private String f = null;
public String execute() {
if ( f != null ) {
final HttpServletRequest request = ServletActionContext.getRequest();
final ServletContext servletContext = ServletActionContext.getServletContext();
HashMap<String, Object> resolvedURI = resourceSelectorService.resolveURI(f);
if ( resolvedURI.get("ERROR").equals(true) ) {
//Generating nice 404 error page content
} else {
//Generating Content
//and put it on request object as:
//request.setAttribute("attrName", resourceContent);
}
f = null;
}
else {
//Generating nice 404 error page content
}
return "index";
}
public void setF(String f) {
this.f = f;
}
public String getF() {
return f;
}
}
and the Null thing problem is almost gone, but there is still conflict with the generated content. For example if user try to open:
http:// www.domain.com/A
http:// www.domain.com/B
http:// www.domain.com/C
http:// www.domain.com/D
http:// www.domain.com/E
simultaneously, all of the pages will be rendered in browser, but the content of A is shown in A and B, C is correct, and there is a very good chance that the content of D and E are incorrect too.