passwd 0.1.5 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (98) hide show
  1. checksums.yaml +4 -4
  2. data/.travis.yml +4 -1
  3. data/CHANGELOG.md +30 -1
  4. data/Gemfile +0 -5
  5. data/LICENSE.txt +2 -1
  6. data/README.md +96 -156
  7. data/Rakefile +2 -1
  8. data/example/.gitignore +16 -0
  9. data/example/Gemfile +25 -0
  10. data/example/README.rdoc +28 -0
  11. data/example/Rakefile +6 -0
  12. data/example/app/assets/images/.keep +0 -0
  13. data/example/app/assets/javascripts/application.js +16 -0
  14. data/example/app/assets/stylesheets/application.css +16 -0
  15. data/example/app/controllers/application_controller.rb +10 -0
  16. data/example/app/controllers/concerns/.keep +0 -0
  17. data/example/app/controllers/profiles_controller.rb +28 -0
  18. data/example/app/controllers/root_controller.rb +5 -0
  19. data/example/app/controllers/sessions_controller.rb +29 -0
  20. data/example/app/helpers/application_helper.rb +2 -0
  21. data/example/app/mailers/.keep +0 -0
  22. data/example/app/models/.keep +0 -0
  23. data/example/app/models/concerns/.keep +0 -0
  24. data/example/app/models/user.rb +4 -0
  25. data/example/app/views/layouts/application.html.erb +15 -0
  26. data/example/app/views/profiles/edit.html.erb +14 -0
  27. data/example/app/views/profiles/show.html.erb +12 -0
  28. data/example/app/views/root/index.html.erb +5 -0
  29. data/example/app/views/sessions/new.html.erb +6 -0
  30. data/example/bin/bundle +3 -0
  31. data/example/bin/rails +4 -0
  32. data/example/bin/rake +4 -0
  33. data/example/config.ru +4 -0
  34. data/example/config/application.rb +40 -0
  35. data/example/config/boot.rb +4 -0
  36. data/example/config/database.yml +26 -0
  37. data/example/config/environment.rb +5 -0
  38. data/example/config/environments/development.rb +37 -0
  39. data/example/config/environments/production.rb +78 -0
  40. data/example/config/environments/test.rb +39 -0
  41. data/example/config/initializers/assets.rb +8 -0
  42. data/example/config/initializers/backtrace_silencers.rb +7 -0
  43. data/example/config/initializers/cookies_serializer.rb +3 -0
  44. data/example/config/initializers/filter_parameter_logging.rb +4 -0
  45. data/example/config/initializers/inflections.rb +16 -0
  46. data/example/config/initializers/mime_types.rb +4 -0
  47. data/example/config/initializers/passwd.rb +41 -0
  48. data/example/config/initializers/session_store.rb +3 -0
  49. data/example/config/initializers/wrap_parameters.rb +14 -0
  50. data/example/config/locales/en.yml +23 -0
  51. data/example/config/routes.rb +16 -0
  52. data/example/config/secrets.yml +22 -0
  53. data/example/db/migrate/20141122165914_create_users.rb +13 -0
  54. data/example/db/schema.rb +25 -0
  55. data/example/db/seeds.rb +7 -0
  56. data/example/lib/assets/.keep +0 -0
  57. data/example/lib/tasks/.keep +0 -0
  58. data/example/lib/tasks/user.rake +12 -0
  59. data/example/log/.keep +0 -0
  60. data/example/public/404.html +67 -0
  61. data/example/public/422.html +67 -0
  62. data/example/public/500.html +66 -0
  63. data/example/public/favicon.ico +0 -0
  64. data/example/public/robots.txt +5 -0
  65. data/example/vendor/assets/javascripts/.keep +0 -0
  66. data/example/vendor/assets/stylesheets/.keep +0 -0
  67. data/lib/generators/passwd/config_generator.rb +13 -0
  68. data/lib/generators/passwd/templates/passwd_config.rb +41 -0
  69. data/lib/passwd.rb +18 -3
  70. data/lib/passwd/action_controller_ext.rb +48 -0
  71. data/lib/passwd/active_record_ext.rb +65 -0
  72. data/lib/passwd/base.rb +17 -62
  73. data/lib/passwd/configuration.rb +82 -0
  74. data/lib/passwd/errors.rb +6 -13
  75. data/lib/passwd/password.rb +73 -25
  76. data/lib/passwd/policy.rb +28 -0
  77. data/lib/passwd/railtie.rb +19 -0
  78. data/lib/passwd/salt.rb +50 -0
  79. data/lib/passwd/version.rb +2 -1
  80. data/passwd.gemspec +8 -2
  81. data/spec/passwd/.keep +0 -0
  82. data/spec/passwd/active_record_ext_spec.rb +80 -0
  83. data/spec/passwd/base_spec.rb +55 -231
  84. data/spec/passwd/configuration_spec.rb +50 -0
  85. data/spec/passwd/password_spec.rb +129 -123
  86. data/spec/spec_helper.rb +14 -3
  87. data/spec/support/data_util.rb +11 -0
  88. data/spec/support/paths.rb +2 -0
  89. metadata +164 -30
  90. data/lib/passwd/active_record.rb +0 -62
  91. data/lib/passwd/configuration/abstract_config.rb +0 -37
  92. data/lib/passwd/configuration/config.rb +0 -24
  93. data/lib/passwd/configuration/policy.rb +0 -46
  94. data/lib/passwd/configuration/tmp_config.rb +0 -18
  95. data/spec/passwd/active_record_spec.rb +0 -163
  96. data/spec/passwd/configuration/config_spec.rb +0 -250
  97. data/spec/passwd/configuration/policy_spec.rb +0 -133
  98. data/spec/passwd/configuration/tmp_config_spec.rb +0 -265
