Passing Sinatra models and validation - ruby

As mentioned on another similar thread I started recently, I am porting an ASP MVC application to Sinatra, partly for learning, partly for production purposes.
Currently my ASP MVC application has no views, it just exposes models as Xml/Json etc and accepts them in the same way. So in the scenario of someone sending a model to the server, it would auto bind the object, perform validation then return validation errors if there are any, if not carry on with the relevant actions.
Now in Sinatra it is a bit more barebones that ASP MVC which is a great thing as I have more choice on how to do these parts, however I dont have a clue what gems/libraries functionality is available for doing this.
My ideal scenario would be I have my pure html/js front end posting a model as json and then have it turned into an actual model where I can validate it (through Sinatra or other framework) then make a decision based on the validation results.

Try taking a look at this gem:
json
Then what you should be able to do is pass in the attributes for an object you would like to create as a hash of attributes converted to a json object:
{:attribute1 => "value1", :attribute2 => "value2"}.to_json
And on your Sinatra app, catch them and build the object from those attributes, and you can use Sinatra to validate as well:
def '/create_object' do
content_type :json
obj = Object.new(JSON.parse(params[:object]))
if obj.save
obj.to_json
else
error 400, user.errors.to_json
end
end

Sounds like an ordinary case for an ORM. So you could for example use DataMapper. With that in your hands, you can simply send a form via HTTP to a specific route and do something like:
post '/create' do
#post = Post.new(params[:your_form])
if #post.save
erb :a_template
else
erb :your_form
end
end
Where params[:your_form] is a hash containing the contents of your form. No JSON needed at all. If the saving process fails (for validation reason or whatever), the model object #post will have errors, which you could process in a different view (:your_form for example).
Best Regards
Tobias

Related

Redirect to another model's index action, and hence view index.erb - Rhomobile

I am developing a Rhomobile application,on submit of a POST method, i am going to my 'Model 1' action, after execution of my action i want to redirect to another model's (Model 2) Index action and list the data.
I am from a Rails background, there i would have used URL, any similar concept exists here or any solution.
i tried, but it didn't worked for me render :controller => :model2, :action => :index
Please Note: I have never used rhodes and all of this information was acquired by simply reading the API documentation and taking a look at the source code.
According to the documentation You should be able to deal with this in a very railsy fashion eg
def some_action
redirect controller: :model2, action: :index
end
Seems the only caveat is if this redirection is to happen in a callback function. In this case the redirection should be handled by the WebView instead e.g.
Rho::WebView.navigate(url_for(controller: :model2 action: :index))
Source for Rho::RhoController#redirect supports these statements in the documentation.
Additionally as you might notice above it appears that Rho has ported a lot of the rails like helpers including url_for

Are find_by_[dynamic] Activerecord methods safe for unsanitized input?

I am using ActiveRecord and Sinatra (probably irrelevant context). ActiveRecord has a dynamic finder method find_by_[column]
I would like to pass a raw parameter string from HTTP directly into one of these methods. I'm pretty sure that ActiveRecord will sanitize the string I pass in. Right? Here's some example code to show what I want to do:
post '/login' do
user = User.find_by_username(params[:username])
...
end
Is it problematic to pass unsanitized http parameters into this kind of ActiveRecord method?
More generally, how should one go about figuring out which ActiveRecord methods sanitize their inputs? I keep reading this site and getting scared! http://rails-sqli.org/
Yes, they are safe. Because the dynamic finders are simply translated into the normal hash form. Find the implementation at github
Note that the new syntax (since version 4) is: find_by(username: params[:username])
To be sure if the user exists you can try something like:
if User.exists? :username => params[:username]
user = User.find_by_username(params[:username])
end
This greatly reduces the chances of SQLi.

Safely save data to django model using AJAX

I have a model say TestModel as follows:
class TestModel(models.Model):
name = models.CharField()
description = models.TextField()
Now I can use a ModelForm to save data to this model. However, say I want to use Ajax and send a url as follows savethis?name=ABC&desc=SomeRandomDescription to a view that handles it as follows:
def savethis(request):
if request.GET['name'] and request.GET['desc']:
name = request.GET['name']
desc = request.GET['desc']
test = TestModel(name=name, description=desc)
test.save
return HttpResponse('Ok')
else:
return HttpResponse('Fail')
What's to stop someone from running a script that can easily hit this url with valid data and thus save data to my model? How do I ensure that incoming data is sent only from the right source?
One option is sending the data as JSON in a Post request but even that's not too hard to emualte.
Seems that you have stumbled upon the great security flaw that is Cross-site Scripting attacks. They are several ways you can get around it, but going into all of them in one answer would be fruitless. I suggest you Google the term and do some poking around, and you will find several different methods on how to protect your site better.
Django has a security page dedicated to talking about how to protect your site.

