Rails + oauth-plugin + mongodb part 2: Consumer

In previous post, we went through the process to create a Provider with Oauth-plugin that works with Mongodb using mongid as the driver. Here we will build the Consumer using mongoid too.

This Consumer app will run on port 4000 (rails s -p 4000) and connect with the Provider app which will be running on port 3000.

Step 1 – step 3

Exactly the same as in previous post for Provider

4. Generates things

rails g devise:install
rails g devise User
rails g controller Welcome index
rails g oath_consumer User
rm public/index.html

Add this to your User model:

references_many :consumer_tokens
index "consumer_tokens.token"

In app/model/consumer_token.rb, find the line reads

embedded_in :user, :inverse_of => :consumer_tokens

change it to

referenced_in :user, :inverse_of => :consumer_tokens

In oauth_consumers_controller.rb, comment out the line reads

before_filter :login_required, :only=>:index

Uncomment/add new line that reads

before_filter :authenticate_user!, :only=>:index

In oauth_consumers_controller.rb, make sure these methods are NOT commented out:

go_back, logged_in?, current_user=, deny_access!

Added this to User model:

references_one :test, :class_name => "TestToken", :dependent => :destroy

(TestToken is the model for the provider, we would have TwitterToken, FacebookToken…etc)

Create a model file named test_token.rb in app/models/ with the content:

class TestToken < ConsumerToken
  TEST_SETTINGS = {
    :site => 'http://localhost:3000', # this is the URL to provider app
    :request_token_path => '/oauth/request_token',
    :access_token_path => '/oauth/access_token',
    :authorize_path => '/oauth/authorize'
  }

  def self.consumer(options={})
    @consumer ||= OAuth::Consumer.new(credentials[:key], credentials[:secret], TEST_SETTINGS.merge(options))
  end
end

In config/routes.rb, add your root path root :to => "welcome#index"

5. Communicate with the Provider

Let’s start provider app on port 3000 and consumer app running on port 4000 (rails s -p 4000)

Navigate to http://localhost:3000/users/sign_up to register an account.

Navigate to http://localhost:3000/oauth_clients/ to register your app with these info:

Name: Test consumer
Main Application URL: http://localhost:4000/
Callback URL: http://localhost:4000/oauth_consumers/test/callback

You will be redirected to oauth_client show page with credentials (yours will be different)

Consumer Key: d8KBiaD98Mnp2vyB9A8ZSAT0vpKu5kdFtAXUsZup
Consumer Secret: UDdD5HAefrRZ1afguDy0WrTALYwZ8KXWKgLiSJCE
Request Token URL http://localhost:3000/oauth/request_token
Access Token URL http://localhost:3000/oauth/access_token
Authorize URL http://localhost:3000/oauth/authorize

In config/initializers/oauth_consumers.rb, add the credentials above. The content will look like:

OAUTH_CREDENTIALS = {
  :test => {
    :key => 'd8KBiaD98Mnp2vyB9A8ZSAT0vpKu5kdFtAXUsZup',
    :secret => 'UDdD5HAefrRZ1afguDy0WrTALYwZ8KXWKgLiSJCE',
    :expose => true
  }
}

Restart your Consumer app if it was running when you changed the content of this initializer.

Modify the content of Welcome#index to get the provider data:

class WelcomeController < ApplicationController
  def index
    @consumer_tokens = TestToken.where(:user_id => current_user.id)
    @token = @consumer_tokens.first.client
    logger.info 'private data' + @token.get('/data/index').body
  end
end

Go to http://localhost:4000/oauth_consumers to see all the services. Actually we have only 1 here, it’s the ‘test’ service which owned by ‘TestToken’ model.

Click on the service (here is test) then give it access

Go to http://localhost:4000 and you will see the data from provider printed out in log console

You can find the source code for Consumer here

Rails + oauth-plugin + mongodb part 1: Provider

Recently I had a chance to work with Oauth and Rails using oauth-plugin and mongodb. Although oauth-plugin itself has a documentation but actually doesn’t cover the mongoid part detailed enough. Also I found this very good tutorial but it’s for SQLite3 only: Oauth Provider, Oauth Consumer

Base on the above posts, to get Rails and oauth-plugin working with mongodb, you need to do some changes.

Firstly, create a oauth-sample directory, you’ll put provider and consumer apps in there.

1. Create new project

    • mkdir oauth
    • cd oauth
    • rails new provider
    • cd provider
    • add these gems to your Gemfile