@@ -0,0 +1,28 @@
1
+ module Passwd
2
+ class Policy
3
+ VALID_OPTIONS = [
4
+ :min_length,
5
+ :require_lower,
6
+ :require_upper,
7
+ :require_number
8
+ ].freeze
9
+
10
+ attr_accessor *VALID_OPTIONS
11
+
12
+ def initialize
13
+ reset
14
+ end
15
+
16
+ def configure
17
+ yield self
18
+ end
19
+
20
+ def reset
21
+ self.min_length = 8
22
+ self.require_lower = true
23
+ self.require_upper = false
24
+ self.require_number = true
25
+ end
26
+ end
27
+ end
28
+
@@ -0,0 +1,19 @@
1
+ module Passwd
2
+ class Railtie < ::Rails::Railtie
3
+ config.passwd = ActiveSupport::OrderedOptions.new
4
+
5
+ initializer "passwd" do
6
+ require "passwd/action_controller_ext"
7
+ require "passwd/active_record_ext"
8
+
9
+ ActiveSupport.on_load(:action_controller) do
10
+ ::ActionController::Base.send(:include, Passwd::ActionControllerExt)
11
+ end
12
+
13
+ ActiveSupport.on_load(:active_record) do
14
+ ::ActiveRecord::Base.send(:extend, Passwd::ActiveRecordExt)
15
+ end
16
+ end
17
+ end
18
+ end
19
+
@@ -0,0 +1,50 @@
1
+ module Passwd
2
+ class Salt
3
+ include Base
4
+
5
+ attr_reader :plain, :hash
6
+
7
+ def initialize(options = {})
8
+ options = default_options.merge(options)
9
+
10
+ @password = options[:password]
11
+ if options.has_key?(:hash)
12
+ @plain = nil
13
+ @hash = options[:hash]
14
+ else
15
+ @plain = options[:plain] || (raise ArgumentError)
16
+ @hash = digest_without_stretching(@plain)
17
+ end
18
+ end
19
+
20
+ def update_plain(value)
21
+ @plain = value
22
+ @hash = digest_without_stretching(@plain)
23
+ update_password!
24
+ end
25
+
26
+ def update_hash(value)
27
+ @plain = nil
28
+ @hash = value
29
+ update_password!
30
+ end
31
+
32
+ def self.from_plain(value, password = nil)
33
+ new(plain: value, password: password)
34
+ end
35
+
36
+ def self.from_hash(value, password = nil)
37
+ new(hash: value, password: password)
38
+ end
39
+
40
+ private
41
+ def default_options
42
+ {plain: Time.now.to_s}
43
+ end
44
+
45
+ def update_password!
46
+ @password.rehash if @password && @password.plain
47
+ end
48
+ end
49
+ end
50
+
@@ -1,3 +1,4 @@
1
1
  module Passwd
2
- VERSION = "0.1.5"
2
+ VERSION = "0.2.0"
3
3
  end
4
+
@@ -17,7 +17,13 @@ Gem::Specification.new do |spec|
17
17
  spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
18
18
  spec.require_paths = ["lib"]
19
19
 
20
- spec.add_development_dependency "bundler", "~> 1.3"
20
+ spec.add_development_dependency "bundler"
21
21
  spec.add_development_dependency "rake"
