Web Server Flow in the Rack OAuth-2 Server - ruby

I'm trying to integrate the Rack OAuth-2 server into my sinatra application, to use it in a web-server flow implementation and I can't make it work :(. I the following code in the oauth controller
require "rack/oauth2/sinatra"
module RestKit
module Network
class OAuth2 < Sinatra::Base
use Rack::Logger
set :sessions, true
set :show_exceptions, true
ENV["DB"] = "test"
DATABASE = Mongo::Connection.new[ENV["DB"]]
register Rack::OAuth2::Sinatra
oauth.authenticator = lambda do |username, password|
"Batman" if username == "cowbell" && password == "more" end
oauth.host = "localhost"
oauth.database = DATABASE
# 3. Obtaining End-User Authorization
before "/oauth/*" do
halt oauth.deny! if oauth.scope.include?("time-travel") # Only Superman can do that
end
get "/oauth/authorize" do
"client: #{oauth.client.display_name}\nscope: #{oauth.scope.join(", ")}\nauthorization: #{oauth.authorization}"
end
post "/oauth/grant" do
oauth.grant! "Batman"
end
post "/oauth/deny" do
oauth.deny!
end
# 5. Accessing a Protected Resource
before { #user = oauth.identity if oauth.authenticated? }
oauth_required "/user"
get "/user" do
#user
end
get "/list_tokens" do
oauth.list_access_tokens("Batman").map(&:token).join(" ")
end
end
end
end
Then I try to obtain an authorization code using curl from terminal with:
curl -i http://localhost:4567/oauth/authorize -F response_type=code -F client_id=[the ID] -F client_secret=[the secret] -F redirect_uri=http://localhost:4567/oauth/showcode
and Just I got as a response:
HTTP/1.1 400 Bad Request
Content-Type: text/plain
Content-Length: 20
Connection: keep-alive
Server: thin 1.2.11 codename Bat-Shit Crazy
Missing redirect URL
Do you have any ideas what I'm doing wrong? Thanks!

The end of your curl request is:
-F redirect_uri=http://localhost:4567/oauth/showcode
but you haven't defined that route in the code above, i.e. where is:
get "/oauth/showcode" do
? That's why the error is "Missing redirect URL".

Related

ORM JSON request handling for dataset insert

I am trying to view the JSON request which is sent from POSTMAN through a POST request to add a security group info to a table, and my request looks like one below
POST /securitygroup HTTP/1.1
Host: localhost:9292
Content-Type: application/json
Cache-Control: no-cache
Postman-Token: c4bef1db-d544-c923-3b0b-e7004e2dd093
{
"securitygroup":{
"secgrp_id": 124,
"secgrp_nm": "SECURITY ADMIN",
"secgrp_profile_nme": "ADMIN"
}
}
Roda code looks like below
# cat config.ru
require "roda"
require "sequel"
require "oci8"
require "json"
DB = Sequel.oracle(host: 'xyz.dev.com', port: '1525', database: 'devbox1', user: 'abc', password: 'pass')
class App < Roda
plugin :json, classes: [Array, Hash, Sequel::Model, Sequel::Dataset]
route do |r|
# secgroup = DB[:security_groups]
# secgroup.insert(r.params["securitygroup"])
# secgroup
# above insert threw the following error
# OCIError: ORA-00947: not enough values,
# because SQL generated as below
# INSERT INTO "SECURITYGROUPS" VALUES (NULL)
# so I am trying to access the request object 'r', I feel that I am doing
# something which is not correct
{"response": r.params.["securitygroup"]["secgrp_id"]}
# throws undefined method `[]' for nil:NilClass
end
end
Can you please take a look at the request and point me, where am I going wrong, Request format not correct or is there a different way to handle the request on the ruby code.
I need help to parse the request that comes in as JSON similar to code presented in https://twin.github.io/introduction-to-roda/
r.post "albums" do
album = Album.create(r.params["album"])
r.redirect album_path(album) # /albums/1
end
You need just a little tweak: Add to the App the plugin :json_parser.
class App < Roda
# use this plugin to convert response to json
plugin :json
# use this plugin to convert request from json
plugin :json_parser
...
end
See in the Roda documentation "Plugins that Ship with Roda" in the group "others" there is the json_parser plugin.

Serving files & handling requests with Webrick on same port

Hi I have a need to be able to receive requests from GitLab (body => JSON) as well as serve files on same port. I am trying to use Webrick for this purpose. I can do these separately.
To serve files I do:
server = WEBrick::HTTPServer.new(:Port => 3030, :DocumentRoot => '/')
server.start
To receive and process jSON I do:
server = WEBrick::HTTPServer.new(:Port => 3030, :DocumentRoot => '/')
server.mount_proc '/' do | req, res |
Queue.new(req.body)
end
But I need this functionality combined, is there a way to do this with Webrick?
Yes, this is certainly possible with Webrick or any HTTP server. There will be two different HTTP actions depending on what the users wants to do, 1.) a GET request to serve the files or 2.) a POST request to process some JSON.
Here's a simple example to show you how to do both:
class Server < WEBrick::HTTPServlet::AbstractServlet
def do_GET (request, response)
puts "this is a get request"
end
def do_POST (request, response)
puts "this is a post request who received #{request.body}"
end
end
server = WEBrick::HTTPServer.new(:Port => 3030)
server.mount "/", Server
trap("INT") {
server.shutdown
}
server.start
Once that is running you can test this by doing the following in a separate terminal window:
curl localhost:3030
output:
this is a get request
localhost - - [23/Apr/2015:06:39:20 EDT] "GET / HTTP/1.1" 200 0
- -> /
To test the POST request:
curl -d "{\"json\":\"payload\"}" localhost:3030
output:
this is a post request who received {"json":"payload"}
localhost - - [23/Apr/2015:06:40:07 EDT] "POST / HTTP/1.1" 200 0
- -> /
Since you mentioned the purpose was a light code-base, here's a light, fast script using the Plezi framework...
This will allow for easier testing, I think (but I'm biased). Also, Plezi is faster on my machine then Webrick (although it's a pure ruby framework, no rack or 'c' extensions involved).
require 'plezi'
class MyController
def index
# parsed JSON is acceible via the params Hash i.e. params[:foo]
# raw JSON request is acceible via request[:body]
# returned response can be set by returning a string...
"The request's params (parsed):\n#{params}\n\nThe raw body:\n#{request[:body]}"
end
end
# start to listen and set the root path for serving files.
listen root: './'
# set a catch-all route so that MyController#index is always called.
route '*', MyController
(if you're running the script from the terminal, remember to exit irb using the exit command - this will activate the web server)

Load session using session id

I have a Sinatra application, in which one of the rules expect the URL to contain the session id (returned from a previously called URL). How to overload the session with the one referenced by the session ID?
use Rack::Session::Pool
get "example/:session_id" do
sessionId = params[:session_id]
# how to reload the session now, using the sessionId?
end
Thanks for the help!
Session ID in Path
If you must use a session id from the path, you can use this hacky way
# app.rb
require 'sinatra'
configure do
use Rack::Session::Pool, :key => 'session_id'
end
get "/example/:session_id" do
session.id = params[:session_id]
session[:force_load] = 'force' # force session.load!()
session.delete(:force_load) # remove dummy value
session[:count] = session[:count].to_i + 1
session.inspect
end
Session ID in Request Params
A supported way would be to set :cookie_only option to false if you are willing to move session_id to a request parameter (query parameter or body parameter)
# app.rb
require 'sinatra'
configure do
use Rack::Session::Pool, {
:key => 'session_id',
:cookie_only => false, # adds request params as source for session id
}
end
# works with /example?session_id=
get '/example' do
session[:count] = session[:count].to_i + 1
session.inspect
end
Demo
$ ruby app.rb &> /dev/null &
$ curl -i localhost:4567/example?session_id=
HTTP/1.1 200 OK
Set-Cookie: session_id=2a06759c64c130bc45036076; path=/; HttpOnly
{"count"=>1}
$ curl -i localhost:4567/example?session_id=2a06759c64c130bc45036076
HTTP/1.1 200 OK
Set-Cookie: session_id=2a06759c64c130bc45036076; path=/; HttpOnly
{"count"=>2}
$ kill %1

HTTP Basic Authentication with Anemone Web Spider

I need collect all "title" from all pages from site.
Site have HTTP Basic Auth configuration.
Without auth I do next:
require 'anemone'
Anemone.crawl("http://example.com/") do |anemone|
anemone.on_every_page do |page|
puts page.doc.at('title').inner_html rescue nil
end
end
But I have some problem with HTTP Basic Auth...
How I can collected titles from site with HTTP Basic Auth?
If I try use "Anemone.crawl("http://username:password#example.com/")" then I have only first page title, but other links have http://example.com/ style and I received 401 error.
HTTP Basic Auth works via HTTP headers. Client, willing to access restricted resource, must provide authentication header, like this one:
Authorization: Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ==
It contains name and password, Base64-encoded. More info is in Wikipedia article: Basic Access Authentication.
I googled a little bit and didn't find a way to make Anemone accept custom request headers. Maybe you'll have more luck.
But I found another crawler that claims it can do it: Messie. Maybe you should give it a try
Update
Here's the place where Anemone sets its request headers: Anemone::HTTP. Indeed, there's no customization there. You can monkeypatch it. Something like this should work (put this somewhere in your app):
module Anemone
class HTTP
def get_response(url, referer = nil)
full_path = url.query.nil? ? url.path : "#{url.path}?#{url.query}"
opts = {}
opts['User-Agent'] = user_agent if user_agent
opts['Referer'] = referer.to_s if referer
opts['Cookie'] = #cookie_store.to_s unless #cookie_store.empty? || (!accept_cookies? && #opts[:cookies].nil?)
retries = 0
begin
start = Time.now()
# format request
req = Net::HTTP::Get.new(full_path, opts)
response = connection(url).request(req)
finish = Time.now()
# HTTP Basic authentication
req.basic_auth 'your username', 'your password' # <<== tweak here
response_time = ((finish - start) * 1000).round
#cookie_store.merge!(response['Set-Cookie']) if accept_cookies?
return response, response_time
rescue Timeout::Error, Net::HTTPBadResponse, EOFError => e
puts e.inspect if verbose?
refresh_connection(url)
retries += 1
retry unless retries > 3
end
end
end
end
Obviously, you should provide your own values for the username and password params to the basic_auth method call. It's quick and dirty and hardcode, yes. But sometimes you don't have time to do things in a proper manner. :)