gem 'rails', '3.0.12'
gem 'devise'
gem 'oauth-plugin', '~> 0.4.0'
gem 'mongoid' gem 'bson_ext'
  • bundle install`

Now you have:

  • an authentication system installed (Devise)
  • oauth-plugin which helps you a lot in generating files for Oauth
  • mongoid a driver for mongodb
  • bson_ext which mainly for mongo peformance improvement

2. Config the Rails app to use mongodb

  • delete config/database.yml
  • rails g mongoid:config
  • replace require 'rails/all' in config/application.rb with
require "actioncontroller/railtie"
require "actionmailer/railtie"
require "activeresource/railtie"
require "rails/testunit/railtie"
#require "sprockets/railtie" # Uncomment this line for Rails 3.1+

3. Config the generators

This step is important, you need to do this before generating/installing Devise model, Oauth things…

Add this code into your application.rb, uncomment haml/rspec if you use them. Keep the mongoid option as that is what we want.

config.generators do |g|
  g.orm :mongoid
  #g.template_engine :haml
  #g.test_framework :rspec
end

From now on, the generators know they should invoke mongoid instead of activerecord when generating things…

4. Generates things

rails g devise:install
rails g devise User
rails g controller Data index
rails g oath_provider
rm public/index.html

Add this into your User model:

references_many :client_applications
references_many :tokens, :class_name => "OauthToken", :order => "authorized_at desc"

Add this into your oauth_clients_controller.rb:

alias :login_required :authenticate_user!

In oauth_clients_controller.rb, find and replace the line reads

@tokens = current_user.tokens.find(:all, :conditions => 'oauth_tokens.invalidated_at is null and oauth_tokens.authorized_at is not null')

with

@tokens = current_user.tokens.includes(:client_application).where('oauth_tokens.invalidated_at is null and oauth_tokens.authorized_at is not null')

In config/application.rb, add this code:

require 'oauth/rack/oauth_filter'
config.middleware.use OAuth::Rack::OAuthFilter

In app/controllers/oauth_controller.rb, add this code:

alias :logged_in? :user_signed_in?
alias :login_required :authenticate_user!

Modify the Data#index to add some data:

class DataController < ApplicationController
  before_filter :oauth_required

  def index
    @data = { "coincoin" => "o< o<" }
    respond_to do |format| 
      format.json { render :json => @data } 
    end
  end
end

In config/routes.rb, add your root path root :to => "data#index"

5. Explain :include issue

As mentioned in oauth-plugin guide we should use

has_many :tokens, :class_name => "OauthToken", :order => "authorized_at desc", :include => [:client_application] with the :include

Actually :include is for ActiveRecord eager-loading feature and it doesn’t work with Mongoid, so I thought I should find an equivalent of it in Mongoid.

After doing a research I found that Mongoid originally didn’t support eager-loading, then started supporting eager-loading from version 2.2.0

We can use this feature by using Model#includes method as guided here

So I think we should remove the :include => [:client_application] option in User model, then add .includes(:client_application) to the controller as above.

6. Issues with oauth-plugin + mongoid

You can make these below changes yourself or simply get from here. I folked from original repo and modified.

You will encounter these one when you trying to connect consumer with provider on step 5 in consumer guide.
- Dynamic finders don’t seem to work. You’ll get the exception with ‘findbykey’, we need to modify the oauth-plugin gem directly (as the author hasn’t provide a fix yet).
Do this commands:

bundle show oauth-plugin # to know where it was installed.
cd /path/to/oauth-plugin/installed
cd lib
grep -rn 'find_by_' .

Let’s change all the .find_by_<a_key>(<a_value>) with .where(:<a_key> => <a_value>).first

E.g:

#this one
@token = ::RequestToken.findbytoken! params[:oauth_token]
#should be changed to
@token = ::RequestToken.where(:token => params[:oauth_token]).first

- undefined method 'expand_complex_criteria' for #<Array:0x00000102934308>

In lib/oauth/rack/oauth_filter.rb of oauth-plugin gem, find the line (around line #27) which reads:

if token = Oauth2Token.first(:conditions => ['invalidated_at IS NULL AND authorized_at IS NOT NULL and token = ?', token_string])

change it to

if token = Oauth2Token.where(:invalidated_at => nil, :authorized_at.ne => nil, :token => token_string).first

Then find the line (around #45) which reads:

oauth_token = client_application.tokens.first(:conditions => { :token => request_proxy.token })

change it to

oauth_token = client_application.tokens.where(:token => request_proxy.token).first

And we’re done for Provider

You can get the source code for Provider here

BrowserCMS from development to production

There’re some Rails CMS such as Radiant, Refinery, BrowserCMS… and I find BrowserCMS the best one to build simple websites for my customers, we can easily create dynamic content for you websites with Portlet, create new object to manage by with Content type, and easily create template for each page (homepage, subpages…).

In this post we won’t talk about how to use BrowserCMS, please go to the if you’re finding that information. This post just concentrates on things you need to do to get BrowserCMS work properly on server with production mode.

For me, I use a VPS with Rails 3, Ruby 1.9.2-p180 (with RVM installed), Rubygem 1.6.2, Nginx + Passenger.

I have it work well on my development machine, but on VPS under production mode, I have 2 problems:

  1. all stylesheets isn’t applied
  2. images can be uploaded but can’t be seen both in back-end and front-end

the first one will be resolved if you read the Deployment Guide of BrowserCMS, you’ll some basic settings need for production mode and set the config.serve_static_assets to true:

# To send CMS files, the new Rails 3 default (using X-Sendfile) must be commented out, or Apache mod_sendfile should be installed.
# See http://codequest.eu/articles/rails3-apache-passenger-and-empty-file-using-send-file for details
#config.action_dispatch.x_sendfile_header = "X-Sendfile"

# This has to be enabled so that core CMS assets (like internal buttons, css, etc) can be served from the bcms gem.
config.serve_static_assets = true

SITE_DOMAIN="www.sitedomain.com" # This should match the URL for your public CMS site.

# You may need to edit your mail server address as well in the next line.
config.action_mailer.smtp_settings = {:address => 'mail.yourmailserver.com', :domain => "#{SITE_DOMAIN}"}

the second one it’s because I’m using Nginx, you need to uncomment this line in config/initializers/browsercms.rb

17 # For nginx:
18 config.action_dispatch.x_sendfile_header = 'X-Accel-Redirect'

then images issue gone.

Really easy but it took me 2 hours to figure out, I had tried to check the chmod of uploaded files, check the location of uploaded files, check the database….

Hope this helps

Mac OS Lock screen with keyboard shortcut

When I step away from my Mac at work, I want a quick way to lock the screen, and hitting a hot-corner with the mouse is problematic for me. This hint details how to lock the screen from the keyboard by using Automator to build a Service in Snow Leopard.

First, check the General tab on the Security System Preferences panel to ensure that the Require password [some period] after sleep or screen saver begins box is checked.

Then, open Automator in the Applications folder, and select Service from the screen that appears. At the top of the new Service’s actions, in the Service receives drop-down, select no input from the options. Make sure that any application is selected in the second drop-down.

Add the Start Screensaver action (in the Utilities group of actions) to the Service by dragging it to the right. Save the Service (Automator does not ask you where to save it, just to name it). Next, open System Preferences and select the Keyboard preference pane. Select the Keyboard Shortcuts tab at the top, then the Services group on the left. The service you created should be near the bottom of the list of Services under the General disclosure triangle.

Double-click on the right side of the entry for the Service you created and assign a keyboard shortcut. This was a bit unintuitive, because the shortcut column is not distinctly visible, so it is not obvious that you can double-click in the assigned shortcut column to add a shortcut.

I had difficulty picking a keyboard shortcut that would work in 10.6.0. Command-L did not work for me, because it is assigned the Show All Preferences menu item in System Preferences. Control-L also did not work for me. Command-Shift-L did work once I reassigned the Search with Google Service a different shortcut.

Exit the keyboard preference pane to give it a try.

By Giang Nguyen Posted in Mac OS

Install MongoDB on Snow Leopard

I have to install MongoDB on a Linode server with Ubuntu 10.04. So I decided to install it on my MBP first. It’s a cool little database, and John Nunemaker’s MongoMapper gem is a treat.

Here’s how I got the server installed and running as a daemon in Snow Leopard for local development.

Download, unpack, and install the pre-compiled 32-bit binaries:

curl -O http://downloads.mongodb.org/osx/mongodb-osx-i386-1.4.0.tgz
tar xzf mongodb-osx-i386-1.4.0.tgz
sudo mv mongodb-osx-i386-1.4.0 /usr/local/mongodb
sudo mkdir /usr/local/mongodb_data /var/log/mongodb
sudo chown -R root /usr/local/mongodb

(If you’re on a 64-bit machine, substitute in x86_64 for each i386 above.)

Next, you’ll have to make a config file so you can change the server’s options without fiddling with command-line arguments.

Save this content as: /usr/local/mongodb/mongod.conf

# Store data alongside MongoDB instead of the default, /data/db/
dbpath = /usr/local/mongodb_data

# Only accept local connections
bind_ip = 127.0.0.1

Now, we’ll make a launchd job to register the server as an OS X daemon. launchd will start the server at startup, stop it before shutdown, make sure it stays up, and redirect its output to a nice log file.

Save this content as: /Library/LaunchDaemons/org.mongodb.mongod.plist





  Label
  org.mongodb.mongod
  ProgramArguments
  
    /usr/local/mongodb/bin/mongod
    run
    --config
    /usr/local/mongodb/mongod.conf
  
  RunAtLoad
  
  KeepAlive
  
  WorkingDirectory
  /usr/local/mongodb
  StandardErrorPath
  /var/log/mongodb/output.log
  StandardOutPath
  /var/log/mongodb/output.log

Now we just need to load the launchd job:

sudo launchctl load /Library/LaunchDaemons/org.mongodb.mongod.plist

And that should do it! Try visiting http://localhost:28017 to see the status console for your database.

This is mine:

One last thing: you should probably add /usr/local/mongodb/bin to your $PATH. That way you can use the other binaries that ship with MongoDB, like the mongo console, mongoexport, and so on.

You can adjust your path the regular way by editing your shell’s profile, or you can use this nice paths.d mechanism that OS X provides:

sudo sh -c 'echo "/usr/local/mongodb/bin" > /etc/paths.d/mongodb'