22
- spec.add_development_dependency "rspec"
22
+ spec.add_development_dependency "coveralls"
23
+ spec.add_development_dependency "simplecov"
24
+ spec.add_development_dependency "rails"
25
+ spec.add_development_dependency "rspec-rails"
26
+ spec.add_development_dependency "sqlite3"
27
+ spec.add_development_dependency "database_rewinder"
23
28
  end
29
+
File without changes
@@ -0,0 +1,80 @@
1
+ require "spec_helper"
2
+
3
+ describe Passwd::ActiveRecordExt do
4
+ describe ".with_authenticate" do
5
+ it { expect(ActiveRecord::Base).to respond_to(:with_authenticate) }
6
+
7
+ context User do
8
+ let(:new_pass) { "NewPassw0rd" }
9
+ let(:password) { Passwd::Password.new }
10
+ let(:user) {
11
+ User.create(
12
+ name: "i2bskn",
13
+ email: "i2bskn@example.com",
14
+ salt: password.salt.hash,
15
+ password: password.hash
16
+ )
17
+ }
18
+
19
+ it { is_expected.to respond_to(:passwd) }
20
+ it { is_expected.to respond_to(:authenticate) }
21
+ it { expect(User).to respond_to(:authenticate) }
22
+ it { is_expected.to respond_to(:set_password) }
23
+ it { is_expected.to respond_to(:update_password) }
24
+
25
+ context "#passwd" do
26
+ after { user.passwd }
27
+
28
+ it { expect(user.passwd.is_a?(Passwd::Password)).to be_truthy }
29
+ it { expect(user).to receive(:reload) }
30
+ it { expect(Passwd::Password).to receive(:from_hash) }
31
+
32
+ it {
33
+ user = User.new
34
+ expect(user).to receive(:set_password)
35
+ user.passwd
36
+ }
37
+ end
38
+
39
+ context ".authenticate" do
40
+ before { user }
41
+
42
+ it { expect(User.authenticate("i2bskn@example.com", password.plain)).not_to be_falsy }
43
+ it { expect(User.authenticate("i2bskn@example.com", "invalid")).to be_falsy }
44
+ end
45
+
46
+ context "#authenticate" do
47
+ it { expect(user.authenticate(password.plain)).not_to be_falsy }
48
+ it { expect(user.authenticate("invalid")).to be_falsy }
49
+ end
50
+
51
+ context "#set_password" do
52
+ it { expect { user.set_password }.to change { user.password } }
53
+ it { expect { user.set_password(new_pass) }.to change { user.password } }
54
+ end
55
+
56
+ context "#update_password" do
57
+ before { user }
58
+
59
+ it {
60
+ expect {
61
+ user.update_password(password.plain, new_pass)
62
+ }.to change { user.password }
63
+ }
64
+
65
+ it {
66
+ expect {
67
+ user.update_password(password.plain, "secret", true)
68
+ }.to raise_error(Passwd::PolicyNotMatch)
69
+ }
70
+
71
+ it {
72
+ expect {
73
+ user.update_password("invalid", new_pass)
74
+ }.to raise_error(Passwd::AuthenticationFails)
75
+ }
76
+ end
77
+ end
78
+ end
79
+ end
80
+
@@ -1,236 +1,60 @@
1
- # coding: utf-8
2
-
3
1
  require "spec_helper"
4
2
 
