Skip to content

Commit

Permalink
Merge pull request #42 from rossta/configurable_client
Browse files Browse the repository at this point in the history
Configurable client
  • Loading branch information
jeremytregunna committed Jan 16, 2013
2 parents b36961d + 3901e49 commit d7fb81f
Show file tree
Hide file tree
Showing 31 changed files with 671 additions and 249 deletions.
33 changes: 33 additions & 0 deletions README.md
Expand Up @@ -16,6 +16,39 @@ Seriously, [check it out](http://www.trello.com/).
Full Disclosure: This library is mostly complete, if you do find anything missing or not functioning as you expect it
to, please [let us know](https://trello.com/card/spot-a-bug-report-it/4f092b2ee23cb6fe6d1aaabd/17).

## Configuration

Basic authorization

```ruby
Trello.configure do |config|
config.developer_public_key = TRELLO_DEVELOPER_PUBLIC_KEY
config.member_token = TRELLO_MEMBER_TOKEN
end
```

2-legged OAuth authorization

```ruby
Trello.configure do |config|
config.consumer_key = TRELLO_CONSUMER_KEY
config.consumer_secret = TRELLO_CONSUMER_SECRET
config.oauth_token = TRELLO_OAUTH_TOKEN
config.oauth_token_secret = TRELLO_OAUTH_TOKEN_SECRET
end
```

3-legged OAuth authorization

```ruby
Trello.configure do |config|
config.consumer_key = TRELLO_CONSUMER_KEY
config.consumer_secret = TRELLO_CONSUMER_SECRET
config.return_url = "http://your.site.com/path/to/receive/post"
config.callback = lambda { |request_token| DB.save(request_token.key, request_token.secret) }
end
```

## Special thanks

A special thanks goes out to [Ben Biddington](https://github.com/ben-biddington) who has contributed a significant amount
Expand Down
18 changes: 9 additions & 9 deletions Rakefile
Expand Up @@ -4,7 +4,7 @@ RSpec::Core::RakeTask.new

task :default => [:spec]

namespace :example do
namespace :example do
require "addressable/uri"
require "trello"
include Trello
Expand All @@ -16,20 +16,20 @@ namespace :example do

@developer_public_key = ENV["DEVELOPER_PUBLIC_KEY"]
@developer_secret = ENV["DEVELOPER_SECRET"]

OAuthPolicy.consumer_credential = OAuthCredential.new @developer_public_key, @developer_secret
OAuthPolicy.token = nil

request = Request.new :get, URI.parse("https://trello.com/1/OAuthGetRequestToken")
response = TInternet.execute OAuthPolicy.authorize(request)

the_request_token = parse_token(response.body)

puts "key => #{the_request_token.key}, secret => #{the_request_token.secret}"
end

desc "convert request token to access token"
task :get_access_token, :request_token_key, :request_token_secret, :oauth_verifier do |t, args|
desc "convert request token to access token"
task :get_access_token, :request_token_key, :request_token_secret, :oauth_verifier do |t, args|
ensure_consumer_credentials
@developer_public_key = ENV["DEVELOPER_PUBLIC_KEY"]
@developer_secret = ENV["DEVELOPER_SECRET"]
Expand All @@ -39,17 +39,17 @@ namespace :example do

access_token_request = Request.new :get, URI.parse("https://trello.com/1/OAuthGetAccessToken?oauth_verifier=#{args.oauth_verifier}")
response = TInternet.execute OAuthPolicy.authorize(access_token_request)

the_access_token = parse_token response.body

puts "key => #{the_access_token.key}, secret => #{the_access_token.secret}"
end

def ensure_consumer_credentials
%w{PUBLIC_KEY SECRET}.each do |name|
fullname = "DEVELOPER_#{name}"
unless ENV[fullname]
puts "ERROR: Missing <#{fullname}> environment variable."
unless ENV[fullname]
puts "ERROR: Missing <#{fullname}> environment variable."
exit 1
end
end
Expand Down
35 changes: 26 additions & 9 deletions lib/trello.rb
@@ -1,3 +1,4 @@

require 'oauth'
require 'json'
require 'logger'
Expand All @@ -7,20 +8,18 @@
#
# First, set up your key information. You can get this information by {clicking here}[https://trello.com/1/appKey/generate].
#
# include Trello
# include Trello::Authorization
#
# Trello::Authorization.const_set :AuthPolicy, OAuthPolicy
#
# OAuthPolicy.consumer_credential = OAuthCredential.new 'PUBLIC_KEY', 'SECRET'
#
# You can get the key by going to this url in your browser:
# https://trello.com/1/authorize?key=PUBLIC_KEY_FROM_ABOVE&name=MyApp&response_type=token&scope=read,write,account&expiration=never
# https://trello.com/1/authorize?key=TRELLO_CONSUMER_KEY_FROM_ABOVE&name=MyApp&response_type=token&scope=read,write,account&expiration=never
# Only request the permissions you need; i.e., scope=read if you only need read, or scope=write if you only need write. Comma separate scopes you need.
# If you want your token to expire after 30 days, drop the &expiration=never. Then run the following code, where KEY denotes the key returned from the
# url above:
#
# OAuthPolicy.token = OAuthCredential.new 'KEY', nil
# Trello.configure do |config|
# config.consumer_key = TRELLO_CONSUMER_KEY
# config.consumer_secret = TRELLO_CONSUMER_SECRET
# config.oauth_token = TRELLO_OAUTH_TOKEN
# config.oauth_token_secret = TRELLO_OAUTH_TOKEN_SECRET
# end
#
# All the calls this library make to Trello require authentication using these keys. Be sure to protect them.
#
Expand All @@ -47,6 +46,7 @@ module Trello
autoload :Card, 'trello/card'
autoload :Checklist, 'trello/checklist'
autoload :Client, 'trello/client'
autoload :Configuration, 'trello/configuration'
autoload :HasActions, 'trello/has_actions'
autoload :Item, 'trello/item'
autoload :CheckItemState, 'trello/item_state'
Expand Down Expand Up @@ -81,4 +81,21 @@ def self.logger
def self.logger=(logger)
@logger = logger
end

def self.client
@client ||= Client.new
end

def self.configure
reset!
yield client.configuration
end

def self.auth_policy
client.auth_policy
end

def self.reset!
@client = nil
end
end
6 changes: 3 additions & 3 deletions lib/trello/action.rb
Expand Up @@ -27,17 +27,17 @@ def update_fields(fields)

# Returns the board this action occurred on.
def board
Client.get("/actions/#{id}/board").json_into(Board)
client.get("/actions/#{id}/board").json_into(Board)
end

# Returns the card the action occurred on.
def card
Client.get("/actions/#{id}/card").json_into(Card)
client.get("/actions/#{id}/card").json_into(Card)
end

# Returns the list the action occurred on.
def list
Client.get("/actions/#{id}/list").json_into(List)
client.get("/actions/#{id}/list").json_into(List)
end

# Returns the member who created the action.
Expand Down
154 changes: 105 additions & 49 deletions lib/trello/authorization.rb
Expand Up @@ -4,21 +4,34 @@
module Trello
module Authorization

AuthPolicy = Class.new
AuthPolicy = Class.new do
def initialize(attrs = {}); end
end

class BasicAuthPolicy
class << self
attr_accessor :developer_public_key, :member_token

def authorize(request)
the_uri = Addressable::URI.parse(request.uri)
existing_values = the_uri.query_values.nil? ? {} : the_uri.query_values
new_values = { :key => @developer_public_key, :token => @member_token }
the_uri.query_values = new_values.merge existing_values

Request.new request.verb, the_uri, request.headers, request.body
new.authorize(request)
end
end

attr_accessor :developer_public_key, :member_token

def initialize(attrs = {})
@developer_public_key = attrs[:developer_public_key] || self.class.developer_public_key
@member_token = attrs[:member_token] || self.class.member_token
end

def authorize(request)
the_uri = Addressable::URI.parse(request.uri)
existing_values = the_uri.query_values.nil? ? {} : the_uri.query_values
new_values = { :key => @developer_public_key, :token => @member_token }
the_uri.query_values = new_values.merge existing_values

Request.new request.verb, the_uri, request.headers, request.body
end
end

class Clock
Expand Down Expand Up @@ -57,60 +70,103 @@ class << self
attr_accessor :consumer_credential, :token, :return_url, :callback

def authorize(request)
unless consumer_credential
Trello.logger.error "The consumer_credential has not been supplied."
fail "The consumer_credential has not been supplied."
end

if token
request.headers = {"Authorization" => get_auth_header(request.uri, :get)}
request
else
consumer(:return_url => return_url, :callback_method => :postMessage)
request_token = consumer.get_request_token
callback.call request_token
return nil
end
new.authorize(request)
end
end

private

def consumer_params(params = {})
{
:scheme => :header,
:scope => 'read,write,account',
:http_method => :get,
:request_token_path => "https://trello.com/1/OAuthGetRequestToken",
:authorize_path => "https://trello.com/1/OAuthAuthorizeToken",
:access_token_path => "https://trello.com/1/OAuthGetAccessToken"
}.merge!(params)
attr_accessor :attributes
attr_accessor :consumer_credential, :token, :return_url, :callback

def initialize(attrs = {})
@consumer_key = attrs[:consumer_key]
@consumer_secret = attrs[:consumer_secret]
@oauth_token = attrs[:oauth_token]
@oauth_token_secret = attrs[:oauth_token_secret]
@return_url = attrs[:return_url] || self.class.return_url
@callback = attrs[:callback] || self.class.callback
end

def authorize(request)
unless consumer_credential
Trello.logger.error "The consumer_credential has not been supplied."
fail "The consumer_credential has not been supplied."
end

def consumer(options = {})
@consumer ||= OAuth::Consumer.new(
consumer_credential.key,
consumer_credential.secret,
consumer_params(options)
)
if token
request.headers = {"Authorization" => get_auth_header(request.uri, :get)}
request
else
consumer(:return_url => return_url, :callback_method => :postMessage)
request_token = consumer.get_request_token
callback.call request_token
return nil
end
end

def get_auth_header(url, verb, options = {})
self.token ||= OAuththCredential.new
def consumer_credential
@consumer_credential ||= build_consumer_credential
end

request = Net::HTTP::Get.new Addressable::URI.parse(url).to_s
def token
@token ||= build_token
end

def consumer_key; consumer_credential.key; end
def consumer_secret; consumer_credential.secret; end
def oauth_token; token.key; end
def oauth_token_secret; token.secret; end

consumer.options[:signature_method] = 'HMAC-SHA1'
consumer.options[:nonce] = Nonce.next
consumer.options[:timestamp] = Clock.timestamp
consumer.options[:uri] = url
consumer.key = consumer_credential.key
consumer.secret = consumer_credential.secret
private

consumer.sign!(request, OAuth::Token.new(token.key, token.secret))
def build_consumer_credential
if @consumer_key && @consumer_secret
OAuthCredential.new @consumer_key, @consumer_secret
else
self.class.consumer_credential
end
end

request['authorization']
def build_token
if @oauth_token
OAuthCredential.new @oauth_token, @oauth_token_secret
else
self.class.token
end
end

def consumer_params(params = {})
{
:scheme => :header,
:scope => 'read,write,account',
:http_method => :get,
:request_token_path => "https://trello.com/1/OAuthGetRequestToken",
:authorize_path => "https://trello.com/1/OAuthAuthorizeToken",
:access_token_path => "https://trello.com/1/OAuthGetAccessToken"
}.merge!(params)
end

def consumer(options = {})
@consumer ||= OAuth::Consumer.new(
consumer_credential.key,
consumer_credential.secret,
consumer_params(options)
)
end

def get_auth_header(url, verb, options = {})
request = Net::HTTP::Get.new Addressable::URI.parse(url).to_s

consumer.options[:signature_method] = 'HMAC-SHA1'
consumer.options[:nonce] = Nonce.next
consumer.options[:timestamp] = Clock.timestamp
consumer.options[:uri] = url
consumer.key = consumer_credential.key
consumer.secret = consumer_credential.secret

consumer.sign!(request, OAuth::Token.new(token.key, token.secret))

request['authorization']
end
end
end
end

0 comments on commit d7fb81f

Please sign in to comment.