multi_auth 0.1.0 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (34) hide show
  1. data/README +11 -0
  2. data/app/controllers/auth/name_controller.rb +31 -0
  3. data/app/controllers/credentials/email_controller.rb +3 -3
  4. data/app/controllers/credentials/name_controller.rb +93 -0
  5. data/app/controllers/credentials_controller.rb +6 -4
  6. data/app/models/name_credential.rb +63 -0
  7. data/app/models/name_credential_edit_form.rb +40 -0
  8. data/app/models/name_login_form.rb +14 -0
  9. data/app/models/{email_password_edit_form.rb → password_edit_form.rb} +5 -13
  10. data/app/views/auth/name/index.html.erb +89 -0
  11. data/app/views/credentials/index.html.erb +46 -0
  12. data/app/views/credentials/name/delete.html.erb +19 -0
  13. data/app/views/credentials/name/edit_password.html.erb +27 -0
  14. data/app/views/credentials/name/new.html.erb +32 -0
  15. data/config/routes.rb +8 -0
  16. data/db/development.sqlite3 +0 -0
  17. data/db/schema.rb +13 -1
  18. data/db/test.sqlite3 +0 -0
  19. data/generators/multi_auth_migration/templates/migration.rb +13 -0
  20. data/generators/multi_auth_migration/templates/upgrade_migration.rb +19 -0
  21. data/generators/multi_auth_migration/upgrade_multi_auth_tables_generator.rb +11 -0
  22. data/lib/multi_auth.rb +1 -0
  23. data/lib/multi_auth/active_record.rb +1 -0
  24. data/locale/ja/LC_MESSAGES/multi_auth.mo +0 -0
  25. data/po/ja/multi_auth.po +357 -259
  26. data/po/multi_auth.pot +309 -215
  27. data/test/functional/auth/name_controller_test.rb +77 -0
  28. data/test/functional/credentials/email_controller_test.rb +3 -4
  29. data/test/functional/credentials/name_controller_test.rb +292 -0
  30. data/test/unit/name_credential_edit_form_test.rb +151 -0
  31. data/test/unit/name_credential_test.rb +173 -0
  32. data/test/unit/name_login_form_test.rb +68 -0
  33. data/test/unit/{email_password_edit_form_test.rb → password_edit_form_test.rb} +7 -6
  34. metadata +22 -6
data/README CHANGED
@@ -54,6 +54,15 @@ You can use default style sheet and icons.
54
54
 
55
55
  $ ruby script/generate multi_auth_public_assets
56
56
 
57
+ Upgrade
58
+ =======
59
+
60
+ $ ruby script/generate multi_auth_migration upgrade_multi_auth_tables
61
+
62
+ Create migrations for multi_auth. Just add a table for NameCredential.
63
+
64
+ $ rake db:migrate
65
+
57
66
 
58
67
  Settings
59
68
  ========
@@ -67,6 +76,8 @@ Ex.
67
76
  s.from_address = 'yourname@example.com'
68
77
  s.user_model = 'YourUserModel'
69
78
  s.session_times_out_in = 1.hour
79
+ # if false do not display in credentials index
80
+ s.credentials = { :open_id => true, :email => true, :name => true }
70
81
  end
71
82
 
72
83
  You can use OpenID::CustomFetcher to use OpenID provider which uses SSL.
@@ -0,0 +1,31 @@
1
+ class Auth::NameController < ApplicationController
2
+ filter_parameter_logging :password
3
+ verify_method_post :only => [:login]
4
+
5
+ # GET /auth/name
6
+ def index
7
+ session[:user_id] = nil
8
+ @login_form = NameLoginForm.new
9
+ end
10
+
11
+ # POST /auth/name/login
12
+ def login
13
+ session[:user_id] = nil
14
+ @login_form = NameLoginForm.new(params[:login_form])
15
+
16
+ if @login_form.valid?
17
+ @name_credential = @login_form.authenticate
18
+ end
19
+
20
+ if @name_credential
21
+ @name_credential.login!
22
+ @login_user = @name_credential.user
23
+ session[:user_id] = @login_user.id
24
+ redirect_to(:controller => "/auth", :action => "logged_in")
25
+ else
26
+ @login_form.password = nil
27
+ set_error_now(p_("MultiAuth", "The name or the password is wrong."))
28
+ render(:action => "index")
29
+ end
30
+ end
31
+ end
@@ -55,14 +55,14 @@ class Credentials::EmailController < ApplicationController
55
55
 
