Bringing HTTP Basic back to the fold, starting with Campfire and Basecamp.

This commit is contained in:
Michael Bleigh 2010-05-01 14:14:46 -04:00
parent 91533b7f45
commit 240d691253
9 changed files with 329 additions and 21 deletions

View File

@ -4,7 +4,7 @@ require 'term/ansicolor'
include Term::ANSIColor
OMNIAUTH_GEMS = %w(oa-core oa-basic oa-oauth oa-openid)
OMNIAUTH_GEMS = %w(omniauth oa-core oa-oauth oa-openid)
desc 'Run specs for all of the gems.'
task :spec do
@ -16,5 +16,12 @@ task :spec do
end
end
desc 'Push all gems to Gemcutter'
task :gemcutter do
OMNIAUTH_GEMS.each_with_index do |dir, i|
Dir.chdir(dir) { system('rake gemcutter') }
end
end
task :default => :spec

View File

@ -1,2 +1,5 @@
require 'omniauth/core'
require 'omniauth/strategies/http_basic'
require 'omniauth/strategies/http_basic'
require 'omniauth/strategies/campfire'
require 'omniauth/strategies/basecamp'

View File

@ -0,0 +1,53 @@
module OmniAuth
module Strategies
class Basecamp < HttpBasic
def initialize(app)
require 'json'
super(app, :basecamp, nil)
end
def endpoint
"http://#{request.params['user']}:#{request.params['password']}@#{request.params['subdomain']}.basecamphq.com/me.xml"
end
def perform_authentication(endpoint)
super(endpoint) rescue super(endpoint.sub('http','https'))
end
def auth_hash
doc = Nokogiri::XML.parse(@response.body)
OmniAuth::Utils.deep_merge(super, {
'uid' => doc.xpath('person/id').text,
'user_info' => user_info(doc),
'credentials' => {
'token' => doc.xpath('person/token').text
}
})
end
def user_info(doc)
hash = {
'nickname' => request.params['user'],
'first_name' => doc.xpath('person/first-name').text,
'last_name' => doc.xpath('person/last-name').text,
'email' => doc.xpath('person/email-address').text,
'image' => doc.xpath('person/avatar-url').text
}
hash['name'] = [hash['first_name'], hash['last_name']].join(' ').strip
hash.delete('image') if hash['image'].include?('missing/avatar.png')
hash
end
def get_credentials
OmniAuth::Form.build('Basecamp Authentication') do
text_field 'Subdomain', 'subdomain'
text_field 'Username', 'user'
password_field 'Password', 'password'
end.to_response
end
end
end
end

View File

@ -0,0 +1,45 @@
module OmniAuth
module Strategies
class Campfire < HttpBasic
def initialize(app)
require 'json'
super(app, :campfire, nil)
end
def endpoint
"http://#{request.params['user']}:#{request.params['password']}@#{request.params['subdomain']}.campfirenow.com/users/me.json"
end
def perform_authentication(endpoint)
super(endpoint) rescue super(endpoint.sub('http','https'))
end
def auth_hash
user_hash = JSON.parse(@response.body)['user']
OmniAuth::Utils.deep_merge(super, {
'uid' => user_hash['id'],
'user_info' => user_info(user_hash),
'credentials' => {
'token' => user_hash['api_auth_token']
}
})
end
def user_info(hash)
{
'nickname' => request.params['user'],
'name' => hash['name'],
'email' => hash['email_address']
}
end
def get_credentials
OmniAuth::Form.build('Campfire Authentication') do
text_field 'Subdomain', 'subdomain'
text_field 'Username', 'user'
password_field 'Password', 'password'
end.to_response
end
end
end
end

View File

@ -14,18 +14,41 @@ module OmniAuth
attr_reader :endpoint, :request_headers
def request_phase
@response = RestClient.get(endpoint, request_headers)
if env['REQUEST_METHOD'] == 'GET'
get_credentials
else
perform
end
end
def title
name.split('_').map{|s| s.capitalize}.join(' ')
end
def get_credentials
OmniAuth::Form.build(title) do
text_field 'Username', 'username'
password_field 'Password', 'password'
end.to_response
end
def perform
@response = perform_authentication(endpoint)
request.POST['auth'] = auth_hash
@env['REQUEST_METHOD'] = 'GET'
@env['PATH_INFO'] = "#{OmniAuth.config.path_prefix}/#{name}/callback"
@app.call(@env)
rescue RestClient::Request::Unauthorized
fail!(:invalid_credentials)
end
def perform_authentication(uri, headers = request_headers)
RestClient.get(uri, headers)
end
def callback_phase
@app.call(env)
fail!(:invalid_credentials)
end
end
end

View File

