NetManiac

Witold Rugowski on web20 wave with Ruby on Rails

Passing current user id to Rails models

Posted on November 30, 2011 - Filed Under RubyOnRails

If you're new here, you may want to subscribe to my RSS feed. You can also get updates by email Thanks for visiting!

Long time since last post, isn’t it? Well, I’m completely busy with my work in Nettigo, selling Arduinos and other nice gears :) I do not do gigs anymore, so there is a lot less reasons to write about Rails.

But I still use this framework, since my backend software is written in Rails, so here is one thing which I think can be useful for You.

Often we want to pass ID of current user to Rails model internals. Most answers are don’t do that, this is controller work to do authorization stuff, MVC is to prevent such thins, etc.

here was not related picture (as usual on this blog). Some readers were complaining it was not only unrelated but even offending. Well, maybe they were right, so, I have removed that picture.

Yeah, right, but what if we want to keep some kind of audit trail what is happening with given model? This is not authorization, just who done what.

Since in my application case audit records are created in observers simple passing user id as some additional parameter is no go for me – I don’t want to change interface just to pass user info, that breaks too much things.

So, I have chosen this approach:

def with_user user, &block
  if user.blank?
    yield
  else
    Thread.current[:user] = user.id
    yield
    Thread.current[:user] = nil
  end
end

And each operation I want to track I invoke:

with_user(current_user) do
 model.do_some_stuff
end

Thread.current acts as a hash accessible in current thread and allow us to pass some info skipping MVC isolation (You wanted that, right? :) )

In observers I can access user ID and store it with record. If it is not present, audit trails is being marked as created by System. That means probably cron job, or console action (well on console I can run code inside with_user block when needed).

This is safe as long each request is being processed in single thread and we make sure that user ID will be cleared after our operation. When ID will stay in Thread.current, next request being processed by this Rails app instance will have access to that value and wrong audit records will be created.

Code has to be updated, since any exception will occur inside block, code after yield will be skipped and user ID won’t be erased from Thread.current. Here it is, final version:

def with_user user, &block
  if user.blank?
    yield
  else
    Thread.current[:user] = user.id

    begin
      yield
    ensure
      Thread.current[:user] = nil
    end
  end
end

Code inside ensure will be executed each time – both when exception was raised and when execution was clean.

Popularity: 43% [?]

wsdl2ruby and failing SSL certificate validation

Posted on January 21, 2011 - Filed Under Uncategorized

When You are about to use Ruby to connect to some SOAP-like API You will probably use SOAP4r gem. First step would be to generate client code from WSDL provided by API. If it is served via HTTPS and server has SSL certificate not signed by some common CA (like self signed certs) You will experience following error running wsdl2ruby.rb:

at depth 0 - 20: unable to get local issuer certificate
F, [2011-01-18T10:13:40.816069 #4035] 
FATAL -- app: Detected an exception. Stopping ... 
SSL_connect returned=1 errno=0 state=SSLv3 read server certificate B: 
certificate verify failed (OpenSSL::SSL::SSLError)

HTTPClient (this is gem used by SOAP4r to do all HTTP communication) tries to validate SSL certificates by default. Good, this is what SSL was thought for :) HTTPClient allows to use own CA (which is better solution than to lower security, especially on production :) ), however I don’t know how to pass this via SOPA4r abstraction (ssl-config.set-trust-ca('path') in HTTPClient (UPDATE – OK I think here You can find info how to specify CA, client key and cert in SOAP client). We can tell HTTPClient not to check SSL certs via options of SOAP::RPC::Driver when using client code:

d = SomeSOAP::ClientClass.new
d.options['protocol.http.ssl_config.verify_mode'] = OpenSSL::SSL::VERIFY_NONE

This may be not very wise, so use that if You know what You are doing.

And what about wsdl2ruby.rb

Above example relates to scenario when You have generated client code, which should connect to HTTPS. But how to recover from certificate verify failed when running wsdl2ruby.rb (in other words – when generating client code and WSDL is on HTTPS server with self signed cert)?

First solution is to download WSDL and generate client code from local file. Will do the trick unless WSDL imports some additional XSDs via HTTPS URL…

This gist gave me idea how to solve that (BTW – very useful trick to debug HTTP traffic if You can not use proxy like Charles Proxy). I did checked out SOAP4r gem code and updated wsdl2ruby.rb adding at begin:

BEGIN {
  require 'rubygems'
  require 'httpclient'
  HTTPClient.class_eval do
    alias_method '__initialize__', 'initialize'
    	    
    def initialize(*args,&block)
      __initialize__(*args, &block)
    ensure
      self.ssl_config.verify_mode=OpenSSL::SSL::VERIFY_NONE
    end
  end
}

That’s it. Well, almost. I was unlucky – due to some configuration twists WSDL and other referenced resources were hosted in some dev environment with wrong network configuration. Fixing that was totally out of my reach.

What was wrong? WSDL was accessible via HTTPS. All resources imported inside were referenced via HTTPS, too. But. When accessing these HTTPS URLs (other than WSDL) from outside network (where I was) plain HTTP error message was displayed, so SOAP code could not be generated. When URL was changed to HTTP – proper XML file was available.

So, another hack to wsdl2ruby.rb:

class  HTTPClient
    alias_method '__get_content__', 'get_content'
    def get_content(uri, query = nil, extheader = {}, &block)
     uri = URI.parse(uri.to_s.gsub(/^https:/,'http:'))
     if block_given?
       __get_content__(uri, query, extheader) {block}
     else
       __get_content__(uri, query, extheader)
     end
    end
end

Now all requests to HTTPS are converted to plain HTTP. Again – do it when You understand what You are doing :)

