secret_service 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +18 -0
- data/.travis.yml +15 -0
- data/Gemfile +4 -0
- data/LICENSE +22 -0
- data/README.md +41 -0
- data/Rakefile +57 -0
- data/lib/secret_service.rb +13 -0
- data/lib/secret_service/database_store.rb +5 -0
- data/lib/secret_service/database_store/active_record_store.rb +66 -0
- data/lib/secret_service/store.rb +35 -0
- data/lib/secret_service/version.rb +3 -0
- data/secret_service.gemspec +19 -0
- data/spec/rails-2.3/Gemfile +10 -0
- data/spec/rails-2.3/Rakefile +11 -0
- data/spec/rails-2.3/app_root/config/boot.rb +128 -0
- data/spec/rails-2.3/app_root/config/database.yml +6 -0
- data/spec/rails-2.3/app_root/config/environment.rb +14 -0
- data/spec/rails-2.3/app_root/config/environments/test.rb +0 -0
- data/spec/rails-2.3/app_root/config/initializers/fix_missing_source_file.rb +1 -0
- data/spec/rails-2.3/app_root/config/preinitializer.rb +20 -0
- data/spec/rails-2.3/app_root/config/routes.rb +13 -0
- data/spec/rails-2.3/app_root/log/.gitignore +1 -0
- data/spec/rails-2.3/rcov.opts +2 -0
- data/spec/rails-2.3/spec.opts +4 -0
- data/spec/rails-2.3/spec/spec_helper.rb +20 -0
- data/spec/rails-3.0/.rspec +2 -0
- data/spec/rails-3.0/Gemfile +10 -0
- data/spec/rails-3.0/Rakefile +11 -0
- data/spec/rails-3.0/app_root/.gitignore +4 -0
- data/spec/rails-3.0/app_root/config/application.rb +31 -0
- data/spec/rails-3.0/app_root/config/boot.rb +13 -0
- data/spec/rails-3.0/app_root/config/database.yml +6 -0
- data/spec/rails-3.0/app_root/config/environment.rb +5 -0
- data/spec/rails-3.0/app_root/config/environments/test.rb +35 -0
- data/spec/rails-3.0/app_root/config/initializers/backtrace_silencers.rb +7 -0
- data/spec/rails-3.0/app_root/config/initializers/inflections.rb +10 -0
- data/spec/rails-3.0/app_root/config/initializers/mime_types.rb +5 -0
- data/spec/rails-3.0/app_root/config/initializers/secret_token.rb +7 -0
- data/spec/rails-3.0/app_root/config/initializers/session_store.rb +8 -0
- data/spec/rails-3.0/app_root/config/routes.rb +3 -0
- data/spec/rails-3.0/app_root/lib/tasks/.gitkeep +0 -0
- data/spec/rails-3.0/app_root/log/.gitkeep +0 -0
- data/spec/rails-3.0/app_root/script/rails +6 -0
- data/spec/rails-3.0/rcov.opts +2 -0
- data/spec/rails-3.0/spec/spec_helper.rb +18 -0
- data/spec/rails-3.2/.rspec +2 -0
- data/spec/rails-3.2/Gemfile +9 -0
- data/spec/rails-3.2/Rakefile +11 -0
- data/spec/rails-3.2/app_root/.gitignore +4 -0
- data/spec/rails-3.2/app_root/config/application.rb +31 -0
- data/spec/rails-3.2/app_root/config/boot.rb +13 -0
- data/spec/rails-3.2/app_root/config/database.yml +6 -0
- data/spec/rails-3.2/app_root/config/environment.rb +5 -0
- data/spec/rails-3.2/app_root/config/environments/test.rb +35 -0
- data/spec/rails-3.2/app_root/config/initializers/backtrace_silencers.rb +7 -0
- data/spec/rails-3.2/app_root/config/initializers/inflections.rb +10 -0
- data/spec/rails-3.2/app_root/config/initializers/mime_types.rb +5 -0
- data/spec/rails-3.2/app_root/config/initializers/secret_token.rb +7 -0
- data/spec/rails-3.2/app_root/config/initializers/session_store.rb +8 -0
- data/spec/rails-3.2/app_root/config/routes.rb +3 -0
- data/spec/rails-3.2/app_root/log/.gitignore +1 -0
- data/spec/rails-3.2/rcov.opts +2 -0
- data/spec/rails-3.2/spec/spec_helper.rb +17 -0
- data/spec/shared/app_root/app/controllers/application_controller.rb +2 -0
- data/spec/shared/app_root/config/database.yml.sample +6 -0
- data/spec/shared/app_root/db/consul_test.db +0 -0
- data/spec/shared/secret_service/secret_service_spec.rb +24 -0
- data/spec/shared/secret_service/store_spec.rb +89 -0
- data/spec/shared/support/wipe_store.rb +10 -0
- metadata +189 -0
data/.gitignore
ADDED
data/.travis.yml
ADDED
data/Gemfile
ADDED
data/LICENSE
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2013 Tobias Kraze
|
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,41 @@
|
|
1
|
+
# SecretService
|
2
|
+
|
3
|
+
SecretService allows you to store random secrets in your app (for example the session secret or other shared secrets) more securely.
|
4
|
+
|
5
|
+
It does this by distributing the actual secret between your code and your database. That is, the final secret can only be calculated if you know both the secret given in your code and a secret stored in your database. The database part is generated randomly on first use.
|
6
|
+
|
7
|
+
As a useful sideeffect this means your different environments (staging / production) will automatically use different secrets.
|
8
|
+
|
9
|
+
|
10
|
+
It only works for *random* secrets though, you cannot use it to store access tokens or the like.
|
11
|
+
|
12
|
+
|
13
|
+
## Caveat
|
14
|
+
|
15
|
+
This currently requires ActiveRecord.
|
16
|
+
|
17
|
+
## Installation
|
18
|
+
|
19
|
+
Add this line to your application's Gemfile:
|
20
|
+
|
21
|
+
gem 'secret_service'
|
22
|
+
|
23
|
+
And then execute:
|
24
|
+
|
25
|
+
$ bundle
|
26
|
+
|
27
|
+
## Usage
|
28
|
+
|
29
|
+
To get a random secret, simply use
|
30
|
+
|
31
|
+
SecretService.secret("dfa24decafdb058448ac1eadb94e2066381cb92ee301e5a43d556555b61c7ea599e06be870e1d90c655c1b56cea172622d2b04a5e986faed42cbae684c5523c9")
|
32
|
+
|
33
|
+
The database entries (and indeed tables) are created on demand.
|
34
|
+
|
35
|
+
## Contributing
|
36
|
+
|
37
|
+
1. Fork it
|
38
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
39
|
+
3. Commit your changes (`git commit -am 'Added some feature'`)
|
40
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
41
|
+
5. Create new Pull Request
|
data/Rakefile
ADDED
@@ -0,0 +1,57 @@
|
|
1
|
+
#!/usr/bin/env rake
|
2
|
+
require "bundler/gem_tasks"
|
3
|
+
|
4
|
+
desc 'Default: Run all specs.'
|
5
|
+
task :default => 'all:spec'
|
6
|
+
|
7
|
+
|
8
|
+
namespace :travis_ci do
|
9
|
+
|
10
|
+
desc 'Things to do before Travis CI begins'
|
11
|
+
task :prepare do
|
12
|
+
Rake::Task['travis_ci:create_database'].invoke &&
|
13
|
+
Rake::Task['travis_ci:create_database_yml'].invoke
|
14
|
+
end
|
15
|
+
|
16
|
+
desc 'Creates a test database'
|
17
|
+
task :create_database do
|
18
|
+
system("mysql -e 'create database secret_service_test;'")
|
19
|
+
end
|
20
|
+
|
21
|
+
desc 'Creates a database.yml'
|
22
|
+
task :create_database_yml do
|
23
|
+
config_dir = "spec/shared/app_root/config"
|
24
|
+
system("cp #{config_dir}/database.yml.sample #{config_dir}/database.yml")
|
25
|
+
end
|
26
|
+
|
27
|
+
end
|
28
|
+
|
29
|
+
|
30
|
+
namespace :all do
|
31
|
+
|
32
|
+
desc "Run specs on all spec apps"
|
33
|
+
task :spec do
|
34
|
+
success = true
|
35
|
+
for_each_directory_of('spec/**/Rakefile') do |directory|
|
36
|
+
env = "SPEC=../../#{ENV['SPEC']} " if ENV['SPEC']
|
37
|
+
success &= system("cd #{directory} && BUNDLE_GEMFILE=./Gemfile #{env} bundle exec rake spec")
|
38
|
+
end
|
39
|
+
fail "Tests failed" unless success
|
40
|
+
end
|
41
|
+
|
42
|
+
desc "Bundle all spec apps"
|
43
|
+
task :bundle do
|
44
|
+
for_each_directory_of('spec/**/Gemfile') do |directory|
|
45
|
+
system("cd #{directory} && BUNDLE_GEMFILE=./Gemfile bundle install")
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
end
|
50
|
+
|
51
|
+
def for_each_directory_of(path, &block)
|
52
|
+
Dir[path].sort.each do |rakefile|
|
53
|
+
directory = File.dirname(rakefile)
|
54
|
+
puts '', "\033[44m#{directory}\033[0m", ''
|
55
|
+
block.call(directory)
|
56
|
+
end
|
57
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
require "secret_service/version"
|
2
|
+
require "secret_service/store"
|
3
|
+
|
4
|
+
module SecretService
|
5
|
+
def self.secret(source_secret, options = {})
|
6
|
+
if options[:plain]
|
7
|
+
source_secret
|
8
|
+
else
|
9
|
+
@secrets ||= {}
|
10
|
+
@secrets[source_secret] ||= Store.instance.get(source_secret)
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
@@ -0,0 +1,66 @@
|
|
1
|
+
module SecretService
|
2
|
+
module DatabaseStore
|
3
|
+
class ActiveRecordStore
|
4
|
+
TABLE_NAME = '_secret_service_secrets'
|
5
|
+
|
6
|
+
class Secret < ::ActiveRecord::Base
|
7
|
+
if respond_to? :table_name=
|
8
|
+
self.table_name = TABLE_NAME
|
9
|
+
else
|
10
|
+
set_table_name TABLE_NAME
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
|
15
|
+
def initialize
|
16
|
+
setup_database unless database_set_up?
|
17
|
+
end
|
18
|
+
|
19
|
+
def find(key)
|
20
|
+
secret_record = find_if_present(key)
|
21
|
+
unless secret_record
|
22
|
+
new_secret = yield
|
23
|
+
begin
|
24
|
+
secret_record = Secret.create!(:key => key, :value => new_secret)
|
25
|
+
rescue ::ActiveRecord::StatementInvalid
|
26
|
+
secret_record = find_if_present(key)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
secret_record.value
|
30
|
+
end
|
31
|
+
|
32
|
+
def drop_database
|
33
|
+
# tests need this
|
34
|
+
Secret.connection.drop_table TABLE_NAME
|
35
|
+
Secret.reset_column_information
|
36
|
+
rescue ::ActiveRecord::StatementInvalid
|
37
|
+
end
|
38
|
+
|
39
|
+
|
40
|
+
private
|
41
|
+
|
42
|
+
def setup_database
|
43
|
+
Secret.connection.create_table TABLE_NAME do |table|
|
44
|
+
table.string :key
|
45
|
+
table.string :value
|
46
|
+
table.timestamps
|
47
|
+
end
|
48
|
+
Secret.connection.add_index TABLE_NAME, :key, :unique => true
|
49
|
+
rescue ::ActiveRecord::StatementInvalid
|
50
|
+
end
|
51
|
+
|
52
|
+
def database_set_up?
|
53
|
+
Secret.table_exists?
|
54
|
+
end
|
55
|
+
|
56
|
+
def find_if_present(key)
|
57
|
+
Secret.first(:conditions => {:key => key})
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
|
62
|
+
def self.get
|
63
|
+
ActiveRecordStore.new
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
require 'secret_service/database_store'
|
2
|
+
|
3
|
+
require 'securerandom'
|
4
|
+
require 'singleton'
|
5
|
+
require 'gibberish'
|
6
|
+
|
7
|
+
module SecretService
|
8
|
+
class Store
|
9
|
+
include Singleton
|
10
|
+
|
11
|
+
def get(source_secret)
|
12
|
+
hash("#{source_secret}--#{database_secret(source_secret)}")
|
13
|
+
end
|
14
|
+
|
15
|
+
private
|
16
|
+
|
17
|
+
def database_store
|
18
|
+
@database_store ||= DatabaseStore.get
|
19
|
+
end
|
20
|
+
|
21
|
+
def database_secret(source_secret)
|
22
|
+
secret = database_store.find(hash(source_secret)) do
|
23
|
+
generate_secret
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
def hash(value)
|
28
|
+
Gibberish::SHA256(value)
|
29
|
+
end
|
30
|
+
|
31
|
+
def generate_secret
|
32
|
+
SecureRandom.hex(32)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
require File.expand_path('../lib/secret_service/version', __FILE__)
|
3
|
+
|
4
|
+
Gem::Specification.new do |gem|
|
5
|
+
gem.authors = ["Tobias Kraze"]
|
6
|
+
gem.email = ["tobias@kraze.eu"]
|
7
|
+
gem.description = %q{Secret service provides encryption of your application secrets with a server side master password}
|
8
|
+
gem.summary = gem.description
|
9
|
+
gem.homepage = ""
|
10
|
+
|
11
|
+
gem.files = `git ls-files`.split($\)
|
12
|
+
gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
|
13
|
+
gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
|
14
|
+
gem.name = "secret_service"
|
15
|
+
gem.require_paths = ["lib"]
|
16
|
+
gem.version = SecretService::VERSION
|
17
|
+
|
18
|
+
gem.add_dependency('gibberish')
|
19
|
+
end
|
@@ -0,0 +1,10 @@
|
|
1
|
+
source 'https://rubygems.org'
|
2
|
+
|
3
|
+
gem 'rails', '~>2.3'
|
4
|
+
gem 'rspec', '~>1.3'
|
5
|
+
gem 'rspec-rails', '~>1.3'
|
6
|
+
gem 'mysql2', '<0.3'
|
7
|
+
gem 'ruby-debug', :platforms => :mri_18
|
8
|
+
gem 'test-unit', '~>1.2', :platforms => :ruby_19
|
9
|
+
|
10
|
+
gem 'secret_service', :path => '../..'
|
@@ -0,0 +1,11 @@
|
|
1
|
+
require 'rake'
|
2
|
+
require 'spec/rake/spectask'
|
3
|
+
|
4
|
+
desc 'Default: Run all specs for a specific rails version.'
|
5
|
+
task :default => :spec
|
6
|
+
|
7
|
+
desc "Run all specs for a specific rails version"
|
8
|
+
Spec::Rake::SpecTask.new() do |t|
|
9
|
+
t.spec_opts = ['--options', "\"spec.opts\""]
|
10
|
+
t.spec_files = defined?(SPEC) ? SPEC : FileList['**/*_spec.rb', '../shared/**/*_spec.rb']
|
11
|
+
end
|
@@ -0,0 +1,128 @@
|
|
1
|
+
# Allow customization of the rails framework path
|
2
|
+
RAILS_FRAMEWORK_ROOT = (ENV['RAILS_FRAMEWORK_ROOT'] || "#{File.dirname(__FILE__)}/../../../../../../vendor/rails") unless defined?(RAILS_FRAMEWORK_ROOT)
|
3
|
+
|
4
|
+
# Don't change this file!
|
5
|
+
# Configure your app in config/environment.rb and config/environments/*.rb
|
6
|
+
|
7
|
+
RAILS_ROOT = "#{File.dirname(__FILE__)}/.." unless defined?(RAILS_ROOT)
|
8
|
+
|
9
|
+
module Rails
|
10
|
+
class << self
|
11
|
+
def boot!
|
12
|
+
unless booted?
|
13
|
+
preinitialize
|
14
|
+
pick_boot.run
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
def booted?
|
19
|
+
defined? Rails::Initializer
|
20
|
+
end
|
21
|
+
|
22
|
+
def pick_boot
|
23
|
+
(vendor_rails? ? VendorBoot : GemBoot).new
|
24
|
+
end
|
25
|
+
|
26
|
+
def vendor_rails?
|
27
|
+
File.exist?(RAILS_FRAMEWORK_ROOT)
|
28
|
+
end
|
29
|
+
|
30
|
+
def preinitialize
|
31
|
+
load(preinitializer_path) if File.exist?(preinitializer_path)
|
32
|
+
end
|
33
|
+
|
34
|
+
def preinitializer_path
|
35
|
+
"#{RAILS_ROOT}/config/preinitializer.rb"
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
class Boot
|
40
|
+
def run
|
41
|
+
load_initializer
|
42
|
+
Rails::Initializer.run(:set_load_path)
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
class VendorBoot < Boot
|
47
|
+
def load_initializer
|
48
|
+
require "#{RAILS_FRAMEWORK_ROOT}/railties/lib/initializer"
|
49
|
+
Rails::Initializer.run(:install_gem_spec_stubs)
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
class GemBoot < Boot
|
54
|
+
def load_initializer
|
55
|
+
self.class.load_rubygems
|
56
|
+
load_rails_gem
|
57
|
+
require 'initializer'
|
58
|
+
end
|
59
|
+
|
60
|
+
def load_rails_gem
|
61
|
+
if version = self.class.gem_version
|
62
|
+
gem 'rails', version
|
63
|
+
else
|
64
|
+
gem 'rails'
|
65
|
+
end
|
66
|
+
rescue Gem::LoadError => load_error
|
67
|
+
$stderr.puts %(Missing the Rails #{version} gem. Please `gem install -v=#{version} rails`, update your RAILS_GEM_VERSION setting in config/environment.rb for the Rails version you do have installed, or comment out RAILS_GEM_VERSION to use the latest version installed.)
|
68
|
+
exit 1
|
69
|
+
end
|
70
|
+
|
71
|
+
class << self
|
72
|
+
def rubygems_version
|
73
|
+
Gem::RubyGemsVersion rescue nil
|
74
|
+
end
|
75
|
+
|
76
|
+
def gem_version
|
77
|
+
if defined? RAILS_GEM_VERSION
|
78
|
+
RAILS_GEM_VERSION
|
79
|
+
elsif ENV.include?('RAILS_GEM_VERSION')
|
80
|
+
ENV['RAILS_GEM_VERSION']
|
81
|
+
else
|
82
|
+
parse_gem_version(read_environment_rb)
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
def load_rubygems
|
87
|
+
require 'rubygems'
|
88
|
+
min_version = '1.1.1'
|
89
|
+
unless rubygems_version >= min_version
|
90
|
+
$stderr.puts %Q(Rails requires RubyGems >= #{min_version} (you have #{rubygems_version}). Please `gem update --system` and try again.)
|
91
|
+
exit 1
|
92
|
+
end
|
93
|
+
|
94
|
+
rescue LoadError
|
95
|
+
$stderr.puts %Q(Rails requires RubyGems >= #{min_version}. Please install RubyGems and try again: http://rubygems.rubyforge.org)
|
96
|
+
exit 1
|
97
|
+
end
|
98
|
+
|
99
|
+
def parse_gem_version(text)
|
100
|
+
$1 if text =~ /^[^#]*RAILS_GEM_VERSION\s*=\s*["']([!~<>=]*\s*[\d.]+)["']/
|
101
|
+
end
|
102
|
+
|
103
|
+
private
|
104
|
+
def read_environment_rb
|
105
|
+
environment_rb = "#{RAILS_ROOT}/config/environment.rb"
|
106
|
+
environment_rb = "#{HELPER_RAILS_ROOT}/config/environment.rb" unless File.exists?(environment_rb)
|
107
|
+
File.read(environment_rb)
|
108
|
+
end
|
109
|
+
end
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
class Rails::Boot
|
114
|
+
def run
|
115
|
+
load_initializer
|
116
|
+
|
117
|
+
Rails::Initializer.class_eval do
|
118
|
+
def load_gems
|
119
|
+
@bundler_loaded ||= Bundler.require :default, Rails.env
|
120
|
+
end
|
121
|
+
end
|
122
|
+
|
123
|
+
Rails::Initializer.run(:set_load_path)
|
124
|
+
end
|
125
|
+
end
|
126
|
+
|
127
|
+
# All that for this:
|
128
|
+
Rails.boot!
|