Permalink
Browse files

Merge pull request #31 from afh/pull/credentials_plugin

Add credentials plugin
  • Loading branch information...
quirkey committed Feb 3, 2012
2 parents f804548 + 3ac653f commit 351e9799f9d4f92318df904521a0fe1a1e62125e
Showing with 128 additions and 0 deletions.
  1. +4 −0 Gemfile
  2. +4 −0 lib/soca/plugin.rb
  3. +75 −0 lib/soca/plugins/credentials.rb
  4. +1 −0 lib/soca/pusher.rb
  5. +1 −0 test/helper.rb
  6. +40 −0 test/test_credentials_plugin.rb
  7. +3 −0 test/testapp/.couchapprc
View
@@ -18,3 +18,7 @@ end
group :test do
gem "shoulda", ">= 0"
end
+
+group :darwin do
+ gem 'keychain_services', '~>0.1.1'
+end
View
@@ -25,5 +25,9 @@ def app_dir
pusher.app_dir
end
+ def config
+ pusher.config
+ end
+
end
end
@@ -0,0 +1,75 @@
+module Soca
+ module Plugins
+ class Credentials < Soca::Plugin
+
+ name 'credentials'
+
+ # Credentials plugin
+ # This plugin is run after the couchapprc is loaded,
+ # it checks the db field in every environment,
+ # searches for strings ending with '_CREDENTIALS' in the URI userinfo field,
+ # passes the URI host to a method handling the requested credentials,
+ # and replaces the userinfo with the username and password from the credentials method.
+ #
+ # When adding a new credentials method, please make sure the platform
+ # specific requirements are met (e.g. external tools or gems)
+ # and configure its platform availability in the credentials_supported? method
+
+ def after_load_couchapprc
+ config['couchapprc']['env'].each do |env, cfg|
+ next unless cfg['db'] =~ /^(https?:\/\/)([^@]+_CREDENTIALS)@(.*)$/i
+ scheme = $1
+ userinfo = $2
+ host = $3
+
+ unless credentials_supported?(userinfo)
+ Soca.logger.error "#{userinfo} are not supported on the #{RUBY_PLATFORM} platform"
+ puts 'skip'
+ next
+ end
+
+ (username, password) = send(userinfo.downcase, host)
+ unless username and password
+ Soca.logger.warn "#{userinfo} returned empty credentials for #{host}"
+ else
+ credentials = "#{username}:#{password}@"
+ config['couchapprc']['env'][env]['db'] = "#{scheme}#{credentials}#{host}"
+ Soca.logger.debug "Replacing #{userinfo} with #{credentials} in #{cfg['db']}"
+ end
+ end
+ end
+
+ private
+ def credentials_supported?(type)
+ Soca.logger.debug "Checking support for #{type} on #{RUBY_PLATFORM}"
+ available_credentials = {
+ 'darwin' => %w[keychain_credentials],
+ #'linux' => %w[]
+ }
+ supported_credentials = available_credentials.map { |os, list| list if RUBY_PLATFORM =~ /#{os}/i }
+ supported_credentials.flatten.compact.include?(type.downcase)
+ end
+
+ # This methods retrieves the user credentials from the Mac OS X Keychain Services
+ def keychain_credentials(host)
+ begin
+ require 'keychain'
+ rescue LoadError
+ warn "Please install the keychain_services gem, in order to retrieve user credentials from your keychain"
+ return
+ end
+
+ Soca.logger.debug "Searching for #{host} in keychain"
+ item = Keychain.items.find { |item| item if item.label == host }
+ unless item
+ # strip url-path from host url and search again
+ (host, db) = host.split('/', 2)
+ Soca.logger.debug "Searching for #{host} in keychain"
+ item = Keychain.items.find { |item| item if item.label == host }
+ end
+ [item.account, item.password] if item
+ end
+
+ end
+ end
+end
View
@@ -26,6 +26,7 @@ def load_config
def load_couchapprc
@config ||= {}
@config['couchapprc'] = JSON.parse(File.read(File.join(app_dir, '.couchapprc')))
+ run_hooks!(:after_load_couchapprc)
end
def build
View
@@ -8,6 +8,7 @@
$LOAD_PATH.unshift(File.dirname(__FILE__))
require 'soca'
require 'soca/plugins/compass'
+require 'soca/plugins/credentials'
class Test::Unit::TestCase
@@ -0,0 +1,40 @@
+require 'helper'
+
+class TestCredentialsPlugin < Test::Unit::TestCase
+ def app_path(relative='')
+ File.expand_path(@test_app_dir + '/' + relative)
+ end
+
+ context 'credentials plugin' do
+ setup do
+ @pusher = Soca::Pusher.new(app_path)
+ @plugin = Soca::Plugins::Credentials.new(@pusher)
+ end
+
+ if RUBY_PLATFORM =~ /darwin/i
+ require 'keychain'
+ context 'given keychain credentials' do
+ should 'check for platform availability' do
+ @plugin.expects(:credentials_supported?).
+ with('KEYCHAIN_CREDENTIALS').
+ returns(true)
+ @plugin.after_load_couchapprc
+ end
+
+ # Note this test requires you have
+ # an application password item
+ # in your keychain with the
+ # name localhost:5894/testapp
+ # username admin
+ # password admin
+ should 'return username and password for host from keychain' do
+ @plugin.expects(:keychain_credentials).
+ with('localhost:5984/testapp').
+ returns(%w[admin admin])
+ @plugin.after_load_couchapprc
+ end
+ end
+ end
+
+ end
+end
View
@@ -5,6 +5,9 @@
},
"production": {
"db": "http://admin:admin@c.ixxr.net/testapp"
+ },
+ "keychain_credentials": {
+ "db": "http://KEYCHAIN_CREDENTIALS@localhost:5984/testapp"
}
}
}

0 comments on commit 351e979

Please sign in to comment.