trocla 0.0.7

Sign up to get free protection for your applications and to get access to all the features.
data/.document ADDED
@@ -0,0 +1,4 @@
1
+ lib/**/*.rb
2
+ bin/*
3
+ -
4
+ LICENSE.txt
data/.rspec ADDED
@@ -0,0 +1 @@
1
+ --color
data/Gemfile ADDED
@@ -0,0 +1,19 @@
1
+ source "http://rubygems.org"
2
+ # Add dependencies required to use your gem here.
3
+ # Example:
4
+ # gem "activesupport", ">= 2.3.5"
5
+
6
+ gem "moneta", ">= 0.6.1", :git => 'git://github.com/duritong/moneta.git'
7
+ gem "highline"
8
+ gem "bcrypt-ruby"
9
+
10
+ # Add dependencies to develop your gem here.
11
+ # Include everything needed to run rake, tests, features, etc.
12
+ group :development do
13
+ gem "rspec", "~> 2.3.0"
14
+ gem "rdoc", "~> 3.8"
15
+ gem "mocha"
16
+ gem "bundler", "~> 1.0.0"
17
+ gem "jeweler", "~> 1.6.4"
18
+ gem "rcov", ">= 0"
19
+ end
data/Gemfile.lock ADDED
@@ -0,0 +1,43 @@
1
+ GIT
2
+ remote: git://github.com/duritong/moneta.git
3
+ revision: 38125d0d598891f27fa6836cbc57cb928831ac4f
4
+ specs:
5
+ moneta (0.6.1)
6
+
7
+ GEM
8
+ remote: http://rubygems.org/
9
+ specs:
10
+ bcrypt-ruby (2.1.4)
11
+ diff-lcs (1.1.2)
12
+ git (1.2.5)
13
+ highline (1.6.2)
14
+ jeweler (1.6.4)
15
+ bundler (~> 1.0)
16
+ git (>= 1.2.5)
17
+ rake
18
+ mocha (0.9.12)
19
+ rake (0.9.2)
20
+ rcov (0.9.9)
21
+ rdoc (3.8)
22
+ rspec (2.3.0)
23
+ rspec-core (~> 2.3.0)
24
+ rspec-expectations (~> 2.3.0)
25
+ rspec-mocks (~> 2.3.0)
26
+ rspec-core (2.3.1)
27
+ rspec-expectations (2.3.0)
28
+ diff-lcs (~> 1.1.2)
29
+ rspec-mocks (2.3.0)
30
+
31
+ PLATFORMS
32
+ ruby
33
+
34
+ DEPENDENCIES
35
+ bcrypt-ruby
36
+ bundler (~> 1.0.0)
37
+ highline
38
+ jeweler (~> 1.6.4)
39
+ mocha
40
+ moneta (>= 0.6.1)!
41
+ rcov
42
+ rdoc (~> 3.8)
43
+ rspec (~> 2.3.0)
data/LICENSE.txt ADDED
@@ -0,0 +1,15 @@
1
+ Trocla - a simple password generator and storage
2
+ Copyright (C) 2011 Marcel Haerry
3
+
4
+ This program is free software: you can redistribute it and/or modify
5
+ it under the terms of the GNU General Public License as published by
6
+ the Free Software Foundation, either version 3 of the License, or
7
+ any later version.
8
+
9
+ This program is distributed in the hope that it will be useful,
10
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
11
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12
+ GNU General Public License for more details.
13
+
14
+ You should have received a copy of the GNU General Public License
15
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
data/README.md ADDED
@@ -0,0 +1,134 @@
1
+ # trocla
2
+
3
+ Trocla provides you a simple way to create and store (random) passwords on a
4
+ central server, which can be retrieved by other applications. An example for
5
+ such an application is puppet and trocla can help you to not store any
6
+ plaintext or hashed passwords in your manifests by keeping these passwords only
7
+ on your puppetmaster.
8
+
9
+ Furthermore it provides you a simple cli that helps you to modify the password
10
+ storage from the cli.
11
+
12
+ Trocla does not only create and/or store a plain password, it is also able to
13
+ generate (and store) any kind hashed passwords based on the plain password.
14
+ As long as the plain password is preset, trocla is able to generate any kind
15
+ of hashed passwords through an easy extendible plugin system.
16
+
17
+ It is not necessary to store the plain password on the server, you can also
18
+ just feed trocla with the hashed password and use that in your other tools.
19
+ A common example for that is that you let puppet retrieve (and hence create)
20
+ a salted md5 password for a user. This will then store the salted md5 of
21
+ a random password AND the plain text password in trocla. Later you can
22
+ retrieve (by deleting) the plain password and send it to the user. Puppet
23
+ will still simply retrieve the hashed password that is stored in trocla,
24
+ while the plain password is not anymore stored on the server.
25
+
26
+ You can use any kind of key/value based storage supported by moneta for
27
+ trocla. By default it uses a simple yaml file.
28
+
29
+ ## Usage
30
+
31
+ ### create
32
+
33
+ Assuming that we have an empty trocla storage.
34
+
35
+ trocla create user1 plain
36
+
37
+ This will create (if not already stored in trocla) a random password and
38
+ store its plain text under key user1. The password will also be returned
39
+ by trocla.
40
+
41
+ trocla create user2 mysql
42
+
43
+ This will create a random password and store its plain and mysql-style hashed
44
+ sha1 password in trocla. The hashed password is returned.
45
+
46
+ trocla create user1 mysql
47
+
48
+ This will take the already stored plain text password of key user1 and generate
49
+ and store the mysql-style hashed sha1 password.
50
+
51
+ It is possible that certain hash formats require additional options. For example
52
+ the pgsql hash requires also the user to create the md5 hash for the password.
53
+ You can pass these additional requirements as yaml-based strings to the format:
54
+
55
+ trocla create user1 pgsql 'username: user1'
56
+
57
+ This will create a pgsql password hash using the username user1.
58
+
59
+ ### get
60
+
61
+ Get simply returns a stored password. It will not create a new password.
62
+
63
+ Assuming that we are still working with the same storage
64
+
65
+ trocla get user2 plain
66
+
67
+ will return the plain text password of the key user2.
68
+
69
+ trocla get user3 plain
70
+
71
+ This will return nothing, as no password with this format have been stored so
72
+ far.
73
+
74
+ ### set
75
+
76
+ trocla set user3 plain
77
+
78
+ This will ask you for a password and set it under the appropriate key/format.
79
+
80
+ trocla set --pwd-from-stdin user4 plain mysupersecretpassword
81
+
82
+ This will take the password from the cli without asking you.
83
+
84
+ trocla set --pwd-from-stdin user5 mysql *ABC....
85
+
86
+ This will store a mysql sha1 hash for the key user5, without storing any kind
87
+ of plain text password.
88
+
89
+ ### reset
90
+
91
+ trocla reset user1 md5crypt
92
+
93
+ This will recreate the salted md5 shadow-style hash. However, it will not create
94
+ a new plain text passwords. Hence, this is mainly usefull to create new hashed
95
+ passwords based on new salts.
96
+
97
+ If the plain password of a key is resetted, every already hashed password is
98
+ deleted as well, as the hashes wouldn't match anymore the plain text password.
99
+
100
+ ### delete
101
+
102
+ trocla delete user1 plain
103
+
104
+ This will delete the plain password of the key user1 and return it.
105
+
106
+ ## Attention
107
+
108
+ If you don't feed trocla initially with a hash and/or delete the generated
109
+ plain text passwords trocla will likely create a lot of plain text passwords
110
+ and store them on your machine/server. This is by intend and is all about which
111
+ problems (mainly passwords in configuration management manifests) trocla tries
112
+ to address.
113
+
114
+ ## Installation
115
+
116
+ Simply build and install the gem. BUT trocla is based on a not yet published
117
+ version of [moneta](https://github.com/wycats/moneta). You can build a new
118
+ moneta version based on the latest master as version 0.6.1 and instal that one.
119
+
120
+ ## Contributing to trocla
121
+
122
+ * Check out the latest master to make sure the feature hasn't been implemented or the bug hasn't been fixed yet
123
+ * Check out the issue tracker to make sure someone already hasn't requested it and/or contributed it
124
+ * Fork the project
125
+ * Start a feature/bugfix branch
126
+ * Commit and push until you are happy with your contribution
127
+ * Make sure to add tests for it. This is important so I don't break it in a future version unintentionally.
128
+ * Please try not to mess with the Rakefile, version, or history. If you want to have your own version, or is otherwise necessary, that is fine, but please isolate to its own commit so I can cherry-pick around it.
129
+
130
+ ## Copyright
131
+
132
+ Copyright (c) 2011 mh. See LICENSE.txt for
133
+ further details.
134
+
data/Rakefile ADDED
@@ -0,0 +1,53 @@
1
+ # encoding: utf-8
2
+
3
+ require 'rubygems'
4
+ require 'bundler'
5
+ begin
6
+ Bundler.setup(:default, :development)
7
+ rescue Bundler::BundlerError => e
8
+ $stderr.puts e.message
9
+ $stderr.puts "Run `bundle install` to install missing gems"
10
+ exit e.status_code
11
+ end
12
+ require 'rake'
13
+
14
+
15
+ require 'jeweler'
16
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), 'lib'))
17
+ require 'trocla'
18
+ Jeweler::Tasks.new do |gem|
19
+ # gem is a Gem::Specification... see http://docs.rubygems.org/read/chapter/20 for more options
20
+ gem.name = "trocla"
21
+ gem.homepage = "https://tech.immerda.ch/2011/12/trocla-get-hashed-passwords-out-of-puppet-manifests/"
22
+ gem.license = "GPLv3"
23
+ gem.summary = "Trocla a simple password generator and storage"
24
+ gem.description = "Trocla helps you to generate random passwords and to store them in various formats (plain, MD5, bcrypt) for later retrival."
25
+ gem.email = "mh+trocla@immerda.ch"
26
+ gem.authors = ["mh"]
27
+ gem.version = Trocla::VERSION::STRING
28
+ # dependencies defined in Gemfile
29
+ end
30
+ Jeweler::RubygemsDotOrgTasks.new
31
+
32
+ require 'rspec/core'
33
+ require 'rspec/core/rake_task'
34
+ RSpec::Core::RakeTask.new(:spec) do |spec|
35
+ spec.pattern = FileList['spec/**/*_spec.rb']
36
+ end
37
+
38
+ RSpec::Core::RakeTask.new(:rcov) do |spec|
39
+ spec.pattern = 'spec/**/*_spec.rb'
40
+ spec.rcov = true
41
+ end
42
+
43
+ task :default => :spec
44
+
45
+ gem 'rdoc'
46
+ require 'rdoc/task'
47
+ RDoc::Task.new do |rdoc|
48
+ version = Trocla::VERSION::STRING
49
+ rdoc.rdoc_dir = 'rdoc'
50
+ rdoc.title = "trocla #{version}"
51
+ rdoc.rdoc_files.include('README*')
52
+ rdoc.rdoc_files.include('lib/**/*.rb')
53
+ end
data/bin/trocla ADDED
@@ -0,0 +1,117 @@
1
+ #!/usr/bin/env ruby
2
+ # CLI client for Trocla.
3
+ #
4
+ require 'rubygems'
5
+ require 'trocla'
6
+ require 'optparse'
7
+ require 'yaml'
8
+
9
+ options = { :config_file => nil, :ask_password => true }
10
+
11
+ OptionParser.new do |opts|
12
+ opts.on("--version", "-V", "Version information") do
13
+ puts Trocla::VERSION::STRING
14
+ exit
15
+ end
16
+
17
+ opts.on("--config CONFIG", "-c", "Configuration file") do |v|
18
+ if File.exist?(v)
19
+ options[:config_file] = v
20
+ else
21
+ STDERR.puts "Cannot find config file: #{v}"
22
+ exit 1
23
+ end
24
+ end
25
+
26
+ opts.on("--no-random") do
27
+ options['random'] = false
28
+ end
29
+
30
+ opts.on("--length LENGTH") do |v|
31
+ options['length'] = v.to_i
32
+ end
33
+
34
+ opts.on("--pwd-from-stdin") do
35
+ options[:ask_password] = false
36
+ end
37
+
38
+
39
+ end.parse!
40
+
41
+ def create(options)
42
+ miss_format unless options[:trocla_format]
43
+ Trocla.new(options.delete(:config_file)).password(
44
+ options.delete(:trocla_key),
45
+ options.delete(:trocla_format),
46
+ options.merge(YAML.load(options.delete(:other_options).shift.to_s)||{})
47
+ )
48
+ end
49
+
50
+ def get(options)
51
+ miss_format unless options[:trocla_format]
52
+ Trocla.new(options.delete(:config_file)).get_password(
53
+ options.delete(:trocla_key),
54
+ options.delete(:trocla_format)
55
+ )
56
+ end
57
+ def set(options)
58
+ miss_format unless options[:trocla_format]
59
+ if options.delete(:ask_password)
60
+ require 'highline/import'
61
+ password = ask("Enter your password: ") { |q| q.echo = "x" }
62
+ pwd2 = ask("Repeat password: ") { |q| q.echo = "x" }
63
+ unless password == pwd2
64
+ STDERR.puts "Passwords did not match, exiting!"
65
+ exit 1
66
+ end
67
+ else
68
+ password = options.delete(:other_options).shift
69
+ end
70
+ Trocla.new(options.delete(:config_file)).set_password(
71
+ options.delete(:trocla_key),
72
+ options.delete(:trocla_format),
73
+ password
74
+ )
75
+ ""
76
+ end
77
+
78
+ def reset(options)
79
+ miss_format unless options[:trocla_format]
80
+ Trocla.new(options.delete(:config_file)).reset_password(
81
+ options.delete(:trocla_key),
82
+ options.delete(:trocla_format),
83
+ options.merge(YAML.load(options.delete(:other_options).shift.to_s)||{})
84
+ )
85
+ end
86
+
87
+ def delete(options)
88
+ Trocla.new(options.delete(:config_file)).delete_password(
89
+ options.delete(:trocla_key),
90
+ options.delete(:trocla_format)
91
+ )
92
+ end
93
+
94
+ def miss_format
95
+ STDERR.puts "Missing format, exiting..."
96
+ exit 1
97
+ end
98
+
99
+ actions=['create','get','set','reset','delete']
100
+
101
+ if !(ARGV.length < 2) && (action=ARGV.shift) && actions.include?(action)
102
+ options[:trocla_key] = ARGV.shift
103
+ options[:trocla_format] = ARGV.shift
104
+ options[:other_options] = ARGV
105
+ begin
106
+ if result = send(action,options)
107
+ puts result.is_a?(String) ? result : result.inspect
108
+ end
109
+ rescue Exception => e
110
+ STDERR.puts "Action failed with the following message: #{e.message}" unless e.message == 'exit'
111
+ exit 1
112
+ end
113
+ else
114
+ STDERR.puts "Please supply one of the following actions: #{actions.join(', ')}"
115
+ exit 1
116
+ end
117
+
data/lib/VERSION ADDED
@@ -0,0 +1,4 @@
1
+ major:0
2
+ minor:0
3
+ patch:7
4
+ build:
@@ -0,0 +1,7 @@
1
+ ---
2
+ options:
3
+ random: true
4
+ length: 12
5
+ adapter: YAML
6
+ adapter_options:
7
+ :path: '/tmp/trocla.yaml'
@@ -0,0 +1,6 @@
1
+ class Trocla::Formats::Bcrypt
2
+ require 'bcrypt'
3
+ def format(plain_password,options={})
4
+ BCrypt::Password.create(plain_password).to_s
5
+ end
6
+ end
@@ -0,0 +1,6 @@
1
+ # salted crypt
2
+ class Trocla::Formats::Md5crypt
3
+ def format(plain_password,options={})
4
+ plain_password.crypt('$1$' << Trocla::Util.salt << '$')
5
+ end
6
+ end
@@ -0,0 +1,6 @@
1
+ class Trocla::Formats::Mysql
2
+ require 'digest/sha1'
3
+ def format(plain_password,options={})
4
+ "*" + Digest::SHA1.hexdigest(Digest::SHA1.digest(plain_password)).upcase
5
+ end
6
+ end
@@ -0,0 +1,7 @@
1
+ class Trocla::Formats::Pgsql
2
+ require 'digest/md5'
3
+ def format(plain_password,options={})
4
+ raise "You need pass the username as an option to use this format" unless options['username']
5
+ "md5" + Digest::MD5.hexdigest(plain_password + options['username'])
6
+ end
7
+ end
@@ -0,0 +1,7 @@
1
+ class Trocla::Formats::Plain
2
+
3
+ def format(plain_password,options={})
4
+ plain_password
5
+ end
6
+
7
+ end
@@ -0,0 +1,6 @@
1
+ # salted crypt
2
+ class Trocla::Formats::Sha256crypt
3
+ def format(plain_password,options={})
4
+ plain_password.crypt('$5$' << Trocla::Util.salt << '$')
5
+ end
6
+ end
@@ -0,0 +1,6 @@
1
+ # salted crypt
2
+ class Trocla::Formats::Sha512crypt
3
+ def format(plain_password,options={})
4
+ plain_password.crypt('$6$' << Trocla::Util.salt << '$')
5
+ end
6
+ end
@@ -0,0 +1,32 @@
1
+ class Trocla::Formats
2
+ class << self
3
+ def [](format)
4
+ formats[format.downcase]
5
+ end
6
+
7
+ def all
8
+ Dir[File.expand_path(File.join(File.dirname(__FILE__),'formats','*.rb'))].collect{|f| File.basename(f,'.rb').downcase }
9
+ end
10
+
11
+ def available?(format)
12
+ all.include?(format.downcase)
13
+ end
14
+
15
+ private
16
+ def formats
17
+ @@formats ||= Hash.new do |hash, format|
18
+ format = format.downcase
19
+ if File.exists?(path(format))
20
+ require "trocla/formats/#{format}"
21
+ hash[format] = (eval "Trocla::Formats::#{format.capitalize}").new
22
+ else
23
+ raise "Format #{format} is not supported!"
24
+ end
25
+ end
26
+ end
27
+
28
+ def path(format)
29
+ File.expand_path(File.join(File.dirname(__FILE__),'formats',"#{format}.rb"))
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,24 @@
1
+ class Trocla
2
+ class Util
3
+ class << self
4
+ def random_str(length=12)
5
+ (1..length).collect{|a| chars[rand(chars.size)] }.join.to_s
6
+ end
7
+
8
+ def salt(length=8)
9
+ (1..length).collect{|a| normal_chars[rand(normal_chars.size)] }.join.to_s
10
+ end
11
+
12
+ private
13
+ def chars
14
+ @chars ||= normal_chars + special_chars
15
+ end
16
+ def normal_chars
17
+ @normal_chars ||= ('a'..'z').to_a + ('A'..'Z').to_a + ('0'..'9').to_a
18
+ end
19
+ def special_chars
20
+ @special_chars ||= "+*%/()@&=?![]{}-_.,;:".split(//)
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,22 @@
1
+ # encoding: utf-8
2
+ class Trocla
3
+ class VERSION
4
+ version = {}
5
+ File.read(File.join(File.dirname(__FILE__), '../', 'VERSION')).each_line do |line|
6
+ type, value = line.chomp.split(":")
7
+ next if type =~ /^\s+$/ || value =~ /^\s+$/
8
+ version[type] = value
9
+ end
10
+
11
+ MAJOR = version['major']
12
+ MINOR = version['minor']
13
+ PATCH = version['patch']
14
+ BUILD = version['build']
15
+
16
+ STRING = [MAJOR, MINOR, PATCH, BUILD].compact.join('.')
17
+
18
+ def self.version
19
+ STRING
20
+ end
21
+ end
22
+ end
data/lib/trocla.rb ADDED
@@ -0,0 +1,91 @@
1
+ require 'trocla/version'
2
+ require 'trocla/util'
3
+ require 'trocla/formats'
4
+
5
+ class Trocla
6
+
7
+ def initialize(config_file=nil)
8
+ if config_file
9
+ @config_file = File.expand_path(config_file)
10
+ elsif File.exists?(def_config_file=File.expand_path('~/.troclarc.yaml')) || File.exists?(def_config_file=File.expand_path('/etc/troclarc.yaml'))
11
+ @config_file = def_config_file
12
+ end
13
+ end
14
+
15
+ def password(key,format,options={})
16
+ options = config['options'].merge(options)
17
+ raise "Format #{format} is not supported! Supported formats: #{Trocla::Formats.all.join(', ')}" unless Trocla::Formats::available?(format)
18
+
19
+ unless (password=get_password(key,format)).nil?
20
+ return password
21
+ end
22
+
23
+ plain_pwd = get_password(key,'plain')
24
+ if options['random'] && plain_pwd.nil?
25
+ plain_pwd = Trocla::Util.random_str(options['length'])
26
+ set_password(key,'plain',plain_pwd) unless format == 'plain'
27
+ elsif !options['random'] && plain_pwd.nil?
28
+ raise "Password must be present as plaintext if you don't want a random password"
29
+ end
30
+ set_password(key,format,Trocla::Formats[format].format(plain_pwd,options))
31
+ end
32
+
33
+ def get_password(key,format)
34
+ cache.fetch(key,{})[format]
35
+ end
36
+
37
+ def reset_password(key,format,options={})
38
+ set_password(key,format,nil)
39
+ password(key,format,options)
40
+ end
41
+
42
+ def delete_password(key,format=nil)
43
+ if format.nil?
44
+ cache.delete(key)
45
+ else
46
+ old_val = (h = cache.fetch(key,{})).delete(format)
47
+ h.empty? ? cache.delete(key) : cache[key] = h
48
+ old_val
49
+ end
50
+ end
51
+
52
+ def set_password(key,format,password)
53
+ if (format == 'plain')
54
+ h = (cache[key] = { 'plain' => password })
55
+ else
56
+ h = (cache[key] = cache.fetch(key,{}).merge({ format => password }))
57
+ end
58
+ h[format]
59
+ end
60
+
61
+ private
62
+ def cache
63
+ @cache ||= build_cache
64
+ end
65
+
66
+ def build_cache
67
+ require 'moneta'
68
+ require "moneta/adapters/#{config['adapter'].downcase}"
69
+ lconfig = config
70
+ Moneta::Builder.new { run eval( "Moneta::Adapters::#{lconfig['adapter']}"), lconfig['adapter_options'] }
71
+ end
72
+
73
+ def config
74
+ @config ||= read_config
75
+ end
76
+
77
+ def read_config
78
+ if @config_file.nil?
79
+ default_config
80
+ else
81
+ raise "Configfile #{@config_file} does not exist!" unless File.exists?(@config_file)
82
+ default_config.merge(YAML.load(File.read(@config_file)))
83
+ end
84
+ end
85
+
86
+ def default_config
87
+ require 'yaml'
88
+ YAML.load(File.read(File.expand_path(File.join(File.dirname(__FILE__),'trocla','default_config.yaml'))))
89
+ end
90
+
91
+ end
data/spec/data/.keep ADDED
File without changes
@@ -0,0 +1,30 @@
1
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
2
+ $LOAD_PATH.unshift(File.dirname(__FILE__))
3
+ require 'rspec'
4
+ require 'mocha'
5
+ require 'trocla'
6
+
7
+ # Requires supporting files with custom matchers and macros, etc,
8
+ # in ./support/ and its subdirectories.
9
+ Dir["#{File.dirname(__FILE__)}/support/**/*.rb"].each {|f| require f}
10
+
11
+ RSpec.configure do |config|
12
+
13
+ end
14
+
15
+ def default_config
16
+ @default_config ||= YAML.load(File.read(File.expand_path(base_dir+'/lib/trocla/default_config.yaml')))
17
+ end
18
+
19
+ def test_config
20
+ return @config unless @config.nil?
21
+ @config = default_config
22
+ yaml_path = File.expand_path(base_dir+'/spec/data/test_config.yaml')
23
+ File.unlink(yaml_path) if File.exists?(yaml_path)
24
+ @config['adapter_options'][:path] = yaml_path
25
+ @config
26
+ end
27
+
28
+ def base_dir
29
+ File.dirname(__FILE__)+'/../'
30
+ end
@@ -0,0 +1,28 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
2
+
3
+ describe "Trocla::Util" do
4
+
5
+ { :random_str => 12, :salt => 8 }.each do |m,length|
6
+ describe m do
7
+ it "should be random" do
8
+ Trocla::Util.send(m).should_not eql(Trocla::Util.send(m))
9
+ end
10
+
11
+ it "should default to length #{length}" do
12
+ Trocla::Util.send(m).length.should == length
13
+ end
14
+
15
+ it "should be possible to change length" do
16
+ Trocla::Util.send(m,8).length.should == 8
17
+ Trocla::Util.send(m,32).length.should == 32
18
+ Trocla::Util.send(m,1).length.should == 1
19
+ end
20
+ end
21
+ end
22
+
23
+ describe :salt do
24
+ it "should only contain characters and numbers" do
25
+ Trocla::Util.salt =~ /^[a-z0-9]+$/i
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,127 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
2
+
3
+ describe "Trocla" do
4
+
5
+ before(:each) do
6
+ Trocla.any_instance.expects(:read_config).returns(test_config)
7
+ @trocla = Trocla.new
8
+ end
9
+
10
+ describe "password" do
11
+ it "should generate random passwords by default" do
12
+ @trocla.password('random1','plain').should_not eql(@trocla.password('random2','plain'))
13
+ end
14
+
15
+ it "should generate passwords of length #{default_config['options']['length']}" do
16
+ @trocla.password('random1','plain').length.should eql(default_config['options']['length'])
17
+ end
18
+
19
+ Trocla::Formats.all.each do |format|
20
+ describe "#{format} password format" do
21
+ it "should return a password hashed in the #{format} format" do
22
+ @trocla.password('some_test',format,format_options[format]).should_not be_empty
23
+ end
24
+
25
+ it "should return the same hashed for the #{format} format on multiple invocations" do
26
+ (round1=@trocla.password('some_test',format,format_options[format])).should_not be_empty
27
+ @trocla.password('some_test',format,format_options[format]).should eql(round1)
28
+ end
29
+
30
+ it "should also store the plain password by default" do
31
+ pwd = @trocla.password('some_test','plain')
32
+ pwd.should_not be_empty
33
+ pwd.length.should eql(12)
34
+ end
35
+ end
36
+ end
37
+
38
+ Trocla::Formats.all.reject{|f| f == 'plain' }.each do |format|
39
+ it "should raise an exception if not a random password is asked but plain password is not present for format #{format}" do
40
+ lambda{ @trocla.password('not_random',format, 'random' => false) }.should raise_error
41
+ end
42
+ end
43
+ end
44
+
45
+ describe "set_password" do
46
+ it "should reset hashed passwords on a new plain password" do
47
+ @trocla.password('set_test','mysql').should_not be_empty
48
+ @trocla.get_password('set_test','mysql').should_not be_nil
49
+ (old_plain=@trocla.password('set_test','mysql')).should_not be_empty
50
+
51
+ @trocla.set_password('set_test','plain','foobar').should_not eql(old_plain)
52
+ @trocla.get_password('set_test','mysql').should be_nil
53
+ end
54
+
55
+ it "should otherwise only update the hash" do
56
+ (mysql = @trocla.password('set_test2','mysql')).should_not be_empty
57
+ (md5crypt = @trocla.password('set_test2','md5crypt')).should_not be_empty
58
+ (plain = @trocla.get_password('set_test2','plain')).should_not be_empty
59
+
60
+ (new_mysql = @trocla.set_password('set_test2','mysql','foo')).should_not eql(mysql)
61
+ @trocla.get_password('set_test2','mysql').should eql(new_mysql)
62
+ @trocla.get_password('set_test2','md5crypt').should eql(md5crypt)
63
+ @trocla.get_password('set_test2','plain').should eql(plain)
64
+ end
65
+ end
66
+
67
+ describe "reset_password" do
68
+ it "should reset a password" do
69
+ plain1 = @trocla.password('reset_pwd','plain')
70
+ plain2 = @trocla.reset_password('reset_pwd','plain')
71
+
72
+ plain1.should_not eql(plain2)
73
+ end
74
+
75
+ it "should not reset other formats" do
76
+ (mysql = @trocla.password('reset_pwd2','mysql')).should_not be_empty
77
+ (md5crypt1 = @trocla.password('reset_pwd2','md5crypt')).should_not be_empty
78
+
79
+ (md5crypt2 = @trocla.reset_password('reset_pwd2','md5crypt')).should_not be_empty
80
+ md5crypt2.should_not eql(md5crypt1)
81
+
82
+ @trocla.get_password('reset_pwd2','mysql').should eql(mysql)
83
+ end
84
+ end
85
+
86
+ describe "delete_password" do
87
+ it "should delete all passwords if no format is given" do
88
+ @trocla.password('delete_test1','mysql').should_not be_nil
89
+ @trocla.get_password('delete_test1','plain').should_not be_nil
90
+
91
+ @trocla.delete_password('delete_test1')
92
+ @trocla.get_password('delete_test1','plain').should be_nil
93
+ @trocla.get_password('delete_test1','mysql').should be_nil
94
+ end
95
+
96
+ it "should delete only a given format" do
97
+ @trocla.password('delete_test2','mysql').should_not be_nil
98
+ @trocla.get_password('delete_test2','plain').should_not be_nil
99
+
100
+ @trocla.delete_password('delete_test2','plain')
101
+ @trocla.get_password('delete_test2','plain').should be_nil
102
+ @trocla.get_password('delete_test2','mysql').should_not be_nil
103
+ end
104
+
105
+ it "should delete only a given non-plain format" do
106
+ @trocla.password('delete_test3','mysql').should_not be_nil
107
+ @trocla.get_password('delete_test3','plain').should_not be_nil
108
+
109
+ @trocla.delete_password('delete_test3','mysql')
110
+ @trocla.get_password('delete_test3','mysql').should be_nil
111
+ @trocla.get_password('delete_test3','plain').should_not be_nil
112
+ end
113
+ end
114
+
115
+ describe "VERSION" do
116
+ it "should return a version" do
117
+ Trocla::VERSION::STRING.should_not be_empty
118
+ end
119
+ end
120
+
121
+ def format_options
122
+ @format_options ||= Hash.new({}).merge({
123
+ 'pgsql' => { 'username' => 'test' }
124
+ })
125
+ end
126
+
127
+ end
data/trocla.gemspec ADDED
@@ -0,0 +1,91 @@
1
+ # Generated by jeweler
2
+ # DO NOT EDIT THIS FILE DIRECTLY
3
+ # Instead, edit Jeweler::Tasks in Rakefile, and run 'rake gemspec'
4
+ # -*- encoding: utf-8 -*-
5
+
6
+ Gem::Specification.new do |s|
7
+ s.name = %q{trocla}
8
+ s.version = "0.0.7"
9
+
10
+ s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
+ s.authors = ["mh"]
12
+ s.date = %q{2012-01-05}
13
+ s.default_executable = %q{trocla}
14
+ s.description = %q{Trocla helps you to generate random passwords and to store them in various formats (plain, MD5, bcrypt) for later retrival.}
15
+ s.email = %q{mh+trocla@immerda.ch}
16
+ s.executables = ["trocla"]
17
+ s.extra_rdoc_files = [
18
+ "LICENSE.txt",
19
+ "README.md"
20
+ ]
21
+ s.files = [
22
+ ".document",
23
+ ".rspec",
24
+ "Gemfile",
25
+ "Gemfile.lock",
26
+ "LICENSE.txt",
27
+ "README.md",
28
+ "Rakefile",
29
+ "bin/trocla",
30
+ "lib/VERSION",
31
+ "lib/trocla.rb",
32
+ "lib/trocla/default_config.yaml",
33
+ "lib/trocla/formats.rb",
34
+ "lib/trocla/formats/bcrypt.rb",
35
+ "lib/trocla/formats/md5crypt.rb",
36
+ "lib/trocla/formats/mysql.rb",
37
+ "lib/trocla/formats/pgsql.rb",
38
+ "lib/trocla/formats/plain.rb",
39
+ "lib/trocla/formats/sha256crypt.rb",
40
+ "lib/trocla/formats/sha512crypt.rb",
41
+ "lib/trocla/util.rb",
42
+ "lib/trocla/version.rb",
43
+ "spec/data/.keep",
44
+ "spec/spec_helper.rb",
45
+ "spec/trocla/util_spec.rb",
46
+ "spec/trocla_spec.rb",
47
+ "trocla.gemspec"
48
+ ]
49
+ s.homepage = %q{https://tech.immerda.ch/2011/12/trocla-get-hashed-passwords-out-of-puppet-manifests/}
50
+ s.licenses = ["GPLv3"]
51
+ s.require_paths = ["lib"]
52
+ s.rubygems_version = %q{1.6.2}
53
+ s.summary = %q{Trocla a simple password generator and storage}
54
+
55
+ if s.respond_to? :specification_version then
56
+ s.specification_version = 3
57
+
58
+ if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
59
+ s.add_runtime_dependency(%q<moneta>, [">= 0.6.1"])
60
+ s.add_runtime_dependency(%q<highline>, [">= 0"])
61
+ s.add_runtime_dependency(%q<bcrypt-ruby>, [">= 0"])
62
+ s.add_development_dependency(%q<rspec>, ["~> 2.3.0"])
63
+ s.add_development_dependency(%q<rdoc>, ["~> 3.8"])
64
+ s.add_development_dependency(%q<mocha>, [">= 0"])
65
+ s.add_development_dependency(%q<bundler>, ["~> 1.0.0"])
66
+ s.add_development_dependency(%q<jeweler>, ["~> 1.6.4"])
67
+ s.add_development_dependency(%q<rcov>, [">= 0"])
68
+ else
69
+ s.add_dependency(%q<moneta>, [">= 0.6.1"])
70
+ s.add_dependency(%q<highline>, [">= 0"])
71
+ s.add_dependency(%q<bcrypt-ruby>, [">= 0"])
72
+ s.add_dependency(%q<rspec>, ["~> 2.3.0"])
73
+ s.add_dependency(%q<rdoc>, ["~> 3.8"])
74
+ s.add_dependency(%q<mocha>, [">= 0"])
75
+ s.add_dependency(%q<bundler>, ["~> 1.0.0"])
76
+ s.add_dependency(%q<jeweler>, ["~> 1.6.4"])
77
+ s.add_dependency(%q<rcov>, [">= 0"])
78
+ end
79
+ else
80
+ s.add_dependency(%q<moneta>, [">= 0.6.1"])
81
+ s.add_dependency(%q<highline>, [">= 0"])
82
+ s.add_dependency(%q<bcrypt-ruby>, [">= 0"])
83
+ s.add_dependency(%q<rspec>, ["~> 2.3.0"])
84
+ s.add_dependency(%q<rdoc>, ["~> 3.8"])
85
+ s.add_dependency(%q<mocha>, [">= 0"])
86
+ s.add_dependency(%q<bundler>, ["~> 1.0.0"])
87
+ s.add_dependency(%q<jeweler>, ["~> 1.6.4"])
88
+ s.add_dependency(%q<rcov>, [">= 0"])
89
+ end
90
+ end
91
+
metadata ADDED
@@ -0,0 +1,227 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: trocla
3
+ version: !ruby/object:Gem::Version
4
+ hash: 17
5
+ prerelease:
6
+ segments:
7
+ - 0
8
+ - 0
9
+ - 7
10
+ version: 0.0.7
11
+ platform: ruby
12
+ authors:
13
+ - mh
14
+ autorequire:
15
+ bindir: bin
16
+ cert_chain: []
17
+
18
+ date: 2012-01-05 00:00:00 +01:00
19
+ default_executable: trocla
20
+ dependencies:
21
+ - !ruby/object:Gem::Dependency
22
+ type: :runtime
23
+ requirement: &id001 !ruby/object:Gem::Requirement
24
+ none: false
25
+ requirements:
26
+ - - ">="
27
+ - !ruby/object:Gem::Version
28
+ hash: 5
29
+ segments:
30
+ - 0
31
+ - 6
32
+ - 1
33
+ version: 0.6.1
34
+ version_requirements: *id001
35
+ name: moneta
36
+ prerelease: false
37
+ - !ruby/object:Gem::Dependency
38
+ type: :runtime
39
+ requirement: &id002 !ruby/object:Gem::Requirement
40
+ none: false
41
+ requirements:
42
+ - - ">="
43
+ - !ruby/object:Gem::Version
44
+ hash: 3
45
+ segments:
46
+ - 0
47
+ version: "0"
48
+ version_requirements: *id002
49
+ name: highline
50
+ prerelease: false
51
+ - !ruby/object:Gem::Dependency
52
+ type: :runtime
53
+ requirement: &id003 !ruby/object:Gem::Requirement
54
+ none: false
55
+ requirements:
56
+ - - ">="
57
+ - !ruby/object:Gem::Version
58
+ hash: 3
59
+ segments:
60
+ - 0
61
+ version: "0"
62
+ version_requirements: *id003
63
+ name: bcrypt-ruby
64
+ prerelease: false
65
+ - !ruby/object:Gem::Dependency
66
+ type: :development
67
+ requirement: &id004 !ruby/object:Gem::Requirement
68
+ none: false
69
+ requirements:
70
+ - - ~>
71
+ - !ruby/object:Gem::Version
72
+ hash: 3
73
+ segments:
74
+ - 2
75
+ - 3
76
+ - 0
77
+ version: 2.3.0
78
+ version_requirements: *id004
79
+ name: rspec
80
+ prerelease: false
81
+ - !ruby/object:Gem::Dependency
82
+ type: :development
83
+ requirement: &id005 !ruby/object:Gem::Requirement
84
+ none: false
85
+ requirements:
86
+ - - ~>
87
+ - !ruby/object:Gem::Version
88
+ hash: 23
89
+ segments:
90
+ - 3
91
+ - 8
92
+ version: "3.8"
93
+ version_requirements: *id005
94
+ name: rdoc
95
+ prerelease: false
96
+ - !ruby/object:Gem::Dependency
97
+ type: :development
98
+ requirement: &id006 !ruby/object:Gem::Requirement
99
+ none: false
100
+ requirements:
101
+ - - ">="
102
+ - !ruby/object:Gem::Version
103
+ hash: 3
104
+ segments:
105
+ - 0
106
+ version: "0"
107
+ version_requirements: *id006
108
+ name: mocha
109
+ prerelease: false
110
+ - !ruby/object:Gem::Dependency
111
+ type: :development
112
+ requirement: &id007 !ruby/object:Gem::Requirement
113
+ none: false
114
+ requirements:
115
+ - - ~>
116
+ - !ruby/object:Gem::Version
117
+ hash: 23
118
+ segments:
119
+ - 1
120
+ - 0
121
+ - 0
122
+ version: 1.0.0
123
+ version_requirements: *id007
124
+ name: bundler
125
+ prerelease: false
126
+ - !ruby/object:Gem::Dependency
127
+ type: :development
128
+ requirement: &id008 !ruby/object:Gem::Requirement
129
+ none: false
130
+ requirements:
131
+ - - ~>
132
+ - !ruby/object:Gem::Version
133
+ hash: 7
134
+ segments:
135
+ - 1
136
+ - 6
137
+ - 4
138
+ version: 1.6.4
139
+ version_requirements: *id008
140
+ name: jeweler
141
+ prerelease: false
142
+ - !ruby/object:Gem::Dependency
143
+ type: :development
144
+ requirement: &id009 !ruby/object:Gem::Requirement
145
+ none: false
146
+ requirements:
147
+ - - ">="
148
+ - !ruby/object:Gem::Version
149
+ hash: 3
150
+ segments:
151
+ - 0
152
+ version: "0"
153
+ version_requirements: *id009
154
+ name: rcov
155
+ prerelease: false
156
+ description: Trocla helps you to generate random passwords and to store them in various formats (plain, MD5, bcrypt) for later retrival.
157
+ email: mh+trocla@immerda.ch
158
+ executables:
159
+ - trocla
160
+ extensions: []
161
+
162
+ extra_rdoc_files:
163
+ - LICENSE.txt
164
+ - README.md
165
+ files:
166
+ - .document
167
+ - .rspec
168
+ - Gemfile
169
+ - Gemfile.lock
170
+ - LICENSE.txt
171
+ - README.md
172
+ - Rakefile
173
+ - bin/trocla
174
+ - lib/VERSION
175
+ - lib/trocla.rb
176
+ - lib/trocla/default_config.yaml
177
+ - lib/trocla/formats.rb
178
+ - lib/trocla/formats/bcrypt.rb
179
+ - lib/trocla/formats/md5crypt.rb
180
+ - lib/trocla/formats/mysql.rb
181
+ - lib/trocla/formats/pgsql.rb
182
+ - lib/trocla/formats/plain.rb
183
+ - lib/trocla/formats/sha256crypt.rb
184
+ - lib/trocla/formats/sha512crypt.rb
185
+ - lib/trocla/util.rb
186
+ - lib/trocla/version.rb
187
+ - spec/data/.keep
188
+ - spec/spec_helper.rb
189
+ - spec/trocla/util_spec.rb
190
+ - spec/trocla_spec.rb
191
+ - trocla.gemspec
192
+ has_rdoc: true
193
+ homepage: https://tech.immerda.ch/2011/12/trocla-get-hashed-passwords-out-of-puppet-manifests/
194
+ licenses:
195
+ - GPLv3
196
+ post_install_message:
197
+ rdoc_options: []
198
+
199
+ require_paths:
200
+ - lib
201
+ required_ruby_version: !ruby/object:Gem::Requirement
202
+ none: false
203
+ requirements:
204
+ - - ">="
205
+ - !ruby/object:Gem::Version
206
+ hash: 3
207
+ segments:
208
+ - 0
209
+ version: "0"
210
+ required_rubygems_version: !ruby/object:Gem::Requirement
211
+ none: false
212
+ requirements:
213
+ - - ">="
214
+ - !ruby/object:Gem::Version
215
+ hash: 3
216
+ segments:
217
+ - 0
218
+ version: "0"
219
+ requirements: []
220
+
221
+ rubyforge_project:
222
+ rubygems_version: 1.6.2
223
+ signing_key:
224
+ specification_version: 3
225
+ summary: Trocla a simple password generator and storage
226
+ test_files: []
227
+