Where Ruby brought me

When I started this blog (Apr 2006) I’ve just have discovered Ruby on Rails. During this almost five years many things have changed. And finally Ruby brought me to this place:

Massimo and Arduino Logo at Maker Faire 2010 in NYC

Massimo and Arduino Logo at Maker Faire 2010 in NYC
CC by http://www.flickr.com/photos/mattrichardson/

And now small announcement. As You can see I was publishing on this blog very seldom in recent months. And reason is very simple. Almost two years ago I have started side business (do freelancer have side businesses? :)) ) selling Arduinos in Poland (and whole Europe). After slow start, it have took off, and between my work as freelancer and running shop there were no time to write on this blog.

Recently I have made decision to stop freelancing and devote all my time to Arduino (and electronics) related topics. Will I stop writing about Ruby? I guess not. Shop is on some hosted platform, but I do run some custom RoR software as backend. I plan to migrate at some point to own e-commerce solution, so I’m not leaving Ruby world.

Popularity: 22% [?]

ActiveMerchant: dumping traffic between Your app and payment gateway

Posted on October 4, 2010 - Filed Under RubyOnRails

Just a quick note – if You need to dump all traffic between payment gateway and Your application using ActiveMerchant just add to Your development.rb:

ActiveMerchant::Billing::QuantumGateway.wiredump_device = File.open("/tmp/q.log", "a+")
ActiveMerchant::Billing::QuantumGateway.wiredump_device.sync = true

Just remember – it will work ONLY You have one application instance, since it uses simple file handle to write all traffic. And replace QunatumGateway with gateway used by You.

Popularity: 29% [?]

ArgumentError: marshal data too short when loading session data

Posted on August 20, 2010 - Filed Under RubyOnRails

I was stuck for a while when application maintained by me have started to throw ArgumentError: marshal data too short errors in random places When user have encountered that problem then it was unable to use application at all.

Marshal

Marshal CC http://www.flickr.com/photos/qmnonic/

Logs were showing that it happens when Rails was trying to create session object. Session store was in ActiveRecord and sessions table was not corrupted.

After watching that for a while it have shown that places in code were this exception was thrown were random but there was pattern. Page visited before was common in each case.

It have turned out that application was storing in session whole ActiveRecord object. Like:

 session[:some_info] = @variable

And later we were trying to use that way:

@variable = Model.find session[:some_info]

Due to Rails magic AcitiveRecord’s find when provided with AR object will return that object (of course if is the same model). Code was working well (maybe not very effectively since You should avoid storing large objects in session) until object stored that way started to grow. Application was collecting some data and amount of data stored have grown to that point that after Marshal.dump size of string was more than 64 kB. And this is default size of text field used to store session data in MySQL.

When You try to store too much data in text field in MySQL, excessive data is being truncated, so Marshal.load throws that exception.

To have that error solved is enough to store just object id in session (session[:some_info] = @variable.id).

Popularity: 60% [?]

CHM should be in…

Posted on July 22, 2010 - Filed Under Uncategorized

Well… Long time… But finally a new post!

If You have drank Linux KoolAid, then You can run on problems when You get some CHM file. It is old format and I think it has place where it can live (like many other proprietary data formats):

Place where we should send .chm files

Place where we should send .chm files (c) Marcin Wichary

But still, You can get documentation in such format, chances are high if You are trying to interface some .Net SOAP service. In Linux – no viewer for CHM.

Or rather no standalone viewer. There is solution – CHM Reader addon for Firefox. It is not perfect (no global search), but allows to navigate through that file. Printouts to PDF usually are stripped from hyperlinks, so navigating through 800 pages is reason why PDF printout is not an option…

Popularity: 22% [?]

older entries »