SOAP + SSL + Ruby Savon - HTTPClient::ConnectTimeoutError: execution expired

I am trying to get a simple ruby script to send requests to a SOAP API but I am not able to get responses back.
This is what I am trying to do:
require 'date'
require 'savon'
# Create the client
client = Savon::Client.new do
wsdl.document = File.expand_path("path to wsdl document", __FILE__)
end
# Setup namespaces and credentials
client.wsdl.namespace = "http://www.example.com"
client.wsse.credentials "[USERNAME]", "[PASSWORD]"
# Setup ssl configuration
client.http.auth.ssl.cert_key_file = "path to key pem file"
client.http.auth.ssl.cert_file = "path to cert pem file"
client.http.auth.ssl.verify_mode=:peer
# execute request
response = client.request :sub, :get_data do
soap.body = {"sub:id" => "123456"}
end
This request finishes with:
D, [2011-05-05T10:21:45.014588 #22136] DEBUG -- : SOAP request: "http://www.example.com"
D, [2011-05-05T10:21:45.014743 #22136] DEBUG -- : Content-Type: text/xml;charset=UTF-8, SOAPAction: "getData"
D, [2011-05-05T10:21:45.014787 #22136] DEBUG -- : <?xml version="1.0" encoding="UTF-8"?><env:Envelope ...(XML request)... </env:Body></env:Envelope>
D, [2011-05-05T10:21:45.014864 #22136] DEBUG -- : HTTPI executes HTTP POST using the httpclient adapter
HTTPClient::ConnectTimeoutError: execution expired
However, when I try to send the same request via curl, it works (copying the xml request above to the soap-request.xml file):
curl -k -v --header "Content-Type: text/xml;charset=UTF-8, SOAPAction: 'getData'" https://www.example.com -d#soap-request.xml --cert-type PEM --cert path_to_pem_file_with_both_key_and_cert
Any ideas about what I'm missing in the ruby script?
Thanks in advance.
UPDATE:
The code above works if the WSDL document is correct. However, in case there isn't one or in case it is erroneous, just replace the client declaration with this:
# Create the client
client = Savon::Client.new do
wsdl.endpoint = "https://whateverendpoint.com"
wsdl.namespace = "http://whatevernamespace.com"
end
Finally, it is also a good idea to catch possible faults as described in Savon's documentation:
begin
# execute request
response = client.request :sub, :get_data do
soap.body = {"sub:id" => "123456"}
end
rescue Savon::SOAP::Fault => fault
puts fault.to_s
end
Have you tried to extend the http timeout? I had the same problem with some of my SOAP call that took very long on the server side. What I did was this
jira = Savon::Client.new do
wsdl.document = 'http://jira.xxx.com/rpc/soap/jirasoapservice-v2?wsdl'
end
jira.http.read_timeout = 300
done = 0
dotPrinter = Thread.new do
sec = 0
while(done==0) do
sleep 1
$STDERR.print "\b\b\b%03i" % sec
sec += 1
end
end
resp = jira.request :get_issues_from_filter do
soap.body = {:in0 => jira_token, :in1 => 18579}
end
done = 1

Resources