passwd 0.0.6 → 0.0.7
Sign up to get free protection for your applications and to get access to all the features.
- data/README.md +1 -1
- data/lib/passwd.rb +18 -15
- data/lib/passwd/active_record.rb +28 -14
- data/lib/passwd/version.rb +1 -1
- data/spec/passwd/password_spec.rb +102 -89
- data/spec/passwd_spec.rb +129 -76
- data/spec/spec_helper.rb +15 -0
- metadata +5 -5
data/README.md
CHANGED
@@ -2,8 +2,8 @@
|
|
2
2
|
|
3
3
|
[](http://badge.fury.io/rb/passwd)
|
4
4
|
[](https://travis-ci.org/i2bskn/passwd)
|
5
|
-
[](https://codeclimate.com/github/i2bskn/passwd)
|
6
5
|
[](https://coveralls.io/r/i2bskn/passwd?branch=master)
|
6
|
+
[](https://codeclimate.com/github/i2bskn/passwd)
|
7
7
|
|
8
8
|
Password utilities.
|
9
9
|
|
data/lib/passwd.rb
CHANGED
@@ -19,33 +19,36 @@ module Passwd
|
|
19
19
|
|
20
20
|
class << self
|
21
21
|
def create(options={})
|
22
|
-
config = @@config.merge(options)
|
22
|
+
config = @@config.merge(config_validator(options))
|
23
23
|
letters = get_retters(config)
|
24
|
-
|
25
|
-
# Create random password
|
26
24
|
Array.new(config[:length]){letters[rand(letters.size)]}.join
|
27
25
|
end
|
28
26
|
|
29
|
-
def get_retters(config)
|
30
|
-
# Create letters
|
31
|
-
letters = Array.new
|
32
|
-
letters += config[:letters_lower] if config[:lower]
|
33
|
-
letters += config[:letters_upper] if config[:upper]
|
34
|
-
letters += config[:letters_number] if config[:number]
|
35
|
-
letters
|
36
|
-
end
|
37
|
-
|
38
27
|
def auth(password_text, salt_hash, password_hash)
|
39
28
|
enc_pass = Passwd.hashing("#{salt_hash}#{password_text}")
|
40
29
|
password_hash == enc_pass
|
41
30
|
end
|
42
31
|
|
43
|
-
def hashing(
|
44
|
-
Digest::SHA1.hexdigest
|
32
|
+
def hashing(password)
|
33
|
+
Digest::SHA1.hexdigest password
|
45
34
|
end
|
46
35
|
|
47
36
|
def config(options={})
|
48
|
-
@@config.merge!(options)
|
37
|
+
@@config.merge!(config_validator(options))
|
38
|
+
end
|
39
|
+
|
40
|
+
private
|
41
|
+
def get_retters(config)
|
42
|
+
["lower", "upper", "number"].inject([]) do |letters, type|
|
43
|
+
letters.concat(config["letters_#{type}".to_sym]) if config[type.to_sym]
|
44
|
+
letters
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
def config_validator(config_hash)
|
49
|
+
parameters = [:length, :lower, :upper, :number, :letters_lower, :letters_upper, :letters_number]
|
50
|
+
config = config_hash.inject({}){|r, (k, v)| r.store(k.to_sym, v); r}
|
51
|
+
config.select{|k, v| parameters.include? k}
|
49
52
|
end
|
50
53
|
end
|
51
54
|
end
|
data/lib/passwd/active_record.rb
CHANGED
@@ -4,30 +4,44 @@ module Passwd
|
|
4
4
|
module ActiveRecord
|
5
5
|
module ClassMethods
|
6
6
|
def define_column(options={})
|
7
|
-
|
8
|
-
|
9
|
-
|
7
|
+
id_name = options[:id] || :email
|
8
|
+
salt_name = options[:salt] || :salt
|
9
|
+
password_name = options[:password] || :password
|
10
10
|
|
11
|
+
define_singleton_auth(id_name, salt_name, password_name)
|
12
|
+
define_instance_auth(id_name, salt_name, password_name)
|
13
|
+
define_set_password(id_name, salt_name, password_name)
|
14
|
+
define_update_password(salt_name, password_name)
|
15
|
+
end
|
16
|
+
|
17
|
+
private
|
18
|
+
def define_singleton_auth(id_name, salt_name, password_name)
|
11
19
|
define_singleton_method :authenticate do |id, pass|
|
12
|
-
user = self.where(
|
13
|
-
user if user && Passwd.auth(pass, user.send(
|
20
|
+
user = self.where(id_name => id).first
|
21
|
+
user if user && Passwd.auth(pass, user.send(salt_name), user.send(password_name))
|
14
22
|
end
|
23
|
+
end
|
15
24
|
|
25
|
+
def define_instance_auth(id_name, salt_name, password_name)
|
16
26
|
define_method :authenticate do |pass|
|
17
|
-
Passwd.auth(pass, self.send(
|
27
|
+
Passwd.auth(pass, self.send(salt_name), self.send(password_name))
|
18
28
|
end
|
29
|
+
end
|
19
30
|
|
31
|
+
def define_set_password(id_name, salt_name, password_name)
|
20
32
|
define_method :set_password do |pass=nil|
|
21
33
|
password = pass || Passwd.create
|
22
|
-
salt = self.send(
|
23
|
-
self.send("#{
|
24
|
-
self.send("#{
|
34
|
+
salt = self.send(salt_name) || Passwd.hashing("#{self.send(id_name)}#{Time.now.to_s}")
|
35
|
+
self.send("#{salt_name.to_s}=", salt)
|
36
|
+
self.send("#{password_name.to_s}=", Passwd.hashing("#{salt}#{password}"))
|
25
37
|
password
|
26
38
|
end
|
39
|
+
end
|
27
40
|
|
28
|
-
|
29
|
-
|
30
|
-
|
41
|
+
def define_update_password(salt_name, password_name)
|
42
|
+
define_method :update_password do |old_pass, new_pass|
|
43
|
+
if Passwd.auth(old_pass, self.send(salt_name), self.send(password_name))
|
44
|
+
set_password(new_pass)
|
31
45
|
else
|
32
46
|
false
|
33
47
|
end
|
@@ -36,8 +50,8 @@ module Passwd
|
|
36
50
|
end
|
37
51
|
|
38
52
|
class << self
|
39
|
-
def included(
|
40
|
-
|
53
|
+
def included(base)
|
54
|
+
base.extend ClassMethods
|
41
55
|
end
|
42
56
|
end
|
43
57
|
end
|
data/lib/passwd/version.rb
CHANGED
@@ -3,136 +3,149 @@
|
|
3
3
|
require "spec_helper"
|
4
4
|
|
5
5
|
describe Passwd::Password do
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
6
|
+
let(:default) {@default}
|
7
|
+
let(:password) {Passwd::Password.new}
|
8
|
+
|
9
|
+
describe "#initialize" do
|
10
|
+
context "with default params" do
|
11
|
+
let!(:password_text) {
|
12
|
+
password_text = Passwd.create
|
13
|
+
Passwd.should_receive(:create).and_return(password_text)
|
14
|
+
password_text
|
15
|
+
}
|
16
|
+
|
17
|
+
let!(:time_now) {
|
18
|
+
time_now = Time.now
|
19
|
+
Time.should_receive(:now).and_return(time_now)
|
20
|
+
time_now
|
21
|
+
}
|
22
|
+
|
23
|
+
it "@text should be a random password" do
|
24
|
+
expect(password.text.size).to eq(default[:length])
|
25
|
+
expect(password.text).to eq(password_text)
|
26
|
+
end
|
27
|
+
|
28
|
+
it "@salt_text should be a auto created" do
|
29
|
+
expect(password.salt_text).to eq(time_now.to_s)
|
30
|
+
end
|
31
|
+
|
32
|
+
it "@salt_hash should be a hashed salt" do
|
33
|
+
expect(password.salt_hash).to eq(Passwd.hashing(time_now.to_s))
|
34
|
+
end
|
35
|
+
|
36
|
+
it "@password_hash should be a hashed password with salt" do
|
37
|
+
password_hash = Passwd.hashing("#{Passwd.hashing(time_now.to_s)}#{password_text}")
|
38
|
+
expect(password.hash).to eq(password_hash)
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
context "with custom params" do
|
43
|
+
let(:password_text) {Passwd.create}
|
44
|
+
let(:salt_text) {"salt"}
|
45
|
+
let!(:time_now) {
|
46
|
+
time_now = Time.now
|
47
|
+
Time.stub(:create).and_return(time_now)
|
48
|
+
time_now
|
49
|
+
}
|
50
|
+
|
51
|
+
it "@text is specified password" do
|
52
|
+
password = Passwd::Password.new(password: password_text)
|
53
|
+
expect(password.text).to eq(password_text)
|
54
|
+
end
|
55
|
+
|
56
|
+
it "@hash is hash of specified password" do
|
57
|
+
password = Passwd::Password.new(password: password_text)
|
58
|
+
password_hash = Passwd.hashing("#{Passwd.hashing(time_now.to_s)}#{password_text}")
|
59
|
+
expect(password.hash).to eq(password_hash)
|
60
|
+
end
|
61
|
+
|
62
|
+
it "@salt_text is specified salt" do
|
63
|
+
password = Passwd::Password.new(salt_text: salt_text)
|
64
|
+
expect(password.salt_text).to eq(salt_text)
|
65
|
+
end
|
66
|
+
|
67
|
+
it "@salt_hash is hash of specified salt" do
|
68
|
+
salt_hash = Passwd.hashing(salt_text)
|
69
|
+
password = Passwd::Password.new(salt_text: salt_text)
|
70
|
+
expect(password.salt_hash).to eq(salt_hash)
|
71
|
+
end
|
40
72
|
end
|
41
73
|
end
|
42
74
|
|
43
|
-
|
44
|
-
before(:each) do
|
45
|
-
@password = Passwd::Password.new(password: "Secret!!")
|
46
|
-
end
|
47
|
-
|
75
|
+
describe "#text=" do
|
48
76
|
it "@text is changed" do
|
49
|
-
old_password =
|
50
|
-
|
51
|
-
expect(
|
77
|
+
old_password = password.text
|
78
|
+
password.text = password.text.reverse
|
79
|
+
expect(password.text).not_to eq(old_password)
|
52
80
|
end
|
53
81
|
|
54
82
|
it "@hash is changed" do
|
55
|
-
old_hash =
|
56
|
-
|
57
|
-
expect(
|
83
|
+
old_hash = password.hash
|
84
|
+
new_password = password.text = password.text.reverse
|
85
|
+
expect(password.hash).not_to eq(old_hash)
|
86
|
+
expect(password.hash).to eq(Passwd.hashing("#{password.salt_hash}#{new_password}"))
|
58
87
|
end
|
59
88
|
end
|
60
89
|
|
61
|
-
|
62
|
-
before(:each) do
|
63
|
-
@password = Passwd::Password.new
|
64
|
-
end
|
65
|
-
|
90
|
+
describe "#hash=" do
|
66
91
|
it "@text is nil" do
|
67
|
-
|
68
|
-
expect(
|
92
|
+
password.hash = Passwd.hashing("secret")
|
93
|
+
expect(password.text).to be_nil
|
69
94
|
end
|
70
95
|
|
71
96
|
it "@hash is changed" do
|
72
|
-
old_hash =
|
73
|
-
|
74
|
-
expect(
|
97
|
+
old_hash = password.hash
|
98
|
+
password.hash = Passwd.hashing("secret")
|
99
|
+
expect(password.hash).not_to eq(old_hash)
|
75
100
|
end
|
76
101
|
end
|
77
102
|
|
78
|
-
|
79
|
-
before(:each) do
|
80
|
-
@password = Passwd::Password.new
|
81
|
-
end
|
82
|
-
|
103
|
+
describe "#salt_text=" do
|
83
104
|
it "@salt_text is changed" do
|
84
|
-
|
85
|
-
|
86
|
-
expect(
|
105
|
+
old_salt = password.salt_text
|
106
|
+
password.salt_text = "salt"
|
107
|
+
expect(password.salt_text).not_to eq(old_salt)
|
87
108
|
end
|
88
109
|
|
89
110
|
it "@salt_hash is changed" do
|
90
|
-
|
91
|
-
|
92
|
-
expect(
|
111
|
+
old_salt = password.salt_hash
|
112
|
+
password.salt_text = "salt"
|
113
|
+
expect(password.salt_hash).not_to eq(old_salt)
|
93
114
|
end
|
94
115
|
|
95
116
|
it "@hash is changed" do
|
96
|
-
old_hash =
|
97
|
-
|
98
|
-
expect(
|
117
|
+
old_hash = password.hash
|
118
|
+
password.salt_text = "salt"
|
119
|
+
expect(password.hash).not_to eq(old_hash)
|
99
120
|
end
|
100
121
|
end
|
101
122
|
|
102
|
-
|
103
|
-
before(:each) do
|
104
|
-
@password = Passwd::Password.new
|
105
|
-
end
|
106
|
-
|
123
|
+
describe "#salt_hash=" do
|
107
124
|
it "@salt_text is nil" do
|
108
|
-
|
109
|
-
expect(
|
125
|
+
password.salt_hash = Passwd.hashing("salt")
|
126
|
+
expect(password.salt_text).to eq(nil)
|
110
127
|
end
|
111
128
|
|
112
129
|
it "@salt_hash is changed" do
|
113
|
-
old_salt_hash =
|
114
|
-
|
115
|
-
expect(
|
130
|
+
old_salt_hash = password.salt_hash
|
131
|
+
password.salt_hash = Passwd.hashing("salt")
|
132
|
+
expect(password.salt_hash).not_to eq(old_salt_hash)
|
116
133
|
end
|
117
134
|
|
118
135
|
it "@hash is changed" do
|
119
|
-
old_hash =
|
120
|
-
|
121
|
-
expect(
|
136
|
+
old_hash = password.hash
|
137
|
+
password.salt_hash = Passwd.hashing("salt")
|
138
|
+
expect(password.hash).not_to eq(old_hash)
|
122
139
|
end
|
123
140
|
end
|
124
141
|
|
125
|
-
|
126
|
-
before(:each) do
|
127
|
-
@password = Passwd::Password.new
|
128
|
-
end
|
129
|
-
|
142
|
+
describe "#==" do
|
130
143
|
it "return true with valid password" do
|
131
|
-
expect(
|
144
|
+
expect(password == password.text).to be_true
|
132
145
|
end
|
133
146
|
|
134
147
|
it "return false with invalid password" do
|
135
|
-
expect(
|
148
|
+
expect(password == "secret").to be_false
|
136
149
|
end
|
137
150
|
end
|
138
151
|
end
|
data/spec/passwd_spec.rb
CHANGED
@@ -3,82 +3,135 @@
|
|
3
3
|
require "spec_helper"
|
4
4
|
|
5
5
|
describe Passwd do
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
6
|
+
let(:default) {@default}
|
7
|
+
|
8
|
+
describe "default settings" do
|
9
|
+
let(:config) {Passwd.config}
|
10
|
+
|
11
|
+
it "length should be a default" do
|
12
|
+
expect(config[:length]).to eq(default[:length])
|
13
|
+
end
|
14
|
+
|
15
|
+
it "lower should be a default" do
|
16
|
+
expect(config[:lower]).to eq(default[:lower])
|
17
|
+
end
|
18
|
+
|
19
|
+
it "upper should be a default" do
|
20
|
+
expect(config[:upper]).to eq(default[:upper])
|
21
|
+
end
|
22
|
+
|
23
|
+
it "number should be a default" do
|
24
|
+
expect(config[:number]).to eq(default[:number])
|
25
|
+
end
|
26
|
+
|
27
|
+
it "letters_lower should be a default" do
|
28
|
+
expect(config[:letters_lower]).to eq(default[:letters_lower])
|
29
|
+
end
|
30
|
+
|
31
|
+
it "letters_upper should be a default" do
|
32
|
+
expect(config[:letters_upper]).to eq(default[:letters_upper])
|
33
|
+
end
|
34
|
+
|
35
|
+
it "letters_number should be a default" do
|
36
|
+
expect(config[:letters_number]).to eq(default[:letters_number])
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
describe ".create" do
|
41
|
+
it "create random password" do
|
42
|
+
password = Passwd.create
|
43
|
+
expect(password.is_a? String).to be_true
|
44
|
+
expect(password.size).to eq(default[:length])
|
45
|
+
end
|
46
|
+
|
47
|
+
it "password was created specified characters" do
|
48
|
+
expect(Passwd.create(length: 10).size).to eq(10)
|
49
|
+
end
|
50
|
+
|
51
|
+
it "password create without lower case" do
|
52
|
+
password = Passwd.create lower: false
|
53
|
+
expect(default[:letters_lower].include? password).to be_false
|
54
|
+
end
|
55
|
+
|
56
|
+
it "password create without upper case" do
|
57
|
+
password = Passwd.create upper: false
|
58
|
+
expect(default[:letters_upper].include? password).to be_false
|
59
|
+
end
|
60
|
+
|
61
|
+
it "password create without number" do
|
62
|
+
password = Passwd.create(number: false)
|
63
|
+
expect(default[:letters_number].include? password).to be_false
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
describe ".auth" do
|
68
|
+
let!(:password) do
|
69
|
+
password = Passwd.create
|
70
|
+
salt_hash = Passwd.hashing(Time.now.to_s)
|
71
|
+
password_hash = Passwd.hashing("#{salt_hash}#{password}")
|
72
|
+
{text: password, salt: salt_hash, hash: password_hash}
|
73
|
+
end
|
74
|
+
|
75
|
+
it "return true with valid password" do
|
76
|
+
expect(Passwd.auth(password[:text], password[:salt], password[:hash])).to be_true
|
77
|
+
end
|
78
|
+
|
79
|
+
it "return false with invalid password" do
|
80
|
+
expect(Passwd.auth("invalid", password[:salt], password[:hash])).to be_false
|
81
|
+
end
|
82
|
+
|
83
|
+
it "should create exception if not specified arguments" do
|
84
|
+
expect(proc{Passwd.auth}).to raise_error
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
describe ".hashing" do
|
89
|
+
it "return hashed password" do
|
90
|
+
Digest::SHA1.should_receive(:hexdigest).with("secret").and_return("hash")
|
91
|
+
expect(Passwd.hashing("secret")).to eq("hash")
|
92
|
+
end
|
93
|
+
|
94
|
+
it "should create exception if not specified argument" do
|
95
|
+
expect(proc{Passwd.hashing}).to raise_error
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
describe ".config" do
|
100
|
+
after {Passwd.config(default)}
|
101
|
+
|
102
|
+
it "return config hash" do
|
103
|
+
expect(Passwd.config.is_a? Hash).to be_true
|
104
|
+
end
|
105
|
+
|
106
|
+
it "set config value" do
|
107
|
+
Passwd.config(length: 10)
|
108
|
+
expect(Passwd.config[:length]).not_to eq(default[:length])
|
109
|
+
expect(Passwd.config[:lower]).to eq(default[:lower])
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
describe ".get_retters" do
|
114
|
+
it "return letters" do
|
115
|
+
letters = Passwd.send(:get_retters, default)
|
116
|
+
expect(letters.is_a? Array).to be_true
|
117
|
+
expect(letters.select{|s| s.is_a? String}).to eq(letters)
|
118
|
+
expect(letters).to eq(default[:letters_lower] + default[:letters_upper] + default[:letters_number])
|
119
|
+
end
|
120
|
+
|
121
|
+
it "should create exception if not specified argument" do
|
122
|
+
expect(proc{Passwd.send(:get_retters)}).to raise_error
|
123
|
+
end
|
124
|
+
end
|
125
|
+
|
126
|
+
describe ".config_validator" do
|
127
|
+
it "convert to symbol the key" do
|
128
|
+
config = default.inject({}){|r, (k, v)| r.store(k.to_s, v); r}
|
129
|
+
expect(Passwd.send(:config_validator, config)).to eq(default)
|
130
|
+
end
|
131
|
+
|
132
|
+
it "delete not defined parameters" do
|
133
|
+
config = default.merge(invalid_parameter: true)
|
134
|
+
expect(Passwd.send(:config_validator, config)).to eq(default)
|
82
135
|
end
|
83
136
|
end
|
84
137
|
end
|
data/spec/spec_helper.rb
CHANGED
@@ -2,3 +2,18 @@ require "passwd"
|
|
2
2
|
|
3
3
|
require "coveralls"
|
4
4
|
Coveralls.wear!
|
5
|
+
|
6
|
+
RSpec.configure do |config|
|
7
|
+
config.order = "random"
|
8
|
+
config.before do
|
9
|
+
@default = {
|
10
|
+
length: 8,
|
11
|
+
lower: true,
|
12
|
+
upper: true,
|
13
|
+
number: true,
|
14
|
+
letters_lower: ('a'..'z').to_a,
|
15
|
+
letters_upper: ('A'..'Z').to_a,
|
16
|
+
letters_number: ('0'..'9').to_a
|
17
|
+
}
|
18
|
+
end
|
19
|
+
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: passwd
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.7
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2013-05-
|
12
|
+
date: 2013-05-29 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: bundler
|
@@ -97,7 +97,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
97
97
|
version: '0'
|
98
98
|
segments:
|
99
99
|
- 0
|
100
|
-
hash:
|
100
|
+
hash: 35413922955959980
|
101
101
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
102
102
|
none: false
|
103
103
|
requirements:
|
@@ -106,10 +106,10 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
106
106
|
version: '0'
|
107
107
|
segments:
|
108
108
|
- 0
|
109
|
-
hash:
|
109
|
+
hash: 35413922955959980
|
110
110
|
requirements: []
|
111
111
|
rubyforge_project:
|
112
|
-
rubygems_version: 1.8.
|
112
|
+
rubygems_version: 1.8.25
|
113
113
|
signing_key:
|
114
114
|
specification_version: 3
|
115
115
|
summary: Password utility
|