Jasig cas how to validate custom model attribute in view state - validation

I have used jasig (3.5.1) cas sever and successfully configured.It works fine. my project have another requirement. I mentioned it below
i need another login mechanism. it mean rather than using stand username password, i need corporate code,mobile number and password authentication for corporate users. so i have created another Credential class for that
public class CodeMobileNumberCredintials implements Credentials{
#NotNull
#Size(min=1,message = "required.code")
private String code;
#NotNull
#Size(min=1, message = "required.mobileNumber")
private String mobileNumber;
#NotNull
#Size(min=1, message = "required.password")
private String password;
...
}
Then i created a variable called "codeMobileNumberCredintials"in web-flow.
<var name="credentials" class="org.jasig.cas.authentication.principal.UsernamePasswordCredentials" />
<var name="codeMobileNumberCredintials" class="org.jasig.cas.authentication.principal.CodeMobileNumberCredintials"/>
<view-state id="viewCorporateLoginForm" view="casCorporateLoginView" model="codeMobileNumberCredintials">
<binder>
<binding property="code" />
<binding property="mobileNumber" />
<binding property="password" />
</binder>
<on-entry>
<set name="viewScope.commandName" value="'codeMobileNumberCredintials'" />
</on-entry>
<transition on="submit" bind="true" validate="true" to="realCorporateSubmit">
<evaluate expression="authenticationViaFormAction.doCorporateBind(flowRequestContext, flowScope.codeMobileNumberCredintials)" />
</transition>
</view-state>
The issue is bean validation process not working for my custom login form.But normal username password form validated(when submiting form without givin username and password , It says "username and password blank"). But my custom autentication form not validated.It directly goes to controller class.
I have spent lot of time to do this. Can anyone help me to do my task.
Thank you
Amila

I had a similar problem. The easiest way to solve it, is to add new validator bean in
cas-servlet.xml Spring configuration file in cas-server-webapp module. In your case it will be:
<bean id="codeMobileNumberCredintialsValidator" class="org.springframework.validation.beanvalidation.LocalValidatorFactoryBean"
p:messageInterpolator-ref="messageInterpolator" />