5
- describe Passwd do
6
- describe "extended Base" do
7
- describe "#create" do
8
- context "without arguments" do
9
- let(:password) {Passwd.create}
10
-
11
- it "TmpConfig should not be generated" do
12
- Passwd::TmpConfig.should_not_receive(:new)
13
- expect{password}.not_to raise_error
14
- end
15
-
16
- it "created password should be String object" do
17
- expect(password.is_a? String).to be_true
18
- end
19
-
20
- it "created password length should be default length" do
21
- expect(password.size).to eq(8)
22
- end
23
- end
24
-
25
- context "with arguments" do
26
- it "TmpConfig should be generated" do
27
- tmp_config = double("tmp_config mock", length: 8, letters: ["a", "b"])
28
- Passwd::TmpConfig.should_receive(:new).and_return(tmp_config)
29
- expect{Passwd.create(length: 10)}.not_to raise_error
30
- end
31
-
32
- it "password was created specified characters" do
33
- expect(Passwd.create(length: 10).size).to eq(10)
34
- end
35
-
36
- it "password create without lower case" do
37
- expect(("a".."z").to_a.include? Passwd.create(lower: false)).to be_false
38
- end
39
-
40
- it "password create without upper case" do
41
- expect(("A".."Z").to_a.include? Passwd.create(upper: false)).to be_false
42
- end
43
-
44
- it "password create without number" do
45
- expect(("0".."9").to_a.include? Passwd.create(number: false)).to be_false
46
- end
47
- end
48
- end
49
-
50
- describe "#auth" do
51
- let!(:password) do
52
- password = Passwd.create
53
- salt_hash = Passwd.hashing(Time.now.to_s)
54
- password_hash = Passwd.hashing("#{salt_hash}#{password}")
55
- {text: password, salt: salt_hash, hash: password_hash}
56
- end
57
-
58
- it "return true with valid password" do
59
- expect(Passwd.auth(password[:text], password[:salt], password[:hash])).to be_true
60
- end
61
-
62
- it "return false with invalid password" do
63
- expect(Passwd.auth("invalid", password[:salt], password[:hash])).to be_false
64
- end
65
-
66
- it "should create exception if not specified arguments" do
67
- expect(proc{Passwd.auth}).to raise_error
68
- end
69
- end
70
-
71
- describe "#hashing" do
72
- it "should call SHA512.#hexdigest" do
73
- Digest::SHA512.should_receive(:hexdigest)
74
- Passwd.hashing("secret")
75
- end
76
-
77
- it "return hashed password" do
78
- hashed = Digest::SHA512.hexdigest "secret"
79
- expect(Passwd.hashing("secret")).to eq(hashed)
80
- end
81
-
82
- it "return hashed password specified algorithm" do
83
- hashed = Digest::SHA256.hexdigest "secret"
84
- expect(Passwd.hashing("secret", :sha256)).to eq(hashed)
85
- end
86
-
87
- it "should create exception if not specified argument" do
88
- expect(proc{Passwd.hashing}).to raise_error
89
- end
90
- end
91
-
92
- describe "#confirm_check" do
93
- context "with out policy check" do
94
- it "should generate exception if password don't match" do
95
- expect{
96
- Passwd.confirm_check("secret", "invalid")
97
- }.to raise_error(Passwd::PasswordNotMatch)
98
- end
99
-
100
- it "return true if password matches" do
101
- expect(Passwd.confirm_check("secret", "secret")).to be_true
102
- end
103
- end
104
-
105
- context "with policy check" do
106
- it "return false if invalid password by policy" do
107
- expect(Passwd.confirm_check("secret", "secret", true)).to be_false
108
- end
109
-
110
- it "return true if valid password by policy" do
111
- expect(Passwd.confirm_check("secretpass", "secretpass", true)).to be_false
112
- end
113
- end
114
- end
115
-
116
- describe "#configure" do
117
- it "return configuration object" do
118
- expect(Passwd.configure.is_a? Passwd::Config).to be_true
119
- end
120
-
121
- it "set config value from block" do
122
- Passwd.configure do |c|
123
- c.length = 10
124
- end
125
- expect(Passwd.configure.length).not_to eq(8)
126
- expect(Passwd.configure.length).to eq(10)
127
- end
128
-
129
- it "set config value from hash" do
130
- Passwd.configure length: 20
131
- expect(Passwd.config.length).not_to eq(8)
132
- expect(Passwd.config.length).to eq(20)
133
- end
134
-
135
- it "alias of configure as config" do
136
- expect(Passwd.configure.object_id).to eq(Passwd.config.object_id)
137
- end
138
- end
139
-
140
- describe "#policy_configure" do
141
- it "return policy object" do
142
- expect(Passwd.policy_configure.is_a? Passwd::Policy).to be_true
143
- end
144
-
145
- it "set policy value from block" do
146
- Passwd.policy_configure do |c|
147
- c.min_length = 10
148
- end
149
- expect(Passwd.policy_configure.min_length).not_to eq(8)
150
- expect(Passwd.policy_configure.min_length).to eq(10)
151
- end
152
- end
153
-
154
- describe "#policy_check" do
155
- it "Policy#valid? should be called" do
156
- Passwd::Policy.instance.should_receive(:valid?).with("secret1234" ,Passwd::Config.instance)
157
- expect(Passwd.policy_check("secret1234")).not_to raise_error
158
- end
159
- end
160
-
161
- describe "#reset_policy" do
162
- let(:policy) {Passwd::Policy.instance}
163
-
164
- before {
165
- policy.configure do |c|
166
- c.min_length = 20
167
- c.require_lower = false
168
- c.require_upper = true
169
- c.require_number = false
170
- end
171
- Passwd.reset_policy
172
- }
173
-
174
- it "min_length should be a default" do
175
- expect(policy.min_length).to eq(8)
176
- end
177
-
178
- it "require_lower should be a default" do
179
- expect(policy.require_lower).to be_true
180
- end
181
-
182
- it "upper should be a default" do
183
- expect(policy.require_upper).to be_false
184
- end
185
-
186
- it "number should be a default" do
187
- expect(policy.require_number).to be_true
188
- end
189
- end
190
-
191
- describe "#reset_config" do
192
- let(:config) {Passwd::Config.instance}
193
-
194
- before {
195
- config.configure do |c|
196
- c.length = 20
197
- c.lower = false
198
- c.upper = false
199
- c.number = false
200
- c.letters_lower = ["a"]
201
- c.letters_upper = ["A"]
202
- c.letters_number = ["0"]
203
- end
204
- Passwd.reset_config
205
- }
206
-
207
- it "length should be a default" do
208
- expect(config.length).to eq(8)
209
- end
210
-
211
- it "lower should be a default" do
212
- expect(config.lower).to be_true
213
- end
214
-
215
- it "upper should be a default" do
216
- expect(config.upper).to be_true
217
- end
218
-
219
- it "number should be a default" do
220
- expect(config.number).to be_true
221
- end
222
-
223
- it "letters_lower should be a default" do
224
- expect(config.letters_lower).to eq(("a".."z").to_a)
225
- end
3
+ describe Passwd::Base do
4
+ let(:plain) { "secret" }
5
+
6
+ context "#random" do
7
+ it { expect(Passwd).to respond_to(:random) }
8
+ it { expect(Passwd.random.is_a?(String)).to be_truthy }
9
+ it { expect(Passwd.random.size).to eq(Passwd::PwConfig.length) }
10
+ it { expect(Passwd.random(lower: false)).not_to include(*"a".."z") }
11
+ it { expect(Passwd.random(upper: false)).not_to include(*"A".."Z") }
12
+ it { expect(Passwd.random(number: false)).not_to include(*"0".."9") }
13
+
14
+ it {
15
+ length = Passwd::PwConfig.length + 1
16
+ expect(Passwd.random(length: length).size).to eq(length)
17
+ }
18
+
19
+ it {
20
+ lower = ["a"]
21
+ expect(
22
+ Passwd.random(letters_lower: lower, upper: false, number: false)
23
+ .chars.uniq
24
+ ).to eq(lower)
25
+ }
26
+ end
226
27
 