56
56
  # GET /credential/email/:email_credential_id/edit_password
57
57
  def edit_password
58
- @edit_form = EmailPasswordEditForm.new
58
+ @edit_form = PasswordEditForm.new
59
59
  end
60
60
 
61
61
  # POST /credential/email/:email_credential_id/update_password
62
62
  def update_password
63
- @edit_form = EmailPasswordEditForm.new(params[:edit_form])
63
+ @edit_form = PasswordEditForm.new(params[:edit_form])
64
64
 
65
- @email_credential.attributes = @edit_form.to_email_credential_hash
65
+ @email_credential.attributes = @edit_form.to_credential_hash
66
66
 
67
67
  if @edit_form.valid? && @email_credential.save
68
68
  set_notice(p_("MultiAuth", "Password was changed."))
@@ -0,0 +1,93 @@
1
+ # -*- coding: utf-8 -*-
2
+ class Credentials::NameController < ApplicationController
3
+
4
+ verify_method_post :only => [:create, :update_password, :destroy, :activate]
5
+ before_filter :authentication
6
+ before_filter :authentication_required
7
+ before_filter :required_param_name_credential_id, :only => [:edit_password, :update_password, :delete, :destroy]
8
+ before_filter :specified_name_credential_belongs_to_login_user, :only => [:edit_password, :update_password, :delete, :destroy]
9
+
10
+ # GET /credentials/name/new
11
+ def new
12
+ @edit_form = NameCredentialEditForm.new
13
+ end
14
+
15
+ # POST /credentials/name/create
16
+ def create
17
+ @edit_form = NameCredentialEditForm.new(params[:edit_form])
18
+
19
+ @name_credential = @login_user.name_credentials.build
20
+ @name_credential.attributes = @edit_form.to_name_credential_hash
21
+
22
+ if @edit_form.valid? && @name_credential.save
23
+ # メール送信はしない
24
+ set_notice(p_("MultiAuth", "Name authentication credential was successfully added."))
25
+ redirect_to(credentials_path)
26
+ else
27
+ @edit_form.password = nil
28
+ @edit_form.password_confirmation = nil
29
+ set_error_now(p_("MultiAuth", "Please confirm your input."))
30
+ render(:action => "new")
31
+ end
32
+ end
33
+
34
+ # GET /credential/name/:name_credential_id/edit_password
35
+ def edit_password
36
+ @edit_form = PasswordEditForm.new
37
+ end
38
+
39
+ # POST /credential/name/:name_credential_id/update_password
40
+ def update_password
41
+ @edit_form = PasswordEditForm.new(params[:edit_form])
42
+
43
+ @name_credential.attributes = @edit_form.to_credential_hash
44
+
45
+ if @edit_form.valid? && @name_credential.save
46
+ set_notice(p_("MultiAuth", "Password was changed."))
47
+ redirect_to(:controller => "/credentials")
48
+ else
49
+ @edit_form.password = nil
50
+ @edit_form.password_confirmation = nil
51
+ set_error_now(p_("MultiAuth", "Please confirm your input."))
52
+ render(:action => "edit_password")
53
+ end
54
+ end
55
+
56
+ # GET /credential/name/:name_credential_id/delete
57
+ def delete
58
+ # nop
59
+ end
60
+
61
+ # POST /credential/email/:email_credential_id/destroy
62
+ def destroy
63
+ @name_credential.destroy
64
+
65
+ set_notice(p_("MultiAuth", "Name authentication credential was successfully deleted."))
66
+ redirect_to(:controller => "/credentials")
67
+ end
68
+
69
+ private
70
+
71
+ # FIXME: login_userに属することを同時に確認
72
+ def required_param_name_credential_id(name_credential_id = params[:name_credential_id])
73
+ @name_credential = NameCredential.find_by_id(name_credential_id)
74
+ if @name_credential
75
+ return true
76
+ else
77
+ set_error(p_("MultiAuth", "It is invalid name authentication credential."))
78
+ redirect_to(root_path)
79
+ return false
80
+ end
81
+ end
82
+
83
+ def specified_name_credential_belongs_to_login_user
84
+ if @name_credential.user_id == @login_user.id
85
+ return true
86
+ else
87
+ set_error(p_("MultiAuth", "It is invalid name authentication credential."))
88
+ redirect_to(root_path)
89
+ return false
90
+ end
91
+ end
92
+
93
+ end
@@ -1,3 +1,4 @@
1
+ # -*- coding: utf-8 -*-
1
2
 
