summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authormh <mh@immerda.ch>2011-07-27 18:43:55 +0200
committermh <mh@immerda.ch>2011-07-27 18:43:55 +0200
commit9146a541ff96b92f4f14c6292307b68dc4673097 (patch)
tree3637ab49056565fad3566d78698634c2a6ab7265
initial release of trocla
-rw-r--r--.document4
-rw-r--r--.gitignore48
-rw-r--r--.rspec1
-rw-r--r--Gemfile18
-rw-r--r--Gemfile.lock36
-rw-r--r--LICENSE.txt15
-rw-r--r--README.rdoc19
-rw-r--r--Rakefile53
-rwxr-xr-xbin/trocla117
-rw-r--r--lib/VERSION4
-rw-r--r--lib/trocla.rb91
-rw-r--r--lib/trocla/default_config.yaml7
-rw-r--r--lib/trocla/formats.rb32
-rw-r--r--lib/trocla/formats/md5crypt.rb6
-rw-r--r--lib/trocla/formats/mysql.rb6
-rw-r--r--lib/trocla/formats/pgsql.rb7
-rw-r--r--lib/trocla/formats/plain.rb7
-rw-r--r--lib/trocla/util.rb14
-rw-r--r--lib/trocla/version.rb22
-rw-r--r--spec/data/.keep0
-rw-r--r--spec/spec_helper.rb30
-rw-r--r--spec/trocla/util_spec.rb19
-rw-r--r--spec/trocla_spec.rb127
23 files changed, 683 insertions, 0 deletions
diff --git a/.document b/.document
new file mode 100644
index 0000000..c98d021
--- /dev/null
+++ b/.document
@@ -0,0 +1,4 @@
+lib/**/*.rb
+bin/*
+-
+LICENSE.txt
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..eb8e985
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,48 @@
+# rcov generated
+coverage
+
+# rdoc generated
+rdoc
+
+# yard generated
+doc
+.yardoc
+
+# bundler
+.bundle
+
+# jeweler generated
+pkg
+
+# Have editor/IDE/OS specific files you need to ignore? Consider using a global gitignore:
+#
+# * Create a file at ~/.gitignore
+# * Include files you want ignored
+# * Run: git config --global core.excludesfile ~/.gitignore
+#
+# After doing this, these files will be ignored in all your git projects,
+# saving you from having to 'pollute' every project you touch with them
+#
+# Not sure what to needs to be ignored for particular editors/OSes? Here's some ideas to get you started. (Remember, remove the leading # of the line)
+#
+# For MacOS:
+#
+#.DS_Store
+
+# For TextMate
+#*.tmproj
+#tmtags
+
+# For emacs:
+#*~
+#\#*
+#.\#*
+
+# For vim:
+#*.swp
+
+# For redcar:
+#.redcar
+
+# For rubinius:
+#*.rbc
diff --git a/.rspec b/.rspec
new file mode 100644
index 0000000..4e1e0d2
--- /dev/null
+++ b/.rspec
@@ -0,0 +1 @@
+--color
diff --git a/Gemfile b/Gemfile
new file mode 100644
index 0000000..874ed53
--- /dev/null
+++ b/Gemfile
@@ -0,0 +1,18 @@
+source "http://rubygems.org"
+# Add dependencies required to use your gem here.
+# Example:
+# gem "activesupport", ">= 2.3.5"
+
+gem "moneta", ">=0.6.1"
+gem "highline"
+
+# Add dependencies to develop your gem here.
+# Include everything needed to run rake, tests, features, etc.
+group :development do
+ gem "rspec", "~> 2.3.0"
+ gem "rdoc", "~> 3.8"
+ gem "mocha"
+ gem "bundler", "~> 1.0.0"
+ gem "jeweler", "~> 1.6.4"
+ gem "rcov", ">= 0"
+end
diff --git a/Gemfile.lock b/Gemfile.lock
new file mode 100644
index 0000000..a48edf1
--- /dev/null
+++ b/Gemfile.lock
@@ -0,0 +1,36 @@
+GEM
+ remote: http://rubygems.org/
+ specs:
+ diff-lcs (1.1.2)
+ git (1.2.5)
+ highline (1.6.2)
+ jeweler (1.6.4)
+ bundler (~> 1.0)
+ git (>= 1.2.5)
+ rake
+ mocha (0.9.12)
+ moneta (0.6.1)
+ rake (0.9.2)
+ rcov (0.9.9)
+ rdoc (3.8)
+ rspec (2.3.0)
+ rspec-core (~> 2.3.0)
+ rspec-expectations (~> 2.3.0)
+ rspec-mocks (~> 2.3.0)
+ rspec-core (2.3.1)
+ rspec-expectations (2.3.0)
+ diff-lcs (~> 1.1.2)
+ rspec-mocks (2.3.0)
+
+PLATFORMS
+ ruby
+
+DEPENDENCIES
+ bundler (~> 1.0.0)
+ highline
+ jeweler (~> 1.6.4)
+ mocha
+ moneta (>= 0.6.1)
+ rcov
+ rdoc (~> 3.8)
+ rspec (~> 2.3.0)
diff --git a/LICENSE.txt b/LICENSE.txt
new file mode 100644
index 0000000..5b0754c
--- /dev/null
+++ b/LICENSE.txt
@@ -0,0 +1,15 @@
+Trocla - a simple password generator and storage
+Copyright (C) 2011 Marcel Haerry
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program. If not, see <http://www.gnu.org/licenses/>.
diff --git a/README.rdoc b/README.rdoc
new file mode 100644
index 0000000..e0b319b
--- /dev/null
+++ b/README.rdoc
@@ -0,0 +1,19 @@
+= trocla
+
+Description goes here.
+
+== Contributing to trocla
+
+* Check out the latest master to make sure the feature hasn't been implemented or the bug hasn't been fixed yet
+* Check out the issue tracker to make sure someone already hasn't requested it and/or contributed it
+* Fork the project
+* Start a feature/bugfix branch
+* Commit and push until you are happy with your contribution
+* Make sure to add tests for it. This is important so I don't break it in a future version unintentionally.
+* 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.
+
+== Copyright
+
+Copyright (c) 2011 mh. See LICENSE.txt for
+further details.
+
diff --git a/Rakefile b/Rakefile
new file mode 100644
index 0000000..ed73e37
--- /dev/null
+++ b/Rakefile
@@ -0,0 +1,53 @@
+# encoding: utf-8
+
+require 'rubygems'
+require 'bundler'
+begin
+ Bundler.setup(:default, :development)
+rescue Bundler::BundlerError => e
+ $stderr.puts e.message
+ $stderr.puts "Run `bundle install` to install missing gems"
+ exit e.status_code
+end
+require 'rake'
+
+
+require 'jeweler'
+$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), 'lib'))
+require 'trocla'
+Jeweler::Tasks.new do |gem|
+ # gem is a Gem::Specification... see http://docs.rubygems.org/read/chapter/20 for more options
+ gem.name = "trocla"
+ gem.homepage = "http://www.puzzle.ch"
+ gem.license = "GPLv3"
+ gem.summary = "Trocla a simple password generator and storage"
+ gem.description = "Trocla helps you to generate random passwords and to store them in various formats (plain, MD5, bcrypt) for later retrival."
+ gem.email = "haerry+trocla@puzzle.ch"
+ gem.authors = ["mh"]
+ gem.version = Trocla::VERSION::STRING
+ # dependencies defined in Gemfile
+end
+Jeweler::RubygemsDotOrgTasks.new
+
+require 'rspec/core'
+require 'rspec/core/rake_task'
+RSpec::Core::RakeTask.new(:spec) do |spec|
+ spec.pattern = FileList['spec/**/*_spec.rb']
+end
+
+RSpec::Core::RakeTask.new(:rcov) do |spec|
+ spec.pattern = 'spec/**/*_spec.rb'
+ spec.rcov = true
+end
+
+task :default => :spec
+
+gem 'rdoc'
+require 'rdoc/task'
+RDoc::Task.new do |rdoc|
+ version = Trocla::VERSION::STRING
+ rdoc.rdoc_dir = 'rdoc'
+ rdoc.title = "trocla #{version}"
+ rdoc.rdoc_files.include('README*')
+ rdoc.rdoc_files.include('lib/**/*.rb')
+end
diff --git a/bin/trocla b/bin/trocla
new file mode 100755
index 0000000..824f5ec
--- /dev/null
+++ b/bin/trocla
@@ -0,0 +1,117 @@
+#!/usr/bin/env ruby
+# CLI client for Trocla.
+#
+require 'rubygems'
+require 'trocla'
+require 'optparse'
+require 'yaml'
+
+options = { :config_file => nil, :ask_password => true }
+
+OptionParser.new do |opts|
+ opts.on("--version", "-V", "Version information") do
+ puts Trocla::VERSION::STRING
+ exit
+ end
+
+ opts.on("--config CONFIG", "-c", "Configuration file") do |v|
+ if File.exist?(v)
+ options[:config_file] = v
+ else
+ STDERR.puts "Cannot find config file: #{v}"
+ exit 1
+ end
+ end
+
+ opts.on("--no-random") do
+ options['random'] = false
+ end
+
+ opts.on("--length LENGTH") do |v|
+ options['length'] = v.to_i
+ end
+
+ opts.on("--pwd-from-stdin") do
+ options[:ask_password] = false
+ end
+
+
+end.parse!
+
+def create(options)
+ miss_format unless options[:trocla_format]
+ Trocla.new(options.delete(:config_file)).password(
+ options.delete(:trocla_key),
+ options.delete(:trocla_format),
+ options.merge(YAML.load(options.delete(:other_options).shift.to_s)||{})
+ )
+end
+
+def get(options)
+ miss_format unless options[:trocla_format]
+ Trocla.new(options.delete(:config_file)).get_password(
+ options.delete(:trocla_key),
+ options.delete(:trocla_format)
+ )
+end
+def set(options)
+ miss_format unless options[:trocla_format]
+ if options.delete(:ask_password)
+ require 'highline/import'
+ password = ask("Enter your password: ") { |q| q.echo = "x" }
+ pwd2 = ask("Repeat password: ") { |q| q.echo = "x" }
+ unless password == pwd2
+ STDERR.puts "Passwords did not match, exiting!"
+ exit 1
+ end
+ else
+ password = options.delete(:other_options).shift
+ end
+ Trocla.new(options.delete(:config_file)).set_password(
+ options.delete(:trocla_key),
+ options.delete(:trocla_format),
+ password
+ )
+ ""
+end
+
+def reset(options)
+ miss_format unless options[:trocla_format]
+ Trocla.new(options.delete(:config_file)).reset_password(
+ options.delete(:trocla_key),
+ options.delete(:trocla_format),
+ options.merge(YAML.load(options.delete(:other_options).shift.to_s)||{})
+ )
+end
+
+def delete(options)
+ Trocla.new(options.delete(:config_file)).delete_password(
+ options.delete(:trocla_key),
+ options.delete(:trocla_format)
+ )
+end
+
+def miss_format
+ STDERR.puts "Missing format, exiting..."
+ exit 1
+end
+
+actions=['create','get','set','reset','delete']
+
+if !(ARGV.length < 2) && (action=ARGV.shift) && actions.include?(action)
+ options[:trocla_key] = ARGV.shift
+ options[:trocla_format] = ARGV.shift
+ options[:other_options] = ARGV
+ begin
+ if result = send(action,options)
+ puts result.is_a?(String) ? result : result.inspect
+ end
+ rescue Exception => e
+ STDERR.puts "Action failed with the following message: #{e.message}" unless e.message == 'exit'
+ exit 1
+ end
+else
+ STDERR.puts "Please supply one of the following actions: #{actions.join(', ')}"
+ exit 1
+end
+
diff --git a/lib/VERSION b/lib/VERSION
new file mode 100644
index 0000000..a81c87a
--- /dev/null
+++ b/lib/VERSION
@@ -0,0 +1,4 @@
+major:0
+minor:0
+patch:1
+build: \ No newline at end of file
diff --git a/lib/trocla.rb b/lib/trocla.rb
new file mode 100644
index 0000000..fe7696c
--- /dev/null
+++ b/lib/trocla.rb
@@ -0,0 +1,91 @@
+require 'trocla/version'
+require 'trocla/util'
+require 'trocla/formats'
+
+class Trocla
+
+ def initialize(config_file=nil)
+ if config_file
+ @config_file = File.expand_path(config_file)
+ elsif File.exists?(def_config_file=File.expand_path('~/.troclarc.yaml')) || File.exists?(def_config_file=File.expand_path('/etc/troclarc.yaml'))
+ @config_file = def_config_file
+ end
+ end
+
+ def password(key,format,options={})
+ options = config['options'].merge(options)
+ raise "Format #{format} is not supported! Supported formats: #{Trocla::Formats.all.join(', ')}" unless Trocla::Formats::available?(format)
+
+ unless (password=get_password(key,format)).nil?
+ return password
+ end
+
+ plain_pwd = get_password(key,'plain')
+ if options['random'] && plain_pwd.nil?
+ plain_pwd = Trocla::Util.random_str(options['length'])
+ set_password(key,'plain',plain_pwd) unless format == 'plain'
+ elsif !options['random'] && plain_pwd.nil?
+ raise "Password must be present as plaintext if you don't want a random password"
+ end
+ set_password(key,format,Trocla::Formats[format].format(plain_pwd,options))
+ end
+
+ def get_password(key,format)
+ cache.fetch(key,{})[format]
+ end
+
+ def reset_password(key,format,options={})
+ set_password(key,format,nil)
+ password(key,format,options)
+ end
+
+ def delete_password(key,format=nil)
+ if format.nil?
+ cache.delete(key)
+ else
+ old_val = (h = cache.fetch(key,{})).delete(format)
+ h.empty? ? cache.delete(key) : cache[key] = h
+ old_val
+ end
+ end
+
+ def set_password(key,format,password,delete_plain=false)
+ if ((format=format) == 'plain') && !delete_plain
+ h = (cache[key] = { 'plain' => password })
+ else
+ h = (cache[key] = cache.fetch(key,{}).merge({ format => password }))
+ end
+ h[format]
+ end
+
+ private
+ def cache
+ @cache ||= build_cache
+ end
+
+ def build_cache
+ require 'moneta'
+ require "moneta/adapters/#{config['adapter'].downcase}"
+ lconfig = config
+ Moneta::Builder.new { run eval( "Moneta::Adapters::#{lconfig['adapter']}"), lconfig['adapter_options'] }
+ end
+
+ def config
+ @config ||= read_config
+ end
+
+ def read_config
+ if @config_file.nil?
+ default_config
+ else
+ raise "Configfile #{@config_file} does not exist!" unless File.exists?(@config_file)
+ default_config.merge(YAML.load(File.read(@config_file)))
+ end
+ end
+
+ def default_config
+ require 'yaml'
+ YAML.load(File.read(File.expand_path(File.join(File.dirname(__FILE__),'trocla','default_config.yaml'))))
+ end
+
+end \ No newline at end of file
diff --git a/lib/trocla/default_config.yaml b/lib/trocla/default_config.yaml
new file mode 100644
index 0000000..502e7f1
--- /dev/null
+++ b/lib/trocla/default_config.yaml
@@ -0,0 +1,7 @@
+---
+options:
+ random: true
+ length: 12
+adapter: YAML
+adapter_options:
+ :path: '/tmp/trocla.yaml'
diff --git a/lib/trocla/formats.rb b/lib/trocla/formats.rb
new file mode 100644
index 0000000..3cf31bd
--- /dev/null
+++ b/lib/trocla/formats.rb
@@ -0,0 +1,32 @@
+class Trocla::Formats
+ class << self
+ def [](format)
+ formats[format.downcase]
+ end
+
+ def all
+ Dir[File.expand_path(File.join(File.dirname(__FILE__),'formats','*.rb'))].collect{|f| File.basename(f,'.rb').downcase }
+ end
+
+ def available?(format)
+ all.include?(format.downcase)
+ end
+
+ private
+ def formats
+ @@formats ||= Hash.new do |hash, format|
+ format = format.downcase
+ if File.exists?(path(format))
+ require "trocla/formats/#{format}"
+ hash[format] = (eval "Trocla::Formats::#{format.capitalize}").new
+ else
+ raise "Format #{format} is not supported!"
+ end
+ end
+ end
+
+ def path(format)
+ File.expand_path(File.join(File.dirname(__FILE__),'formats',"#{format}.rb"))
+ end
+ end
+end \ No newline at end of file
diff --git a/lib/trocla/formats/md5crypt.rb b/lib/trocla/formats/md5crypt.rb
new file mode 100644
index 0000000..f52e2a1
--- /dev/null
+++ b/lib/trocla/formats/md5crypt.rb
@@ -0,0 +1,6 @@
+# salted crypt
+class Trocla::Formats::Md5crypt
+ def format(plain_password,options={})
+ plain_password.crypt('$1$' << Trocla::Util.random_str(8) << '$')
+ end
+end \ No newline at end of file
diff --git a/lib/trocla/formats/mysql.rb b/lib/trocla/formats/mysql.rb
new file mode 100644
index 0000000..7fbc3a7
--- /dev/null
+++ b/lib/trocla/formats/mysql.rb
@@ -0,0 +1,6 @@
+class Trocla::Formats::Mysql
+ require 'digest/sha1'
+ def format(plain_password,options={})
+ "*" + Digest::SHA1.hexdigest(Digest::SHA1.digest(plain_password)).upcase
+ end
+end \ No newline at end of file
diff --git a/lib/trocla/formats/pgsql.rb b/lib/trocla/formats/pgsql.rb
new file mode 100644
index 0000000..05c23ce
--- /dev/null
+++ b/lib/trocla/formats/pgsql.rb
@@ -0,0 +1,7 @@
+class Trocla::Formats::Pgsql
+ require 'digest/md5'
+ def format(plain_password,options={})
+ raise "You need pass the username in the options for this format" unless options['username']
+ "md5" + Digest::MD5.hexdigest(plain_password + options['username'])
+ end
+end \ No newline at end of file
diff --git a/lib/trocla/formats/plain.rb b/lib/trocla/formats/plain.rb
new file mode 100644
index 0000000..98797d2
--- /dev/null
+++ b/lib/trocla/formats/plain.rb
@@ -0,0 +1,7 @@
+class Trocla::Formats::Plain
+
+ def format(plain_password,options={})
+ plain_password
+ end
+
+end \ No newline at end of file
diff --git a/lib/trocla/util.rb b/lib/trocla/util.rb
new file mode 100644
index 0000000..468206d
--- /dev/null
+++ b/lib/trocla/util.rb
@@ -0,0 +1,14 @@
+class Trocla
+ class Util
+ class << self
+ def random_str(length=12)
+ (1..length).collect{|a| chars[rand(chars.size)] }.join.to_s
+ end
+
+ private
+ def chars
+ @chars ||= (('a'..'z').to_a + ('A'..'Z').to_a + ('0'..'9').to_a) + "+*%/()@&=?![]{}-_.,;:<>".split(//)
+ end
+ end
+ end
+end \ No newline at end of file
diff --git a/lib/trocla/version.rb b/lib/trocla/version.rb
new file mode 100644
index 0000000..6d9476b
--- /dev/null
+++ b/lib/trocla/version.rb
@@ -0,0 +1,22 @@
+# encoding: utf-8
+class Trocla
+ class VERSION
+ version = {}
+ File.read(File.join(File.dirname(__FILE__), '../', 'VERSION')).each_line do |line|
+ type, value = line.chomp.split(":")
+ next if type =~ /^\s+$/ || value =~ /^\s+$/
+ version[type] = value
+ end
+
+ MAJOR = version['major']
+ MINOR = version['minor']
+ PATCH = version['patch']
+ BUILD = version['build']
+
+ STRING = [MAJOR, MINOR, PATCH, BUILD].compact.join('.')
+
+ def self.version
+ STRING
+ end
+ end
+end
diff --git a/spec/data/.keep b/spec/data/.keep
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/spec/data/.keep
diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb
new file mode 100644
index 0000000..64ce0de
--- /dev/null
+++ b/spec/spec_helper.rb
@@ -0,0 +1,30 @@
+$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
+$LOAD_PATH.unshift(File.dirname(__FILE__))
+require 'rspec'
+require 'mocha'
+require 'trocla'
+
+# Requires supporting files with custom matchers and macros, etc,
+# in ./support/ and its subdirectories.
+Dir["#{File.dirname(__FILE__)}/support/**/*.rb"].each {|f| require f}
+
+RSpec.configure do |config|
+
+end
+
+def default_config
+ @default_config ||= YAML.load(File.read(File.expand_path(base_dir+'/lib/trocla/default_config.yaml')))
+end
+
+def test_config
+ return @config unless @config.nil?
+ @config = default_config
+ yaml_path = File.expand_path(base_dir+'/spec/data/test_config.yaml')
+ File.unlink(yaml_path) if File.exists?(yaml_path)
+ @config['adapter_options'][:path] = yaml_path
+ @config
+end
+
+def base_dir
+ File.dirname(__FILE__)+'/../'
+end
diff --git a/spec/trocla/util_spec.rb b/spec/trocla/util_spec.rb
new file mode 100644
index 0000000..4e02d97
--- /dev/null
+++ b/spec/trocla/util_spec.rb
@@ -0,0 +1,19 @@
+require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
+
+describe "Trocla::Util" do
+ describe "random_str" do
+ it "should be random" do
+ Trocla::Util.random_str.should_not eql(Trocla::Util.random_str)
+ end
+
+ it "should default to length 12" do
+ Trocla::Util.random_str.length.should == 12
+ end
+
+ it "should be possible to change length" do
+ Trocla::Util.random_str(8).length.should == 8
+ Trocla::Util.random_str(32).length.should == 32
+ Trocla::Util.random_str(1).length.should == 1
+ end
+ end
+end \ No newline at end of file
diff --git a/spec/trocla_spec.rb b/spec/trocla_spec.rb
new file mode 100644
index 0000000..c6cb94c
--- /dev/null
+++ b/spec/trocla_spec.rb
@@ -0,0 +1,127 @@
+require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
+
+describe "Trocla" do
+
+ before(:each) do
+ Trocla.any_instance.expects(:read_config).returns(test_config)
+ @trocla = Trocla.new
+ end
+
+ describe "password" do
+ it "should generate random passwords by default" do
+ @trocla.password('random1','plain').should_not eql(@trocla.password('random2','plain'))
+ end
+
+ it "should generate passwords of length #{default_config['options']['length']}" do
+ @trocla.password('random1','plain').length.should eql(default_config['options']['length'])
+ end
+
+ Trocla::Formats.all.each do |format|
+ describe "#{format} password format" do
+ it "should return a password hashed in the #{format} format" do
+ @trocla.password('some_test',format,format_options[format]).should_not be_empty
+ end
+
+ it "should return the same hashed for the #{format} format on multiple invocations" do
+ (round1=@trocla.password('some_test',format,format_options[format])).should_not be_empty
+ @trocla.password('some_test',format,format_options[format]).should eql(round1)
+ end
+
+ it "should also store the plain password by default" do
+ pwd = @trocla.password('some_test','plain')
+ pwd.should_not be_empty
+ pwd.length.should eql(12)
+ end
+ end
+ end
+
+ Trocla::Formats.all.reject{|f| f == 'plain' }.each do |format|
+ it "should raise an exception if not a random password is asked but plain password is not present for format #{format}" do
+ lambda{ @trocla.password('not_random',format, 'random' => false) }.should raise_error
+ end
+ end
+ end
+
+ describe "set_password" do
+ it "should reset hashed passwords on a new plain password" do
+ @trocla.password('reset_test','mysql').should_not be_empty
+ @trocla.get_password('reset_test','mysql').should_not be_nil
+ (old_plain=@trocla.password('reset_test','mysql')).should_not be_empty
+
+ @trocla.set_password('reset_test','plain','foobar').should_not eql(old_plain)
+ @trocla.get_password('reset_test','mysql').should be_nil
+ end
+
+ it "should otherwise only update the hash" do
+ (mysql = @trocla.password('reset_test2','mysql')).should_not be_empty
+ (md5crypt = @trocla.password('reset_test2','md5crypt')).should_not be_empty
+ (plain = @trocla.get_password('reset_test2','plain')).should_not be_empty
+
+ (new_mysql = @trocla.set_password('reset_test2','mysql','foo')).should_not eql(mysql)
+ @trocla.get_password('reset_test2','mysql').should eql(new_mysql)
+ @trocla.get_password('reset_test2','md5crypt').should eql(md5crypt)
+ @trocla.get_password('reset_test2','plain').should eql(plain)
+ end
+ end
+
+ describe "reset_password" do
+ it "should reset a password" do
+ plain1 = @trocla.password('reset_pwd','plain')
+ plain2 = @trocla.reset_password('reset_pwd','plain')
+
+ plain1.should_not eql(plain2)
+ end
+
+ it "should not reset other formats" do
+ (mysql = @trocla.password('reset_pwd2','mysql')).should_not be_empty
+ (md5crypt1 = @trocla.password('reset_pwd2','md5crypt')).should_not be_empty
+
+ (md5crypt2 = @trocla.reset_password('reset_pwd2','md5crypt')).should_not be_empty
+ md5crypt2.should_not eql(md5crypt1)
+
+ @trocla.get_password('reset_pwd2','mysql').should eql(mysql)
+ end
+ end
+
+ describe "delete_password" do
+ it "should delete all passwords if no format is given" do
+ @trocla.password('delete_test1','mysql').should_not be_nil
+ @trocla.get_password('delete_test1','plain').should_not be_nil
+
+ @trocla.delete_password('delete_test1')
+ @trocla.get_password('delete_test1','plain').should be_nil
+ @trocla.get_password('delete_test1','mysql').should be_nil
+ end
+
+ it "should delete only a given format" do
+ @trocla.password('delete_test2','mysql').should_not be_nil
+ @trocla.get_password('delete_test2','plain').should_not be_nil
+
+ @trocla.delete_password('delete_test2','plain')
+ @trocla.get_password('delete_test2','plain').should be_nil
+ @trocla.get_password('delete_test2','mysql').should_not be_nil
+ end
+
+ it "should delete only a given non-plain format" do
+ @trocla.password('delete_test3','mysql').should_not be_nil
+ @trocla.get_password('delete_test3','plain').should_not be_nil
+
+ @trocla.delete_password('delete_test3','mysql')
+ @trocla.get_password('delete_test3','mysql').should be_nil
+ @trocla.get_password('delete_test3','plain').should_not be_nil
+ end
+ end
+
+ describe "VERSION" do
+ it "should return a version" do
+ Trocla::VERSION::STRING.should_not be_empty
+ end
+ end
+
+ def format_options
+ @format_options ||= Hash.new({}).merge({
+ 'pgsql' => { 'username' => 'test' }
+ })
+ end
+
+end