227
- it "letters_upper should be a default" do
228
- expect(config.letters_upper).to eq(("A".."Z").to_a)
229
- end
28
+ context "#digest" do
29
+ it { expect(Passwd.respond_to?(:digest)).to be_truthy }
30
+ it { expect(Passwd.digest(plain).is_a?(String)).to be_truthy }
31
+ it { expect(Passwd.digest(plain)).not_to eq(plain) }
32
+
33
+ it {
34
+ hashed = Passwd.send(:algorithm, Passwd::PwConfig.algorithm).hexdigest plain
35
+ expect(Passwd.digest(plain)).to eq(hashed)
36
+ }
37
+
38
+ it {
39
+ not_default = :md5
40
+ hashed = Passwd.send(:algorithm, not_default).hexdigest plain
41
+ expect(Passwd.digest(plain, not_default)).to eq(hashed)
42
+ }
43
+ end
230
44
 
231
- it "letters_number should be a default" do
232
- expect(config.letters_number).to eq(("0".."9").to_a)
233
- end
234
- end
45
+ context "#algorithm" do
46
+ it { expect(Passwd.send(:algorithm, :sha1)).to eq(Digest::SHA1) }
47
+ it { expect(Passwd.send(:algorithm, :sha256)).to eq(Digest::SHA256) }
48
+ it { expect(Passwd.send(:algorithm, :sha384)).to eq(Digest::SHA384) }
49
+ it { expect(Passwd.send(:algorithm, :sha512)).to eq(Digest::SHA512) }
50
+ it { expect(Passwd.send(:algorithm, :md5)).to eq(Digest::MD5) }
51
+ it { expect(Passwd.send(:algorithm, :rmd160)).to eq(Digest::RMD160) }
52
+
53
+ it {
54
+ expect {
55
+ Passwd.send(:algorithm, :unknowAn)
56
+ }.to raise_error
57
+ }
235
58
  end
236
- end
59
+ end
60
+