I am trying to do form validation in Spring Web Flow. For this I am using a validator class, which is named after the model. Just like it is stated in the documentation.
The validator gets instantiated as a bean but is never called during validation. Any pointers on that issue?
flow config
<?xml version="1.0" encoding="UTF-8"?>
<flow xmlns="http://www.springframework.org/schema/webflow"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/webflow http://www.springframework.org/schema/webflow/spring-webflow-2.4.xsd">
<view-state id="createTotpKeyView" view="/templates/totp/create/create" model="key">
<on-entry>
<evaluate expression="createTotpKeyAction"/>
</on-entry>
<transition on="submit" to="successfullyCreated" bind="true" validate="true"/>
</view-state>
<end-state id="successfullyCreated" view="/templates/totp/create/success"/>
</flow>
This is the action that is called in the view-state.
createTotpKeyAction
#Component
public class CreateTotpKeyAction implements Action
{
String uid = "random";
#Override
public Event execute(RequestContext context) throws Exception
{
try
{
// Create a TOTP key and put it in the view scope
TOTPKey totpKey = client.createTotpKeyForUid(uid, null);
context.getViewScope().put("key", totpKey);
return new Event(this, "success");
}
catch (Exception e)
{
log.error("Error while creating TOTP key for user: " + uid + ".\n" + e.getMessage());
// Put response message in flash scope to show it once
context.getFlashScope().put("fetchingError", true);
return new Event(this, "error");
}
}
}
This is the validator I am trying to use. EDIT renamed to match documentation.
KeyValidator
#Component
public class KeyValidator
{
[...]
public void validateCreateTotpKeyView(TOTPKey key, ValidationContext context)
{
System.out.println("VALIDATE VIEW STATE");
}
public void validate(TOTPKey key, ValidationContext context)
{
System.out.println("DEFAULT VALIDATE");
}
}
I also tried different naming schemes such as TOTPKeyValidator or TotpKeyValidator. None of them worked.
The only thing that is working, is creating a validation method in the TOTPKey class, but I don't want to use that approach.
In addition this is the log file produced during the attempted validation
Log
Mapping request with URI '/totp/create' to flow with id 'totp/create'
Resuming flow execution with key 'e5s1
Locking conversation 5
Getting flow execution with key 'e5s1'
Getting FlowDefinition with id 'totp/create'
Resuming in org.springframework.webflow.mvc.servlet.MvcExternalContext#2b551393
Restoring [FlowVariable#3b66a2de name = 'key', valueFactory = [BeanFactoryVariableValueFactory#2fbc89 type = TOTPKey]]
Processing user event 'submit'
Resolved model twofa.core.domain.TOTPKey#505439d0
Binding to model
Adding default mapping for parameter 'execution'
Adding default mapping for parameter 'totpKeyId'
Adding default mapping for parameter 'token'
Adding empty value mapping for parameter 'eventId_submit'
Validating model
Event 'submit' returned from view [ServletMvcView#19f8532f view = org.springframework.web.servlet.view.velocity.VelocityLayoutView: name '/templates/totp/create/create'; URL [/templates/totp/create/create.vm]]
Executing [Transition#2feb5361 on = submit, to = successfullyCreated]
Exiting state 'createTotpKeyView'
Entering state 'successfullyCreated' of flow 'totp/create'
Executing org.springframework.webflow.action.ViewFactoryActionAdapter#423fa131
Rendering MVC [org.springframework.web.servlet.view.velocity.VelocityLayoutView: name '/templates/totp/create/success'; URL [/templates/totp/create/success.vm]] with model map [{currentUser=null, flashScope=map[[empty]], flowRequestContext=[RequestControlContextImpl#70144045 externalContext = org.springframework.webflow.mvc.servlet.MvcExternalContext#2b551393, currentEvent = submit, requestScope = map[[empty]], attributes = map[[empty]], messageContext = [DefaultMessageContext#149807b4 sourceMessages = map[[null] -> list[[empty]]]], flowExecution = [FlowExecutionImpl#1c4b2c3e flow = 'totp/create', flowSessions = list[[FlowSessionImpl#6eea5d26 flow = 'totp/create', state = 'successfullyCreated', scope = map['key' -> twofa.core.domain.TOTPKey#73f32d0a]]]]], flowExecutionKey=e5s1, flowExecutionUrl=/totp/create?execution=e5s1, key=twofa.core.domain.TOTPKey#73f32d0a}]
Finished executing org.springframework.webflow.action.ViewFactoryActionAdapter#423fa131; result = success
Completed transition execution. As a result, the flow execution has ended
Removing flow execution '[Ended execution of 'totp/create']' from repository
Ending conversation 5
Unlocking conversation 5
It says Validating Model but nothing happens...
It came down to a wrong import statement in my validator class.
Using org.relaxng.datatype.ValidationContext instead of org.springframework.binding.validation.ValidationContext will not work.
Related
I'm trying to call a URL which should return me a XML file.
I want to Display this XML file.
Right now I don't know how to make the call to get the return file.
I tryied it with a <p:commandButton process="#this" action="http://..." value="Test" /> but I get 2 warnings.
Warning for file: Couldn't find a Mime-Type, add a Mime-Type mapping in your web.xml
Warning for Ressource: Can not be found or operated.
You need to use for example restfull client to get your xml and parse it.
Here's your content of actionlistener that will be called from your button:
Client client = ClientBuilder.newClient( new ClientConfig().register( LoggingFilter.class ) );
WebTarget webTarget = client.target("http://localhost:8080/JerseyDemos/rest").path("employees");
Invocation.Builder invocationBuilder = webTarget.request(MediaType.APPLICATION_XML);
Response response = invocationBuilder.get();
Employees employees = response.readEntity(Employees.class);
List<Employee> listOfEmployees = employees.getEmployeeList();
(from http://howtodoinjava.com/jersey/jersey-restful-client-examples/#get-list)
Try this:
action=#{yourBean.yourAction}
Inside your bean:
public void yourAction() {
FacesContext fc = FacesContext.getCurrentInstance();
fc.getExternalContext().redirect("http://...");
fc.responseComplete();
}
I'm not sure if it's needed, but also you might wanna set ajax=false on p:commandButton
Hi all I have a problem when working with struts validation as the following :
an edit profile page loaded , when user click save a validation should be executed if an error exist a redirect to the same page must happen with validation error showed, and data loaded, else execute method called. what happen that a errors list has values but no errors showed up in the screen, a snap of my code is :
I am using struts 1
<action path="/selectUserAction" validate="true" input="/selectUserAction.do" type="com.bg.sharjah.usermanagement.manageusersgroups.web.actions.SelectUserAction"
name="editUserProfileFormBean" scope="request">
#Override
public ActionErrors validate(ActionMapping mapping,
HttpServletRequest request) {
//Logger.getAnonymousLogger().log(Level.WARNING,"");
ActionErrors errors = new ActionErrors();
try{
errors = super.validate(mapping, request);
System.out.println("validate <<<<<<<<<<< "+errors.size());
}catch(Exception e)
{
e.printStackTrace();
}
return errors;
// Validation.match(errors, password, confirmPassword);
//Validation.match(errors, email, confirmEmail);
}
Note:
my form bean is used with different actions, also I need to know how to redirect to the same page using input attribute ?
Thanx all I find out the problem it's a resource bundle issue.
I have an application where some users belong to a Role, but may not actually have access to certain data within a URL. For instance the following url is open to all users
/Library/GetFile/1
However, some users may not have access to file1, but I can't use the Authorize attribute to detect that. I want instead to redirect those users to an unauthorized or accessdenied page. I'm using Forms Authentication and my config is set up like this
<authentication mode="Forms">
<forms loginUrl="~/Home/Index" timeout="2880" />
</authentication>
my custom errors block is like this
<customErrors mode="On" defaultRedirect="Error" redirectMode="ResponseRewrite" >
<error statusCode="401" redirect="Unauthorized"/>
</customErrors>
I am attempting to return the HttpUnauthorizedResult if the user does not have access, but I just get redirected to the login page, which isn't valid here because the User is Authenticated already.
It appears that the HttpUnauthorizedResult is setting the HTTP Response Code to 401 which Forms Authentication is hijacking and sending the user to the Login page.
Throwing the UnauthorizedAccessException doesn't seem to work either always redirecting the user to an IIS Error page even though I've updated my RegisterGlobalFilters to
filters.Add(new HandleErrorAttribute
{
ExceptionType = typeof(UnauthorizedAccessException),
View = "Unauthorized",
Order = 3
});
If I change UnauthorizedAccessException to a custom Exception the redirect works and for now that's what I've done.
Your solution is similar to mine except that I did this:
Create a custom exception, UnauthorizedDataAccessException.
Create a custom exception filter (so that it could log the invalid access attempt).
Register my custom exception attribute as a global filter in App_start.
Create a marker interface, ISecureOwner and added it to my entity.
Add a secure 'Load' extension method to my repository, which throws the exception if the current user is not the owner of the entity that was loaded. For this to work, entity has to implement ISecureOwner that returns the id of the user that saved the entity.
Note that this just shows a pattern: the details of how you implement GetSecureUser and what you use to retrieve data will vary. However, although this pattern is okay for a small app, it is a bit of hack, since that kind of security should be implemented deep down at the data level, using ownership groups in the database, which is another question :)
public class UnauthorizedDataAccessException : Exception
{
// constructors
}
public class UnauthorizedDataAccessAttribute : HandleErrorAttribute
{
public override void OnException(ExceptionContext filterContext)
{
if (filterContext.Exception.GetType() == Typeof(UnauthorizedDataAccessException))
{
// log error
filterContext.ExceptionHandled = true;
filterContext.Result = new RedirectToRouteResult(new RouteValueDictionary(new { controller = "Error", action = "UnauthorizedDataAccess" }));
}
else
{
base.OnException(filterContext);
}
}
// marker interface for entity and extension method
public interface ISecureOwner
{
Guid OwnerId { get; }
}
// extension method
public static T SecureFindOne<T>(this IRepository repository, Guid id) where T : class, ISecureOwner, new()
{
var user = GetSecureUser();
T entity = repository.FindOne<T>(id);
if (entity.OwnerId != user.GuidDatabaseId)
{
throw new UnauthorizedDataAccessException(string.Format("User id '{0}' attempted to access entity type {1}, id {2} but was not the owner. The real owner id is {3}.", user.GuidDatabaseId, typeof(T).Name, id, entity.OwnerId));
}
return entity;
}
// Register in global.asax
public static void RegisterGlobalFilters(GlobalFilterCollection filters)
{
var filter = new UnauthorizedDataAccessAttribute { ExceptionType = typeof(UnauthorizedDataAccessException) };
filters.Add(filter);
filters.Add(new HandleErrorAttribute());
}
// Usage:
var ownedThing = myRepository.SecureFindOne<myEntity>(id))
You can restrict access to certain roles. If an unauthorized role tries to access a resource you can redirect them to a specific url.
Look at this other SO question: attribute-for-net-mvc-controller-action-method, there are good answers there.
You can check in your code if a user belongs to a role:
User.IsInRole("RoleToTest");
you can also apply attributes to your controllers/action methods. Anyhow it is all explained in the link I specified above.
* EDIT *
You could override OnException in your base Controller. Implement a custom exception, e.g., AccessNotAuthorizedAccessException.
In OnExcepton, if you detect your custom exception, just redirect to a friendly url that shows the 'Not authorized...' message.
I have a project using Struts2 on the server side and I am trying to make it work with jqGrid (using JSON format). I have several tables made with jqGrid and I am using the add/edit/delete buttons from navGrid.
The main problem I have is with server validation error messages. I have created custom validators and they work with jsp pages, using s:fielderror, but I don't know how to make them work for add/edit popups from jqGrid. I am aware that jqGrid provides the users with custom validation on client, but this has its limitations(think about testing whether the email of a user is unique, you definitely must use the database for that, or if some fields depend on each other and must be tested together, like if isManager is true, then the managerCode must be not empty and vice versa...).
When I use the client validation, there is a message in the add/edit window whenever an error occurs. Can I somehow display my server validation error messages in the window in the same way?
I managed to solve the issue. I will explain how using a simple custom validator for age field, which must be > 18 for an Employee. It is supposed next that the validator was already declared in validators.xml and mapped on the action and that the message in case of ValidationException is "An employee should be older than 18.".
Using Firebug, I figured out that the id of the error area in the form is FormError. It is possible to configure a callback function errorTextFormat in jqgrid, in order to get a response from the server and process it. In the jqgrid configuration, one could write
errorTextFormat : errorFormat,
with
var errorFormat = function(response) {
var text = response.responseText;
$('#FormError').text(text); //sets the text in the error area to the validation //message from the server
return text;
};
The problem is now that the server will send implicitly a response containing the whole exception stack trace. To deal with it, I decided to create a new result type.
public class MyResult implements Result {
/**
*
*/
private static final long serialVersionUID = -6814596446076941639L;
private int errorCode = 500;
public void execute(ActionInvocation invocation) throws Exception {
ActionContext actionContext = invocation.getInvocationContext();
HttpServletResponse response = (HttpServletResponse) actionContext
.get("com.opensymphony.xwork2.dispatcher.HttpServletResponse");
Exception exception = (Exception) actionContext
.getValueStack().findValue("exception");
response.setStatus(getErrorCode());
try {
PrintWriter out = response.getWriter();
out.print(exception.getMessage());
} catch (IOException e) {
throw e;
}
}
/**
* #return the errorCode
*/
public int getErrorCode() {
return errorCode;
}
/**
* #param errorCode the errorCode to set
*/
public void setErrorCode(int errorCode) {
this.errorCode = errorCode;
}
}
It must also be configured in struts.xml as follows:
<package name="default" abstract="true" extends="struts-default">
...
<result-types>
<result-type name="validationError"
class="exercises.ex5.result.MyResult">
</result-type>
</result-types>
...
<action name="myaction">
...
<result name="validationException" type="validationError"></result>
<exception-mapping result="validationException"
exception="java.lang.Exception"></exception-mapping>
</action>
...
</package>
These are the steps I followed to get a validation error message in the add/edit window and now it works.
I will first show the code that works in a non-ssl (http) environment. This code uses a custom json error handler, and all errors thrown, do get bubbled up to the client javascript (ajax).
// Create webservice endpoint
WebHttpBinding binding = new WebHttpBinding();
ServiceEndpoint serviceEndPoint = new ServiceEndpoint(ContractDescription.GetContract(Type.GetType(svcHost.serviceContract + ", " + svcHost.assemblyName)), binding, new EndpointAddress(svcHost.hostUrl));
// Add exception handler
serviceEndPoint.Behaviors.Add(new FaultingWebHttpBehavior());
// Create host and add webservice endpoint
WebServiceHost webServiceHost = new WebServiceHost(svcHost.obj, new Uri(svcHost.hostUrl));
webServiceHost.Description.Endpoints.Add(serviceEndPoint);
webServiceHost.Open();
I'll also show you what the FaultingWebHttpBehavior class looks like:
public class FaultingWebHttpBehavior : WebHttpBehavior
{
public FaultingWebHttpBehavior()
{
}
protected override void AddServerErrorHandlers(ServiceEndpoint endpoint, EndpointDispatcher endpointDispatcher)
{
endpointDispatcher.ChannelDispatcher.ErrorHandlers.Clear();
endpointDispatcher.ChannelDispatcher.ErrorHandlers.Add(new ErrorHandler());
}
public class ErrorHandler : IErrorHandler
{
public bool HandleError(Exception error)
{
return true;
}
public void ProvideFault(Exception error, MessageVersion version, ref Message fault)
{
// Build an object to return a json serialized exception
GeneralFault generalFault = new GeneralFault();
generalFault.BaseType = "Exception";
generalFault.Type = error.GetType().ToString();
generalFault.Message = error.Message;
// Create the fault object to return to the client
fault = Message.CreateMessage(version, "", generalFault, new DataContractJsonSerializer(typeof(GeneralFault)));
WebBodyFormatMessageProperty wbf = new WebBodyFormatMessageProperty(WebContentFormat.Json);
fault.Properties.Add(WebBodyFormatMessageProperty.Name, wbf);
}
}
}
[DataContract]
public class GeneralFault
{
[DataMember]
public string BaseType;
[DataMember]
public string Type;
[DataMember]
public string Message;
}
The AddServerErrorHandlers() method gets called automatically, once webServiceHost.Open() gets called. This sets up the custom json error handler, and life is good :-)
The problem comes, when we switch to and SSL (https) environment. I'll now show you endpoint creation code for SSL:
// Create webservice endpoint
WebHttpBinding binding = new WebHttpBinding();
ServiceEndpoint serviceEndPoint = new ServiceEndpoint(ContractDescription.GetContract(Type.GetType(svcHost.serviceContract + ", " + svcHost.assemblyName)), binding, new EndpointAddress(svcHost.hostUrl));
// This exception handler code below (FaultingWebHttpBehavior) doesn't work with SSL communication for some reason, need to resarch...
// Add exception handler
serviceEndPoint.Behaviors.Add(new FaultingWebHttpBehavior());
//Add Https Endpoint
WebServiceHost webServiceHost = new WebServiceHost(svcHost.obj, new Uri(svcHost.hostUrl));
binding.Security.Mode = WebHttpSecurityMode.Transport;
binding.Security.Transport.ClientCredentialType = HttpClientCredentialType.None;
webServiceHost.AddServiceEndpoint(svcHost.serviceContract, binding, string.Empty);
Now, with this SSL endpoint code, the service starts up correctly, and wcf hosted objects can be communicated with just fine via client javascript. However, the custom error handler doesn't work. The reason is, the AddServerErrorHandlers() method never gets called when webServiceHost.Open() is run.
So, can anyone tell me what is wrong with this picture? And why, is AddServerErrorHandlers() not getting called automatically, like it does when I'm using non-ssl endpoints?
Thanks!
I will refer you to MSDN docs
If the Transport value is specified by
the
WebHttpBinding(WebHttpSecurityMode),
then the settings provided by the
Transport property become effective
for the service endpoint. The value of
WebHttpSecurityMode can only be set in
the WebHttpBinding constructor that
takes it as an explicit parameter and
its value cannot be set again after
the binding instance is created.
see : http://msdn.microsoft.com/en-us/library/bb348328.aspx
So you need to pass this value
binding.Security.Mode = WebHttpSecurityMode.Transport;
into your .ctor() like that
WebHttpBinding binding = new WebHttpBinding(WebHttpSecurityMode.Transport);
I have never used this before as I always declare my bindings into web.config file but according to MSDN, this is what you should do.