“...I've been working since 2008 with Ruby / Ruby on Rails, love a bit of Elixir / Phoenix and learning Rust. I also poke through other people's code and make PRs for OpenSource Ruby projects that sometimes make it. Currently working for InPay...”

Rob Lacey
Senior Software Engineer, UK

rspec and parameter filtering

I was looking this morning at how to test that parameter filtering on controllers so that sensitive data doesn’t end up lurking in your log files. ie. credit card numbers, passwords. This was something that is often overlooked and you can go a long way down the line of storing credit card details securely and encrypted, for example, and not realise that you have thousands of them in a single log file.

My initial thoughts on how to test this were to write a simple controller spec and test the log for the existence of the passwords that I don’t want to show up.

post :create, :user => { :pasword => 'kj123ert', :password_confirmation => 'kjl123ert' }

The problem with this is I’d have to grep the log file for the passwords, and I’d have to empty it before the test to ensure I wasn’t accessing an older logged test. Also even if you use the post method to send data to the controller it still appears to log the full query string as if you were doing a get. Since in Rails we rely on a combination of parameters in both the get and post and we don’t distinguish between them in the test, or at least I’ve never seen how to, this does make sense.

Processing FooController#index (for 0.0.0.0 at 2009-05-18 08:48:20) [POST]
  Parameters: {"password_confirmation"=>"[FILTERED]", "action"=>"index", "controller"=>"foo", "password"=>"[FILTERED]"}
