keystorage 0.5.1 → 0.5.7
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.travis.yml +9 -0
- data/Gemfile +2 -6
- data/README.md +7 -0
- data/Rakefile +1 -1
- data/VERSION +1 -1
- data/bin/keystorage +1 -0
- data/keystorage.gemspec +3 -2
- data/lib/keystorage.rb +0 -56
- data/lib/keystorage/cli.rb +17 -5
- data/lib/keystorage/manager.rb +74 -0
- data/spec/keystorage/cli_spec.rb +117 -4
- data/spec/keystorage/files/1.yml +3 -0
- data/spec/keystorage/manager_spec.rb +378 -8
- data/spec/keystorage_spec.rb +19 -0
- data/spec/spec_helper.rb +10 -0
- metadata +21 -6
- data/test/helper.rb +0 -18
- data/test/test_keystorage.rb +0 -36
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 5772937588dc3df5bec1a71d4decdb377c44cc5d
|
4
|
+
data.tar.gz: cea3aa650a0c1e8ff6737b5c89a24efd19c3d926
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 3c94373cfc511b914ed78315639242b650bd79158d2f0c7dff814f8490ad1e555b7c280f30fd9e5175d9c8336be2d9a8343fe07a91c6bc16344bdaaa2f58bcfe
|
7
|
+
data.tar.gz: bf1b0665cd890f17fb6c3c0294224861038f942ee1a5b60f585c1fd93d976fe2d394b07b22346ce79ce15437f29d9197313f0f81fc16ab6c7e119892872dd1d3
|
data/.travis.yml
ADDED
data/Gemfile
CHANGED
@@ -1,9 +1,5 @@
|
|
1
1
|
source "http://rubygems.org"
|
2
2
|
gemspec
|
3
3
|
|
4
|
-
|
5
|
-
|
6
|
-
# gem "activesupport", ">= 2.3.5"
|
7
|
-
|
8
|
-
# Add dependencies to develop your gem here.
|
9
|
-
# Include everything needed to run rake, tests, features, etc.
|
4
|
+
gem "simplecov", :require=>false, group: :test
|
5
|
+
gem "codeclimate-test-reporter", group: :test
|
data/README.md
CHANGED
@@ -1,6 +1,11 @@
|
|
1
1
|
keystorage
|
2
2
|
----------
|
3
3
|
|
4
|
+
[![Gem Version](https://badge.fury.io/rb/keystorage.svg)](http://badge.fury.io/rb/keystorage)
|
5
|
+
[![Build Status](https://travis-ci.org/tumf/keystorage.svg?branch=master)](https://travis-ci.org/tumf/keystorage)
|
6
|
+
[![Code Climate](https://codeclimate.com/github/tumf/keystorage/badges/gpa.svg)](https://codeclimate.com/github/tumf/keystorage)
|
7
|
+
[![Test Coverage](https://codeclimate.com/github/tumf/keystorage/badges/coverage.svg)](https://codeclimate.com/github/tumf/keystorage)
|
8
|
+
|
4
9
|
Simple password storage.
|
5
10
|
|
6
11
|
## Install
|
@@ -12,12 +17,14 @@ Simple password storage.
|
|
12
17
|
|
13
18
|
-> % keystorage
|
14
19
|
Commands:
|
20
|
+
keystorage exec # execute child process with set envvars
|
15
21
|
keystorage get # Get a encrypted value of the key of the group
|
16
22
|
keystorage groups # List groups
|
17
23
|
keystorage help [COMMAND] # Describe available commands or one specific command
|
18
24
|
keystorage keys # List keys of the group
|
19
25
|
keystorage password # Update storage secret
|
20
26
|
keystorage set # Set a value of the key of the group
|
27
|
+
keystorage version # show version info
|
21
28
|
|
22
29
|
Options:
|
23
30
|
-v, [--verbose], [--no-verbose]
|
data/Rakefile
CHANGED
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.5.
|
1
|
+
0.5.7
|
data/bin/keystorage
CHANGED
data/keystorage.gemspec
CHANGED
@@ -27,8 +27,9 @@ Gem::Specification.new do |s|
|
|
27
27
|
s.summary = "Simple password storage"
|
28
28
|
|
29
29
|
s.add_dependency('thor')
|
30
|
-
|
30
|
+
s.add_dependency('version')
|
31
|
+
|
31
32
|
s.add_development_dependency('rake')
|
32
33
|
s.add_development_dependency('rspec')
|
33
|
-
s.add_development_dependency('
|
34
|
+
s.add_development_dependency('fakefs')
|
34
35
|
end
|
data/lib/keystorage.rb
CHANGED
@@ -15,62 +15,6 @@ module Keystorage
|
|
15
15
|
class NoSecret < StandardError; end
|
16
16
|
class FormatNotSupport < StandardError; end
|
17
17
|
|
18
|
-
def sign message,secret=secret
|
19
|
-
raise NoSecret.new("set env KEYSTORAGE_SECRET") unless secret
|
20
|
-
OpenSSL::HMAC.hexdigest('sha512',secret, message)
|
21
|
-
end
|
22
|
-
|
23
|
-
def token
|
24
|
-
SecureRandom.urlsafe_base64(nil, false)
|
25
|
-
end
|
26
|
-
|
27
|
-
def root
|
28
|
-
raise NoRootGroup unless file.has_key?("@")
|
29
|
-
file["@"] || {}
|
30
|
-
end
|
31
|
-
|
32
|
-
def root! secret=secret,data=file
|
33
|
-
data["@"] = {}
|
34
|
-
data["@"]["token"] = token
|
35
|
-
data["@"]["sig"] = sign(data["@"]["token"],secret)
|
36
|
-
data
|
37
|
-
end
|
38
|
-
|
39
|
-
# file validation
|
40
|
-
def valid?
|
41
|
-
sign(root["token"]) == root["sig"]
|
42
|
-
rescue NoRootGroup
|
43
|
-
write root!
|
44
|
-
end
|
45
|
-
|
46
|
-
def encode(str,secret=secret)
|
47
|
-
enc = OpenSSL::Cipher::Cipher.new('aes256')
|
48
|
-
enc.encrypt.pkcs5_keyivgen(secret)
|
49
|
-
((enc.update(str) + enc.final).unpack("H*")).first.to_s
|
50
|
-
end
|
51
|
-
|
52
|
-
def decode(str,secret=secret)
|
53
|
-
dec = OpenSSL::Cipher::Cipher.new('aes256')
|
54
|
-
dec.decrypt.pkcs5_keyivgen(secret)
|
55
|
-
(dec.update(Array.new([str]).pack("H*")) + dec.final)
|
56
|
-
end
|
57
|
-
|
58
|
-
def path
|
59
|
-
options[:file] || ENV['KEYSTORAGE_FILE'] || DEFAULT_FILE
|
60
|
-
end
|
61
|
-
|
62
|
-
def file
|
63
|
-
YAML.load(File.new(path)) || {}
|
64
|
-
end
|
65
|
-
|
66
|
-
def write data
|
67
|
-
File.open(path,'w',0600) { |f| YAML.dump(data,f) }
|
68
|
-
end
|
69
|
-
|
70
|
-
def secret
|
71
|
-
options[:secret] || ENV['KEYSTORAGE_SECRET'] || DEFAULT_SECRET
|
72
|
-
end
|
73
|
-
|
74
18
|
def render out,format =:text
|
75
19
|
case format
|
76
20
|
when :text then
|
data/lib/keystorage/cli.rb
CHANGED
@@ -3,6 +3,7 @@ require 'keystorage'
|
|
3
3
|
require 'keystorage/manager'
|
4
4
|
require 'thor'
|
5
5
|
require 'optparse'
|
6
|
+
require 'version'
|
6
7
|
|
7
8
|
module Keystorage
|
8
9
|
class CLI < Thor
|
@@ -12,22 +13,22 @@ module Keystorage
|
|
12
13
|
def start(given_args = ARGV, config = {})
|
13
14
|
# parse global options: Thor is not support global-options.
|
14
15
|
# Like: command global-options subcommand options
|
15
|
-
|
16
|
+
new_global_options = []
|
16
17
|
OptionParser.new do |opt|
|
17
18
|
@global_options.each { |name,config|
|
18
19
|
case config[:type]
|
19
20
|
when :boolean then
|
20
|
-
opt.on(config[:aliases],"--#{name.to_s}") { |v|
|
21
|
-
opt.on(config[:aliases],"--no-#{name.to_s}") { |v| global_options << "--no-#{name.to_s}"}
|
21
|
+
opt.on(config[:aliases],"--#{name.to_s}") { |v| new_global_options << "--#{name.to_s}"}
|
22
22
|
when :string then
|
23
|
-
opt.on(config[:aliases],"--#{name.to_s}=VALUE") { |v|
|
23
|
+
opt.on(config[:aliases],"--#{name.to_s}=VALUE") { |v| new_global_options << "--#{name.to_s}=#{v}"}
|
24
24
|
end
|
25
25
|
}
|
26
26
|
opt.parse!(given_args)
|
27
27
|
end
|
28
|
-
given_args+=
|
28
|
+
given_args+=new_global_options
|
29
29
|
super(given_args,config)
|
30
30
|
end
|
31
|
+
attr_reader :global_options
|
31
32
|
|
32
33
|
def global_option *params
|
33
34
|
@global_options ||= {}
|
@@ -68,5 +69,16 @@ module Keystorage
|
|
68
69
|
Manager.new(options).password(new_secret)
|
69
70
|
end
|
70
71
|
|
72
|
+
desc "exec","execute child process with set envvars"
|
73
|
+
def exec *command
|
74
|
+
#@todo: ask if new_secret == nil
|
75
|
+
Manager.new(options).exec(command)
|
76
|
+
end
|
77
|
+
|
78
|
+
desc "version","show version info"
|
79
|
+
def version
|
80
|
+
puts Version.current
|
81
|
+
end
|
82
|
+
|
71
83
|
end
|
72
84
|
end
|
data/lib/keystorage/manager.rb
CHANGED
@@ -56,5 +56,79 @@ module Keystorage
|
|
56
56
|
retry
|
57
57
|
end
|
58
58
|
|
59
|
+
def exec *cmd
|
60
|
+
raise SecretMissMatch unless valid?
|
61
|
+
system(envs.collect{ |k,v| "#{k}='#{v}'" }.join(' ') + " " + cmd.join(' '))
|
62
|
+
end
|
63
|
+
|
64
|
+
private
|
65
|
+
|
66
|
+
def envs
|
67
|
+
result = {}
|
68
|
+
groups.each { |g|
|
69
|
+
keys(g).each { |k|
|
70
|
+
result["#{g}_#{k}"] = get(g,k)
|
71
|
+
}
|
72
|
+
}
|
73
|
+
result
|
74
|
+
end
|
75
|
+
|
76
|
+
def sign message,_secret=secret
|
77
|
+
raise NoSecret.new("set env KEYSTORAGE_SECRET") unless _secret
|
78
|
+
OpenSSL::HMAC.hexdigest('sha512',_secret, message)
|
79
|
+
end
|
80
|
+
|
81
|
+
def token
|
82
|
+
SecureRandom.urlsafe_base64(nil, false)
|
83
|
+
end
|
84
|
+
|
85
|
+
def root
|
86
|
+
raise NoRootGroup unless file.has_key?("@")
|
87
|
+
file["@"] || {}
|
88
|
+
end
|
89
|
+
|
90
|
+
def root! _secret=secret,data=file
|
91
|
+
data["@"] = {}
|
92
|
+
data["@"]["token"] = token
|
93
|
+
data["@"]["sig"] = sign(data["@"]["token"],_secret)
|
94
|
+
data
|
95
|
+
end
|
96
|
+
|
97
|
+
# file validation
|
98
|
+
def valid?
|
99
|
+
sign(root["token"]) == root["sig"]
|
100
|
+
rescue NoRootGroup
|
101
|
+
write root! and true
|
102
|
+
end
|
103
|
+
|
104
|
+
def encode(str,_secret=secret)
|
105
|
+
enc = OpenSSL::Cipher::Cipher.new('aes256')
|
106
|
+
enc.encrypt.pkcs5_keyivgen(_secret)
|
107
|
+
((enc.update(str) + enc.final).unpack("H*")).first.to_s
|
108
|
+
end
|
109
|
+
|
110
|
+
def decode(str,_secret=secret)
|
111
|
+
dec = OpenSSL::Cipher::Cipher.new('aes256')
|
112
|
+
dec.decrypt.pkcs5_keyivgen(_secret)
|
113
|
+
(dec.update(Array.new([str]).pack("H*")) + dec.final)
|
114
|
+
end
|
115
|
+
|
116
|
+
def path
|
117
|
+
options[:file] || ENV['KEYSTORAGE_FILE'] || DEFAULT_FILE
|
118
|
+
end
|
119
|
+
|
120
|
+
def file
|
121
|
+
YAML.load(File.new(path)) || {}
|
122
|
+
end
|
123
|
+
|
124
|
+
def write data
|
125
|
+
FileUtils.mkdir_p(File.dirname(path))
|
126
|
+
File.open(path,'w',0600) { |f| YAML.dump(data,f) }
|
127
|
+
end
|
128
|
+
|
129
|
+
def secret
|
130
|
+
options[:secret] || ENV['KEYSTORAGE_SECRET'] || DEFAULT_SECRET
|
131
|
+
end
|
132
|
+
|
59
133
|
end
|
60
134
|
end
|
data/spec/keystorage/cli_spec.rb
CHANGED
@@ -1,7 +1,120 @@
|
|
1
|
+
# coding: utf-8
|
1
2
|
require 'keystorage/cli'
|
3
|
+
|
2
4
|
describe Keystorage::CLI do
|
3
|
-
subject { Keystorage::CLI
|
4
|
-
|
5
|
-
|
6
|
-
|
5
|
+
subject { Keystorage::CLI.start(argv) }
|
6
|
+
|
7
|
+
describe ".start" do
|
8
|
+
context "unknown global-options are specified" do
|
9
|
+
let(:argv) { ['--aaa=myfile','groups'] }
|
10
|
+
it "puts options back of subcommand" do
|
11
|
+
expect { subject }.to raise_error(OptionParser::InvalidOption)
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
context "string-type global-options are specified" do
|
16
|
+
let(:argv) { ['-f','myfile','groups'] }
|
17
|
+
it "puts options back of subcommand" do
|
18
|
+
expect(Thor).to receive(:start).with(['groups','--file=myfile'],{}).once
|
19
|
+
subject
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
context "boolean-type `-v` global-options are specified" do
|
24
|
+
let(:argv) { ['-v','groups'] }
|
25
|
+
it "puts options back of subcommand" do
|
26
|
+
expect(Thor).to receive(:start).with(['groups','--verbose'],{}).once
|
27
|
+
subject
|
28
|
+
end
|
29
|
+
end
|
30
|
+
context "boolean-type `--no-verbose` global-options are specified" do
|
31
|
+
let(:argv) { ['--no-verbose','groups'] }
|
32
|
+
it "puts options back of subcommand" do
|
33
|
+
expect { subject }.to raise_error(OptionParser::InvalidOption)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
describe ".global_option" do
|
39
|
+
it "adds to @global_options and call `class_option`" do
|
40
|
+
expect(Keystorage::CLI).to receive(:class_option)
|
41
|
+
.with(:test, :aliases =>"-t", :type => :boolean).once
|
42
|
+
Keystorage::CLI.global_option(:test, :aliases =>"-t", :type => :boolean)
|
43
|
+
expect(Keystorage::CLI.global_options.has_key?(:test)).to be true
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
describe "#groups" do
|
48
|
+
let(:argv) { ['groups'] }
|
49
|
+
it "puts list of groups" do
|
50
|
+
allow(Keystorage::Manager).to receive_message_chain('new.groups')
|
51
|
+
.and_return( ["group1","group2","group3"] )
|
52
|
+
expect(STDOUT).to receive(:puts).with("group1\ngroup2\ngroup3").once
|
53
|
+
subject
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
describe "#keys" do
|
58
|
+
let(:argv) { ['keys','group1'] }
|
59
|
+
it "puts keys in the group" do
|
60
|
+
allow(Keystorage::Manager).to receive_message_chain('new.keys')
|
61
|
+
.and_return( ["key1","key2","key3"] )
|
62
|
+
|
63
|
+
expect(STDOUT).to receive(:puts).with("key1\nkey2\nkey3").once
|
64
|
+
subject
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
describe "#get" do
|
69
|
+
let(:argv) { ['get','group1','key1'] }
|
70
|
+
it "puts value of the key in the group" do
|
71
|
+
allow(Keystorage::Manager).to receive_message_chain('new.get')
|
72
|
+
.and_return( ["value"] )
|
73
|
+
|
74
|
+
expect(STDOUT).to receive(:puts).with("value").once
|
75
|
+
subject
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
|
80
|
+
describe "#set" do
|
81
|
+
let(:argv) { ['set','group1','key1','vaule'] }
|
82
|
+
it "sets value of the key in the group" do
|
83
|
+
allow(Keystorage::Manager).to receive_message_chain('new.set')
|
84
|
+
.and_return("value")
|
85
|
+
|
86
|
+
expect(STDOUT).to receive(:puts).with("value").once
|
87
|
+
subject
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
describe "#password" do
|
92
|
+
let(:argv) { ['password','p@ssw0rd'] }
|
93
|
+
it "updates secret of all keys in the file" do
|
94
|
+
expect(Keystorage::Manager).to receive_message_chain('new.password')
|
95
|
+
.with('p@ssw0rd')
|
96
|
+
subject
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
describe "#exec" do
|
101
|
+
let(:argv) { ['exec','mycommand','arg1','arg2'] }
|
102
|
+
it "updates secret of all keys in the file" do
|
103
|
+
expect(Keystorage::Manager).to receive_message_chain('new.exec')
|
104
|
+
.with(['mycommand','arg1','arg2'])
|
105
|
+
subject
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
describe "#version" do
|
110
|
+
let(:argv) { ['version'] }
|
111
|
+
let(:ver) {
|
112
|
+
File.read(File.join(File.dirname(__FILE__),'..','..','VERSION')).chomp
|
113
|
+
}
|
114
|
+
it "shows version" do
|
115
|
+
expect(STDOUT).to receive(:puts).with(ver).once
|
116
|
+
subject
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
7
120
|
end
|
@@ -1,13 +1,383 @@
|
|
1
1
|
require 'keystorage/manager'
|
2
|
+
|
3
|
+
def yaml name
|
4
|
+
File.join(File.dirname(__FILE__),'files',"#{name}.yml")
|
5
|
+
end
|
6
|
+
|
2
7
|
describe Keystorage::Manager do
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
is_expected.to be ""
|
8
|
+
let(:files){
|
9
|
+
{
|
10
|
+
"1"=>{"@"=>{"sig"=>"abc"}},
|
11
|
+
"2"=>{"@"=>{"sig"=>"abc","token"=>"def"},"g1"=>{ "k1" => "abcdefg", "k2" =>"" },"g2"=>{}},
|
12
|
+
}
|
9
13
|
}
|
14
|
+
before { @manager = Keystorage::Manager.new }
|
15
|
+
|
16
|
+
# public methods
|
17
|
+
describe "#groups" do
|
18
|
+
subject { @manager.groups }
|
19
|
+
it "returns array of groups" do
|
20
|
+
allow(@manager).to receive(:file)
|
21
|
+
.and_return(files["2"])
|
22
|
+
is_expected.to eq ["g1","g2"]
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
describe "#keys" do
|
27
|
+
subject { @manager.keys("g1") }
|
28
|
+
it "returns array of keys" do
|
29
|
+
allow(@manager).to receive(:file)
|
30
|
+
.and_return(files["2"])
|
31
|
+
|
32
|
+
is_expected.to eq ["k1","k2"]
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
describe "#get" do
|
37
|
+
subject { @manager.get("g1","k1") }
|
38
|
+
before {
|
39
|
+
allow(@manager).to receive(:file)
|
40
|
+
.and_return(files["2"])
|
41
|
+
}
|
42
|
+
context "has valid secret" do
|
43
|
+
before {
|
44
|
+
allow(@manager).to receive(:valid?)
|
45
|
+
.and_return(true)
|
46
|
+
}
|
47
|
+
it "returns value of the key in the group" do
|
48
|
+
expect(@manager).to receive(:decode).with("abcdefg").once
|
49
|
+
subject
|
50
|
+
end
|
51
|
+
end
|
52
|
+
context "has no valid secret" do
|
53
|
+
before {
|
54
|
+
allow(@manager).to receive(:valid?)
|
55
|
+
.and_return(false) }
|
56
|
+
it "raise Keystorage::SecretMissMatch" do
|
57
|
+
expect { subject }.to raise_error(Keystorage::SecretMissMatch)
|
58
|
+
end
|
59
|
+
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
describe "#set" do
|
64
|
+
before {
|
65
|
+
allow(@manager).to receive(:file)
|
66
|
+
.and_return(files["2"])
|
67
|
+
}
|
68
|
+
|
69
|
+
context "group name '@'" do
|
70
|
+
subject { @manager.set("@","k1","v1") }
|
71
|
+
it "raise Keystorage::RejectGroupName" do
|
72
|
+
expect { subject }.to raise_error(Keystorage::RejectGroupName)
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
context "has valid secret" do
|
77
|
+
subject { @manager.set("g1","k1","v1") }
|
78
|
+
before {
|
79
|
+
allow(@manager).to receive(:valid?)
|
80
|
+
.and_return(true)
|
81
|
+
}
|
82
|
+
it "sets the value to the key in the group" do
|
83
|
+
FakeFS do
|
84
|
+
subject
|
85
|
+
expect(@manager.get('g1','k1')).to eq "v1"
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
context "has no valid secret" do
|
91
|
+
subject { @manager.set("g1","k1","v1") }
|
92
|
+
before {
|
93
|
+
allow(@manager).to receive(:valid?)
|
94
|
+
.and_return(false)
|
95
|
+
}
|
96
|
+
it "raise Keystorage::SecretMissMatch" do
|
97
|
+
expect { subject }.to raise_error(Keystorage::SecretMissMatch)
|
98
|
+
end
|
99
|
+
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
describe "#password" do
|
104
|
+
let(:new_password) { "zxcvbnm" }
|
105
|
+
subject { @manager.password(new_password) }
|
106
|
+
|
107
|
+
context "file not found" do
|
108
|
+
before {
|
109
|
+
@manager = Keystorage::Manager.new( {:file=>"hoge",:secret=>"fuga"})
|
110
|
+
}
|
111
|
+
it "creates new keystorage file" do
|
112
|
+
FakeFS do
|
113
|
+
subject
|
114
|
+
expect(File.exists?("hoge")).to eq true
|
115
|
+
expect(@manager.send(:file).has_key?("@")).to eq true
|
116
|
+
end
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
context "has valid secret" do
|
121
|
+
|
122
|
+
before {
|
123
|
+
@manager = Keystorage::Manager.new( {:file=>"hoge",:secret=>"fuga"})
|
124
|
+
}
|
125
|
+
it "updates password to `new_password`" do
|
126
|
+
FakeFS do
|
127
|
+
@manager.set("a","b","c")
|
128
|
+
subject
|
129
|
+
expect { @manager.get("a","b") }.to raise_error(Keystorage::SecretMissMatch)
|
130
|
+
@manager = Keystorage::Manager.new( {:file=>"hoge",:secret=>new_password})
|
131
|
+
expect(@manager.get("a","b")).to eq "c"
|
132
|
+
end
|
133
|
+
end
|
134
|
+
end
|
135
|
+
|
136
|
+
context "has no valid secret" do
|
137
|
+
before {
|
138
|
+
allow(@manager).to receive(:valid?)
|
139
|
+
.and_return(false)
|
140
|
+
}
|
141
|
+
it "raise Keystorage::SecretMissMatch" do
|
142
|
+
expect { subject }.to raise_error(Keystorage::SecretMissMatch)
|
143
|
+
end
|
144
|
+
|
145
|
+
end
|
146
|
+
end
|
147
|
+
|
148
|
+
describe "#exec" do
|
149
|
+
let(:cmd) { ["ls","-a b"] }
|
150
|
+
subject { @manager.exec(cmd) }
|
151
|
+
|
152
|
+
context "has valid secret" do
|
153
|
+
before {
|
154
|
+
@manager = Keystorage::Manager.new( {:file=>"hoge",:secret=>"fuga"} )
|
155
|
+
allow(@manager).to receive(:valid?)
|
156
|
+
.and_return(true)
|
157
|
+
}
|
158
|
+
it "execute command with env-vars" do
|
159
|
+
FakeFS do
|
160
|
+
@manager.set("a","b","c")
|
161
|
+
@manager.set("a","b","d e f")
|
162
|
+
@manager.set("x","y","z")
|
163
|
+
|
164
|
+
expect(@manager).to receive(:system)
|
165
|
+
.with("a_b='d e f' x_y='z' ls -a b")
|
166
|
+
.and_return(true)
|
167
|
+
subject
|
168
|
+
end
|
169
|
+
end
|
170
|
+
end
|
171
|
+
|
172
|
+
context "has no valid secret" do
|
173
|
+
before {
|
174
|
+
allow(@manager).to receive(:valid?)
|
175
|
+
.and_return(false)
|
176
|
+
}
|
177
|
+
it "raise Keystorage::SecretMissMatch" do
|
178
|
+
expect { subject }.to raise_error(Keystorage::SecretMissMatch)
|
179
|
+
end
|
180
|
+
|
181
|
+
end
|
182
|
+
end
|
183
|
+
|
184
|
+
|
185
|
+
# private methods
|
186
|
+
describe "#sign" do
|
187
|
+
context "secret is nil" do
|
188
|
+
it "raise NoSecret" do
|
189
|
+
expect { @manager.send(:sign,"text",nil) }
|
190
|
+
.to raise_error(Keystorage::NoSecret)
|
191
|
+
end
|
192
|
+
|
193
|
+
end
|
194
|
+
context "secret is not nil" do
|
195
|
+
subject { @manager.send(:sign,"text","p@ssw0rd") }
|
196
|
+
it "returns sign" do
|
197
|
+
allow(OpenSSL::HMAC).to receive(:hexdigest)
|
198
|
+
.and_return("sample-sig")
|
199
|
+
|
200
|
+
is_expected.to eq "sample-sig"
|
201
|
+
end
|
202
|
+
|
203
|
+
end
|
204
|
+
end
|
205
|
+
|
206
|
+
describe "#token" do
|
207
|
+
subject { @manager.send(:token) }
|
208
|
+
|
209
|
+
it "returns token" do
|
210
|
+
allow(SecureRandom).to receive(:urlsafe_base64)
|
211
|
+
.and_return("sample-token")
|
212
|
+
is_expected.to eq "sample-token"
|
213
|
+
end
|
214
|
+
|
215
|
+
end
|
216
|
+
|
217
|
+
describe "#root" do
|
218
|
+
context "file has '@' key" do
|
219
|
+
let(:root) { {"test1"=>"test2"} }
|
220
|
+
let(:data) { { "@" => root,"group1" => {"key1"=>"value1"} } }
|
221
|
+
subject { @manager.send(:root) }
|
222
|
+
|
223
|
+
it "returns Hash of root" do
|
224
|
+
allow(@manager).to receive(:file)
|
225
|
+
.and_return(data)
|
226
|
+
is_expected.to eq root
|
227
|
+
end
|
228
|
+
end
|
229
|
+
context "file has no '@' key" do
|
230
|
+
let(:data) { { "group1" => {"key1"=>"value1"} } }
|
231
|
+
it "raise NoRootGroup" do
|
232
|
+
allow(@manager).to receive(:file)
|
233
|
+
.and_return(data)
|
234
|
+
|
235
|
+
expect { @manager.send(:root) }
|
236
|
+
.to raise_error(Keystorage::NoRootGroup)
|
237
|
+
end
|
238
|
+
|
239
|
+
end
|
240
|
+
end
|
241
|
+
|
242
|
+
describe "#valid?" do
|
243
|
+
context "file has no root" do
|
244
|
+
subject { @manager.send(:valid?) }
|
245
|
+
let(:data) { { "group1" => {"key1"=>"value1"} } }
|
246
|
+
it "writes root and return true" do
|
247
|
+
allow(@manager).to receive(:file)
|
248
|
+
.and_return(data)
|
249
|
+
allow(@manager).to receive(:write)
|
250
|
+
.and_return(true)
|
251
|
+
expect(@manager).to receive(:write)
|
252
|
+
is_expected.to eq true
|
253
|
+
end
|
254
|
+
end
|
255
|
+
end
|
256
|
+
|
257
|
+
describe "#encode" do
|
258
|
+
let(:str) { "test message" }
|
259
|
+
let(:secret) { "s3cr3t" }
|
260
|
+
subject { @manager.send(:encode,str,secret) }
|
261
|
+
it "returns encoded string" do
|
262
|
+
is_expected.to eq "a191a5111cfee36a94bba4dc0a17c31d"
|
263
|
+
end
|
264
|
+
end
|
265
|
+
|
266
|
+
describe "#decode" do
|
267
|
+
let(:str) { "test message" }
|
268
|
+
let(:encoded) { "a191a5111cfee36a94bba4dc0a17c31d" }
|
269
|
+
let(:secret) { "s3cr3t" }
|
270
|
+
subject { @manager.send(:decode,encoded,secret) }
|
271
|
+
it "returns decoded string" do
|
272
|
+
is_expected.to eq str
|
273
|
+
end
|
274
|
+
end
|
275
|
+
|
276
|
+
describe "#file" do
|
277
|
+
subject { @manager.send(:file) }
|
278
|
+
it "returns data from path" do
|
279
|
+
allow(@manager).to receive(:path)
|
280
|
+
.and_return(yaml("1"))
|
281
|
+
is_expected.to eq files["1"]
|
282
|
+
end
|
283
|
+
end
|
284
|
+
|
285
|
+
describe "#write" do
|
286
|
+
let(:data) { { :test => "rspec"} }
|
287
|
+
subject { @manager.send(:write,data) }
|
288
|
+
it "writes data to file" do
|
289
|
+
FakeFS do
|
290
|
+
expect { subject }.not_to raise_error
|
291
|
+
expect(@manager.send(:file).has_key?(:test)).to eq true
|
292
|
+
end
|
293
|
+
end
|
294
|
+
end
|
295
|
+
|
296
|
+
describe "#path" do
|
297
|
+
subject { @manager.send(:path) }
|
298
|
+
let(:file_path) { "abcdefgh" }
|
299
|
+
|
300
|
+
context "options[:path] is set" do
|
301
|
+
before{
|
302
|
+
allow(@manager).to receive(:options)
|
303
|
+
.and_return({:file =>file_path})
|
304
|
+
}
|
305
|
+
|
306
|
+
it "returns options[:path]" do
|
307
|
+
is_expected.to eq file_path
|
308
|
+
end
|
309
|
+
end
|
310
|
+
context "options[:path] is not set" do
|
311
|
+
before {
|
312
|
+
allow(@manager).to receive(:options)
|
313
|
+
.and_return({})
|
314
|
+
}
|
315
|
+
|
316
|
+
context "ENV KEYSTORAGE_FILE is set" do
|
317
|
+
before{
|
318
|
+
allow(ENV).to receive(:[])
|
319
|
+
.with('KEYSTORAGE_FILE')
|
320
|
+
.and_return(file_path)
|
321
|
+
}
|
322
|
+
it "returns KEYSTORAGE_FILE" do
|
323
|
+
is_expected.to eq file_path
|
324
|
+
end
|
325
|
+
end
|
326
|
+
|
327
|
+
context "ENV KEYSTORAGE_FILE is not set" do
|
328
|
+
before{
|
329
|
+
allow(ENV).to receive(:[])
|
330
|
+
.with('KEYSTORAGE_FILE')
|
331
|
+
.and_return(nil)
|
332
|
+
}
|
333
|
+
it "returns DEFAULT_FILE" do
|
334
|
+
is_expected.to eq Keystorage::DEFAULT_FILE
|
335
|
+
end
|
336
|
+
end
|
337
|
+
end
|
338
|
+
|
339
|
+
end
|
340
|
+
|
341
|
+
describe "#secret" do
|
342
|
+
subject { @manager.send(:secret) }
|
343
|
+
let(:password) { "abcedfg" }
|
344
|
+
|
345
|
+
context "options[:secret] is set" do
|
346
|
+
it "returns options[:secret]" do
|
347
|
+
allow(@manager).to receive(:options)
|
348
|
+
.and_return({:secret =>password})
|
349
|
+
is_expected.to eq password
|
350
|
+
end
|
351
|
+
end
|
352
|
+
context "options[:secret] is not set" do
|
353
|
+
before {
|
354
|
+
allow(@manager).to receive(:options)
|
355
|
+
.and_return({})
|
356
|
+
}
|
357
|
+
context "ENV KEYSTORAGE_SECRET is set" do
|
358
|
+
before {
|
359
|
+
allow(ENV).to receive(:[])
|
360
|
+
.with('KEYSTORAGE_SECRET')
|
361
|
+
.and_return(password)
|
362
|
+
}
|
363
|
+
|
364
|
+
it "returns secret ENV[:secret]" do
|
365
|
+
is_expected.to eq password
|
366
|
+
end
|
367
|
+
end
|
368
|
+
context "ENV KEYSTORAGE_SECRET is not set" do
|
369
|
+
before {
|
370
|
+
allow(ENV).to receive(:[])
|
371
|
+
.with('KEYSTORAGE_SECRET')
|
372
|
+
.and_return(nil)
|
373
|
+
}
|
374
|
+
it "returns DEFAULT_SECRET" do
|
375
|
+
is_expected.to eq Keystorage::DEFAULT_SECRET
|
376
|
+
end
|
377
|
+
end
|
378
|
+
end
|
379
|
+
|
380
|
+
end
|
10
381
|
|
382
|
+
after { FakeFS::FileSystem.clear }
|
11
383
|
end
|
12
|
-
# ks = keystorage::Manager.new(:file =>"",:secret=> "P@ssword")
|
13
|
-
# ks.get("mygroup","mykey") # => "mysecret"
|
@@ -0,0 +1,19 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
require 'keystorage'
|
3
|
+
|
4
|
+
describe Keystorage do
|
5
|
+
let(:test_class) { Struct.new(:a){ include Keystorage } }
|
6
|
+
subject { test_class.new }
|
7
|
+
|
8
|
+
describe "#render" do
|
9
|
+
|
10
|
+
context "Unknown format ':unknown' given" do
|
11
|
+
let(:format) { :unknown }
|
12
|
+
let(:string) { SecureRandom.urlsafe_base64(nil, false) }
|
13
|
+
it "raises Keystorage::FormatNotSupport" do
|
14
|
+
expect { subject.render(string,format) }.to raise_error(Keystorage::FormatNotSupport)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
end
|
19
|
+
end
|
data/spec/spec_helper.rb
CHANGED
@@ -1,3 +1,12 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
require 'simplecov'
|
3
|
+
require "codeclimate-test-reporter"
|
4
|
+
SimpleCov.formatter = SimpleCov::Formatter::MultiFormatter[
|
5
|
+
SimpleCov::Formatter::HTMLFormatter,
|
6
|
+
CodeClimate::TestReporter::Formatter
|
7
|
+
]
|
8
|
+
SimpleCov.start
|
9
|
+
|
1
10
|
# This file was generated by the `rspec --init` command. Conventionally, all
|
2
11
|
# specs live under a `spec` directory, which RSpec adds to the `$LOAD_PATH`.
|
3
12
|
# The generated `.rspec` file contains `--require spec_helper` which will cause
|
@@ -89,3 +98,4 @@ RSpec.configure do |config|
|
|
89
98
|
Kernel.srand config.seed
|
90
99
|
=end
|
91
100
|
end
|
101
|
+
require 'fakefs/spec_helpers'
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: keystorage
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.5.
|
4
|
+
version: 0.5.7
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Yoshihiro TAKAHARA
|
@@ -24,6 +24,20 @@ dependencies:
|
|
24
24
|
- - ">="
|
25
25
|
- !ruby/object:Gem::Version
|
26
26
|
version: '0'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: version
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0'
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ">="
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0'
|
27
41
|
- !ruby/object:Gem::Dependency
|
28
42
|
name: rake
|
29
43
|
requirement: !ruby/object:Gem::Requirement
|
@@ -53,7 +67,7 @@ dependencies:
|
|
53
67
|
- !ruby/object:Gem::Version
|
54
68
|
version: '0'
|
55
69
|
- !ruby/object:Gem::Dependency
|
56
|
-
name:
|
70
|
+
name: fakefs
|
57
71
|
requirement: !ruby/object:Gem::Requirement
|
58
72
|
requirements:
|
59
73
|
- - ">="
|
@@ -76,6 +90,7 @@ extra_rdoc_files:
|
|
76
90
|
files:
|
77
91
|
- ".gitignore"
|
78
92
|
- ".rspec"
|
93
|
+
- ".travis.yml"
|
79
94
|
- Gemfile
|
80
95
|
- LICENSE.txt
|
81
96
|
- README.md
|
@@ -87,10 +102,10 @@ files:
|
|
87
102
|
- lib/keystorage/cli.rb
|
88
103
|
- lib/keystorage/manager.rb
|
89
104
|
- spec/keystorage/cli_spec.rb
|
105
|
+
- spec/keystorage/files/1.yml
|
90
106
|
- spec/keystorage/manager_spec.rb
|
107
|
+
- spec/keystorage_spec.rb
|
91
108
|
- spec/spec_helper.rb
|
92
|
-
- test/helper.rb
|
93
|
-
- test/test_keystorage.rb
|
94
109
|
homepage: http://github.com/tumf/keystorage
|
95
110
|
licenses:
|
96
111
|
- MIT
|
@@ -117,8 +132,8 @@ specification_version: 4
|
|
117
132
|
summary: Simple password storage
|
118
133
|
test_files:
|
119
134
|
- spec/keystorage/cli_spec.rb
|
135
|
+
- spec/keystorage/files/1.yml
|
120
136
|
- spec/keystorage/manager_spec.rb
|
137
|
+
- spec/keystorage_spec.rb
|
121
138
|
- spec/spec_helper.rb
|
122
|
-
- test/helper.rb
|
123
|
-
- test/test_keystorage.rb
|
124
139
|
has_rdoc:
|
data/test/helper.rb
DELETED
@@ -1,18 +0,0 @@
|
|
1
|
-
require 'rubygems'
|
2
|
-
require 'bundler'
|
3
|
-
begin
|
4
|
-
Bundler.setup(:default, :development)
|
5
|
-
rescue Bundler::BundlerError => e
|
6
|
-
$stderr.puts e.message
|
7
|
-
$stderr.puts "Run `bundle install` to install missing gems"
|
8
|
-
exit e.status_code
|
9
|
-
end
|
10
|
-
require 'test/unit'
|
11
|
-
require 'shoulda'
|
12
|
-
|
13
|
-
$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
|
14
|
-
$LOAD_PATH.unshift(File.dirname(__FILE__))
|
15
|
-
require 'keystorage'
|
16
|
-
|
17
|
-
class Test::Unit::TestCase
|
18
|
-
end
|
data/test/test_keystorage.rb
DELETED
@@ -1,36 +0,0 @@
|
|
1
|
-
require 'helper'
|
2
|
-
require 'fileutils'
|
3
|
-
|
4
|
-
class TestKeystorage < Test::Unit::TestCase
|
5
|
-
@@file = ".testkey"
|
6
|
-
context "set / delete" do
|
7
|
-
setup do
|
8
|
-
Keystorage.set("abc","def","ghi",@@file)
|
9
|
-
Keystorage.set("abc","123","456",@@file)
|
10
|
-
end
|
11
|
-
teardown do
|
12
|
-
FileUtils.rm(@@file)
|
13
|
-
end
|
14
|
-
|
15
|
-
should "file exists" do
|
16
|
-
assert File.exists?(@@file)
|
17
|
-
end
|
18
|
-
|
19
|
-
should "exist def and 123" do
|
20
|
-
assert_equal "ghi",Keystorage.get("abc","def",@@file)
|
21
|
-
assert_equal "456",Keystorage.get("abc","123",@@file)
|
22
|
-
end
|
23
|
-
|
24
|
-
should "delete only def" do
|
25
|
-
Keystorage.delete("abc","def",@@file)
|
26
|
-
assert_equal "456",Keystorage.get("abc","123",@@file)
|
27
|
-
assert_equal false,Keystorage.get("abc","def",@@file)
|
28
|
-
end
|
29
|
-
|
30
|
-
should "delete abc group" do
|
31
|
-
Keystorage.delete("abc",nil,@@file)
|
32
|
-
assert_equal false,Keystorage.get("abc","123",@@file)
|
33
|
-
assert_equal false,Keystorage.get("abc","def",@@file)
|
34
|
-
end
|
35
|
-
end
|
36
|
-
end
|