Package: trocla
Version: 0.2.3-1
Severity: normal
Tags: patch  pending

Dear maintainer,

I've prepared an NMU for trocla (versioned as 0.3.0-0.1) and
uploaded it to DELAYED/10. Please feel free to tell me if I
should delay it longer.

I also uploaded it to experimental, given that we're in a freeze, as a
precaution, in case you want to just cherry-pick the two patches to just
fix the RC bugs. But considering that 0.2.3 is pretty broken, I suspect
that wouldn't be a good idea anyways.

Note that this diff includes a pile of changes that had accumulated in
the git repository on salsa and are therefore not only my changes. My
work may be more easily reviewed here:

https://salsa.debian.org/anarcat/trocla/-/commits/debian/experimental/

You can also see the pipeline has been fixed by those commits here:

https://salsa.debian.org/anarcat/trocla/-/pipelines/251950

Regards.
diff -Nru trocla-0.2.3/bin/trocla trocla-0.3.0/bin/trocla
--- trocla-0.2.3/bin/trocla	2016-03-01 15:40:25.000000000 -0500
+++ trocla-0.3.0/bin/trocla	2021-05-04 15:31:47.000000000 -0400
@@ -47,18 +47,20 @@
 end.parse!
 
 def create(options)
-  Trocla.new(options.delete(:config_file)).password(
+  [ 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)||{})
-  )
+  ) , 0 ]
 end
 
 def get(options)
-  Trocla.new(options.delete(:config_file)).get_password(
+  res = Trocla.new(options.delete(:config_file)).get_password(
     options.delete(:trocla_key),
-    options.delete(:trocla_format)
+    options.delete(:trocla_format),
+    options.merge(YAML.load(options.delete(:other_options).shift.to_s)||{})
   )
+  [ res, res.nil? ? 1 : 0 ]
 end
 def set(options)
   if options.delete(:ask_password)
@@ -67,7 +69,7 @@
     pwd2 = ask('Repeat password: ') { |q| q.echo = 'x' }.to_s
     unless password == pwd2
       STDERR.puts 'Passwords did not match, exiting!'
-      exit 1
+      return [ nil, 1 ]
     end
   else
     password = options.delete(:password) || STDIN.read.chomp
@@ -78,29 +80,29 @@
   value = if no_format
     password
   else
-    trocla.formats(format).format(password, options.delete(:other_options).shift.to_s)
+    trocla.formats(format).format(password, (YAML.load(options.delete(:other_options).shift.to_s)||{}))
   end
   trocla.set_password(
     options.delete(:trocla_key),
     format,
     value
   )
-  ''
+  [ '', 0 ]
 end
 
 def reset(options)
-  Trocla.new(options.delete(:config_file)).reset_password(
+  [ 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)||{})
-  )
+  ), 0 ]
 end
 
 def delete(options)
-  Trocla.new(options.delete(:config_file)).delete_password(
+  [ Trocla.new(options.delete(:config_file)).delete_password(
     options.delete(:trocla_key),
     options.delete(:trocla_format)
-  )
+  ), 0 ]
 end
 
 def formats(options)
@@ -125,7 +127,8 @@
     options[:other_options] = ARGV
     check_format(options[:trocla_format]) unless ['delete','formats'].include?(action)
     begin
-      if result = send(action,options)
+      result, excode = send(action,options)
+      if result
         puts result.is_a?(String) ? result : result.inspect
       end
     rescue Exception => e
@@ -136,6 +139,7 @@
       raise e if options[:trace]
       exit 1
     end
+    exit excode.nil? ? 0 : excode
 else
     STDERR.puts "Please supply one of the following actions: #{actions.join(', ')}"
     STDERR.puts "Use #{$0} --help to get a list of options for these actions"
diff -Nru trocla-0.2.3/CHANGELOG.md trocla-0.3.0/CHANGELOG.md
--- trocla-0.2.3/CHANGELOG.md	2016-03-01 15:40:25.000000000 -0500
+++ trocla-0.3.0/CHANGELOG.md	2021-05-04 15:31:47.000000000 -0400
@@ -1,5 +1,14 @@
 # Changelog
 
