safe_credentials 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 54bec910e5e20a0e32bccd0d099a9e6626e76865
4
+ data.tar.gz: 93c2902537cc72b1677bc1db7a75b504c7be2fbe
5
+ SHA512:
6
+ metadata.gz: ec293af4e8ae3cbf286df92493aa0811c5003705542776327a67c7b9cf26d606a847b420aa3433bea548f4bfa854eda7c5be43f93514ff7b8ec59ed48782da87
7
+ data.tar.gz: 0075d799c2e3b6b6768bf363e8dca7c096e1fe2163968963241042e57ef2725b2302b0cd25cb4dff060fbc6c92c576d4b5fb073e209b4b916bd3434bb4245789
data/.gitignore ADDED
@@ -0,0 +1,20 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
18
+ config/encrypted_config.yml
19
+ .DS_Store
20
+ config/config.yml
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in safe_credentials.gemspec
4
+ gemspec
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2013 Alberto F. Capel
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,97 @@
1
+ # Safe Credentials
2
+
3
+ Safe Credentials allows you to encrypt sensitive credentials so you can
4
+ store your configuration files in source control.
5
+
6
+ ## Motivation
7
+
8
+ To store configuration files in source control is always a tricky issue. You shouldn't
9
+ store your credentials in clear text in source control, but often your team needs a subset
10
+ of those credentials to test and execute the project.
11
+
12
+ A usual approach is to create a configuration file (config.yml or similar) but don't push it
13
+ to source control. Instead, you also create a dummy example file (config.yml.example) with dummy
14
+ values. When someone needs to access the real credentials he or she has to ask the project owner
15
+ for them.
16
+
17
+ This solution is not ideal, especially when you need to add add or change some configuration
18
+ parameter.
19
+
20
+ ## Usage
21
+
22
+ Install the gem
23
+
24
+ ```shell
25
+
26
+ $ gem install safe_credentials
27
+
28
+ ```
29
+
30
+ Run the provided executable:
31
+
32
+ ```shell
33
+
34
+ $ safe_credentials encrypt
35
+
36
+ Encrypting file config/config.yml
37
+ Enter your password:
38
+ Result stored in config/encrypted_config.yml
39
+ Adding config/config.yml to .gitignore.
40
+
41
+ ```
42
+
43
+ Later, when you need to decrypt the credentials
44
+
45
+ ```shell
46
+ $ bin/safe_credentials decrypt
47
+
48
+ Decrypting file config/encrypted_config.yml
49
+ Enter your password:
50
+ Result stored in config/config.yml
51
+
52
+ ```
53
+
54
+ ## Options
55
+
56
+ Choose the path to the real config file and the encrypted one:
57
+
58
+ ```shell
59
+
60
+ safe_credentials encrypt --from path/to/config.yml --to path/to/decrypted_config.yml
61
+
62
+ ```
63
+
64
+ Also you can choose to encrypt only some configuration parameters:
65
+
66
+ ```shell
67
+
68
+ # Encrypt database variables in all environments
69
+
70
+ safe_credentials encrypt --vars **.database.*
71
+
72
+ # Encrypt production variables
73
+
74
+ safe_credentials encrypt --vars producion
75
+
76
+ # Encrypt only password variables
77
+
78
+ safe_credentials encrypt --vars **password
79
+
80
+ ```
81
+
82
+ ## Credits
83
+
84
+ Original idea seen on [John Resig's blog](http://ejohn.org/blog/keeping-passwords-in-source-control/)
85
+
86
+ ## TODO
87
+
88
+ * Capistrano integration. Upload config file to remote server and decrypt it there.
89
+ * Support other formats beside YAML, like TOML or JSON.
90
+
91
+ ## Contributing
92
+
93
+ 1. Fork it
94
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
95
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
96
+ 4. Push to the branch (`git push origin my-new-feature`)
97
+ 5. Create new Pull Request
data/Rakefile ADDED
@@ -0,0 +1 @@
1
+ require "bundler/gem_tasks"
@@ -0,0 +1,5 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require_relative '../lib/safe_credentials'
4
+
5
+ SafeCredentialsCLI.start(ARGV)
@@ -0,0 +1,7 @@
1
+ require 'gibberish'
2
+
3
+ require_relative "safe_credentials/version"
4
+ require_relative "safe_credentials/hash_query"
5
+ require_relative "safe_credentials/hash_encryptor"
6
+ require_relative "safe_credentials/config"
7
+ require_relative "safe_credentials/cli"
@@ -0,0 +1,60 @@
1
+ require "thor"
2
+ require 'io/console'
3
+
4
+ class SafeCredentialsCLI < Thor
5
+
6
+ desc "encrypt", "Encrypt configuration file"
7
+ option :from, desc: 'source file to encrypt', default: 'config/config.yml'
8
+ option :to, desc: 'target file', default: 'config/encrypted_config.yml'
9
+ option :vars, desc: 'glob expression with the keys to encode', default: '*'
10
+ def encrypt
11
+
12
+ puts " Encrypting file #{options[:from]}"
13
+ print " Enter your password: "
14
+ password = STDIN.noecho(&:gets)
15
+ puts ''
16
+
17
+ config = SafeCredentials::Config.new(options[:from], password)
18
+
19
+ config.encrypt!(options[:vars])
20
+ config.save(options[:to])
21
+
22
+ puts " Result stored in #{options[:to]}"
23
+ add_to_gitignore(options[:from])
24
+ puts ""
25
+ end
26
+
27
+ desc "decrypt", "Decrypt configuration file"
28
+ option :from, desc: 'source file to encrypt', default: 'config/encrypted_config.yml'
29
+ option :to, desc: 'target file', default: 'config/config.yml'
30
+ option :vars, desc: 'glob expression with the keys to decode', default: '**encrypted_*'
31
+ def decrypt
32
+ puts ""
33
+ puts " Decrypting file #{options[:from]}"
34
+ print " Enter your password: "
35
+ password = STDIN.noecho(&:gets)
36
+ puts ''
37
+
38
+ config = SafeCredentials::Config.load_encrypted(password, options[:from])
39
+
40
+ config.decrypt!(options[:vars])
41
+ config.save(options[:to])
42
+
43
+ puts " Result stored in #{options[:to]}"
44
+ add_to_gitignore(options[:to])
45
+ puts ""
46
+ end
47
+
48
+
49
+ private
50
+
51
+ def add_to_gitignore(config_file)
52
+ return unless File.exist?('.gitignore') && !File.read('.gitignore').match(config_file)
53
+
54
+ puts " Adding #{config_file} to .gitignore."
55
+
56
+ File.open('.gitignore', 'a') do |f|
57
+ f << "#{config_file}\n"
58
+ end
59
+ end
60
+ end
@@ -0,0 +1,61 @@
1
+ require 'openssl'
2
+ require 'yaml'
3
+ require 'erb'
4
+
5
+ module SafeCredentials
6
+
7
+ class Config
8
+ attr_reader :path, :config_file, :envs
9
+
10
+ def initialize(path = 'config/config.yml', password = nil)
11
+ @path = File.expand_path(path)
12
+ yaml_source = ERB.new(File.read(path)).result
13
+
14
+ @cipher = Gibberish::AES.new(password)
15
+
16
+ @attributes = YAML.load(yaml_source)
17
+ @encryptor = HashEncryptor.new(@attributes, @cipher)
18
+ end
19
+
20
+ def [](env_name)
21
+ @attributes[env_name.to_s]
22
+ end
23
+
24
+ def self.load_encrypted(password, path = 'config/encrypted_config.yml')
25
+ config = new(path, password)
26
+ config.decrypt!
27
+ config
28
+
29
+ rescue OpenSSL::Cipher::CipherError
30
+ $stderr.puts "Wrong password!"
31
+ exit -1
32
+ end
33
+
34
+ def encrypt!(paths = '*')
35
+ @encryptor.encrypt_paths!(paths)
36
+
37
+ rescue OpenSSL::Cipher::CipherError
38
+ $stderr.puts "Wrong password!"
39
+ exit -1
40
+ end
41
+
42
+ def decrypt!(paths = '**encrypted_*')
43
+ @encryptor.decrypt_paths!(paths)
44
+
45
+ rescue OpenSSL::Cipher::CipherError
46
+ $stderr.puts "Wrong password!"
47
+ exit -1
48
+ end
49
+
50
+ def save(file_name = nil)
51
+ unless file_name
52
+ basename = File.basename(@path)
53
+ dirname = File.dirname(@path)
54
+
55
+ file_name = File.join(dirname, "encrypted_#{basename}")
56
+ end
57
+
58
+ File.write(file_name, @attributes.to_yaml)
59
+ end
60
+ end
61
+ end
@@ -0,0 +1,7 @@
1
+ module SafeCredentials
2
+ class Engine < Rails::Engine
3
+
4
+ initializer "loading configuration" do
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,81 @@
1
+ module SafeCredentials
2
+ class HashEncryptor
3
+ attr_reader :name, :attributes
4
+
5
+ def initialize(attributes, cipher)
6
+ @attributes, @cipher = attributes, cipher
7
+ @attributes.extend SafeCredentials::HashQuery
8
+ end
9
+
10
+ def encrypt_val(val)
11
+ case val
12
+ when String then @cipher.enc(val).strip
13
+ when Hash then encrypt_hash(val)
14
+ end
15
+ end
16
+
17
+ def encrypt_hash(h)
18
+ Hash.new.tap do |encrypted|
19
+ h.each do |k, v|
20
+ encrypted["encrypted_#{k}"] = encrypt_val(v)
21
+ end
22
+ end
23
+ end
24
+
25
+ def decrypt_val(val)
26
+ case val
27
+ when String then @cipher.dec(val).strip
28
+ when Hash then decrypt_hash(val)
29
+ end
30
+ end
31
+
32
+ def decrypt_hash(h)
33
+ Hash.new.tap do |decrypted|
34
+ h.each do |k, v|
35
+ key = k.sub(/^encrypted_/, '')
36
+ decrypted[key] = decrypt_val(v)
37
+ end
38
+ end
39
+ end
40
+
41
+ def encrypt(path)
42
+ encrypt_val(value_for(path))
43
+ end
44
+
45
+ def encrypt!(path)
46
+ last_key, subhash = subhash_for(path)
47
+ new_key = "encrypted_#{last_key}"
48
+
49
+ subhash[new_key] = encrypt_val(subhash.delete(last_key))
50
+ end
51
+
52
+ def decrypt!(path)
53
+ last_key, subhash = subhash_for(path)
54
+ new_key = last_key.sub(/^encrypted_/,'')
55
+
56
+ subhash[new_key] = decrypt_val(subhash.delete(last_key))
57
+ end
58
+
59
+ def encrypt_paths!(query)
60
+ @attributes.query(query).each { |path| encrypt!(path) }
61
+ end
62
+
63
+ def decrypt_paths!(query)
64
+ @attributes.query(query).each { |path| decrypt!(path) }
65
+ end
66
+
67
+ def value_for(path)
68
+ parts = path.split('.')
69
+ parts.inject(attributes) { |attrs, key| attrs[key] }
70
+ end
71
+
72
+ def subhash_for(path)
73
+ parts = path.split('.')
74
+ last_key = parts.pop
75
+
76
+ subhash = parts.inject(attributes) { |attrs, key| attrs[key] }
77
+
78
+ [last_key, subhash]
79
+ end
80
+ end
81
+ end
@@ -0,0 +1,28 @@
1
+ require 'set'
2
+
3
+ module SafeCredentials
4
+ module HashQuery
5
+
6
+ def query(path, subhash = self, prefix = nil)
7
+ all_subpaths(path, subhash, prefix).find_all { |k| File.fnmatch?(path.gsub('.', '/'), k.gsub('.', '/')) }
8
+ end
9
+
10
+ def all_subpaths(path = '*', subhash = self, prefix = nil)
11
+ parts = path.split('.')
12
+ first_part = parts.shift
13
+
14
+ matching_pathes = subhash.keys
15
+
16
+ subpaths = matching_pathes.collect do |subpath|
17
+ if Hash === subhash[subpath]
18
+ new_prefix = [prefix, subpath].compact.join('.')
19
+ all_subpaths(parts.join('.'), subhash[subpath], new_prefix)
20
+ else
21
+ [prefix, subpath].join('.')
22
+ end
23
+ end
24
+
25
+ Set.new(subpaths).flatten
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,3 @@
1
+ module SafeCredentials
2
+ VERSION = "0.0.1"
3
+ end
@@ -0,0 +1,28 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'safe_credentials/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "safe_credentials"
8
+ spec.version = SafeCredentials::VERSION
9
+ spec.authors = ["Alberto F. Capel"]
10
+ spec.email = ["afcapel@gmail.com"]
11
+ spec.description = %q{Encrypt sensitive credentials so you can store your configuration files in source control}
12
+ spec.summary = %q{Safe Credentials allows you to encrypt sensitive credentials so you can
13
+ store your configuration files in source control.}
14
+ spec.homepage = "https://github.com/afcapel/safe_credentials"
15
+ spec.license = "MIT"
16
+
17
+ spec.files = `git ls-files`.split($/)
18
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
19
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
20
+ spec.require_paths = ["lib"]
21
+
22
+ spec.add_dependency "thor"
23
+ spec.add_dependency "gibberish"
24
+
25
+ spec.add_development_dependency "bundler", "~> 1.3"
26
+ spec.add_development_dependency "rake"
27
+ spec.add_development_dependency "rspec"
28
+ end
@@ -0,0 +1,21 @@
1
+ development:
2
+ database:
3
+ adapter: sqlite3
4
+ db_user: scott
5
+ db_password: tiger
6
+ user: wadus
7
+ password: secret
8
+ test:
9
+ database:
10
+ adapter: sqlite3
11
+ db_user: scott
12
+ db_password: tiger
13
+ user: wadus
14
+ password: secret
15
+ production:
16
+ database:
17
+ adapter: postgresql
18
+ db_user: scott
19
+ db_password: tiger
20
+ user: wadus
21
+ password: secret
@@ -0,0 +1,22 @@
1
+ ---
2
+ development:
3
+ database:
4
+ encrypted_adapter: U2FsdGVkX1/+7+ZLqxcnxDwe06l8CwS8HAY8cfFpeQY=
5
+ encrypted_db_user: U2FsdGVkX18VQzi5P3XfAAmoixK6bGzVsDNzAwl1Ajo=
6
+ encrypted_db_password: U2FsdGVkX196w5Ct9xZhzpVoTacPg70pQ2bUpL/Iqco=
7
+ encrypted_user: U2FsdGVkX19CORFfXnIxh7VTL7J5CWrpLdq2fAgKEuE=
8
+ encrypted_password: U2FsdGVkX1+xamsMYDDVMLOq5jSoS0JrQcNtL2IcsOA=
9
+ test:
10
+ database:
11
+ encrypted_adapter: U2FsdGVkX19w9GcGmV7Jh3U221nNw8WLjdbAkr3yiFA=
12
+ encrypted_db_user: U2FsdGVkX18YQWsQJ9GnaTyPK0aRwS42CbuhEutaEAs=
13
+ encrypted_db_password: U2FsdGVkX1/eBxmVhmf2RmmZzD2WMQ0Pe8eUQGamJRk=
14
+ encrypted_user: U2FsdGVkX18rXFzZCaRnKfdmjeNiTegSG9h43B2UO74=
15
+ encrypted_password: U2FsdGVkX19EFT/I71fdwONxfAkOBVok9tgF3OUf0kY=
16
+ production:
17
+ database:
18
+ encrypted_adapter: U2FsdGVkX1/R5d/ET8H9PjUH0jhnPEdfj/XuaORgfjQ=
19
+ encrypted_db_user: U2FsdGVkX184exEAZE7oTPkB2N0oDeskyGD52aJr3zI=
20
+ encrypted_db_password: U2FsdGVkX1/ikJtE7VHZlkNufe/6UGsO+T51mnqJuvE=
21
+ encrypted_user: U2FsdGVkX18IPO/BNBSg+FVLOAeuSS7hObLOqL6Z+nY=
22
+ encrypted_password: U2FsdGVkX18L5ZjBRvnt0deVWcmNa26ZkCKJ4ueNWmQ=
@@ -0,0 +1,32 @@
1
+ require 'spec_helper'
2
+
3
+ describe SafeCredentials::Config do
4
+
5
+ let(:config_file) { File.expand_path('../../../fixtures/config.yml', __FILE__)}
6
+ let(:encrypted_config_file) { File.expand_path('../../../fixtures/encrypted_config.yml', __FILE__)}
7
+
8
+ let(:config) { SafeCredentials::Config.new(config_file, 'Super secret password') }
9
+
10
+
11
+
12
+
13
+ it "can save encrypted config file" do
14
+ orig_dev_env = deep_copy(config['development'])
15
+ orig_test_env = deep_copy(config['test'])
16
+ orig_prod_env = deep_copy(config['production'])
17
+
18
+ config.encrypt!
19
+
20
+ config.save
21
+
22
+ loaded_config = SafeCredentials::Config.load_encrypted('Super secret password', encrypted_config_file)
23
+
24
+ loaded_config['development'].should == orig_dev_env
25
+ loaded_config['production'].should == orig_prod_env
26
+ loaded_config['test'].should == orig_test_env
27
+ end
28
+
29
+ def deep_copy(hash)
30
+ Marshal.load(Marshal.dump(hash))
31
+ end
32
+ end
@@ -0,0 +1,144 @@
1
+ require 'spec_helper'
2
+
3
+ describe SafeCredentials::HashEncryptor do
4
+
5
+ before :each do
6
+ cipher = Gibberish::AES.new('abracadabra')
7
+
8
+ @config = {
9
+ 'development' => {
10
+ 'user' => 'wadus',
11
+ 'password' => 'secret',
12
+ 'database' => {
13
+ 'adapter' => 'sqlite',
14
+ 'db_user' => 'scott',
15
+ 'db_password' => 'tiger'
16
+ }
17
+ },
18
+ 'production' => {
19
+ 'user' => 'admin',
20
+ 'password' => '1234',
21
+ 'database' => {
22
+ 'adapter' => 'postgresql',
23
+ 'db_user' => 'scott',
24
+ 'db_password' => 'tiger'
25
+ }
26
+ }
27
+ }
28
+
29
+ @hash_encryptor = SafeCredentials::HashEncryptor.new(@config, cipher)
30
+ end
31
+
32
+ it "can encrypt/decrypt a value" do
33
+ encrypted = @hash_encryptor.encrypt_val('testing')
34
+ encrypted.should_not == 'testing'
35
+ @hash_encryptor.decrypt_val(encrypted).should == 'testing'
36
+ end
37
+
38
+ it "can encrypt/decrypt an env" do
39
+ encrypted_env = @hash_encryptor.encrypt('development')
40
+
41
+ encrypted_env['encrypted_user'].should_not be_nil
42
+ encrypted_env['encrypted_password'].should_not be_nil
43
+
44
+ encrypted_env['user'].should be_nil
45
+ encrypted_env['password'].should be_nil
46
+
47
+ decrypted_env = @hash_encryptor.decrypt_val(encrypted_env)
48
+
49
+ decrypted_env['user'].should == 'wadus'
50
+ decrypted_env['password'].should == 'secret'
51
+ end
52
+
53
+ it "can find the value for a nested path" do
54
+ @hash_encryptor.value_for('production.database').should == {
55
+ 'adapter' => 'postgresql',
56
+ 'db_user' => 'scott',
57
+ 'db_password' => 'tiger'
58
+ }
59
+
60
+ @hash_encryptor.value_for('production.database.db_password').should == 'tiger'
61
+ end
62
+
63
+ it "can encrypt/decrypt a nested path" do
64
+ encrypted_db = @hash_encryptor.encrypt('production.database')
65
+
66
+ encrypted_db['encrypted_db_user'].should_not be_nil
67
+ encrypted_db['encrypted_db_password'].should_not be_nil
68
+
69
+ encrypted_db['db_user'].should be_nil
70
+ encrypted_db['db_password'].should be_nil
71
+
72
+ decrypted_db = @hash_encryptor.decrypt_val(encrypted_db)
73
+
74
+ decrypted_db.should == {
75
+ 'adapter' => 'postgresql',
76
+ 'db_user' => 'scott',
77
+ 'db_password' => 'tiger'
78
+ }
79
+ end
80
+
81
+ it "can encrypt/decrypt an attribute within a nested path" do
82
+ encrypted_password = @hash_encryptor.encrypt('production.database.db_password')
83
+ encrypted_password.should_not == 'tiger'
84
+
85
+ @hash_encryptor.decrypt_val(encrypted_password).should == 'tiger'
86
+ end
87
+
88
+ it 'can replace attributes in the hash with encrypted values' do
89
+ original_development = @config['development'].dup
90
+
91
+ @hash_encryptor.encrypt!('development')
92
+
93
+ @config['encrypted_development']['encrypted_user'].should_not be_nil
94
+ @config['encrypted_development']['encrypted_password'].should_not be_nil
95
+
96
+ @config['encrypted_development']['user'].should be_nil
97
+ @config['encrypted_development']['password'].should be_nil
98
+
99
+ decrypted_env = @hash_encryptor.decrypt_val(@config['encrypted_development'])
100
+
101
+ decrypted_env.should == original_development
102
+ end
103
+
104
+ it 'can deep replace attributes in the hash with encrypted values' do
105
+ @hash_encryptor.encrypt!('production')
106
+
107
+ @config['encrypted_production']['encrypted_user'].should_not be_nil
108
+ @config['encrypted_production']['encrypted_password'].should_not be_nil
109
+ @config['encrypted_production']['encrypted_database'].should_not be_nil
110
+ @config['encrypted_production']['encrypted_database']['encrypted_db_user'].should_not be_nil
111
+ @config['encrypted_production']['encrypted_database']['encrypted_db_password'].should_not be_nil
112
+
113
+ @config['production'].should be_nil
114
+ @config['encrypted_production']['database'].should be_nil
115
+
116
+ decrypted_env = @hash_encryptor.decrypt_val(@config['encrypted_production'])
117
+
118
+ decrypted_env.should == {
119
+ 'user' => 'admin',
120
+ 'password' => '1234',
121
+ 'database' => {
122
+ 'adapter' => 'postgresql',
123
+ 'db_user' => 'scott',
124
+ 'db_password' => 'tiger'
125
+ }
126
+ }
127
+ end
128
+
129
+ it 'can match keys to replace against a glob expresion' do
130
+ @hash_encryptor.encrypt_paths!('*.database.db_*')
131
+
132
+ @config['development']['database']['adapter'].should == 'sqlite'
133
+ @config['development']['database']['user'].should be_nil
134
+ @config['development']['database']['password'].should be_nil
135
+ @config['development']['database']['encrypted_db_user'].should_not be_nil
136
+ @config['development']['database']['encrypted_db_password'].should_not be_nil
137
+
138
+ @config['production']['database']['adapter'].should == 'postgresql'
139
+ @config['production']['database']['user'].should be_nil
140
+ @config['production']['database']['password'].should be_nil
141
+ @config['production']['database']['encrypted_db_user'].should_not be_nil
142
+ @config['production']['database']['encrypted_db_password'].should_not be_nil
143
+ end
144
+ end
@@ -0,0 +1,45 @@
1
+ require 'spec_helper'
2
+
3
+ describe SafeCredentials::HashQuery do
4
+
5
+ before :each do
6
+ @hash = {
7
+ 'development' => {
8
+ 'user' => 'wadus',
9
+ 'password' => 'secret',
10
+ 'database' => {
11
+ 'adapter' => 'sqlite',
12
+ 'db_user' => 'scott',
13
+ 'db_password' => 'tiger'
14
+ }
15
+ },
16
+ 'production' => {
17
+ 'user' => 'admin',
18
+ 'password' => '1234',
19
+ 'database' => {
20
+ 'adapter' => 'postgresql',
21
+ 'db_user' => 'scott',
22
+ 'db_password' => 'tiger'
23
+ }
24
+ }
25
+ }
26
+
27
+ @hash.extend SafeCredentials::HashQuery
28
+ end
29
+
30
+ it "can query paths without glob expressions" do
31
+ @hash.query('production.database.adapter').should == ['production.database.adapter']
32
+ end
33
+
34
+ it "can query matching using glob expressions" do
35
+ @hash.query('*.database.db_*').should =~ [
36
+ 'development.database.db_password', 'development.database.db_user',
37
+ 'production.database.db_password', 'production.database.db_user'
38
+ ]
39
+
40
+ @hash.query('**.*password').should =~ [
41
+ 'development.password', 'development.database.db_password',
42
+ 'production.password', 'production.database.db_password'
43
+ ]
44
+ end
45
+ end
@@ -0,0 +1 @@
1
+ require_relative '../lib/safe_credentials'
metadata ADDED
@@ -0,0 +1,147 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: safe_credentials
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Alberto F. Capel
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2013-06-20 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: thor
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - '>='
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - '>='
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: gibberish
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - '>='
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - '>='
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: bundler
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ~>
46
+ - !ruby/object:Gem::Version
47
+ version: '1.3'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ~>
53
+ - !ruby/object:Gem::Version
54
+ version: '1.3'
55
+ - !ruby/object:Gem::Dependency
56
+ name: rake
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - '>='
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - '>='
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: rspec
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - '>='
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - '>='
81
+ - !ruby/object:Gem::Version
82
+ version: '0'
83
+ description: Encrypt sensitive credentials so you can store your configuration files
84
+ in source control
85
+ email:
86
+ - afcapel@gmail.com
87
+ executables:
88
+ - safe_credentials
89
+ extensions: []
90
+ extra_rdoc_files: []
91
+ files:
92
+ - .gitignore
93
+ - Gemfile
94
+ - LICENSE.txt
95
+ - README.md
96
+ - Rakefile
97
+ - bin/safe_credentials
98
+ - lib/safe_credentials.rb
99
+ - lib/safe_credentials/cli.rb
100
+ - lib/safe_credentials/config.rb
101
+ - lib/safe_credentials/engine.rb
102
+ - lib/safe_credentials/hash_encryptor.rb
103
+ - lib/safe_credentials/hash_query.rb
104
+ - lib/safe_credentials/version.rb
105
+ - safe_credentials.gemspec
106
+ - spec/.DS_Store
107
+ - spec/fixtures/config.yml
108
+ - spec/fixtures/encrypted_config.yml
109
+ - spec/lib/.DS_Store
110
+ - spec/lib/safe_credential/config_spec.rb
111
+ - spec/lib/safe_credential/hash_encryptor_spec.rb
112
+ - spec/lib/safe_credential/hash_query_spec.rb
113
+ - spec/spec_helper.rb
114
+ homepage: https://github.com/afcapel/safe_credentials
115
+ licenses:
116
+ - MIT
117
+ metadata: {}
118
+ post_install_message:
119
+ rdoc_options: []
120
+ require_paths:
121
+ - lib
122
+ required_ruby_version: !ruby/object:Gem::Requirement
123
+ requirements:
124
+ - - '>='
125
+ - !ruby/object:Gem::Version
126
+ version: '0'
127
+ required_rubygems_version: !ruby/object:Gem::Requirement
128
+ requirements:
129
+ - - '>='
130
+ - !ruby/object:Gem::Version
131
+ version: '0'
132
+ requirements: []
133
+ rubyforge_project:
134
+ rubygems_version: 2.0.0
135
+ signing_key:
136
+ specification_version: 4
137
+ summary: Safe Credentials allows you to encrypt sensitive credentials so you can store
138
+ your configuration files in source control.
139
+ test_files:
140
+ - spec/.DS_Store
141
+ - spec/fixtures/config.yml
142
+ - spec/fixtures/encrypted_config.yml
143
+ - spec/lib/.DS_Store
144
+ - spec/lib/safe_credential/config_spec.rb
145
+ - spec/lib/safe_credential/hash_encryptor_spec.rb
146
+ - spec/lib/safe_credential/hash_query_spec.rb
147
+ - spec/spec_helper.rb