2
3
  # 認証情報コントローラ
3
4
  class CredentialsController < ApplicationController
@@ -6,9 +7,10 @@ class CredentialsController < ApplicationController
6
7
 
7
8
  # GET /credentials
8
9
  def index
9
- @open_id_credentials = @login_user.open_id_credentials.all(
10
- :order => "open_id_credentials.identity_url ASC")
11
- @email_credentials = @login_user.email_credentials.all(
12
- :order => "email_credentials.email ASC")
10
+ @open_id_credentials =
11
+ @login_user.open_id_credentials.all(:order => "open_id_credentials.identity_url ASC")
12
+ @email_credentials =
13
+ @login_user.email_credentials.all(:order => "email_credentials.email ASC")
14
+ @name_credentials = @login_user.name_credentials.all(:order => "name_credentials.name ASC")
13
15
  end
14
16
  end
@@ -0,0 +1,63 @@
1
+ # -*- coding: utf-8 -*-
2
+ class NameCredential < ActiveRecord::Base
3
+
4
+ untranslate :created_at, :user_id, :hashed_password
5
+
6
+ NameMaximumLength = 200
7
+ HashedPasswordPattern = /\A([0-9a-f]{8}):([0-9a-f]{64})\z/
8
+ MaximumRecordsPerUser = 10
9
+
10
+ belongs_to :user, :class_name => MultiAuth.user_model, :foreign_key => 'user_id'
11
+
12
+ validates_presence_of :name
13
+ validates_presence_of :hashed_password
14
+ validates_length_of :name, :maximum => NameMaximumLength, :allow_nil => true
15
+ validates_format_of :hashed_password, :with => HashedPasswordPattern, :allow_nil => true
16
+ validates_uniqueness_of :name
17
+ validates_each(:user_id, :on => :create) { |record, attr, value|
18
+ if record.user && record.user.name_credentials(true).size >= MaximumRecordsPerUser
19
+ record.errors.add(attr, "これ以上%{fn}に#{_(record.class.to_s.downcase)}を追加できません。")
20
+ end
21
+ }
22
+
23
+ def self.create_hashed_password(password)
24
+ salt = 8.times.map { rand(16).to_s(16) }.join
25
+ return salt + ":" + Digest::SHA256.hexdigest(salt + ":" + password)
26
+ end
27
+
28
+ def self.compare_hashed_password(password, hashed_password)
29
+ return false unless HashedPasswordPattern =~ hashed_password
30
+ salt, digest = $1, $2
31
+ return (Digest::SHA256.hexdigest(salt + ":" + password) == digest)
32
+ end
33
+
34
+ def self.authenticate(name, password)
35
+ credential = self.find_by_name(name)
36
+ return nil unless credential
37
+ return nil unless credential.authenticated?(password)
38
+ return credential
39
+ end
40
+
41
+ def authenticated?(password)
42
+ return false unless self.class.compare_hashed_password(password, self.hashed_password)
43
+ return false unless self.activated?
44
+ return true
45
+ end
46
+
47
+ def activated?
48
+ true
49
+ end
50
+
51
+ def activate!
52
+ true
53
+ end
54
+
55
+ def login!
56
+ self.update_attributes!(:loggedin_at => Time.now)
57
+ end
58
+
59
+ def to_label
60
+ name
61
+ end
62
+
63
+ end
@@ -0,0 +1,40 @@
1
+ # -*- coding: utf-8 -*-
2
+
3
+ class NameCredentialEditForm < ActiveForm
4
+ PasswordLengthRange = 4..20
5
+ PasswordPattern = /\A[\x21-\x7E]+\z/
6
+
7
+ column :name, :type => :text
8
+ column :password, :type => :text
9
+ column :password_confirmation, :type => :text
10
+
11
+ N_("NameCredentialEditForm|Name")
12
+ N_("NameCredentialEditForm|Password")
13
+ N_("NameCredentialEditForm|Password confirmation")
14
+
15
+ validates_presence_of :name
16
+ validates_presence_of :password
17
+ validates_presence_of :password_confirmation
18
+ validates_length_of :name, :maximum => ::NameCredential::NameMaximumLength, :allow_nil => true
19
+ validates_length_of :password, :in => PasswordLengthRange, :allow_nil => true
20
+ validates_format_of :password, :with => PasswordPattern, :allow_nil => true
21
+ validates_each(:password) { |record, attr, value|
22
+ # MEMO: validates_confirmation_of は password_confirmation 属性を上書きしてしまうため、
23
+ # ここでは使用できない。そのため、validates_confirmation_of を参考に独自に実装。
24
+ confirmation = record.__send__("#{attr}_confirmation")
25
+ if confirmation.blank? || value != confirmation
26
+ record.errors.add(attr, :confirmation)
27
+ end
28
+ }
29
+
30
+ def masked_password
31
+ return self.password.to_s.gsub(/./, "*")
32
+ end
33
+
34
+ def to_name_credential_hash
35
+ return {
36
+ :name => self.name,
37
+ :hashed_password => NameCredential.create_hashed_password(self.password.to_s),
38
+ }
39
+ end
40
+ end
@@ -0,0 +1,14 @@
1
+ class NameLoginForm < ActiveForm
2
+ column :name, :type => :text
3
+ column :password, :type => :text
4
+
5
+ N_("NameLoginForm|Name")
6
+ N_("NameLoginForm|Password")
7
+
8
+ validates_presence_of :name
9
+ validates_presence_of :password
10
+
11
+ def authenticate
12
+ NameCredential.authenticate(self.name, self.password)
13
+ end
14
+ end
@@ -1,14 +1,6 @@
1
- # == Schema Information
2
- # Schema version: 20090529051529
3
- #
4
- # Table name: active_forms
5
- #
6
- # password :text
7
- # password_confirmation :text
8
- #
1
+ # -*- coding: utf-8 -*-
9
2
 
