Java play routing pass complex objects [Play framework 2.0] - java

I've been experimenting with the Java Play Framework 2.0 for a few weeks now, but I am now struggling with the following:
How can I pass Java object from one Play template to another?
I can pass simple objects about no problem:
GET /Login/:email controllers.Application.login(email:String)
With the following code in my controller:
public static Result login(String email) {
//Do some stuff
return ok("");
}
But what I need to be able to is something like this:
GET /Test/:user controllers.Application.test(user:User)
With the following code in my controller:
public static Result test(User user) {
//Do some stuff
return ok("");
}
When I try compiling, I get the following error:
not found: type User
Does anybody know what I need to do to get this working? Is it even possible? Appreciate any help!

Using basic (don't want to write 'ordinary') types as routes params is clean approach which will not cause a headache. For an example instead trying to send whole object it's better to send its id - most probably some kind of numeric type or unique String for an example if your user model has id of Long type you can just do it as easy as:
GET /Test/:userId controllers.Application.test(userId: Long)
controller
public static Result test(Long userId) {
User user = User.find.byId(userId);
return ok("Now you're watching user: " + user.name);
}
Other useful sample are booleans which, instead passing it it's just easier use 0/1 Int/int types (with default value set as false):
GET /set-admin/:userId controllers.App.setAdmin(userId: Long, setTo: Int ?= 0)
or true
GET /set-admin/:userId controllers.App.setAdmin(userId: Long, setTo: Int ?= 1)
controller
public static Result setAdmin(Long userId, int setTo) {
User user = User.find.byId(userId);
user.isAdmin = (setTo == 1); // of course isAdmin field of User model is type of Boolean
user.update(id);
return ok("User " + user.name + " is " + user.isAdmin);
}
so in template you can just make a link:
<a href='#routes.App.setAdmin(user.id, 1)'>Set as admin</a>
<a href='#routes.App.setAdmin(user.id, 0)'>Set as common user</a>

Related

Java Jersey REST Request Parameter Sanitation

I'm trying to make sure my Jersey request parameters are sanitized.
When processing a Jersey GET request, do I need to filter non String types?
For example, if the parameter submitted is an integer are both option 1 (getIntData) and option 2 (getStringData) hacker safe? What about a JSON PUT request, is my ESAPI implementation enough, or do I need to validate each data parameter after it is mapped? Could it be validated before it is mapped?
Jersey Rest Example Class:
public class RestExample {
//Option 1 Submit data as an Integer
//Jersey throws an internal server error if the type is not Integer
//Is that a valid way to validate the data?
//Integer Data, not filtered
#Path("/data/int/{data}/")
#GET
#Produces(MediaType.TEXT_HTML)
public Response getIntData(#PathParam("data") Integer data){
return Response.ok("You entered:" + data).build();
}
//Option 2 Submit data as a String, then validate it and cast it to an Integer
//String Data, filtered
#Path("/data/string/{data}/")
#GET
#Produces(MediaType.TEXT_HTML)
public Response getStringData(#PathParam("data") String data) {
data = ESAPI.encoder().canonicalize(data);
if (ESAPI.validator().isValidInteger("data", data, 0, 999999, false))
{
int intData = Integer.parseInt(data);
return Response.ok("You entered:" + intData).build();
}
return Response.status(404).entity("404 Not Found").build();
}
//JSON data, HTML encoded
#Path("/post/{requestid}")
#POST
#Consumes({MediaType.APPLICATION_FORM_URLENCODED, MediaType.APPLICATION_JSON})
#Produces(MediaType.TEXT_HTML)
public Response postData(String json) {
json = ESAPI.encoder().canonicalize(json);
json = ESAPI.encoder().encodeForHTML(json);
//Is there a way to iterate through each JSON KeyValue and filter here?
ObjectMapper mapper = new ObjectMapper();
DataMap dm = new DataMap();
try {
dm = mapper.readValue(json, DataMap.class);
} catch (Exception e) {
e.printStackTrace();
}
//Do we need to validate each DataMap object value and is there a dynamic way to do it?
if (ESAPI.validator().isValidInput("strData", dm.strData, "HTTPParameterValue", 25, false, true))
{
//Is Integer validation needed or will the thrown exception be good enough?
return Response.ok("You entered:" + dm.strData + " and " + dm.intData).build();
}
return Response.status(404).entity("404 Not Found").build();
}
}
Data Map Class:
public class DataMap {
public DataMap(){}
String strData;
Integer intData;
}
The short answer is yes, though by "filter" I interpret it as "validate," because no amount of "filtering" will EVER provide you with SAFE data. You can still run into integer overflows in Java, and while those may not have immediate security concerns, they could still put parts of your application in an unplanned for state, and hacking is all about perturbing the system in ways you can control.
You packed waaaaay too many questions into one "question," but here we go:
First off, the lines
json = ESAPI.encoder().canonicalize(json);
json = ESAPI.encoder().encodeForHTML(json);
Aren't doing what you think they're doing. If your JSON is coming in as a raw String right here, these two calls are going to be applying mass rules across the entire string, when you really need to handle these with more surgical precision, which you seem to at least be subconsciously aware of in the next question.
//Is there a way to iterate through each JSON KeyValue and filter
here?
Partial duplicate of this question.
While you're in the loop discussed here, you can perform any data transformations you want, but what you should really be considering is using the JSONObject class referenced in that first link. Then you'll have JSON parsed into an object where you'll have better access to JSON key/value pairs.
//Do we need to validate each DataMap object value and is there a
dynamic way to do it?
Yes, we validate everything that comes from a user. All users are assumed to be trained hackers, and smarter than you. However if you handled filtering before you do your data mapping transformation, you don't need to do it a second time. Doing it dynamically?
Something like:
JSONObject json = new JSONObject(s);
Iterator iterator = json.keys();
while( iterator.hasNext() ){
String data = iterator.next();
//filter and or business logic
}
^^That syntax is skipping typechecks but it should get you where you need to go.
/Is Integer validation needed or will the thrown exception be good
enough?
I don't see where you're throwing an exception with these lines of code:
if (ESAPI.validator().isValidInput("strData", dm.strData, "HTTPParameterValue", 25, false, true))
{
//Is Integer validation needed or will the thrown exception be good enough?
return Response.ok("You entered:" + dm.strData + " and " + dm.intData).build();
}
Firstly, in java we have autoboxing which means this:
int foo = 555555;
String bar = "";
//the code
foo + bar;
Will be cast to a string in any instance. The compiler will promote the int to an Integer and then silently call the Integer.toString() method. Also, in your Response.ok( String ); call, THIS is where you're going to want to encodeForHTML or whatever the output context may be. Encoding methods are ALWAYS For outputting data to user, whereas canonicalize you want to call when receiving data. Finally, in this segment of code we also have an error where you're assuming that you're dealing with an HTTPParameter. NOT at this point in the code. You'll validate http Parameters in instances where you're calling request.getParameter("id"): where id isn't a large blob of data like an entire JSON response or an entire XML response. At this point you should be validating for things like "SafeString"
Usually there are parsing libraries in Java that can at least get you to the level of Java objects, but on the validation side you're always going to be running through every item and punting whatever might be malicious.
As a final note, while coding, keep these principles in mind your code will be cleaner and your thought process much more focused:
user input is NEVER safe. (Yes, even if you've run it through an XSS filter.)
Use validate and canonicalize methods whenever RECEIVING data, and encode methods whenever transferring data to a different context, where context is defined as "Html field. Http attribute. Javascript input, etc...)
Instead of using the method isValidInput() I'd suggest using getValidInput() because it will call canonicalize for you, making you have to provide one less call.
Encode ANY time your data is going to be passed to another dynamic language, like SQL, groovy, Perl, or javascript.

Playframework Result how check is working

Hello I have got small problem I'm learning play 2.2.1 framework and I was making controllers like
public static Result Name(){
List<Account> names = Account.find.all();
List name = new ArrayList();
for(Account a: names)
{
name.add(a.getName());
}
return ok(Json.toJson(name));
}
And in routes I added line
GET /api/name controllers.AccController.Name()
And this function gives me all names from database now I wanted to make function where i can choose what column from database name/surname/country I want to get I made something like this:
public static Result typewhat(String what) {
String[] type = what.split(" ");
then I made if type[1] == name and same like upper but I dont know how to test now thats working or not in Routes I add line:
PUT /api/findwhat controllers.AccController.typewhat(what: String)
Im using Open HttpRequester and for localhost:9000/api/name it is working
but I totally dont know how to make it for this functiong typewhat
I will be very thankful for every help.
PUT /api/findwhat controllers.AccController.typewhat(what: String)
for above path try
localhost:9000/api/name?what=this is that
dont know java well but in scala it works fine
def typewhat(what:String) = Action { implicit request =>
println("gs",what)
val strAr = what.split(" ")
println(strAr)
Ok(strAr(0))
}

Enum saving in Hibernate

I am trying to save an Enum field to database but I am having a problem mapping the field to database. The code I have is as follows:
public enum InvoiceStatus {
PAID,
UNPAID;
}
and I am using this enum in one of my application classes as follows:
public class Invoice {
Enumerated(EnumType.ORDINAL)
#Column(name="INVOICE_STATUS", nullable = false, unique=false)
private InvoiceStatus invoiceStatus;
}
finally I let the app user select the Invoice Status from the view (JSP) using a drop down menu.
But I am not sure how to map the value received from the drop down menu selection to the Invoice Status field
I tried mapping the value received to short as follows, but it won't compile
invoice.setInvoiceStatus(Short.parseShort(request.getParameter("inbStatus")));
can someone please tell me how to map the data received from the view to the enum field?
Enum ordinal values are zero based indexes. In your case:
PAID = 0
UNPAID = 1
So the following code will return PAID:
int invoiceStatus = 0;
invoice.setInvoiceStatus(InvoiceStatus.values()[invoiceStatus]);
And the following code will return UNPAID:
int invoiceStatus = 1;
invoice.setInvoiceStatus(InvoiceStatus.values()[invoiceStatus]);
That means you should be able to do this way:
short invoiceStatus = Short.parseShort(request.getParameter("inbStatus"));
invoice.setInvoiceStatus(InvoiceStatus.values()[invoiceStatus]);
But only if inbStatus is 0 or 1. You should always validate user input for null and invalid values.
I see that u are using
Enumerated(EnumType.ORDINAL)
however after a while it could be quite difficult to troubleshoot if your enum will grow. Another issue with the ordinal is that you could refactor your code and change the order of the enum values and after that you could be in trouble. Mainly if it is a shared codebase and someone just decides to cleanup the code and "group the relevant enum constants together". If you'll use:
Enumerated(EnumType.STRING)
Directly the enum "name" will be inserted into the database. (Therefore you need Varchar type). If you want to present more user friendly version of your enum you could probably have:
public enum InvoiceStatus {
PAID(0, "Paid"), UNPAID(1, "Unpaid"), FAILED(2, "Failed"), PENDING(3, "Pending");
private int st;
private in uiLabel;
private InvoiceStatus(int st, String uiLabel){
this.st = st;
this.uiLabel = uiLabel;
}
private Map<String, InvoiceStatus> uiLabelMap = new HashMap<String, InvoiceStatus> ();
static {
for(InvoiceStatus status : values()) {
uiLableMap.put(status.getUiLabel(), status);
}
}
/** Returns the appropriate enum based on the String representation used in ui forms */
public InvoiceStatus fromUiLabel(String uiLabel) {
return uiLableMap.get(uiLabel); // plus some tweaks (null check or whatever)
}
//
// Same logic for the ORDINAL if you are keen to use it
//
}
Probably this could be also a solution for your problem, however i would really not use the ORDINAL based mapping. But just personal feeling.

Object to Rest-URL

The user can set a number of values (which are optional) for a search query that will be passed on to a REST api.
So I create a POJO to hold all the set values like
class Search{
private String minPrice;
private String maxPrice;
private String category;
// etc.pp.
}
When I construct the URL for the api call, i have to check wether that value is set all the time like
if (search.getMinPrice() != null){
url.append("&minprice=" + search.getMinPrice());
}
Is there a more convenient/ elegant way to do this
with pure Java, just a way to "do sth if sth is not null"
or even a library/tool that lets you construct Urls from objects?
Something like:
String url = "http://base.com/some/path?" + (maxPrice==null ? "" : "maxPrice="+maxPrice);

Find concrete class using a dynamic expression (where a Class instance is passed to a DAO)

I have this method signature:
public int nrOfEntities(Class<? extends MailConfirmation> clazz, User user, String email)
I would like nrOfEntities to return the number of entities that:
Are of the concrete class clazz
Have a matching User if user != null
Have a matching email if user == null
It's the class matching I'm having a problem with. I've tried a few statements without any luck.
Can clazz have subtypes that should not be counted?
If not, is it not sufficient to create the query on clazz?
Criteria criteria = session.createCriteria(clazz);
if (user == null) {
criteria.add(Restrictions.eq("email", email);
} else {
criteria.add(Restrictions.eq("user", user);
}
int result = (Integer) criteria.setProjection(Projections.rowCount()).uniqueResult();
Now I am guessing how your mapping looks (that there are "email" and "user" properties).
If that is not working, I know that there is a pseudo property named "class", at least in HQL. Maybe you can experiment with that.
Are you looking for "from " + clazz.getSimpleName() + " where ..."?
If you want to test the class of an object, you should be able to use something like the following:
Object entity = ... // Get the entity however
boolean matchesClass = entity.getClass().equals(clazz);
If this isn't working for you, give some examples of how it fails since it should be this straightforward!

Categories