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
@@ -1,62 +0,0 @@
1
- # coding: utf-8
2
-
3
- module Passwd
4
- module ActiveRecord
5
- module ClassMethods
6
- def define_column(options={})
7
- id_name = options.fetch(:id, :email)
8
- salt_name = options.fetch(:salt, :salt)
9
- password_name = options.fetch(:password, :password)
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)
19
- define_singleton_method :authenticate do |id, pass|
20
- user = self.where(id_name => id).first
21
- user if user && Passwd.auth(pass, user.send(salt_name), user.send(password_name))
22
- end
23
- end
24
-
25
- def define_instance_auth(id_name, salt_name, password_name)
26
- define_method :authenticate do |pass|
27
- Passwd.auth(pass, self.send(salt_name), self.send(password_name))
28
- end
29
- end
30
-
31
- def define_set_password(id_name, salt_name, password_name)
32
- define_method :set_password do |pass=nil|
33
- password = pass || Passwd.create
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}"))
37
- password
38
- end
39
- end
40
-
41
- def define_update_password(salt_name, password_name)
42
- define_method :update_password do |old_pass, new_pass, policy_check=false|
43
- if Passwd.auth(old_pass, self.send(salt_name), self.send(password_name))
44
- if policy_check
45
- raise Passwd::PolicyNotMatch, "Policy not match" unless Passwd.policy_check(new_pass)
46
- end
47
-
48
- set_password(new_pass)
49
- else
50
- raise Passwd::AuthError
51
- end
52
- end
53
- end
54
- end
55
-
56
- class << self
57
- def included(base)
58
- base.extend ClassMethods
59
- end
60
- end
61
- end
62
- end
@@ -1,37 +0,0 @@
1
- # coding: utf-8
2
-
3
- module Passwd
4
- class AbstractConfig
5
- VALID_OPTIONS_KEYS = [
6
- :algorithm,
7
- :length,
8
- :lower,
9
- :upper,
10
- :number,
11
- :letters_lower,
12
- :letters_upper,
13
- :letters_number
14
- ].freeze
15
-
16
- attr_accessor *VALID_OPTIONS_KEYS
17
-
18
- def configure
19
- yield self
20
- end
21
-
22
- def merge(configs)
23
- configs.keys.each do |k|
24
- send("#{k}=", configs[k])
25
- end
26
- end
27
-
28
- def letters
29
- chars = []
30
- chars.concat(self.letters_lower) if self.lower
31
- chars.concat(self.letters_upper) if self.upper
32
- chars.concat(self.letters_number) if self.number
33
- raise "letters is empty" if chars.empty?
34
- chars
35
- end
36
- end
37
- end
@@ -1,24 +0,0 @@
1
- # coding: utf-8
2
-
3
- require "passwd/configuration/abstract_config"
4
-
5
- module Passwd
6
- class Config < AbstractConfig
7
- include Singleton
8
-
9
- def initialize
10
- reset
11
- end
12
-
13
- def reset
14
- self.algorithm = :sha512
15
- self.length = 8
16
- self.lower = true
17
- self.upper = true
18
- self.number = true
19
- self.letters_lower = ("a".."z").to_a
20
- self.letters_upper = ("A".."Z").to_a
21
- self.letters_number = ("0".."9").to_a
22
- end
23
- end
24
- end
@@ -1,46 +0,0 @@
1
- # coding: utf-8
2
-
3
- module Passwd
4
- class Policy
5
- include Singleton
6
-
7
- VALID_OPTIONS_KEYS = [
8
- :min_length,
9
- :require_lower,
10
- :require_upper,
11
- :require_number
12
- ].freeze
13
-
14
- attr_accessor *VALID_OPTIONS_KEYS
15
-
16
- def initialize
17
- reset
18
- end
19
-
20
- def configure
21
- yield self
22
- end
23
-
24
- def valid?(password, config)
25
- return false if self.min_length > password.size
26
- return false if self.require_lower && !include_char?(config.letters_lower, password)
27
- return false if self.require_upper && !include_char?(config.letters_upper, password)
28
- return false if self.require_number && !include_char?(config.letters_number, password)
29
- true
30
- end
31
-
32
- def include_char?(letters, strings)
33
- strings.each_char do |c|
34
- return true if letters.include? c
35
- end
36
- false
37
- end
38
-
39
- def reset
40
- self.min_length = 8
41
- self.require_lower = true
42
- self.require_upper = false
43
- self.require_number = true
44
- end
45
- end
46
- end
@@ -1,18 +0,0 @@
1
- # coding: utf-8
2
-
3
- require "passwd/configuration/abstract_config"
4
-
5
- module Passwd
6
- class TmpConfig < AbstractConfig
7
- def initialize(config, options)
8
- config.instance_variables.each do |v|
9
- key = v.to_s.sub(/^@/, "").to_sym
10
- if options.has_key? key
11
- instance_variable_set v, options[key]
12
- else
13
- instance_variable_set v, config.instance_variable_get(v)
14
- end
15
- end
16
- end
17
- end
18
- end
@@ -1,163 +0,0 @@
1
- # coding: utf-8
2
-
3
- require "spec_helper"
4
-
5
- describe Passwd::ActiveRecord do
6
- class User
7
- include Passwd::ActiveRecord
8
- define_column
9
- end
10
-
11
- let(:salt) {Digest::SHA512.hexdigest("salt")}
12
- let(:password_text) {"secret"}
13
- let(:password_hash) {Digest::SHA512.hexdigest("#{salt}#{password_text}")}
14
-
15
- describe ".#included" do
16
- it "define singleton methods" do
17
- expect(User.respond_to? :define_column).to be_true
18
- end
19
- end
20
-
21
- describe "extend methods" do
22
- describe ".#define_column" do
23
- let(:user) {User.new}
24
-
25
- it "define singleton methods" do
26
- expect(User.respond_to? :authenticate).to be_true
27
- end
28
-
29
- it "define authenticate method" do
30
- expect(user.respond_to? :authenticate).to be_true
31
- end
32
-
33
- it "define set_password method" do
34
- expect(user.respond_to? :set_password).to be_true
35
- end
36
-
37
- it "define update_password" do
38
- expect(user.respond_to? :update_password).to be_true
39
- end
40
- end
41
- end
42
-
43
- describe "defined methods from define_column" do
44
- describe ".#authenticate" do
45
- let!(:record) {
46
- record = double("record mock")
47
- record.stub(:salt).and_return(salt)
48
- record.stub(:password).and_return(password_hash)
49
- response = [record]
50
- User.stub(:where).and_return(response)
51
- record
52
- }
53
-
54
- it "user should be returned if authentication is successful" do
55
- User.should_receive(:where)
56
- expect(User.authenticate("valid_id", password_text)).to eq(record)
57
- end
58
-
59
- it "should return nil if authentication failed" do
60
- User.should_receive(:where)
61
- expect(User.authenticate("valid_id", "invalid_secret")).to be_nil
62
- end
63
-
64
- it "should return nil if user not found" do
65
- User.should_receive(:where).with(:email => "invalid_id").and_return([])
66
- expect(User.authenticate("invalid_id", password_text)).to be_nil
67
- end
68
- end
69
-
70
- describe "#authenticate" do
71
- let!(:user) {
72
- user = User.new
73
- user.stub(:salt).and_return(salt)
74
- user.stub(:password).and_return(password_hash)
75
- user
76
- }
77
-
78
- it "should return true if authentication is successful" do
79
- expect(user.authenticate(password_text)).to be_true
80
- end
81
-
82
- it "should return false if authentication failed" do
83
- expect(user.authenticate("invalid_pass")).to be_false
84
- end
85
- end
86
-
87
- describe "#set_password" do
88
- let!(:user) {
89
- user = User.new
90
- user.stub(:salt).and_return(salt)
91
- user
92
- }
93
-
94
- it "should return set password" do
95
- user.should_receive(:salt=).with(salt)
96
- user.should_receive(:password=).with(Passwd.hashing("#{salt}#{password_text}"))
97
- expect(user.set_password(password_text)).to eq(password_text)
98
- end
99
-
100
- it "should set random password if not specified" do
101
- user.should_receive(:salt=).with(salt)
102
- random_password = Passwd.create
103
- Passwd.should_receive(:create).and_return(random_password)
104
- user.should_receive(:password=).with(Passwd.hashing("#{salt}#{random_password}"))
105
- user.set_password
106
- end
107
-
108
- it "should set salt if salt is nil" do
109
- mail_addr = "foo@example.com"
110
- time_now = Time.now
111
- salt2 = Passwd.hashing("#{mail_addr}#{time_now.to_s}")
112
- Time.stub(:now).and_return(time_now)
113
- user.stub(:email).and_return(mail_addr)
114
- user.should_receive(:salt).and_return(nil)
115
- user.should_receive(:salt=).with(salt2)
116
- user.should_receive(:password=).with(Passwd.hashing("#{salt2}#{password_text}"))
117
- user.set_password(password_text)
118
- end
119
- end
120
-
121
- describe "#update_password" do
122
- let!(:user) {
123
- user = User.new
124
- user.stub(:salt).and_return(salt)
125
- user.stub(:password).and_return(password_hash)
126
- user
127
- }
128
-
129
- context "without policy check" do
130
- it "should return update password" do
131
- pass = "new_password"
132
- user.should_receive(:set_password).with(pass).and_return(pass)
133
- expect(user.update_password(password_text, pass)).to eq(pass)
134
- end
135
-
136
- it "should generate exception if authentication failed" do
137
- Passwd.should_receive(:auth).and_return(false)
138
- user.should_not_receive(:set_password)
139
- expect {
140
- user.update_password("invalid_password", "new_password")
141
- }.to raise_error(Passwd::AuthError)
142
- end
143
- end
144
-
145
- context "with policy check" do
146
- it "should return update password" do
147
- pass = "new_password"
148
- Passwd.should_receive(:policy_check).and_return(true)
149
- user.should_receive(:set_password).with(pass).and_return(pass)
150
- expect(user.update_password(password_text, pass, true)).to eq(pass)
151
- end
152
-
153
- it "should generate exception if policy not match" do
154
- pass = "new_password"
155
- Passwd.should_receive(:policy_check).and_return(false)
156
- expect {
157
- user.update_password(password_text, pass, true)
158
- }.to raise_error(Passwd::PolicyNotMatch)
159
- end
160
- end
161
- end
162
- end
163
- end
@@ -1,250 +0,0 @@
1
- # coding: utf-8
2
-
3
- require "spec_helper"
4
-
5
- describe Passwd::Config do
6
- let(:config) {Passwd::Config.instance}
7
-
8
- describe "defined accessors" do
9
- it "defined algorithm" do
10
- expect(config.respond_to? :algorithm).to be_true
11
- end
12
-
13
- it "defined length" do
14
- expect(config.respond_to? :length).to be_true
15
- end
16
-
17
- it "defined lower" do
18
- expect(config.respond_to? :lower).to be_true
19
- end
20
-
21
- it "defined upper" do
22
- expect(config.respond_to? :upper).to be_true
23
- end
24
-
25
- it "defined number" do
26
- expect(config.respond_to? :number).to be_true
27
- end
28
-
29
- it "defined letters_lower" do
30
- expect(config.respond_to? :letters_lower).to be_true
31
- end
32
-
33
- it "defined letters_upper" do
34
- expect(config.respond_to? :letters_upper).to be_true
35
- end
36
-
37
- it "defined letters_number" do
38
- expect(config.respond_to? :letters_number).to be_true
39
- end
40
- end
41
-
42
- describe "#initialize" do
43
- it "algorithm should be a default" do
44
- expect(config.algorithm).to eq(:sha512)
45
- end
46
-
47
- it "length should be a default" do
48
- expect(config.length).to eq(8)
49
- end
50
-
51
- it "lower should be a default" do
52
- expect(config.lower).to be_true
53
- end
54
-
55
- it "upper should be a default" do
56
- expect(config.upper).to be_true
57
- end
58
-
59
- it "number should be a default" do
60
- expect(config.number).to be_true
61
- end
62
-
63
- it "letters_lower should be a default" do
64
- expect(config.letters_lower).to eq(("a".."z").to_a)
65
- end
66
-
67
- it "letters_upper should be a default" do
68
- expect(config.letters_upper).to eq(("A".."Z").to_a)
69
- end
70
-
71
- it "letters_number should be a default" do
72
- expect(config.letters_number).to eq(("0".."9").to_a)
73
- end
74
- end
75
-
76
- describe "#configure" do
77
- before {
78
- config.configure do |c|
79
- c.length = 20
80
- c.lower = false
81
- c.upper = false
82
- c.number = false
83
- c.letters_lower = ["a"]
84
- c.letters_upper = ["A"]
85
- c.letters_number = ["0"]
86
- end
87
- }
88
-
89
- it "set length from block" do
90
- expect(config.length).to eq(20)
91
- end
92
-
93
- it "set lower from block" do
94
- expect(config.lower).to be_false
95
- end
96
-
97
- it "set upper from block" do
98
- expect(config.upper).to be_false
99
- end
100
-
101
- it "set number from block" do
102
- expect(config.number).to be_false
103
- end
104
-
105
- it "set letters_lower from block" do
106
- expect(config.letters_lower).to eq(["a"])
107
- end
108
-
109
- it "set letters_upper from block" do
110
- expect(config.letters_upper).to eq(["A"])
111
- end
112
-
113
- it "set letters_number from block" do
114
- expect(config.letters_number).to eq(["0"])
115
- end
116
-
117
- it "raise error unknown setting" do
118
- expect {
119
- config.configure do |c|
120
- c.unknown = true
121
- end
122
- }.to raise_error
123
- end
124
- end
125
-
126
- describe "#merge" do
127
- it "set length from hash" do
128
- config.merge(length: 10)
129
- expect(config.length).to eq(10)
130
- end
131
-
132
- it "set lower from hash" do
133
- config.merge(lower: false)
134
- expect(config.lower).to be_false
135
- end
136
-
137
- it "set upper from hash" do
138
- config.merge(upper: false)
139
- expect(config.upper).to be_false
140
- end
141
-
142
- it "set number from hash" do
143
- config.merge(number: false)
144
- expect(config.number).to be_false
145
- end
146
-
147
- it "set letters_lower from hash" do
148
- config.merge(letters_lower: ["a"])
149
- expect(config.letters_lower).to eq(["a"])
150
- end
151
-
152
- it "set letters_upper from hash" do
153
- config.merge(letters_upper: ["A"])
154
- expect(config.letters_upper).to eq(["A"])
155
- end
156
-
157
- it "set letters_number from hash" do
158
- config.merge(letters_number: ["0"])
159
- expect(config.letters_number).to eq(["0"])
160
- end
161
-
162
- it "raise error unknown setting" do
163
- expect {
164
- config.merge(unknown: true)
165
- }.to raise_error
166
- end
167
- end
168
-
169
- describe "#letters" do
170
- it "return Array object" do
171
- expect(config.letters.is_a? Array).to be_true
172
- end
173
-
174
- it "all elements of the string" do
175
- config.letters.each do |l|
176
- expect(l.is_a? String).to be_true
177
- end
178
- end
179
-
180
- it "return all letters" do
181
- all_letters = ("a".."z").to_a.concat(("A".."Z").to_a).concat(("0".."9").to_a)
182
- expect(config.letters).to eq(all_letters)
183
- end
184
-
185
- it "return except for the lower case" do
186
- config.merge(lower: false)
187
- expect(config.letters.include? "a").to be_false
188
- end
189
-
190
- it "return except for the upper case" do
191
- config.merge(upper: false)
192
- expect(config.letters.include? "A").to be_false
193
- end
194
-
195
- it "return except for the number case" do
196
- config.merge(number: false)
197
- expect(config.letters.include? "0").to be_false
198
- end
199
-
200
- it "raise error if letters is empty" do
201
- config.merge(lower: false, upper: false, number: false)
202
- expect {
203
- config.letters
204
- }.to raise_error
205
- end
206
- end
207
-
208
- describe "#reset" do
209
- before {
210
- config.configure do |c|
211
- c.length = 20
212
- c.lower = false
213
- c.upper = false
214
- c.number = false
215
- c.letters_lower = ["a"]
216
- c.letters_upper = ["A"]
217
- c.letters_number = ["0"]
218
- end
219
- config.reset
220
- }
221
-
222
- it "length should be a default" do
223
- expect(config.length).to eq(8)
224
- end
225
-
226
- it "lower should be a default" do
227
- expect(config.lower).to be_true
228
- end
229
-
230
- it "upper should be a default" do
231
- expect(config.upper).to be_true
232
- end
233
-
234
- it "number should be a default" do
235
- expect(config.number).to be_true
236
- end
237
-
238
- it "letters_lower should be a default" do
239
- expect(config.letters_lower).to eq(("a".."z").to_a)
240
- end
241
-
242
- it "letters_upper should be a default" do
243
- expect(config.letters_upper).to eq(("A".."Z").to_a)
244
- end
245
-
246
- it "letters_number should be a default" do
247
- expect(config.letters_number).to eq(("0".."9").to_a)
248
- end
249
- end
250
- end