+## to 0.3.0
+
+* Add open method to be able to immediately close a trocla store after using it - thanks martinpfeiffer
+* Add typesafe charset - thanks hggh
+* Support cost option for bcrypt
+* address concurrency corner cases, when 2 concurrent threads or even processes
+  are currently calculating the same (expensive) format.
+* parse additional options on cli (#39 & #46) - thanks fe80
+
 ## to 0.2.3
 
 1. Add extended CA validity profiles
diff -Nru trocla-0.2.3/debian/changelog trocla-0.3.0/debian/changelog
--- trocla-0.2.3/debian/changelog	2016-03-01 16:05:19.000000000 -0500
+++ trocla-0.3.0/debian/changelog	2021-05-04 15:32:32.000000000 -0400
@@ -1,3 +1,39 @@
+trocla (0.3.0-0.1) experimental; urgency=medium
+
+  [ Antoine Beaupré ]
+  * Non-maintainer upload.
+  * New upstream release (Closes: #974697)
+  * Remove no-pending_for patch: dependency packaged in Debian and added
+    to Build-Depends.
+  * remove fix_version.patch: just ship the version instead of chasing the
+    version number in a patch (!?)
+  * disable the failing test, after discussion with upstream (Closes:
+    #982154)
+  * change section to admin (Closes: #832261)
+  * expand search path for sample config file to fix autopkgtest (Closes:
+    #830240)
+
+  [ Cédric Boutillier ]
+  * Bump debhelper compatibility level to 9
+  * Use https:// in Vcs-* fields
+  * Run wrap-and-sort on packaging files
+  * Use new default gem2deb Rakefile to run the tests
+
+  [ Utkarsh Gupta ]
+  * Add salsa-ci.yml
+
+  [ Debian Janitor ]
+  * Use secure copyright file specification URI.
+  * Use secure URI in debian/watch.
+  * Bump debhelper from deprecated 9 to 12.
+  * Set debhelper-compat version in Build-Depends.
+  * Set upstream metadata fields: Bug-Database, Bug-Submit, Repository,
+    Repository-Browse.
+  * Update Vcs-* headers from URL redirect.
+  * Use canonical URL in Vcs-Git.
+
+ -- Antoine Beaupré <anar...@debian.org>  Tue, 04 May 2021 15:32:32 -0400
+
 trocla (0.2.3-1) unstable; urgency=medium
 
   * Team upload.
diff -Nru trocla-0.2.3/debian/compat trocla-0.3.0/debian/compat
--- trocla-0.2.3/debian/compat	2016-03-01 16:05:19.000000000 -0500
+++ trocla-0.3.0/debian/compat	1969-12-31 19:00:00.000000000 -0500
@@ -1 +0,0 @@
-7
diff -Nru trocla-0.2.3/debian/control trocla-0.3.0/debian/control
--- trocla-0.2.3/debian/control	2016-03-01 16:05:19.000000000 -0500
+++ trocla-0.3.0/debian/control	2021-05-04 15:32:32.000000000 -0400
@@ -1,19 +1,20 @@
 Source: trocla
-Section: ruby
+Section: admin
 Priority: optional
-Maintainer: Debian Ruby Extras Maintainers <pkg-ruby-extras-maintain...@lists.alioth.debian.org>
+Maintainer: Debian Ruby Team <pkg-ruby-extras-maintain...@lists.alioth.debian.org>
 Uploaders: Jonas Genannt <gena...@debian.org>
-Build-Depends: debhelper (>= 7.0.50~),
+Build-Depends: debhelper-compat (= 12),
                gem2deb,
                rake,
                ruby-bcrypt,
                ruby-highline,
+               ruby-mocha,
                ruby-moneta,
                ruby-rspec,
-               ruby-mocha
+               ruby-rspec-pending-for,
 Standards-Version: 3.9.7
-Vcs-Git: git://anonscm.debian.org/pkg-ruby-extras/trocla.git
-Vcs-Browser: https://anonscm.debian.org/cgit/pkg-ruby-extras/trocla.git
+Vcs-Git: https://salsa.debian.org/ruby-team/trocla.git
+Vcs-Browser: https://salsa.debian.org/ruby-team/trocla
 Homepage: https://github.com/duritong/trocla
 Testsuite: autopkgtest-pkg-ruby
 XS-Ruby-Versions: all
diff -Nru trocla-0.2.3/debian/copyright trocla-0.3.0/debian/copyright
--- trocla-0.2.3/debian/copyright	2016-03-01 16:05:19.000000000 -0500
+++ trocla-0.3.0/debian/copyright	2021-05-04 15:32:32.000000000 -0400
@@ -1,4 +1,4 @@
-Format: http://www.debian.org/doc/packaging-manuals/copyright-format/1.0/
+Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/
 Upstream-Name: trocla
 Source: https://github.com/duritong/trocla
 
diff -Nru trocla-0.2.3/debian/patches/830240.patch trocla-0.3.0/debian/patches/830240.patch
--- trocla-0.2.3/debian/patches/830240.patch	1969-12-31 19:00:00.000000000 -0500
+++ trocla-0.3.0/debian/patches/830240.patch	2021-05-04 15:32:32.000000000 -0400
@@ -0,0 +1,35 @@
+Description: load trocla from installed code
+ In autopkgtest, we fail to find the trocla config file because
+ "gem2deb-test-runner will remove lib directory from source tree
+ during tests", according to:
+ .
+ https://wiki.debian.org/Teams/Ruby/Packaging/Tests
+ .
+ The workaround is to find out where we loaded trocla from and use
+ *that* as a base directory.
+Author: Antoine Beaupré <anar...@debian.org>
+Bug-Debian: https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=830240
+Origin: Debian
+Last-Update: 2021-05-04
+Forwarded: https://github.com/duritong/trocla/pull/64
+
+Index: b/spec/spec_helper.rb
+===================================================================
+--- a/spec/spec_helper.rb	2021-05-04 20:40:41.480961893 -0400
++++ b/spec/spec_helper.rb	2021-05-04 20:41:20.300799490 -0400
+@@ -225,7 +225,14 @@ RSpec.shared_examples 'store_validation'
+ end
+ 
+ def default_config
+-  @default_config ||= YAML.load(File.read(File.expand_path(base_dir+'/lib/trocla/default_config.yaml')))
++  config_path = false
++  for p in [
++          File.expand_path(base_dir+'/lib/trocla/default_config.yaml'),
++          File.expand_path(File.dirname($LOADED_FEATURES.grep(/trocla.rb/)[0])+'/trocla/default_config.yaml'),
++        ] do
++    config_path = p if File.exists?(p)
++  end
++  @default_config ||= YAML.load(File.read(config_path))
+ end
+ 
+ def test_config
diff -Nru trocla-0.2.3/debian/patches/fix_version.patch trocla-0.3.0/debian/patches/fix_version.patch
--- trocla-0.2.3/debian/patches/fix_version.patch	2016-03-01 16:05:19.000000000 -0500
+++ trocla-0.3.0/debian/patches/fix_version.patch	1969-12-31 19:00:00.000000000 -0500
@@ -1,27 +0,0 @@
-Description: do not ship VERSION file on top library level
-Author: Jonas Genannt <gena...@debian.org>
-Forwared: not-needed
-
---- a/lib/trocla/version.rb
-+++ b/lib/trocla/version.rb
-@@ -2,16 +2,11 @@
- 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']
-+    MAJOR = 0
-+    MINOR = 2
-+    PATCH = 3
-+    BUILD = ''
-     
-     STRING = [MAJOR, MINOR, PATCH, BUILD].compact.join('.')
-     
diff -Nru trocla-0.2.3/debian/patches/no-pending_for trocla-0.3.0/debian/patches/no-pending_for
--- trocla-0.2.3/debian/patches/no-pending_for	2016-03-01 16:05:19.000000000 -0500
+++ trocla-0.3.0/debian/patches/no-pending_for	1969-12-31 19:00:00.000000000 -0500
@@ -1,65 +0,0 @@
-Index: trocla/spec/trocla/formats/x509_spec.rb
-===================================================================
---- trocla.orig/spec/trocla/formats/x509_spec.rb
-+++ trocla/spec/trocla/formats/x509_spec.rb
-@@ -184,18 +184,14 @@ describe "Trocla::Format::X509" do
-       ca2 = OpenSSL::X509::Certificate.new(ca2_str)
-       expect(ca2.issuer.to_s).to eq(@ca.subject.to_s)
-       expect((Date.parse(ca2.not_after.localtime.to_s) - Date.today).to_i).to eq(365)
--      pending_for(:engine => 'jruby',:reason => 'NameConstraints verification seem to be broken in jRuby: https://github.com/jruby/jruby/issues/3502') do
-         expect(verify(@ca,ca2)).to be true
--      end
- 
-       expect(ca2.extensions.find{|e| e.oid == 'basicConstraints' }.value).to eq('CA:TRUE')
-       ku = ca2.extensions.find{|e| e.oid == 'keyUsage' }.value
-       expect(ku).to match(/Certificate Sign/)
-       expect(ku).to match(/CRL Sign/)
-       nc = ca2.extensions.find{|e| e.oid == 'nameConstraints' }.value
--      pending_for(:engine => 'jruby',:reason => 'NameConstraints verification seem to be broken in jRuby: https://github.com/jruby/jruby/issues/3502') do
-         expect(nc).to match(/Permitted:\n  DNS:example.com\n  DNS:bla.example.net/)
--      end
-       valid_cert_str = @trocla.password('myvalidexamplecert','x509', {
-         'subject'  => '/C=ZZ/O=Trocla Inc./CN=foo.example.com/emailAddress=exam...@example.com',
-         'ca' => 'mycert_with_nc'
-@@ -224,9 +220,7 @@ describe "Trocla::Format::X509" do
-       ca2 = OpenSSL::X509::Certificate.new(ca2_str)
-       expect(ca2.issuer.to_s).to eq(@ca.subject.to_s)
-       expect((Date.parse(ca2.not_after.localtime.to_s) - Date.today).to_i).to eq(365)
--      pending_for(:engine => 'jruby',:reason => 'NameConstraints verification seem to be broken in jRuby: https://github.com/jruby/jruby/issues/3502') do
-         expect(verify(@ca,ca2)).to be true
--      end
- 
-       expect(ca2.extensions.find{|e| e.oid == 'basicConstraints' }.value).to eq('CA:TRUE')
-       ku = ca2.extensions.find{|e| e.oid == 'keyUsage' }.value
-@@ -245,9 +239,7 @@ describe "Trocla::Format::X509" do
-       if %x{openssl version} =~ /1\.0\.[2-9]/
-         expect(verify([@ca,ca2],valid_cert)).to be true
-       else
--        skip_for(:engine => 'ruby',:reason => 'NameConstraints verification is broken on older openssl versions https://rt.openssl.org/Ticket/Display.html?id=3562') do
-           expect(verify([@ca,ca2],valid_cert)).to be true
--        end
-       end
- 
-       false_cert_str = @trocla.password('myfalseexamplecert','x509', {
-@@ -277,9 +269,7 @@ describe "Trocla::Format::X509" do
-       cert2 = OpenSSL::X509::Certificate.new(cert2_str)
-       expect(cert2.issuer.to_s).to eq(ca2.subject.to_s)
-       expect((Date.parse(cert2.not_after.localtime.to_s) - Date.today).to_i).to eq(365)
--      skip_for(:engine => 'jruby',:reason => 'Chained CA validation seems to be broken on jruby atm.') do
-         expect(verify([@ca,ca2],cert2)).to be true
--      end
-     end
- 
-     it 'respects all options' do
-Index: trocla/spec/spec_helper.rb
-===================================================================
---- trocla.orig/spec/spec_helper.rb
-+++ trocla/spec/spec_helper.rb
-@@ -1,7 +1,6 @@
- $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
- $LOAD_PATH.unshift(File.dirname(__FILE__))
- require 'rspec'
--require 'rspec/pending_for'
- require 'yaml'
- require 'trocla'
- 
diff -Nru trocla-0.2.3/debian/patches/series trocla-0.3.0/debian/patches/series
--- trocla-0.2.3/debian/patches/series	2016-03-01 16:05:19.000000000 -0500
+++ trocla-0.3.0/debian/patches/series	2021-05-04 15:32:32.000000000 -0400
@@ -1,2 +1,2 @@
-fix_version.patch
-no-pending_for
+830240.patch
+skip-test-982154-2e8082c.patch
diff -Nru trocla-0.2.3/debian/patches/skip-test-982154-2e8082c.patch trocla-0.3.0/debian/patches/skip-test-982154-2e8082c.patch
--- trocla-0.2.3/debian/patches/skip-test-982154-2e8082c.patch	1969-12-31 19:00:00.000000000 -0500
+++ trocla-0.3.0/debian/patches/skip-test-982154-2e8082c.patch	2021-05-04 15:32:32.000000000 -0400
@@ -0,0 +1,38 @@
+Description: OpenSSL 1.1.1h changed the way self-signed certs work
+ It is not clear yet why, but this started failing in
+ bullseye. Upstream recommends skipping the test until the figure out
+ the problem.
+Origin: upstream
+Bug: https://github.com/duritong/trocla/issues/63
+Bug-Debian: https://bugs.debian.org/982154
+Last-Update: 2021-05-04
+
+From 2e8082c994a245cc27c8ed1b60d76adcb4f5a679 Mon Sep 17 00:00:00 2001
+From: mh <m...@immerda.ch>
+Date: Wed, 5 May 2021 00:18:24 +0200
+Subject: [PATCH] fix #63 - skip self-signed cert verification until openssl
+ behavior change has been clarified
+
+---
+ spec/trocla/formats/x509_spec.rb | 8 +++++++-
+ 1 file changed, 7 insertions(+), 1 deletion(-)
+
+diff --git a/spec/trocla/formats/x509_spec.rb b/spec/trocla/formats/x509_spec.rb
+index 299f807..a3309d4 100644
+--- a/spec/trocla/formats/x509_spec.rb
++++ b/spec/trocla/formats/x509_spec.rb
+@@ -44,7 +44,13 @@ def verify(ca,cert)
+       expect(cert.public_key.n.num_bytes * 8).to eq(4096)
+       expect((Date.parse(cert.not_after.localtime.to_s) - Date.today).to_i).to eq(365)
+       # it's a self signed cert and NOT a CA
+-      expect(verify(cert,cert)).to be false
++      if Gem::Version.new(%x{openssl version}.split(' ')[1]) > Gem::Version.new('1.1.1g')
++        skip_for(:engine => 'ruby',:reason => 'Requires clarification in behavior change on openssl side: https://github.com/openssl/openssl/issues/15146') do
++          expect(verify(cert,cert)).to be false
++        end
++      else
++        expect(verify(cert,cert)).to be false
++      end
+ 
+       v = cert.extensions.find{|e| e.oid == 'basicConstraints' }.value
+       expect(v).to eq('CA:FALSE')
diff -Nru trocla-0.2.3/debian/ruby-tests.rake trocla-0.3.0/debian/ruby-tests.rake
--- trocla-0.2.3/debian/ruby-tests.rake	2016-03-01 16:05:19.000000000 -0500
+++ trocla-0.3.0/debian/ruby-tests.rake	2021-05-04 15:32:32.000000000 -0400
@@ -1,7 +1,5 @@
-require 'rspec/core/rake_task'
+require 'gem2deb/rake/spectask'
 
-RSpec::Core::RakeTask.new(:spec) do |spec|
+Gem2Deb::Rake::RSpecTask.new do |spec|
   spec.pattern = './spec/**/*_spec.rb'
 end
-
-task :default => :spec
diff -Nru trocla-0.2.3/debian/rules trocla-0.3.0/debian/rules
--- trocla-0.2.3/debian/rules	2016-03-01 16:05:19.000000000 -0500
+++ trocla-0.3.0/debian/rules	2021-05-04 15:32:32.000000000 -0400
@@ -16,7 +16,3 @@
 
 %:
 	dh $@ --buildsystem=ruby --with ruby
-
-override_dh_auto_install:
-	dh_auto_install
-	rm debian/trocla/usr/lib/ruby/vendor_ruby/VERSION
diff -Nru trocla-0.2.3/debian/upstream/metadata trocla-0.3.0/debian/upstream/metadata
--- trocla-0.2.3/debian/upstream/metadata	1969-12-31 19:00:00.000000000 -0500
+++ trocla-0.3.0/debian/upstream/metadata	2021-05-04 15:32:32.000000000 -0400
@@ -0,0 +1,4 @@
+Bug-Database: https://github.com/duritong/trocla/issues
+Bug-Submit: https://github.com/duritong/trocla/issues/new
+Repository: https://github.com/duritong/trocla.git
+Repository-Browse: https://github.com/duritong/trocla
diff -Nru trocla-0.2.3/debian/watch trocla-0.3.0/debian/watch
--- trocla-0.2.3/debian/watch	2016-03-01 16:05:19.000000000 -0500
+++ trocla-0.3.0/debian/watch	2021-05-04 15:32:32.000000000 -0400
@@ -1,2 +1,2 @@
 version=3
-http://pkg-ruby-extras.alioth.debian.org/cgi-bin/gemwatch/trocla .*/trocla-(.*).tar.gz
+https://pkg-ruby-extras.alioth.debian.org/cgi-bin/gemwatch/trocla .*/trocla-(.*).tar.gz
diff -Nru trocla-0.2.3/ext/redhat/rubygem-trocla.spec trocla-0.3.0/ext/redhat/rubygem-trocla.spec
--- trocla-0.2.3/ext/redhat/rubygem-trocla.spec	2016-03-01 15:40:25.000000000 -0500
+++ trocla-0.3.0/ext/redhat/rubygem-trocla.spec	2021-05-04 15:31:47.000000000 -0400
@@ -2,7 +2,7 @@
 %global gem_name trocla
 
 Name: rubygem-%{gem_name}
-Version: 0.2.2
+Version: 0.3.0
 Release: 1%{?dist}
 Summary: Trocla a simple password generator and storage
 Group: Development/Languages
@@ -74,9 +74,11 @@
 
 cat <<EOF > %{buildroot}/%{_sysconfdir}/%{gem_name}rc.yaml
 ---
-adapter: :YAML
-adapter_options:
-      :file: '%{_sharedstatedir}/%{gem_name}/%{gem_name}_data.yaml'
+store: :moneta
+store_options:
+  adapter: :YAML
+  adapter_options:
+    :file: '%{_sharedstatedir}/%{gem_name}/%{gem_name}_data.yaml'
 EOF
 
 # Run the test suite
diff -Nru trocla-0.2.3/Gemfile trocla-0.3.0/Gemfile
--- trocla-0.2.3/Gemfile	2016-03-01 15:40:25.000000000 -0500
+++ trocla-0.3.0/Gemfile	2021-05-04 15:31:47.000000000 -0400
@@ -3,12 +3,22 @@
 # Example:
 #   gem "activesupport", ">= 2.3.5"
 
+if RUBY_VERSION.to_f <= 2.2
+  gem 'rack', '< 2.0'
+end
+
+if RUBY_VERSION.to_f < 2.1
+  gem 'nokogiri', '< 1.7'
+end
+
 if RUBY_VERSION.to_f > 1.8
   gem "moneta"
   gem "highline"
 else
   gem "moneta", "~> 0.7.20"
   gem "highline", "~> 1.6.2"
+  gem 'rake', '< 11'
+  gem 'git', '< 1.3'
 end
 
 if defined?(RUBY_ENGINE) && (RUBY_ENGINE == 'jruby')
@@ -22,7 +32,14 @@
   if RUBY_VERSION.to_f > 1.8
     gem "rspec"
     gem "rdoc"
-    gem "jeweler"
+    if RUBY_VERSION.to_f < 2.2
+      gem 'jeweler', '< 2.2'
+    else
+      gem "jeweler"
+    end
+    if RUBY_VERSION.to_f < 2.0
+      gem 'public_suffix', '~> 1.4.6'
+    end
   else
     gem "rspec", "~> 2.4"
     gem "rdoc", "~> 3.8"
diff -Nru trocla-0.2.3/lib/trocla/formats/bcrypt.rb trocla-0.3.0/lib/trocla/formats/bcrypt.rb
--- trocla-0.2.3/lib/trocla/formats/bcrypt.rb	2016-03-01 15:40:25.000000000 -0500
+++ trocla-0.3.0/lib/trocla/formats/bcrypt.rb	2021-05-04 15:31:47.000000000 -0400
@@ -1,6 +1,7 @@
 class Trocla::Formats::Bcrypt < Trocla::Formats::Base
+  expensive true
   require 'bcrypt'
   def format(plain_password,options={})
-    BCrypt::Password.create(plain_password).to_s
+    BCrypt::Password.create(plain_password, :cost => options['cost']||BCrypt::Engine.cost).to_s
   end
 end
diff -Nru trocla-0.2.3/lib/trocla/formats/x509.rb trocla-0.3.0/lib/trocla/formats/x509.rb
--- trocla-0.2.3/lib/trocla/formats/x509.rb	2016-03-01 15:40:25.000000000 -0500
+++ trocla-0.3.0/lib/trocla/formats/x509.rb	2021-05-04 15:31:47.000000000 -0400
@@ -1,5 +1,6 @@
 require 'openssl'
 class Trocla::Formats::X509 < Trocla::Formats::Base
+  expensive true
   def format(plain_password,options={})
 
     if plain_password.match(/-----BEGIN RSA PRIVATE KEY-----.*-----END RSA PRIVATE KEY-----.*-----BEGIN CERTIFICATE-----.*-----END CERTIFICATE-----/m)
diff -Nru trocla-0.2.3/lib/trocla/formats.rb trocla-0.3.0/lib/trocla/formats.rb
--- trocla-0.2.3/lib/trocla/formats.rb	2016-03-01 15:40:25.000000000 -0500
+++ trocla-0.3.0/lib/trocla/formats.rb	2021-05-04 15:31:47.000000000 -0400
@@ -8,6 +8,17 @@
     def render(output,render_options={})
       output
     end
+    def expensive?
+      self.class.expensive?
+    end
+    class << self
+      def expensive(is_expensive)
+        @expensive = is_expensive
+      end
+      def expensive?
+        @expensive == true
+      end
+    end
   end
 
   class << self
diff -Nru trocla-0.2.3/lib/trocla/store.rb trocla-0.3.0/lib/trocla/store.rb
--- trocla-0.2.3/lib/trocla/store.rb	2016-03-01 15:40:25.000000000 -0500
+++ trocla-0.3.0/lib/trocla/store.rb	2021-05-04 15:31:47.000000000 -0400
@@ -6,6 +6,12 @@
     @trocla = trocla
   end
 
+  # closes the store
+  # when called do whatever "closes" your
+  # store, e.g. close database connections.
+  def close
+  end
+
   # should return value for key & format
   # returns nil if nothing or a nil value
   # was found.
diff -Nru trocla-0.2.3/lib/trocla/stores/moneta.rb trocla-0.3.0/lib/trocla/stores/moneta.rb
--- trocla-0.2.3/lib/trocla/stores/moneta.rb	2016-03-01 15:40:25.000000000 -0500
+++ trocla-0.3.0/lib/trocla/stores/moneta.rb	2021-05-04 15:31:47.000000000 -0400
@@ -10,6 +10,10 @@
     @moneta = Moneta.new(store_config['adapter'],adapter_options)
   end
 
+  def close
+    moneta.close
+  end
+
   def get(key,format)
     moneta.fetch(key, {})[format]
   end
diff -Nru trocla-0.2.3/lib/trocla/util.rb trocla-0.3.0/lib/trocla/util.rb
--- trocla-0.2.3/lib/trocla/util.rb	2016-03-01 15:40:25.000000000 -0500
+++ trocla-0.3.0/lib/trocla/util.rb	2021-05-04 15:31:47.000000000 -0400
@@ -24,6 +24,7 @@
             'numeric'      => numeric,
             'hexadecimal'  => hexadecimal,
             'consolesafe'  => consolesafe,
+            'typesafe'     => typesafe,
           }
           h.each { |k, v| h[k] = v.uniq }
         end
@@ -50,6 +51,9 @@
       def numeric
         @numeric ||= ('0'..'9').to_a
       end
+      def typesafe
+        @typesafe ||= ('a'..'x').to_a - ['i'] - ['l'] + ('A'..'X').to_a - ['I'] - ['L'] + ('1'..'9').to_a
+      end
       def special_chars
         @special_chars ||= "*()&![]{}-".split(//)
       end
diff -Nru trocla-0.2.3/lib/trocla.rb trocla-0.3.0/lib/trocla.rb
--- trocla-0.2.3/lib/trocla.rb	2016-03-01 15:40:25.000000000 -0500
+++ trocla-0.3.0/lib/trocla.rb	2021-05-04 15:31:47.000000000 -0400
@@ -13,6 +13,17 @@
     end
   end
 
+  def self.open(config_file=nil)
+    trocla = Trocla.new(config_file)
+
+    if block_given?
+      yield trocla
+      trocla.close
+    else
+      trocla
+    end
+  end
+
   def password(key,format,options={})
     # respect a default profile, but let the
     # profiles win over the default options
@@ -35,10 +46,15 @@
     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,
-      self.formats(format).format(plain_pwd,options),
-      options)
+    pwd = self.formats(format).format(plain_pwd,options)
+    # it's possible that meanwhile another thread/process was faster in
+    # formating the password. But we want todo that second lookup
+    # only for expensive formats
+    if self.formats(format).expensive?
+      get_password(key,format,options) || set_password(key, format, pwd, options)
+    else
+      set_password(key, format, pwd, options)
+    end
   end
 
   def get_password(key, format, options={})
@@ -78,6 +94,10 @@
     @config ||= read_config
   end
 
+  def close
+    store.close
+  end
+
   private
   def store
     @store ||= build_store
diff -Nru trocla-0.2.3/lib/VERSION trocla-0.3.0/lib/VERSION
--- trocla-0.2.3/lib/VERSION	2016-03-01 15:40:25.000000000 -0500
+++ trocla-0.3.0/lib/VERSION	2021-05-04 15:31:47.000000000 -0400
@@ -1,4 +1,4 @@
 major:0
-minor:2
-patch:3
+minor:3
+patch:0
 build:
diff -Nru trocla-0.2.3/metadata.yml trocla-0.3.0/metadata.yml
--- trocla-0.2.3/metadata.yml	2016-03-01 15:40:25.000000000 -0500
+++ trocla-0.3.0/metadata.yml	1969-12-31 19:00:00.000000000 -0500
@@ -1,188 +0,0 @@
---- !ruby/object:Gem::Specification
-name: trocla
-version: !ruby/object:Gem::Version
-  version: 0.2.3
-platform: ruby
-authors:
-- mh
-autorequire: 
-bindir: bin
-cert_chain: []
-date: 2016-02-15 00:00:00.000000000 Z
-dependencies:
-- !ruby/object:Gem::Dependency
-  name: moneta
-  requirement: !ruby/object:Gem::Requirement
-    requirements:
-    - - ">="
-      - !ruby/object:Gem::Version
-        version: '0'
-  type: :runtime
-  prerelease: false
-  version_requirements: !ruby/object:Gem::Requirement
-    requirements:
-    - - ">="
-      - !ruby/object:Gem::Version
-        version: '0'
-- !ruby/object:Gem::Dependency
-  name: highline
-  requirement: !ruby/object:Gem::Requirement
-    requirements:
-    - - ">="
-      - !ruby/object:Gem::Version
-        version: '0'
-  type: :runtime
-  prerelease: false
-  version_requirements: !ruby/object:Gem::Requirement
-    requirements:
-    - - ">="
-      - !ruby/object:Gem::Version
-        version: '0'
-- !ruby/object:Gem::Dependency
-  name: bcrypt
-  requirement: !ruby/object:Gem::Requirement
-    requirements:
-    - - ">="
-      - !ruby/object:Gem::Version
-        version: '0'
-  type: :runtime
-  prerelease: false
-  version_requirements: !ruby/object:Gem::Requirement
-    requirements:
-    - - ">="
-      - !ruby/object:Gem::Version
-        version: '0'
-- !ruby/object:Gem::Dependency
-  name: rspec
-  requirement: !ruby/object:Gem::Requirement
-    requirements:
-    - - ">="
-      - !ruby/object:Gem::Version
-        version: '0'
-  type: :development
-  prerelease: false
-  version_requirements: !ruby/object:Gem::Requirement
-    requirements:
-    - - ">="
-      - !ruby/object:Gem::Version
-        version: '0'
-- !ruby/object:Gem::Dependency
-  name: rdoc
-  requirement: !ruby/object:Gem::Requirement
-    requirements:
-    - - ">="
-      - !ruby/object:Gem::Version
-        version: '0'
-  type: :development
-  prerelease: false
-  version_requirements: !ruby/object:Gem::Requirement
-    requirements:
-    - - ">="
-      - !ruby/object:Gem::Version
-        version: '0'
-- !ruby/object:Gem::Dependency
-  name: jeweler
-  requirement: !ruby/object:Gem::Requirement
-    requirements:
-    - - ">="
-      - !ruby/object:Gem::Version
-        version: '0'
-  type: :development
-  prerelease: false
-  version_requirements: !ruby/object:Gem::Requirement
-    requirements:
-    - - ">="
-      - !ruby/object:Gem::Version
-        version: '0'
-- !ruby/object:Gem::Dependency
-  name: rspec-pending_for
-  requirement: !ruby/object:Gem::Requirement
-    requirements:
-    - - ">="
-      - !ruby/object:Gem::Version
-        version: '0'
-  type: :development
-  prerelease: false
-  version_requirements: !ruby/object:Gem::Requirement
-    requirements:
-    - - ">="
-      - !ruby/object:Gem::Version
-        version: '0'
-description: Trocla helps you to generate random passwords and to store them in various
-  formats (plain, MD5, bcrypt) for later retrival.
-email: mh+tro...@immerda.ch
-executables:
-- trocla
-extensions: []
-extra_rdoc_files:
-- LICENSE.txt
-- README.md
-files:
-- ".document"
-- ".rspec"
-- ".travis.yml"
-- CHANGELOG.md
-- Gemfile
-- LICENSE.txt
-- README.md
-- Rakefile
-- bin/trocla
-- ext/redhat/rubygem-trocla.spec
-- lib/VERSION
-- lib/trocla.rb
-- lib/trocla/default_config.yaml
-- lib/trocla/encryptions.rb
-- lib/trocla/encryptions/none.rb
-- lib/trocla/encryptions/ssl.rb
-- lib/trocla/formats.rb
-- lib/trocla/formats/bcrypt.rb
-- lib/trocla/formats/md5crypt.rb
-- lib/trocla/formats/mysql.rb
-- lib/trocla/formats/pgsql.rb
-- lib/trocla/formats/plain.rb
-- lib/trocla/formats/sha1.rb
-- lib/trocla/formats/sha256crypt.rb
-- lib/trocla/formats/sha512crypt.rb
-- lib/trocla/formats/ssha.rb
-- lib/trocla/formats/x509.rb
-- lib/trocla/store.rb
-- lib/trocla/stores.rb
-- lib/trocla/stores/memory.rb
-- lib/trocla/stores/moneta.rb
-- lib/trocla/util.rb
-- lib/trocla/version.rb
-- spec/data/.keep
-- spec/spec_helper.rb
-- spec/trocla/encryptions/none_spec.rb
-- spec/trocla/encryptions/ssl_spec.rb
-- spec/trocla/formats/x509_spec.rb
-- spec/trocla/store/memory_spec.rb
-- spec/trocla/store/moneta_spec.rb
-- spec/trocla/util_spec.rb
-- spec/trocla_spec.rb
-- trocla.gemspec
-homepage: https://tech.immerda.ch/2011/12/trocla-get-hashed-passwords-out-of-puppet-manifests/
-licenses:
-- GPLv3
-metadata: {}
-post_install_message: 
-rdoc_options: []
-require_paths:
-- lib
-required_ruby_version: !ruby/object:Gem::Requirement
-  requirements:
-  - - ">="
-    - !ruby/object:Gem::Version
-      version: '0'
-required_rubygems_version: !ruby/object:Gem::Requirement
-  requirements:
-  - - ">="
-    - !ruby/object:Gem::Version
-      version: '0'
-requirements: []
-rubyforge_project: 
-rubygems_version: 2.2.2
-signing_key: 
-specification_version: 4
-summary: Trocla a simple password generator and storage
-test_files: []
diff -Nru trocla-0.2.3/README.md trocla-0.3.0/README.md
--- trocla-0.2.3/README.md	2016-03-01 15:40:25.000000000 -0500
+++ trocla-0.3.0/README.md	2021-05-04 15:31:47.000000000 -0400
@@ -168,6 +168,11 @@
 Password hashes for PostgreSQL servers. Requires the option `username` to be set
 to the username to which the password will be assigned.
 
+### bcrypt
+
+You are able to tune the [cost factor of bcrypt](https://github.com/codahale/bcrypt-ruby#cost-factors) by passing the option `cost`.
+Note: ruby bcrypt does not support a [cost > 31](https://github.com/codahale/bcrypt-ruby/blob/master/lib/bcrypt/password.rb#L45).
+
 ### x509
 
 This format takes a set of additional options. Required are:
diff -Nru trocla-0.2.3/spec/spec_helper.rb trocla-0.3.0/spec/spec_helper.rb
--- trocla-0.2.3/spec/spec_helper.rb	2016-03-01 15:40:25.000000000 -0500
+++ trocla-0.3.0/spec/spec_helper.rb	2021-05-04 15:31:47.000000000 -0400
@@ -282,3 +282,9 @@
 def remove_yaml_store
   File.unlink(trocla_yaml_file)
 end
+class Trocla::Formats::Sleep < Trocla::Formats::Base
+  def format(plain_password,options={})
+    sleep options['sleep'] ||= 0
+    (options['sleep'] + 1 ).times.collect{ plain_password }.join(' ')
+  end
+end
diff -Nru trocla-0.2.3/spec/trocla/formats/x509_spec.rb trocla-0.3.0/spec/trocla/formats/x509_spec.rb
--- trocla-0.2.3/spec/trocla/formats/x509_spec.rb	2016-03-01 15:40:25.000000000 -0500
+++ trocla-0.3.0/spec/trocla/formats/x509_spec.rb	2021-05-04 15:31:47.000000000 -0400
@@ -242,12 +242,12 @@
       expect(valid_cert.issuer.to_s).to eq(ca2.subject.to_s)
       expect((Date.parse(valid_cert.not_after.localtime.to_s) - Date.today).to_i).to eq(365)
       # workaround broken openssl
-      if %x{openssl version} =~ /1\.0\.[2-9]/
-        expect(verify([@ca,ca2],valid_cert)).to be true
-      else
+      if Gem::Version.new(%x{openssl version}.split(' ')[1]) < Gem::Version.new('1.0.2')
         skip_for(:engine => 'ruby',:reason => 'NameConstraints verification is broken on older openssl versions https://rt.openssl.org/Ticket/Display.html?id=3562') do
           expect(verify([@ca,ca2],valid_cert)).to be true
         end
+      else
+        expect(verify([@ca,ca2],valid_cert)).to be true
       end
 
       false_cert_str = @trocla.password('myfalseexamplecert','x509', {
@@ -311,7 +311,9 @@
       # https://stackoverflow.com/questions/13747212/determine-key-size-from-public-key-pem-format
       expect(cert.public_key.n.num_bytes * 8).to eq(2048)
       expect(verify(@ca,cert)).to be true
-      expect(cert.extensions.find{|e| e.oid == 'subjectAltName' }.value).to eq('DNS:www.test, DNS:test, DNS:test1, DNS:test2, DNS:test3')
+      skip_for(:engine => 'jruby',:reason => 'subjectAltName represenation is broken in jruby-openssl -> https://github.com/jruby/jruby-openssl/pull/123') do
+        expect(cert.extensions.find{|e| e.oid == 'subjectAltName' }.value).to eq('DNS:www.test, DNS:test, DNS:test1, DNS:test2, DNS:test3')
+      end
 
       expect(cert.extensions.find{|e| e.oid == 'basicConstraints' }.value).to eq('CA:FALSE')
       ku = cert.extensions.find{|e| e.oid == 'keyUsage' }.value
diff -Nru trocla-0.2.3/spec/trocla/util_spec.rb trocla-0.3.0/spec/trocla/util_spec.rb
--- trocla-0.2.3/spec/trocla/util_spec.rb	2016-03-01 15:40:25.000000000 -0500
+++ trocla-0.3.0/spec/trocla/util_spec.rb	2021-05-04 15:31:47.000000000 -0400
@@ -36,6 +36,14 @@
     end
   end
 
+  describe :typesafe_generator do
+    10.times.each do |i|
+      it "creates random typesafe password #{i}" do
+        expect(Trocla::Util.random_str(12, 'typesafe')).to match(/^[1-9a-hj-km-xA-HJ-KM-X]{12}$/)
+      end
+    end
+  end
+
   describe :salt do
     10.times.each do |i|
       it "contains only characters and numbers #{i}" do
diff -Nru trocla-0.2.3/spec/trocla_spec.rb trocla-0.3.0/spec/trocla_spec.rb
--- trocla-0.2.3/spec/trocla_spec.rb	2016-03-01 15:40:25.000000000 -0500
+++ trocla-0.3.0/spec/trocla_spec.rb	2021-05-04 15:31:47.000000000 -0400
@@ -4,144 +4,231 @@
 
   before(:each) do
     expect_any_instance_of(Trocla).to receive(:read_config).and_return(test_config)
-    @trocla = Trocla.new
   end
-
-  describe "password" do
-    it "generates random passwords by default" do
-      expect(@trocla.password('random1','plain')).not_to eq(@trocla.password('random2','plain'))
+  context 'in normal usage with' do
+    before(:each) do
+      @trocla = Trocla.new
+      @trocla.password('init','plain')
     end
 
-    it "generates passwords of length #{default_config['options']['length']}" do
-      expect(@trocla.password('random1','plain').length).to eq(default_config['options']['length'])
-    end
+    describe "password" do
+      it "generates random passwords by default" do
+        expect(@trocla.password('random1','plain')).not_to eq(@trocla.password('random2','plain'))
+      end
+
+      it "generates passwords of length #{default_config['options']['length']}" do
+        expect(@trocla.password('random1','plain').length).to eq(default_config['options']['length'])
+      end
+
+      Trocla::Formats.all.each do |format|
+        describe "#{format} password format" do
+          it "retursn a password hashed in the #{format} format" do
+            expect(@trocla.password('some_test',format,format_options[format])).not_to be_empty
+          end
 
-    Trocla::Formats.all.each do |format|
-      describe "#{format} password format" do
-        it "retursn a password hashed in the #{format} format" do
-          expect(@trocla.password('some_test',format,format_options[format])).not_to be_empty
+          it "returns the same hashed for the #{format} format on multiple invocations" do
+            expect(round1=@trocla.password('some_test',format,format_options[format])).not_to be_empty
+            expect(@trocla.password('some_test',format,format_options[format])).to eq(round1)
+          end
+
+          it "also stores the plain password by default" do
+            pwd = @trocla.password('some_test','plain')
+            expect(pwd).not_to be_empty
+            expect(pwd.length).to eq(16)
+          end
+        end
+      end
+
+      Trocla::Formats.all.reject{|f| f == 'plain' }.each do |format|
+        it "raises an exception if not a random password is asked but plain password is not present for format #{format}" do
+          expect{@trocla.password('not_random',format, 'random' => false)}.to raise_error(/Password must be present as plaintext/)
         end
+      end
 
-        it "returns the same hashed for the #{format} format on multiple invocations" do
-          expect(round1=@trocla.password('some_test',format,format_options[format])).not_to be_empty
-          expect(@trocla.password('some_test',format,format_options[format])).to eq(round1)
+      describe 'with profiles' do
+        it 'raises an exception on unknown profile' do
+          expect{@trocla.password('no profile known','plain',
+            'profiles' => 'unknown_profile') }.to raise_error(/No such profile unknown_profile defined/)
         end
 
-        it "also stores the plain password by default" do
-          pwd = @trocla.password('some_test','plain')
+        it 'takes a profile and merge its options' do
+          pwd = @trocla.password('some_test','plain', 'profiles' => 'rootpw')
+          expect(pwd).not_to be_empty
+          expect(pwd.length).to eq(32)
+          expect(pwd).to_not match(/[={}\[\]\?%\*()&!]+/)
+        end
+
+        it 'is possible to combine profiles but first profile wins' do
+          pwd = @trocla.password('some_test1','plain', 'profiles' => ['rootpw','login'])
+          expect(pwd).not_to be_empty
+          expect(pwd.length).to eq(32)
+          expect(pwd).not_to match(/[={}\[\]\?%\*()&!]+/)
+        end
+        it 'is possible to combine profiles but first profile wins 2' do
+          pwd = @trocla.password('some_test2','plain', 'profiles' => ['login','mysql'])
           expect(pwd).not_to be_empty
           expect(pwd.length).to eq(16)
+          expect(pwd).not_to match(/[={}\[\]\?%\*()&!]+/)
+        end
+        it 'is possible to combine profiles but first profile wins 3' do
+          pwd = @trocla.password('some_test3','plain', 'profiles' => ['mysql','login'])
+          expect(pwd).not_to be_empty
+          expect(pwd.length).to eq(32)
+          expect(pwd).to match(/[+%\/@=\?_.,:]+/)
         end
       end
     end
 
-    Trocla::Formats.all.reject{|f| f == 'plain' }.each do |format|
-      it "raises an exception if not a random password is asked but plain password is not present for format #{format}" do
-        expect{@trocla.password('not_random',format, 'random' => false)}.to raise_error(/Password must be present as plaintext/)
-      end
-    end
+    describe "set_password" do
+      it "resets hashed passwords on a new plain password" do
+        expect(@trocla.password('set_test','mysql')).not_to be_empty
+        expect(@trocla.get_password('set_test','mysql')).not_to be_nil
+        expect(old_plain=@trocla.password('set_test','mysql')).not_to be_empty
 
-    describe 'with profiles' do
-      it 'raises an exception on unknown profile' do
-        expect{@trocla.password('no profile known','plain',
-          'profiles' => 'unknown_profile') }.to raise_error(/No such profile unknown_profile defined/)
-      end
-
-      it 'takes a profile and merge its options' do
-        pwd = @trocla.password('some_test','plain', 'profiles' => 'rootpw')
-        expect(pwd).not_to be_empty
-        expect(pwd.length).to eq(32)
-        expect(pwd).to_not match(/[={}\[\]\?%\*()&!]+/)
-      end
-
-      it 'is possible to combine profiles but first profile wins' do
-        pwd = @trocla.password('some_test','plain', 'profiles' => ['rootpw','login'])
-        expect(pwd).not_to be_empty
-        expect(pwd.length).to eq(32)
-        expect(pwd).not_to match(/[={}\[\]\?%\*()&!]+/)
-      end
-      it 'is possible to combine profiles but first profile wins 2' do
-        pwd = @trocla.password('some_test','plain', 'profiles' => ['login','mysql'])
-        expect(pwd).not_to be_empty
-        expect(pwd.length).to eq(16)
-        expect(pwd).not_to match(/[={}\[\]\?%\*()&!]+/)
-      end
-      it 'is possible to combine profiles but first profile wins 3' do
-        pwd = @trocla.password('some_test','plain', 'profiles' => ['mysql','login'])
-        expect(pwd).not_to be_empty
-        expect(pwd.length).to eq(32)
-        expect(pwd).to match(/[+%\/@=\?_.,:]+/)
+        expect(@trocla.set_password('set_test','plain','foobar')).not_to eq(old_plain)
+        expect(@trocla.get_password('set_test','mysql')).to be_nil
       end
-    end
-  end
 
-  describe "set_password" do
-    it "resets hashed passwords on a new plain password" do
-      expect(@trocla.password('set_test','mysql')).not_to be_empty
-      expect(@trocla.get_password('set_test','mysql')).not_to be_nil
-      expect(old_plain=@trocla.password('set_test','mysql')).not_to be_empty
-
-      expect(@trocla.set_password('set_test','plain','foobar')).not_to eq(old_plain)
-      expect(@trocla.get_password('set_test','mysql')).to be_nil
-    end
-
-    it "otherwise updates only the hash" do
-      expect(mysql = @trocla.password('set_test2','mysql')).not_to be_empty
-      expect(md5crypt = @trocla.password('set_test2','md5crypt')).not_to be_empty
-      expect(plain = @trocla.get_password('set_test2','plain')).not_to be_empty
-
-      expect(new_mysql = @trocla.set_password('set_test2','mysql','foo')).not_to eql(mysql)
-      expect(@trocla.get_password('set_test2','mysql')).to eq(new_mysql)
-      expect(@trocla.get_password('set_test2','md5crypt')).to eq(md5crypt)
-      expect(@trocla.get_password('set_test2','plain')).to eq(plain)
+      it "otherwise updates only the hash" do
+        expect(mysql = @trocla.password('set_test2','mysql')).not_to be_empty
+        expect(md5crypt = @trocla.password('set_test2','md5crypt')).not_to be_empty
+        expect(plain = @trocla.get_password('set_test2','plain')).not_to be_empty
+
+        expect(new_mysql = @trocla.set_password('set_test2','mysql','foo')).not_to eql(mysql)
+        expect(@trocla.get_password('set_test2','mysql')).to eq(new_mysql)
+        expect(@trocla.get_password('set_test2','md5crypt')).to eq(md5crypt)
+        expect(@trocla.get_password('set_test2','plain')).to eq(plain)
+      end
     end
-  end
 
-  describe "reset_password" do
-    it "resets a password" do
-      plain1 = @trocla.password('reset_pwd','plain')
-      plain2 = @trocla.reset_password('reset_pwd','plain')
+    describe "reset_password" do
+      it "resets a password" do
+        plain1 = @trocla.password('reset_pwd','plain')
+        plain2 = @trocla.reset_password('reset_pwd','plain')
 
-      expect(plain1).not_to eq(plain2)
-    end
+        expect(plain1).not_to eq(plain2)
+      end
 
-    it "does not reset other formats" do
-      expect(mysql = @trocla.password('reset_pwd2','mysql')).not_to be_empty
-      expect(md5crypt1 = @trocla.password('reset_pwd2','md5crypt')).not_to be_empty
+      it "does not reset other formats" do
+        expect(mysql = @trocla.password('reset_pwd2','mysql')).not_to be_empty
+        expect(md5crypt1 = @trocla.password('reset_pwd2','md5crypt')).not_to be_empty
 
-      expect(md5crypt2 = @trocla.reset_password('reset_pwd2','md5crypt')).not_to be_empty
-      expect(md5crypt2).not_to eq(md5crypt1)
+        expect(md5crypt2 = @trocla.reset_password('reset_pwd2','md5crypt')).not_to be_empty
+        expect(md5crypt2).not_to eq(md5crypt1)
 
-      expect(@trocla.get_password('reset_pwd2','mysql')).to eq(mysql)
+        expect(@trocla.get_password('reset_pwd2','mysql')).to eq(mysql)
+      end
     end
-  end
 
-  describe "delete_password" do
-    it "deletes all passwords if no format is given" do
-      expect(@trocla.password('delete_test1','mysql')).not_to be_nil
-      expect(@trocla.get_password('delete_test1','plain')).not_to be_nil
+    describe "delete_password" do
+      it "deletes all passwords if no format is given" do
+        expect(@trocla.password('delete_test1','mysql')).not_to be_nil
+        expect(@trocla.get_password('delete_test1','plain')).not_to be_nil
 
-      @trocla.delete_password('delete_test1')
-      expect(@trocla.get_password('delete_test1','plain')).to be_nil
-      expect(@trocla.get_password('delete_test1','mysql')).to be_nil
-    end
+        @trocla.delete_password('delete_test1')
+        expect(@trocla.get_password('delete_test1','plain')).to be_nil
+        expect(@trocla.get_password('delete_test1','mysql')).to be_nil
+      end
+
+      it "deletes only a given format" do
+        expect(@trocla.password('delete_test2','mysql')).not_to be_nil
+        expect(@trocla.get_password('delete_test2','plain')).not_to be_nil
 
-    it "deletes only a given format" do
-      expect(@trocla.password('delete_test2','mysql')).not_to be_nil
-      expect(@trocla.get_password('delete_test2','plain')).not_to be_nil
+        @trocla.delete_password('delete_test2','plain')
+        expect(@trocla.get_password('delete_test2','plain')).to be_nil
+        expect(@trocla.get_password('delete_test2','mysql')).not_to be_nil
+      end
+
+      it "deletes only a given non-plain format" do
+        expect(@trocla.password('delete_test3','mysql')).not_to be_nil
+        expect(@trocla.get_password('delete_test3','plain')).not_to be_nil
 
-      @trocla.delete_password('delete_test2','plain')
-      expect(@trocla.get_password('delete_test2','plain')).to be_nil
-      expect(@trocla.get_password('delete_test2','mysql')).not_to be_nil
+        @trocla.delete_password('delete_test3','mysql')
+        expect(@trocla.get_password('delete_test3','mysql')).to be_nil
+        expect(@trocla.get_password('delete_test3','plain')).not_to be_nil
+      end
     end
 
-    it "deletes only a given non-plain format" do
-      expect(@trocla.password('delete_test3','mysql')).not_to be_nil
-      expect(@trocla.get_password('delete_test3','plain')).not_to be_nil
+    context 'concurrent access' do
+      context 'on expensive flagged formats' do
+        before(:each) do
+          expect(Trocla::Formats).to receive(:[]).with('sleep').at_least(:once).and_return(Trocla::Formats::Sleep)
+          expect(Trocla::Formats::Sleep).to receive(:expensive?).at_least(:once).and_return(true)
+          expect(Trocla::Formats).to receive(:available?).with('sleep').at_least(:once).and_return(true)
+        end
+        it 'should not overwrite a value if it takes longer' do
+          t1 = Thread.new{ @trocla.password('threadpwd','sleep','sleep' => 4) }
+          t2 = Thread.new{ @trocla.password('threadpwd','sleep','sleep' => 1) }
+          pwd1 = t1.value
+          pwd2 = t2.value
+          real_value = @trocla.password('threadpwd','sleep')
+          # as t2 finished first this should win
+          expect(pwd1).to eql(pwd2)
+          expect(real_value).to eql(pwd1)
+          expect(real_value).to eql(pwd2)
+        end
+      end
+      context 'on inexpensive flagged formats' do
+        before(:each) do
+          expect(Trocla::Formats).to receive(:[]).with('sleep').at_least(:once).and_return(Trocla::Formats::Sleep)
+          expect(Trocla::Formats::Sleep).to receive(:expensive?).at_least(:once).and_return(false)
+          expect(Trocla::Formats).to receive(:available?).with('sleep').at_least(:once).and_return(true)
+        end
+        it 'should not overwrite a value if it takes longer' do
+          t1 = Thread.new{ @trocla.password('threadpwd_inexp','sleep','sleep' => 4) }
+          t2 = Thread.new{ @trocla.password('threadpwd_inexp','sleep','sleep' => 1) }
+          pwd1 = t1.value
+          pwd2 = t2.value
+          real_value = @trocla.password('threadpwd_inexp','sleep')
+          # as t2 finished first but the format is inexpensive it gets overwritten
+          expect(pwd1).not_to eql(pwd2)
+          expect(real_value).to eql(pwd1)
+          expect(real_value).not_to eql(pwd2)
+        end
+      end
+      context 'real world example' do
+        it 'should store the quicker one' do
+          t1 = Thread.new{ @trocla.password('threadpwd_real','bcrypt','cost' => 17) }
+          t2 = Thread.new{ @trocla.password('threadpwd_real','bcrypt') }
+          pwd1 = t1.value
+          pwd2 = t2.value
+          real_value = @trocla.password('threadpwd_real','bcrypt')
+          # t2 should still win but both should be the same
+          expect(pwd1).to eql(pwd2)
+          expect(real_value).to eql(pwd1)
+          expect(real_value).to eql(pwd2)
+        end
+        it 'should store the quicker one test 2' do
+          t1 = Thread.new{ @trocla.password('my_shiny_selfsigned_ca', 'x509', {
+            'CN'        => 'This is my self-signed certificate',
+            'become_ca' => false,
+          }) }
+          t2 = Thread.new{ @trocla.password('my_shiny_selfsigned_ca', 'x509', {
+            'CN'        => 'This is my self-signed certificate',
+            'become_ca' => false,
+          }) }
+          cert1 = t1.value
+          cert2 = t2.value
+          real_value = @trocla.password('my_shiny_selfsigned_ca','x509')
+          # t2 should still win but both should be the same
+          expect(cert1).to eql(cert2)
+          expect(real_value).to eql(cert1)
+          expect(real_value).to eql(cert2)
+        end
+      end
+    end
 
-      @trocla.delete_password('delete_test3','mysql')
-      expect(@trocla.get_password('delete_test3','mysql')).to be_nil
-      expect(@trocla.get_password('delete_test3','plain')).not_to be_nil
+  end
+  context 'with .open' do
+    it 'closes the connection with a block' do
+      expect_any_instance_of(Trocla::Stores::Memory).to receive(:close)
+      Trocla.open{|t|
+        t.password('plain_open','plain')
+      }
+    end
+    it 'keeps the connection without a block' do
+      expect_any_instance_of(Trocla::Stores::Memory).not_to receive(:close)
+      Trocla.open.password('plain_open','plain')
     end
   end
 
diff -Nru trocla-0.2.3/.travis.yml trocla-0.3.0/.travis.yml
--- trocla-0.2.3/.travis.yml	2016-03-01 15:40:25.000000000 -0500
+++ trocla-0.3.0/.travis.yml	2021-05-04 15:31:47.000000000 -0400
@@ -1,10 +1,11 @@
 language: ruby
 sudo: false
 rvm:
+  - jruby
   - jruby-18mode
   - jruby-19mode
+  - 2.4.0
   - 2.2.0
-  - 2.1.0
   - 2.0.0
   - 1.9.3
   - 1.8.7
diff -Nru trocla-0.2.3/trocla.gemspec trocla-0.3.0/trocla.gemspec
--- trocla-0.2.3/trocla.gemspec	2016-03-01 15:40:25.000000000 -0500
+++ trocla-0.3.0/trocla.gemspec	2021-05-04 15:31:47.000000000 -0400
@@ -2,19 +2,19 @@
 # DO NOT EDIT THIS FILE DIRECTLY
 # Instead, edit Jeweler::Tasks in Rakefile, and run 'rake gemspec'
 # -*- encoding: utf-8 -*-
-# stub: trocla 0.2.3 ruby lib
+# stub: trocla 0.3.0 ruby lib
 
 Gem::Specification.new do |s|
-  s.name = "trocla"
-  s.version = "0.2.3"
+  s.name = "trocla".freeze
+  s.version = "0.3.0"
 
-  s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
-  s.require_paths = ["lib"]
-  s.authors = ["mh"]
-  s.date = "2016-02-15"
-  s.description = "Trocla helps you to generate random passwords and to store them in various formats (plain, MD5, bcrypt) for later retrival."
-  s.email = "mh+tro...@immerda.ch"
-  s.executables = ["trocla"]
+  s.required_rubygems_version = Gem::Requirement.new(">= 0".freeze) if s.respond_to? :required_rubygems_version=
+  s.require_paths = ["lib".freeze]
+  s.authors = ["mh".freeze]
+  s.date = "2017-08-04"
+  s.description = "Trocla helps you to generate random passwords and to store them in various formats (plain, MD5, bcrypt) for later retrival.".freeze
+  s.email = "mh+tro...@immerda.ch".freeze
+  s.executables = ["trocla".freeze]
   s.extra_rdoc_files = [
     "LICENSE.txt",
     "README.md"
@@ -64,39 +64,39 @@
     "spec/trocla_spec.rb",
     "trocla.gemspec"
   ]
-  s.homepage = "https://tech.immerda.ch/2011/12/trocla-get-hashed-passwords-out-of-puppet-manifests/";
-  s.licenses = ["GPLv3"]
-  s.rubygems_version = "2.2.2"
-  s.summary = "Trocla a simple password generator and storage"
+  s.homepage = "https://tech.immerda.ch/2011/12/trocla-get-hashed-passwords-out-of-puppet-manifests/".freeze
+  s.licenses = ["GPLv3".freeze]
+  s.rubygems_version = "2.6.11".freeze
+  s.summary = "Trocla a simple password generator and storage".freeze
 
   if s.respond_to? :specification_version then
     s.specification_version = 4
 
     if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
-      s.add_runtime_dependency(%q<moneta>, [">= 0"])
-      s.add_runtime_dependency(%q<highline>, [">= 0"])
-      s.add_runtime_dependency(%q<bcrypt>, [">= 0"])
-      s.add_development_dependency(%q<rspec>, [">= 0"])
-      s.add_development_dependency(%q<rdoc>, [">= 0"])
-      s.add_development_dependency(%q<jeweler>, [">= 0"])
-      s.add_development_dependency(%q<rspec-pending_for>, [">= 0"])
+      s.add_runtime_dependency(%q<moneta>.freeze, [">= 0"])
+      s.add_runtime_dependency(%q<highline>.freeze, [">= 0"])
+      s.add_runtime_dependency(%q<bcrypt>.freeze, [">= 0"])
+      s.add_development_dependency(%q<rspec>.freeze, [">= 0"])
+      s.add_development_dependency(%q<rdoc>.freeze, [">= 0"])
+      s.add_development_dependency(%q<jeweler>.freeze, [">= 0"])
+      s.add_development_dependency(%q<rspec-pending_for>.freeze, [">= 0"])
     else
-      s.add_dependency(%q<moneta>, [">= 0"])
-      s.add_dependency(%q<highline>, [">= 0"])
-      s.add_dependency(%q<bcrypt>, [">= 0"])
-      s.add_dependency(%q<rspec>, [">= 0"])
-      s.add_dependency(%q<rdoc>, [">= 0"])
-      s.add_dependency(%q<jeweler>, [">= 0"])
-      s.add_dependency(%q<rspec-pending_for>, [">= 0"])
+      s.add_dependency(%q<moneta>.freeze, [">= 0"])
+      s.add_dependency(%q<highline>.freeze, [">= 0"])
+      s.add_dependency(%q<bcrypt>.freeze, [">= 0"])
+      s.add_dependency(%q<rspec>.freeze, [">= 0"])
+      s.add_dependency(%q<rdoc>.freeze, [">= 0"])
+      s.add_dependency(%q<jeweler>.freeze, [">= 0"])
+      s.add_dependency(%q<rspec-pending_for>.freeze, [">= 0"])
     end
   else
-    s.add_dependency(%q<moneta>, [">= 0"])
-    s.add_dependency(%q<highline>, [">= 0"])
-    s.add_dependency(%q<bcrypt>, [">= 0"])
-    s.add_dependency(%q<rspec>, [">= 0"])
-    s.add_dependency(%q<rdoc>, [">= 0"])
-    s.add_dependency(%q<jeweler>, [">= 0"])
-    s.add_dependency(%q<rspec-pending_for>, [">= 0"])
+    s.add_dependency(%q<moneta>.freeze, [">= 0"])
+    s.add_dependency(%q<highline>.freeze, [">= 0"])
+    s.add_dependency(%q<bcrypt>.freeze, [">= 0"])
+    s.add_dependency(%q<rspec>.freeze, [">= 0"])
+    s.add_dependency(%q<rdoc>.freeze, [">= 0"])
+    s.add_dependency(%q<jeweler>.freeze, [">= 0"])
+    s.add_dependency(%q<rspec-pending_for>.freeze, [">= 0"])
   end
 end
 

Attachment: signature.asc
Description: PGP signature

Reply via email to