diff --git a/Rakefile b/Rakefile index ade1812..fb9f656 100644 --- a/Rakefile +++ b/Rakefile @@ -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 \ No newline at end of file diff --git a/oa-basic/lib/omniauth/basic.rb b/oa-basic/lib/omniauth/basic.rb index e2f3c2b..58b1d36 100644 --- a/oa-basic/lib/omniauth/basic.rb +++ b/oa-basic/lib/omniauth/basic.rb @@ -1,2 +1,5 @@ require 'omniauth/core' -require 'omniauth/strategies/http_basic' \ No newline at end of file +require 'omniauth/strategies/http_basic' + +require 'omniauth/strategies/campfire' +require 'omniauth/strategies/basecamp' \ No newline at end of file diff --git a/oa-basic/lib/omniauth/strategies/basecamp.rb b/oa-basic/lib/omniauth/strategies/basecamp.rb new file mode 100644 index 0000000..c10eab2 --- /dev/null +++ b/oa-basic/lib/omniauth/strategies/basecamp.rb @@ -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 \ No newline at end of file diff --git a/oa-basic/lib/omniauth/strategies/campfire.rb b/oa-basic/lib/omniauth/strategies/campfire.rb new file mode 100644 index 0000000..be1554b --- /dev/null +++ b/oa-basic/lib/omniauth/strategies/campfire.rb @@ -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 \ No newline at end of file diff --git a/oa-basic/lib/omniauth/strategies/http_basic.rb b/oa-basic/lib/omniauth/strategies/http_basic.rb index ac09591..11f4e57 100644 --- a/oa-basic/lib/omniauth/strategies/http_basic.rb +++ b/oa-basic/lib/omniauth/strategies/http_basic.rb @@ -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 diff --git a/oa-core/lib/omniauth/core.rb b/oa-core/lib/omniauth/core.rb index e5fa483..f9aba85 100644 --- a/oa-core/lib/omniauth/core.rb +++ b/oa-core/lib/omniauth/core.rb @@ -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 + "" + 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] diff --git a/oa-core/lib/omniauth/form.rb b/oa-core/lib/omniauth/form.rb new file mode 100644 index 0000000..30d1954 --- /dev/null +++ b/oa-core/lib/omniauth/form.rb @@ -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" + self + end + + def input_field(type, name) + @html << "\n" + 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 + + + + #{title} + #{css} + + +

#{title}

+
+ HTML + self + end + + def footer + return self if @footer + @html << <<-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" + end + end +end \ No newline at end of file diff --git a/oa-openid/lib/omniauth/strategies/open_id.rb b/oa-openid/lib/omniauth/strategies/open_id.rb index 851dfaa..1e7bf10 100644 --- a/oa-openid/lib/omniauth/strategies/open_id.rb +++ b/oa-openid/lib/omniauth/strategies/open_id.rb @@ -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) diff --git a/omniauth/VERSION b/omniauth/VERSION new file mode 100644 index 0000000..8a9ecc2 --- /dev/null +++ b/omniauth/VERSION @@ -0,0 +1 @@ +0.0.1 \ No newline at end of file