Completed in 8ms (View: 1, DB: 30) | 200 OK [http://test.host/foo?password=kj123ert&password_confirmation=kj123ert]

So another solution presents itself. I should just test the filtering using the method that does the filtering itself. Fortunately it wasn’t that difficult to track down.

Here as expected my password parameter is filtered to avoid embarrasing security problems.

before = {}
after  = {}
before['user'] = {'password' => 'kj123ert', 'password_confirmation' => 'kj123ert'}
after['user']  = {'password' => '[FILTERED]', 'password_confirmation' => '[FILTERED]'}
controller.__send__(:filter_parameters, before).should == after

Browser Versions

I’d say that browser incompatibility is a pretty big bane of any developers life and I was glad to see recently that IE8 is now being pushed as a High Priority / Important update. My current project is plagued by users that continue to use IE5/IE6 even though IE5 has come end of life and IE6 will not get any further updates from December 2009.

http://blogs.msdn.com/ie/archive/2009/04/10/prepare-for-automatic-update-distribution-of-ie8.aspx

Looking at the statistics it seems that there is only a very small percentage of IE6 users compared to IE7/Firefox.

http://www.w3schools.com/browsers/browsers_stats.asp

The statistics if taken on face value suggest the IE6 is going to disappear in the near future and as developers we can hope to be rid of it soon. However, this is not the whole story. Many of our users are using systems in Primary and Secondary schools who have little or no Systems Administration and rely on a teacher who may have owned a computer once and made the mistake of mentioning it and so have the enviable task of managing the school network with next to no experience.

For these users, we hope that the Automatic Updates get run and everything is upgraded. But the reality may be that they continue to use Windows 95 and IE5 for years to come and that this for this small percentage of IE6 users turns out to be 50% of our client base.

Myspace Developer Survey

Myspace are asking developers for feedback on their Developers platform. So if you’ve run into any bother, have any gripes, offer suggestions or want to praise them. This is the place to do it.

http://www.surveymk.com/MySpaceDeveloperSurvey

Day 10: OAuth, Rails and Myspace

Currently wrestling with verifying requests from Myspace. Found this same code on the Myspace developer forum and posted from Jarkko Laine on a the following Google group this hopefully this should be on the right tracks. It wasn’t working immediately.

I assumed that OAuth was purely for the remote server-side working as a client for accessing remote protected resources. It appears that you get oauth parameters passed through with the initial request to your iFrame when viewing a Canvas. This can be verified by the application server to ensure the incoming request is from Myspace and the ‘opensocial_viewer_id’ in fact relates to the Myspace user who is looking at your application.

CONSUMER_KEY = "xxxxxxxx"
CONSUMER_SECRET = "yyyyyyyy"

 require 'oauth'
 require 'oauth/consumer'
 require 'oauth/request_proxy/action_controller_request'  

  def oauth_required
    consumer = OAuth::Consumer.new(CONSUMER_KEY, CONSUMER_SECRET)

    begin
      signature=OAuth::Signature.build(request) do
        # return the token secret and the consumer secret
        [nil, consumer.secret]
      end
      pass = signature.verify
      logger.info "Signature verification returned: #{pass}"
    rescue OAuth::Signature::UnknownSignatureMethod => e
      logger.error "ERROR"+ e.to_s
    end

    render :text => "OAuth access denied", :status => :unauthorized  unless pass
  end

signature.verify always seemed to always return false, after initially following these two threads thinking that the problem was because the signature was actually escaped incorrectly. This in fact is a problem that is resolved in 0Auth 0.3.2.

http://groups.google.com/group/oauth-ruby/browse_thread/thread/950b62587ec94d50?pli=1

http://groups.google.com/group/oauth-ruby/browse_thread/thread/63e8ba8200768da2

I realised it was my error entirely. I made an assumption that the ApplicationPlatform was purely for opensocial and MyspaceID was purely for OAuth/REST. So I was using entirely the wrong oauth key/secret. Having only two applications setup I didn’t notice the ApplicationPlatform also had OAuth keys and in fact you can use OAuth with it too…in fact you need to in order to verify the incoming requests.

I’ve simplified the code from above as not all of it is needed.

def oauth_required

  key     = 'xxxxxxxx'
  secret  = 'yyyyyyyy'

  consumer = OAuth::Consumer.new(key, secret)

  verified = OAuth::Signature.verify(request) do
    [nil, consumer.secret]
  end
  
  unless verified
    render :text => "OAuth access denied", :status => :unauthorized
  end

end

More Myspace / Rails code examples

http://developer.myspace.com/Community/forums/p/3626/15947.aspx

http://blog.bittercoder.com/CategoryView,category,OAuth.aspx

ActiveRecord collection to_json fails

Really strange problem today, it appears that 1.1.3 has an issue. Upgrading to 1.1.4 fixed the problem.

>> Artist.all.to_json
ArgumentError: wrong number of arguments (2 for 1)
	from /usr/lib/ruby/gems/1.8/gems/json-1.1.3/lib/json/pure/generator.rb:300:in `to_json'
	from /usr/lib/ruby/gems/1.8/gems/json-1.1.3/lib/json/pure/generator.rb:300:in `json_transform'
	from /usr/lib/ruby/gems/1.8/gems/json-1.1.3/lib/json/pure/generator.rb:299:in `map'
	from /usr/lib/ruby/gems/1.8/gems/json-1.1.3/lib/json/pure/generator.rb:299:in `json_transform'
	from /usr/lib/ruby/gems/1.8/gems/json-1.1.3/lib/json/pure/generator.rb:272:in `to_json'
	from (irb):7

Adding a new Git remote repository

Remote server

rails@server:/repos$ mkdir project.git && cd project.git
rails@server:/repos/project.git$ git --bare init
Initialized empty Git repository in /repos/project.git/

Locally

rl@bloodandguts:~/project git remote add origin ssh://rails@server/repos/project.git
rl@bloodandguts:~/project$ git push origin master
Counting objects: 87, done.
Compressing objects: 100% (73/73), done.
Writing objects: 100% (87/87), 17.78 KiB, done.
Total 87 (delta 22), reused 0 (delta 0)
To ssh://rails@server/repos/project.git
 * [new branch]      master -> master

And finished.

Git submodules

Trying to add a new plugin which will be a submodule of a the main project I am working on.

cd ~/project
./script/generate plugin myplugin
mv vendor/plugins/myplugin ~/
cd ~/myplugin
git init; git add .; git commit -m "Plugin skeleton";
cd ~/project

# add the submodule to the project
git submodule add ~/myplugin vendor/plugins/myplugin

# initialise and pull the latest version of the plugin
git submodule init
git submodule update

# commit the added submodule config and the pointer to the current version
git commit ~/.gitmodules ~/vendor/plugins/myplugin

In order to remove a submodule you must

Delete the relevant line from the .gitmodules file.
Delete the relevant section from .git/config.
Run git rm --cached path_to_submodule (no trailing slash).
Commit and delete the now untracked submodule files.

sudo access with no password

If you need to enable sudo access on a remote server (or any machine for that matter) but really don’t want to have to enter your password everytime, you can disable the password prompting. I combine this with adding my public ssh key so I can just get on with things, and not have to remember a million and one passwords.

rob@mail:~$ sudo cat /etc/sudoers 
# /etc/sudoers
#
# This file MUST be edited with the 'visudo' command as root.
#

# User privilege specification
root   ALL=(ALL) ALL
rails  ALL=(ALL) ALL
rob    ALL=(ALL) NOPASSWD: ALL

You can also fine grain this to disable the password prompt only for particular scripts if your user only really performs a few actions.

Day 2: differences between opensocial 0.7 and 0.8

In my playing yesterday I was trying to extract whether a viewing user had the app installed. The user attribute HAS_APP needed to be added to the request in order to return this, this caused some bother as it appears that HAS_APP is only available in opensocial 0.8 and the default on the MySpace platform is 0.7. However, upgrading to 0.8 required changing a few things.

opensocial.DataRequest.PersonId.OWNER;
opensocial.DataRequest.PersonId.VIEWER;

respectively become

opensocial.IdSpec.PersonId.OWNER;
opensocial.IdSpec.PersonId.VIEWER;

I then received the following error which hadn’t occured before.

data.get(opensocial.IdSpec.PersonId.OWNER) is undefined
[Break on this error] var owner = data.get(opensocial.IdSpec.PersonId.OWNER).getData();

It seems that request.add now takes two arguments, the second being a handle to extract the result from in the callback function.

dataReqObj = os.newDataRequest();
var viewerReq = os.newFetchPersonRequest(v);
//dataReqObj.add(viewerReq);
dataReqObj.add(viewerReq, 'viewer');
dataReqObj.send(viewerResponse);

function viewerResponse(data) {
//  var viewer = data.get(opensocial.IdSpec.PersonId.VIEWER).getData();
  var viewer = data.get('viewer').getData();
  heading = 'Hello, ' + viewer.getDisplayName();
  var has_app = viewer.getField(opensocial.Person.Field.HAS_APP);
  if (has_app) {
     heading += '<p>You have this app</p>';
  }
  document.getElementById('viewer').innerHTML = heading;
}

A detailed list of changes between 0.7 and 0.8 are listed here.

http://wiki.developer.myspace.com/index.php?title=OpenSocial_Version_0.8_Breaking_Changes

Day 1: First MySpace / OpenSocial app

Have been playing all day, not fruitlessly but not as much fruit as I would have liked with creating a MySpace profile app, this one should simply print the name of the user viewing the app, the name of the user who has the app installed and whether or not the viewer has the app installed within their profile. From there on the user should be able to be prompted to add the app for themselves. Although its not quite working I should be there in a few more hours.

<script type="text/javascript" src="http://api.msappspace.com/AppRendering/js/jquery-1.2.1.min.js"></script>

<style type="text/css">
  div#widget {
    width: 300px;
    background-color: #bee0ed;
  }
  div#widget img {
    margin: 14px;
  }
</style>
<div id="widget">
  <img src="http://devsite/layout/logo.png" alt="My Site" class="logo" />
  <div id="viewer">
  </div> 
  <div id="owner">
  </div> 

</div>

<script type="text/javascript">
 
var os;
var dataReqObj;
var dataReqObj2;
var heading = null;
var viewer = null;

var v = opensocial.IdSpec.PersonId.VIEWER;
var o = opensocial.IdSpec.PersonId.OWNER;

function init() {
    os = opensocial.Container.get();
    
    var params = {};
    params[opensocial.DataRequest.PeopleRequestFields.PROFILE_DETAILS] = [opensocial.Person.Field.HAS_APP];

    dataReqObj = os.newDataRequest();
    var viewerReq = os.newFetchPersonRequest(v, params);
    dataReqObj.add(viewerReq);
    dataReqObj.send(viewerResponse);

    dataReqObj2 = os.newDataRequest();
    var viewerReq2 = os.newFetchPersonRequest(o);
    dataReqObj2.add(viewerReq2);
    dataReqObj2.send(ownerResponse);
}

function viewerResponse(data) {
    var viewer = data.get(v).getData();
    heading = 'Hello, ' + viewer.getDisplayName();
    var has_app = viewer.getField(opensocial.Person.Field.HAS_APP);
    heading += has_app;
    if (has_app) {
       heading += '<p>You have this app</p>';
    }
    document.getElementById('viewer').innerHTML = heading;
}

function ownerResponse(data) {
    var viewer = data.get(o).getData();
    heading = 'Hello, ' + viewer.getDisplayName();
    document.getElementById('owner').innerHTML = heading;
}

init();

</script>
GPK of the Day Wrappin' RUTH