@ -1,16 +1,27 @@
require 'rack'
require 'singleton'
require 'omniauth/form'
module OmniAuth
class Configuration
include Singleton
def initialize
@path_prefix = '/auth'
@on_failure = Proc.new do |env, message_key|
@@defaults = {
:path_prefix => '/auth',
:on_failure => Proc.new do |env, message_key|
new_path = "#{OmniAuth.config.path_prefix}/failure?message=#{message_key}"
[302, {'Location' => "#{new_path}"}, []]
end
end,
:form_css => Form::DEFAULT_CSS
}
def self.defaults
@@defaults
end
def initialize
@@defaults.each_pair{|k,v| self.send("#{k}=",v)}
end
def on_failure(&block)
@ -21,7 +32,8 @@ module OmniAuth
end
end
attr_accessor :path_prefix
attr_writer :on_failure
attr_accessor :path_prefix, :form_css
end
def self.config
@ -33,7 +45,19 @@ module OmniAuth
end
module Utils
extend self
CAMELIZE_SPECIAL = {
'oauth' => 'OAuth',
'oauth2' => 'OAuth2',
'openid' => 'OpenID',
'open_id' => 'OpenID',
'github' => 'GitHub'
}
module_function
def form_css
"<style type='text/css'>#{OmniAuth.config.form_css}</style>"
end
def deep_merge(hash, other_hash)
target = hash.dup
@ -50,14 +74,6 @@ module OmniAuth
target
end
CAMELIZE_SPECIAL = {
'oauth' => 'OAuth',
'oauth2' => 'OAuth2',
'openid' => 'OpenID',
'open_id' => 'OpenID',
'github' => 'GitHub'
}
def camelize(word, first_letter_in_uppercase = true)
return CAMELIZE_SPECIAL[word.to_s] if CAMELIZE_SPECIAL[word.to_s]

View File

@ -0,0 +1,145 @@
module OmniAuth
class Form
DEFAULT_CSS = <<-CSS
body {
background: #ccc;
font-family: "Lucida Grande", "Lucida Sans", Helvetica, Arial, sans-serif;
}
h1 {
text-align: center;
margin: 30px auto 0px;
font-size: 18px;
padding: 10px 10px 15px;
background: #555;
color: white;
width: 320px;
border: 10px solid #444;
border-bottom: 0;
-moz-border-radius-topleft: 10px;
-moz-border-radius-topright: 10px;
-webkit-border-top-left-radius: 10px;
-webkit-border-top-right-radius: 10px;
border-top-left-radius: 10px;
border-top-right-radius: 10px;
}
h1, form {
-moz-box-shadow: 2px 2px 7px rgba(0,0,0,0.3);
-webkit-box-shadow: 2px 2px 7px rgba(0,0,0,0.3);
}
form {
background: white;
border: 10px solid #eee;
border-top: 0;
padding: 20px;
margin: 0px auto 40px;
width: 300px;
-moz-border-radius-bottomleft: 10px;
-moz-border-radius-bottomright: 10px;
-webkit-border-bottom-left-radius: 10px;
-webkit-border-bottom-right-radius: 10px;
border-bottom-left-radius: 10px;
border-bottom-right-radius: 10px;
}
label {
display: block;
font-weight: bold;
margin-bottom: 5px;
}
input {
font-size: 18px;
padding: 4px 8px;
display: block;
margin-bottom: 10px;
width: 280px;
}
button {
font-size: 22px;
padding: 4px 8px;
display: block;
margin: 20px auto 0;
}
CSS
def initialize(title=nil)
title ||= "Authentication Info Required"
@html = ""
header(title)
end
def self.build(title=nil, &block)
form = OmniAuth::Form.new(title)
form.instance_eval(&block)
end
def label_field(text, target)
@html << "\n<label for='#{target}'>#{text}:</label>"
self
end
def input_field(type, name)
@html << "\n<input type='#{type}' id='#{name}' name='#{name}'/>"
self
end
def text_field(label, name)
label_field(label, name)
input_field('text', name)
self
end
def password_field(label, name)
label_field(label, name)
input_field('password', name)
self
end
def header(title)
@html << <<-HTML
<!DOCTYPE html>
<html>
<head>
<title>#{title}</title>
#{css}
</head>
<body>
<h1>#{title}</h1>
<form method='post'>
HTML
self
end
def footer
return self if @footer
@html << <<-HTML
<button type='submit'>Connect</button>
</form>
</body>
</html>
HTML
@footer = true
self
end
def to_html
footer
@html
end
def to_response
footer
Rack::Response.new(@html).finish
end
protected
def css
"\n<style type='text/css'>#{OmniAuth.config.form_css}</style>"
end
end
end

View File

@ -45,7 +45,10 @@ module OmniAuth
end
def request_phase
return fail!(:missing_information) unless identifier
identifier ? start : get_identifier
end
def start
openid = Rack::OpenID.new(dummy_app, @store)
response = openid.call(env)
case env['rack.openid.response']
@ -56,11 +59,23 @@ module OmniAuth
end
end
def get_identifier
response = app.call(env)
if response[0] < 400
response
else
OmniAuth::Form.build('OpenID Authentication') do
text_field('OpenID Identifier', 'identifier')
end.to_response
end
end
def callback_phase
env['REQUEST_METHOD'] = 'GET'
openid = Rack::OpenID.new(lambda{|env| [200,{},[]]}, @store)
openid.call(env)
resp = env.delete('rack.openid.response')
case resp.status
when :failure
fail!(:invalid_credentials)

1
omniauth/VERSION Normal file
View File

@ -0,0 +1 @@
0.0.1