CakePHP and reusable approach

I would develop my CakePHP application in the most reusable way. I'd like to treat it as webservices, so I don't want to strictly bind controller with view. My idea is: controller just returns json info, the view calls the controller and get the json and make html output.
How can I realize that? Could be a good approch, developing pages rather than views, and inside that pages call the webservices previously developed.
You can even forget about creating view files, using $this->set('_serialize', array('people')); in your PeopleController::show()
Well Cake is kinda' works like this "out of the box". You can use Router::parseExtensions(); to define what type of data you would like to serve. For example in app/Config/routes.php:
Router::parseExtensions('xml','json');
This will make it possible to detect what kind of request is incoming. For example if someone requests:
www.example.com/people/list.json or www.example.com/people/list.xml, in your PeopleController's list() method you'd be able to detect what kind of resource is being requested - json or xml, or of course any other
extension you define. This is what the RequestHandlerComponent is used for. You can check if it is xml for example:
if($this->RequestHandler->isXml()) {
//Some code
}
The different extensions are only different representation of the data, so it shouldn't matter what exactly you're serving. From v2.1 Cake will automatically switch the view class when it sees a JSON or XML request, which takes us to the new JSON and XML views.
All you will have to do is provide the views in the appropriate places.
In View/People (as for this example) you would have:
..View/People/
list.ctp
xml/
list.ctp - XML view
json/
list.ctp - JSON view

Grab Facebook signed_request with Sinatra

I'm trying to figure out whether or not a user likes our brand page. Based off of that, we want to show either a like button or some 'thank you' text.
I'm working with a sinatra application hosted on heroku.
I tried the code from this thread: Decoding Facebook's signed request in Ruby/Sinatra
However, it doesn't seem to grab the signed_request and I can't figure out why.
I have the following methods:
get "/tab" do
#encoded_request = params[:signed_request]
#json_request = decode_data(#encoded_request)
#signed_request = Crack::JSON.parse(#json_request)
erb :index
end
# used by Canvas apps - redirect the POST to be a regular GET
post "/tab" do
#encoded_request = params[:signed_request]
#json_request = decode_data(#encoded_request)
#signed_request = Crack::JSON.parse(#json_request)
redirect '/tab'
end
I also have the helper messages from that thread, as they seem to make sense to me:
helpers do
def base64_url_decode(payload)
encoded_str = payload.gsub('-','+').gsub('_','/')
encoded_str += '=' while !(encoded_str.size % 4).zero?
Base64.decode64(encoded_str)
end
def decode_data(signed_request)
payload = signed_request.split('.')
data = base64_url_decode(payload)
end
end
However, when I just do
#encoded_request = params[:signed_request]
and read that out in my view with:
<%= #encoded_request %>
I get nothing at all.
Shouldn't this return at least something? My app seems to be crashing because well, there's nothing to be decoded.
I can't seem to find a lot of information about this around the internet so I'd be glad if someone could help me out.
Are there better ways to know whether or not a user likes our page? Or, is this the way to go and am I just overlooking something obvious?
Thanks!
The hint should be in your app crashing because there's nothing to decode.
I suspect the parameters get lost when redirecting. Think about it at the HTTP level:
The client posts to /tab with the signed_request in the params.
The app parses the signed_request and stores the result in instance variables.
The app redirects to /tab, i.e. sends a response with code 302 (or similar) and a Location header pointing to /tab. This completes the request/response cycle and the instance variables get discarded.
The client makes a new request: a GET to /tab. Because of the way redirects work, this will no longer have the params that were sent with the original POST.
The app tries to parse the signed_request param but crashes because no such param was sent.
The simplest solution would be to just render the template in response to the POST instead of redirecting.
If you really need to redirect, you need to carefully pass along the signed_request as query parameters in the redirect path. At least that's a solution I've used in the past. There may be simpler ways to solve this, or libraries that handle some of this for you.

Resources