There was an issue with spring web flow, resulting an error during validation. It occurs with stack trace as below and error page displayed, instead redisplaying login form:
2013-10-04 09:48:31,683 TRACE [org.jasig.cas.web.init.SafeDispatcherServlet] - <Entering method [service with arguments [[org.apache.catalina.connector.RequestFacade#411c345a, org.apache.catalina.connector.ResponseFacade#22b1221b]]>
2013-10-04 09:48:31,686 DEBUG [org.jasig.cas.web.FlowExecutionExceptionResolver] - <Ignoring the received exception due to a type mismatch> org.springframework.webflow.execution.repository.BadlyFormattedFlowExecutionKeyException: Badly formatted flow execution key '[Ljava.lang.String;#58714e00', the expected format is 'e<executionId>s<snapshotId>'
The fix is described here CAS-1142. It help me, and it summarize with adding one line <webflow:redirect-in-same-state value="false" /> to cas-servlet.xml resulting as below:
<webflow:flow-executor id="flowExecutor" flow-registry="flowRegistry">
<webflow:flow-execution-attributes>
<webflow:always-redirect-on-pause value="false"/>
<webflow:redirect-in-same-state value="false" />
</webflow:flow-execution-attributes>
<webflow:flow-execution-listeners>
<webflow:listener ref="terminateWebSessionListener" />
</webflow:flow-execution-listeners>
</webflow:flow-executor>

Related

Null context in a Spring flow

I am trying to customize the login process in Shibboleth IdP 3.0. It's basically a Spring web application, that amongst other things uses Spring flows.
I am getting the following exception (null context):
org.springframework.expression.spel.SpelEvaluationException: EL1011E:(pos 23): Method call: Attempted to call method verify(java.lang.String) on null context object
at org.springframework.expression.spel.ast.MethodReference.throwIfNotNullSafe(MethodReference.java:144)
It happens on the evaluate expression below, in this Spring web flow:
<action-state id="VerifyOtp">
<set name="flowScope.enteredOtp" value="requestParameters.j_otp"
type="java.lang.String" />
<set name="flowScope.otpSecretKey"
value="keyManager.findSecretKey(flowScope.otpUsername)"
type="ch.eifr.oshi.jaas.totp.SecretKey" />
<evaluate expression="flowScope.otpSecretKey.verify(flowScope.enteredOtp)"
result="flowScope.otpIsValid" />
<transition on="#{ flowScope.otpIsValid }" to="proceed" />
<transition on="#{ !flowScope.otpIsValid }" to="DisplayOtpPage" />
</action-state>
The flow imports the following beans:
(keyManager gets instantiated as expected)
<bean class="ch.eifr.oshi.jaas.totp.SecretKeysManagerImpl" id="keyManager" scope="singleton" />
<bean class="ch.eifr.oshi.jaas.totp.SecretKey" scope="prototype" />
I tried with and without the last line, it made no difference. I thought maybe I had to tell Spring about the classes it needs to load. That doesn't seem to be the case.
I think my problem is related to scope or dependencies, because I tried calling keyManager.toString() in a view and it returned something.
But if I call keyManager.findSecretKey('john.doe').getId() I get a null context exception. Note that the findSecretKey(String) method always returns a new key, it never returns null.
I was hoping to do it a better way, but only this worked:
<action-state id="ExtractOtpFromFormRequestAndValidate">
<set name="flashScope.enteredOtp" value="requestParameters.j_otp" type="java.lang.String"/>
<transition on="#{ keyManager.findSecretKey(flowScope.otpUsername).verify(flashScope.enteredOtp) }" to="proceed" />
<transition to="DisplayOtpPage" />
</action-state>

getViewRoot null from on-render execution action

I need to achieve viewRoot because I add a new htmlPanelGroup dynamically using DB results but always return null.
How is possible throw a evaluate expression from SWF fragment which achieve viewRoot properly?
Thanks!
Flow
<on-entry>
<set name="viewScope.code" value="requestParameters.code" />
</on-entry>
<on-render>
<evaluate expression="eventProvider.createEvent(viewScope.code)" />
</on-render>
Method
public void createEvent(String idEvent){
logger.entry("EventProvider.createEvent()");
Page p=pageBo.getDao().get(Integer.valueOf(idEvent));
pageBo.getDao().initializeElements(p);
Set<Element> elements=p.getElements();
Application app=FacesContextWrapper.getCurrentInstance().getApplication();
UIComponent parent=FacesContextWrapper.getCurrentInstance().getViewRoot().findComponent("eventContainer");
....
....
....}
Finally, I've chosen to save requestParameter in a flowScope var and so I can use it when on-render execution is invoked
<on-entry>
<set name="flowScope.code" value="requestParameters.code" />
</on-entry>
<on-render>
<evaluate expression="eventProvider.loadPage(flowScope.code)" />
</on-render>

Adding spring webflow dynamic messages for general purposes (no validating forms)?

Every documentation and examples that I read to be about validation form fields but I wish to show messages for others cases, for example to warn the view or flow have changed. So, that messages must be added on action methods.
In my case the app send to users a confirmation email, on flow there is a decision-state which redirect to views depending on link got declared a request parameter or not. Then I define a action-state which evaluate a method to confirm the email or warning to user about account's state (deleted or already enabled)
<on-start>
<set name="requestScope.code" value="requestParameters.code" />
<set name="requestScope.ln" value="requestParameters.ln" />
</on-start>
<decision-state id="checkConfirmation">
<if test="requestScope.code==null" then="login" else="confirmation" />
</decision-state>
<action-state id="confirmation">
<evaluate expression="login.confirmation(requestScope.ln,requestScope.code)" />
<transition on="yes" to="confirmationOk" />
<transition on="noUserFound" to="noUserFound" />
<transition on="userEnabled" to="userEnabled" />
<transition on="error" to="error" />
</action-state>
<view-state id="userEnabled" view="confirmation.xhtml">
<on-entry>
<set name="viewScope.operation" value="'enabled'" />
</on-entry>
<transition on="login" to="login" />
</view-state>
confirmation method
public String confirmation(String language,String emailCode){
logger.entry("Login.confirmation()");
String emailDecode=new String(Base64.decode(emailCode.getBytes()));
User user=userBo.getDao().findNamedQueryUnique("getUserByEmail",emailDecode);
if(user!=null){
if(!user.isEnabled()){
user.setEnabled(true);
try{
userBo.getDao().merge(user);
if(!EmailProvider.sendEmailEnabled(language,emailDecode)){
return "error";
}
}catch(DataAccessException e){
e.printStackTrace();
FlashMessages.addMessage(language,"es.project.properties.message.msg","unexpected.exception");
return "error";
}
}else{
// JSF way - FlashMessages.addMessage(language,"es.project.properties.message.msg","user.enable.confirmation");
//Spring webflow way - MessageContext context=new DefaultMessageContext();
//MessageBuilder builder=new MessageBuilder();
//context.addMessage(builder.code("user.enable.confirmation").build());
**********************************************************
*** I wish to add messages here and show in next view ****
**********************************************************
return "userEnabled";
}
}else{
//FlashMessages.addMessage(language,"es.project.properties.message.msg","user.validation.norfound.exception");
return "noUserFound";
}
return "yes";
}
I tried to follow spring webflow examples (booking-faces and booking-mvc) adding messages.properties on flow folder or creating a MessageSource bean, but I'm really lost on this matter.
I believe you are not having the messages.properties in classpath properly.
Suppose your file is in folder structure as:
src
|
|-resources
|
|-messages.properties
i.e make sure its in classpath.
You need to declare the ResourceBundleMessageSource with properties file in your servlet context xml file as:
<bean id="messageSource" class="org.springframework.context.support.ResourceBundleMessageSource">
<property name="basename" value="messages" />
</bean>
Then you can access the properties as:
<action-state id="confirmation">
...
<transition on="userEnabled" to="userEnabled">
<!--This is the property you want to set in else part of confirmation method - instead include here-->
<set name="flashScope.flashMessageKey" value="'es.project.properties.message.msg'" />
</transition>
</action-state>
Include this in view:
<%# taglib prefix="fmt" uri="http://java.sun.com/jstl/fmt" %>
Then you can view this message on view render by:
<fmt:message key="${flashMessageKey}"/>
I found a solution for Spring,JSF 2.2 and i18 in this post. Basically, you should define a spring message source and create a spring component which will be called for jsf outputtext tag. Then how #Prasad wrote it's necessary to set a key in a spring flow or programatically.
Properties resources are src/main/resources/error_en.properties and src/main/resources/error_es.properties
MessageSource Bean
#Bean
public ReloadableResourceBundleMessageSource messageSource(){
ReloadableResourceBundleMessageSource msg=new ReloadableResourceBundleMessageSource();
msg.setBasename("classpath*:error");
return msg;
}
Spring component
#Component(value="message")
public class MessageSourceProvider extends HashMap {
private static final long serialVersionUID = 1L;
#Autowired
private MessageSource messageSource;
#Override
public String get(Object key) {
ServletRequest request = (ServletRequest) FacesContextWrapper.getCurrentInstance().getExternalContext().getRequest();
String message;
try {
message = messageSource.getMessage((String) key, null, request.getLocale());
}
catch (NoSuchMessageException e) {
message = "???" + key + "???";
}
return message;
}
}
JSF tag
<h:outputText value="#{message.unexpected}" />

Spring webflow: handling NoSuchFlowDefinitionException with a flow

I'm trying to implement exception handling for my Spring 3.2.0, Spring webflow, JSF 2.1.12 application.
So far I can catch most of the exceptions using an Exception handler flow:
<persistence-context/>
<decision-state id="handleException" >
<if test="deviceManager.isMobileDevice()" then="mobileException" else="generalException"/>
</decision-state>
<view-state id="generalException" view="../views/exception/generalException.xhtml">
<on-entry>
<evaluate expression="exceptionManager.extractMessages(flowExecutionException, rootCauseException)" result="viewScope.exc"/>
</on-entry>
</view-state>
<view-state id="mobileException" view="../views/exception/mobileException.xhtml">
<on-entry>
<evaluate expression="exceptionManager.extractMessages(flowExecutionException, rootCauseException)" result="viewScope.exc"/>
</on-entry>
</view-state>
<global-transitions>
<transition on-exception="java.lang.Exception" to="handleException"/>
</global-transitions>
But this can't handle NoSuchFlowDefinitionException and similar as, if I understood well, they happens outside the flow execution.
Anyone know how to handle these exceptions?
Also I would need to map the exception to a flow and not to a static view like, for example, error.html. This because my system requires to do some extra work in the background to load the page information.
Any help will be greatly appreciated, have a good day,
Mattia
<<<<< EDIT >>>>>
I added a SimpleMappingExceptionResolver to catch the NoSuchFlowDefinitionException:
<bean class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver">
<property name="exceptionMappings">
<props>
<prop key="org.springframework.webflow.definition.registry.NoSuchFlowDefinitionException">
exceptionFlow
</prop>
</props>
</property>
</bean>
The Exception is catched but the resolver translate the target flow to a view, looking for a view: WEB-INF/exceptionFlow.xhtml This view does not exist as I would need to handle the exception with the flow exceptionFlow.xml
com.sun.faces.context.FacesFileNotFoundException: /WEB-INF/exceptionView.xhtml Not Found in ExternalContext as a Resource
How could I redirect that to a flow instead of a view? I tried adding a controller that maps on the view:
#Controller
#RequestMapping("/WEB-INF/exceptionFlow.xhtml")
public class ExceptionController {
#RequestMapping(method = RequestMethod.GET)
public String redirectToPublicFlowGet(ModelMap model) {
return "spring/flows/public";
}
#RequestMapping(method = RequestMethod.POST)
public String redirectToPublicFlowPost(ModelMap model) {
return "spring/flows/public";
}
}
But whatever I put on the request mapping (I tried "/WEB-INF/exceptionFlow.xhtml", "/exceptionFlow.xhtml", "exceptionFlow.xhtml". "exceptionFlow" The code never acces the methods (I put some debug breakpoint).
Anyone knows what I'm doing wrong?
Thanks have a good day
You can do it by changing
<prop key="org.springframework.webflow.definition.registry.NoSuchFlowDefinitionException">
exceptionFlow
</prop>
to
redirect:/exceptionFlow
this will create new flow execution for given flow
but you will lose information about the exception...

Using the setAllowedFields() method in Spring

I'm using Spring 3.2.0. I have registered a few custom property editors for some basic needs as follows.
import editors.DateTimeEditor;
import editors.StrictNumberFormatEditor;
import java.math.RoundingMode;
import java.net.URL;
import java.text.DecimalFormat;
import java.text.NumberFormat;
import org.joda.time.DateTime;
import org.springframework.beans.propertyeditors.StringTrimmerEditor;
import org.springframework.beans.propertyeditors.URLEditor;
import org.springframework.web.bind.WebDataBinder;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.InitBinder;
import org.springframework.web.context.request.WebRequest;
#ControllerAdvice
public final class GlobalDataBinder
{
#InitBinder
public void initBinder(WebDataBinder binder, WebRequest request)
{
binder.setIgnoreInvalidFields(true);
binder.setIgnoreUnknownFields(true);
//binder.setAllowedFields(someArray);
NumberFormat numberFormat=DecimalFormat.getInstance();
numberFormat.setGroupingUsed(false);
numberFormat.setMaximumFractionDigits(2);
numberFormat.setRoundingMode(RoundingMode.HALF_UP);
binder.registerCustomEditor(DateTime.class, new DateTimeEditor("MM/dd/yyyy HH:mm:ss", true));
binder.registerCustomEditor(Double.class, new StrictNumberFormatEditor(Double.class, numberFormat, true));
binder.registerCustomEditor(String.class, new StringTrimmerEditor(true));
binder.registerCustomEditor(URL.class, new URLEditor());
}
}
I have this many editors registered so far. Two of them DateTimeEditor and StrictNumberFormatEditor have been customized by overriding respective methods to fulfill custom needs of number format and Joda-Time.
Since I'm using Spring 3.2.0, I can take advantage of #ControllerAdvice.
Spring recommends to list a set of allowed fields with the setAllowedFields() method so that malicious users can not inject values into bound objects.
From the docs about DataBinder
Binder that allows for setting property values onto a target object,
including support for validation and binding result analysis. The
binding process can be customized through specifying allowed fields,
required fields, custom editors, etc.
Note that there are potential security implications in failing to set
an array of allowed fields. In the case of HTTP form POST data for
example, malicious clients can attempt to subvert an application by
supplying values for fields or properties that do not exist on the
form. In some cases this could lead to illegal data being set on
command objects or their nested objects. For this reason, it is highly
recommended to specify the allowedFields property on the DataBinder.
I have a big application and obviously there are thousands of fields. Specifying and listing all of them with the setAllowedFields() is a tedious job. Additionally, somehow I need to remember them.
Changing a web page to remove some fields or add additional fields as the need arises again requires to modify the parameter value of the setAllowedFields() method to reflect those changes.
Is there any alternative to this?
Instead of using setAllowedFields() to white-list, you can use setDisallowedFields() to black-list. For example, from the petclinic sample application:
#InitBinder
public void setAllowedFields(WebDataBinder dataBinder) {
dataBinder.setDisallowedFields("id");
}
From a pure security standpoint white-listing is preferred to black-listing, but it maybe help ease the burden some.
setAllowedFields() is very handy when using entity objects directly in web layer. Alternatively, one could use dedicated data transfer objects (DTO), from which entity objects are constructed in the service layer. Not only can the factories be re-used, but also used outside the web context, e.g. for asynchronous messages. Besides, DTO inheritance doesn't have to follow entity inheritance, so you are free to design the DTO hierarchy according to the needs of the use-cases.
from http://static.springsource.org/spring-webflow/docs/2.0.x/reference/htmlsingle/spring-webflow-reference.html#view-model
4.9. Specifying bindings explicitly
Use the binder element to configure the exact set of model bindings usable by the view. This is particularly useful in a Spring MVC environment for restricting the set of "allowed fields" per view.
<view-state id="enterBookingDetails" model="booking">
<binder>
<binding property="creditCard" />
<binding property="creditCardName" />
<binding property="creditCardExpiryMonth" />
<binding property="creditCardExpiryYear" />
</binder>
<transition on="proceed" to="reviewBooking" />
<transition on="cancel" to="cancel" bind="false" />
</view-state>
If the binder element is not specified, all public properties of the model are eligible for binding by the view. With the binder element specified, only the explicitly configured bindings are allowed.
Each binding may also apply a converter to format the model property value for display in a custom manner. If no converter is specified, the default converter for the model property's type will be used.
<view-state id="enterBookingDetails" model="booking">
<binder>
<binding property="checkinDate" converter="shortDate" />
<binding property="checkoutDate" converter="shortDate" />
<binding property="creditCard" />
<binding property="creditCardName" />
<binding property="creditCardExpiryMonth" />
<binding property="creditCardExpiryYear" />
</binder>
<transition on="proceed" to="reviewBooking" />
<transition on="cancel" to="cancel" bind="false" />
</view-state>
In the example above, the shortDate converter is bound to the checkinDate and checkoutDate properties. Custom converters may be registered with the application's ConversionService.
Each binding may also apply a required check that will generate a validation error if the user provided value is null on form postback:
<view-state id="enterBookingDetails" model="booking">
<binder>
<binding property="checkinDate" converter="shortDate" required="true" />
<binding property="checkoutDate" converter="shortDate" required="true" />
<binding property="creditCard" required="true" />
<binding property="creditCardName" required="true" />
<binding property="creditCardExpiryMonth" required="true" />
<binding property="creditCardExpiryYear" required="true" />
</binder>
<transition on="proceed" to="reviewBooking">
<transition on="cancel" to="bookingCancelled" bind="false" />
</view-state>
In the example above, all of the bindings are required. If one or more blank input values are bound, validation errors will be generated and the view will re-render with those errors.
A solution to use binder with DTO (companydata in example) in case most of the form input values should be converted to null if empty, but there is a need to add few exceptions (setDisallowedFields didn't work for me).
#InitBinder()
public void initBinder(WebDataBinder binder) {
binder.registerCustomEditor(String.class, new StringTrimmerEditor(true));
}
#InitBinder
protected void initBinder(HttpServletRequest request, ServletRequestDataBinder binder) {
binder.registerCustomEditor(String.class, "companydata.companyName", new StringTrimmerEditor(false));
binder.registerCustomEditor(String.class, "companydata.companyNumber", new StringTrimmerEditor(false));
}

Resources