10
- # メール認証情報パスワード編集フォーム
11
- class EmailPasswordEditForm < ActiveForm
3
+ class PasswordEditForm < ActiveForm
12
4
  column :password, :type => :text
13
5
  column :password_confirmation, :type => :text
14
6
 
@@ -17,8 +9,8 @@ class EmailPasswordEditForm < ActiveForm
17
9
 
18
10
  validates_presence_of :password
19
11
  validates_presence_of :password_confirmation
20
- validates_length_of :password, :in => EmailCredentialEditForm::PasswordLengthRange, :allow_nil => true
21
- validates_format_of :password, :with => EmailCredentialEditForm::PasswordPattern, :allow_nil => true
12
+ validates_length_of :password, :in => ::EmailCredentialEditForm::PasswordLengthRange, :allow_nil => true
13
+ validates_format_of :password, :with => ::EmailCredentialEditForm::PasswordPattern, :allow_nil => true
22
14
  validates_each(:password) { |record, attr, value|
23
15
  # MEMO: validates_confirmation_ofはpassword_confirmation属性を上書きしてしまうため、
24
16
  # ここでは使用できない。そのため、validates_confirmation_ofを参考に独自に実装。
@@ -28,7 +20,7 @@ class EmailPasswordEditForm < ActiveForm
28
20
  end
29
21
  }
30
22
 
31
- def to_email_credential_hash
23
+ def to_credential_hash
32
24
  return {
33
25
  :hashed_password => EmailCredential.create_hashed_password(self.password.to_s),
34
26
  }
@@ -0,0 +1,89 @@
1
+ <%- @title = p_("MultiAuth", "Login") -%>
2
+ <%- @enable_side_column = false -%>
3
+
4
+ <%- additional_head { -%>
5
+ <style type="text/css">
6
+ #dialog
7
+ {
8
+ margin: 100px auto;
9
+ padding: 15px;
10
+ width: 400px;
11
+ border-width: 1px;
12
+ border-style: solid;
13
+ border-color: #CCCCCC;
14
+ }
15
+ #dialog h1
16
+ {
17
+ margin: 0 0 0.4em 0;
18
+ color: #666666;
19
+ font-size: 130%;
20
+ font-weight: bold;
21
+ }
22
+
23
+ table#name-login
24
+ {
25
+ margin: 0 auto;
26
+ border-collapse: collapse;
27
+ border-width: 0px;
28
+ }
29
+
30
+ table#name-login th,
31
+ table#name-login td
32
+ {
33
+ padding: 5px;
34
+ border-width: 0px;
35
+ }
36
+
37
+ table#name-login th
38
+ {
39
+ text-align: right;
40
+ font-size: 95%;
41
+ font-weight: bold;
42
+ vertical-align: top;
43
+ color: #666666;
44
+ }
45
+ table#name-login td
46
+ {
47
+ color: #333333;
48
+ }
49
+
50
+ div.fieldWithErrors label
51
+ {
52
+ color: #990000;
53
+ }
54
+ div.formError
55
+ {
56
+ font-size: 80%;
57
+ color: #990000;
58
+ }
59
+ </style>
60
+ <%- } -%>
61
+
62
+ <div id="dialog">
63
+ <h1><%=h p_("MultiAuth", "Login") %></h1>
64
+ <%- form_for(:login_form, @login_form, :url => {:action => "login"}) do |f| -%>
65
+ <table id="name-login">
66
+ <tr>
67
+ <th><%= f.label(:name) %></th>
68
+ <td>
69
+ <%= f.text_field(:name, :size => 30) %>
70
+ <%= error_message_on(:login_form, :name) %>
71
+ </td>
72
+ </tr>
73
+ <tr>
74
+ <th><%= f.label(:password) %></th>
75
+ <td>
76
+ <%= f.password_field(:password, :size => 30) %>
77
+ <%= error_message_on(:login_form, :password) %>
78
+ </td>
79
+ </tr>
80
+ </table>
81
+ <%= submit_tag(p_("MultiAuth", "Login")) %>
82
+ <%- end -%>
83
+ </div>
84
+
85
+ <%- unless production? -%>
86
+ <div class="debug">
87
+ <%= error_messages_for(:login_form) %>
88
+ </div>
89
+ <%- end -%>
@@ -1,6 +1,7 @@
1
1
 
