Skip to content

Commit

Permalink
Merge pull request #31 from afh/pull/credentials_plugin
Browse files Browse the repository at this point in the history
Add credentials plugin
  • Loading branch information
quirkey committed Feb 3, 2012
2 parents f804548 + 3ac653f commit 351e979
Show file tree
Hide file tree
Showing 7 changed files with 128 additions and 0 deletions.
4 changes: 4 additions & 0 deletions Gemfile
Expand Up @@ -18,3 +18,7 @@ end
group :test do group :test do
gem "shoulda", ">= 0" gem "shoulda", ">= 0"
end end

group :darwin do
gem 'keychain_services', '~>0.1.1'
end
4 changes: 4 additions & 0 deletions lib/soca/plugin.rb
Expand Up @@ -25,5 +25,9 @@ def app_dir
pusher.app_dir pusher.app_dir
end end


def config
pusher.config
end

end end
end end
75 changes: 75 additions & 0 deletions lib/soca/plugins/credentials.rb
@@ -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
1 change: 1 addition & 0 deletions lib/soca/pusher.rb
Expand Up @@ -26,6 +26,7 @@ def load_config
def load_couchapprc def load_couchapprc
@config ||= {} @config ||= {}
@config['couchapprc'] = JSON.parse(File.read(File.join(app_dir, '.couchapprc'))) @config['couchapprc'] = JSON.parse(File.read(File.join(app_dir, '.couchapprc')))
run_hooks!(:after_load_couchapprc)
end end


def build def build
Expand Down
1 change: 1 addition & 0 deletions test/helper.rb
Expand Up @@ -8,6 +8,7 @@
$LOAD_PATH.unshift(File.dirname(__FILE__)) $LOAD_PATH.unshift(File.dirname(__FILE__))
require 'soca' require 'soca'
require 'soca/plugins/compass' require 'soca/plugins/compass'
require 'soca/plugins/credentials'


class Test::Unit::TestCase class Test::Unit::TestCase


Expand Down
40 changes: 40 additions & 0 deletions test/test_credentials_plugin.rb
@@ -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
3 changes: 3 additions & 0 deletions test/testapp/.couchapprc
Expand Up @@ -5,6 +5,9 @@
}, },
"production": { "production": {
"db": "http://admin:admin@c.ixxr.net/testapp" "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.