2
2
  <%- @title = p_("MultiAuth", "Login setting") -%>
3
3
 
4
+ <%- if MultiAuth.credentials[:open_id] -%>
4
5
  <h2><%=h p_("MultiAuth", "OpenID authentication") %></h2>
5
6
 
6
7
  <table class="list">
@@ -39,7 +40,9 @@
39
40
  <%- end -%>
40
41
  </tbody>
41
42
  </table>
43
+ <%- end -%>
42
44
 
45
+ <%- if MultiAuth.credentials[:email] -%>
43
46
  <h2><%=h p_("MultiAuth", "Email address authentication") %></h2>
44
47
 
45
48
  <table class="list">
@@ -84,3 +87,46 @@
84
87
  <%- end -%>
85
88
  </tbody>
86
89
  </table>
90
+ <%- end -%>
91
+
92
+ <%- if MultiAuth.credentials[:name] -%>
93
+ <h2><%=h p_("MultiAuth", "Name authentication") %></h2>
94
+
95
+ <table class="list">
96
+ <thead>
97
+ <tr>
98
+ <th><%=h s_("NameCredential|Loggedin at") %></th>
99
+ <th><%=h s_("NameCredential|Name") %></th>
100
+ <th colspan="2">&nbsp;</th>
101
+ </tr>
102
+ </thead>
103
+ <tfoot>
104
+ <tr>
105
+ <td colspan="4">
106
+ <%- can_add_name_credential = (@name_credentials.size < NameCredential::MaximumRecordsPerUser) -%>
107
+ <%= link_to_if(can_add_name_credential, add_icon + h(" " + p_("MultiAuth", "Add name authentication")), :controller => "credentials/name", :action => "new") %>
108
+ </td>
109
+ </tr>
110
+ </tfoot>
111
+ <tbody>
112
+ <%- if @name_credentials.empty? -%>
113
+ <tr>
114
+ <td colspan="4" style="padding: 1em;">
115
+ <%=h p_("MultiAuth", "There are no name authentication.") %>
116
+ </td>
117
+ </tr>
118
+ <%- else -%>
119
+ <%- @name_credentials.each_with_index do |name_credential, index| -%>
120
+ <tr class="<%= even_or_odd(index) %>">
121
+ <td><%=h yyyymmdd_hhmm(name_credential.loggedin_at) %></td>
122
+ <td>
123
+ <div style="font-family: monospace;"><%= name_credential.name %></div>
124
+ </td>
125
+ <td><%= link_to(icon16("icons/fam/key.png", p_("MultiAuth", "Update password")) + h(" " + p_("MultiAuth", "Update password")), :controller => "credentials/name", :action => "edit_password", :name_credential_id => name_credential.id) %></td>
126
+ <td><%= link_to(delete_icon + h(" " + p_("MultiAuth", "Delete")), :controller => "credentials/name", :action => "delete", :name_credential_id => name_credential.id) %></td>
127
+ </tr>
128
+ <%- end -%>
129
+ <% end -%>
130
+ </tbody>
131
+ </table>
132
+ <%- end -%>