aerogel-users 1.4.3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (80) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +19 -0
  3. data/Gemfile +4 -0
  4. data/LICENSE.txt +22 -0
  5. data/README.md +16 -0
  6. data/Rakefile +1 -0
  7. data/aerogel-users.gemspec +30 -0
  8. data/app/helpers/access_control.rb +32 -0
  9. data/app/helpers/auth.rb +58 -0
  10. data/app/helpers/auth_login_providers.rb +18 -0
  11. data/app/mailers/user.rb +43 -0
  12. data/app/routes/access_control.rb +15 -0
  13. data/app/routes/auth.rb +53 -0
  14. data/app/routes/user.rb +192 -0
  15. data/assets/README.md +1 -0
  16. data/assets/javascripts/.gitkeep +0 -0
  17. data/assets/stylesheets/.gitkeep +0 -0
  18. data/config/auth.conf +43 -0
  19. data/config/development/.keep +0 -0
  20. data/config/production/.keep +0 -0
  21. data/db/model/access.rb +67 -0
  22. data/db/model/authentication.rb +54 -0
  23. data/db/model/role.rb +17 -0
  24. data/db/model/user.rb +223 -0
  25. data/db/model/user_email.rb +24 -0
  26. data/db/model/user_registration_form.rb +23 -0
  27. data/db/model/user_request_account_activation_form.rb +30 -0
  28. data/db/model/user_request_email_confirmation_form.rb +40 -0
  29. data/db/model/user_request_password_reset_form.rb +37 -0
  30. data/db/model/user_reset_password_form.rb +41 -0
  31. data/db/seed/01_user_role.seed +8 -0
  32. data/db/seed/02_access_user.seed +25 -0
  33. data/db/seed/development/.keep +0 -0
  34. data/db/seed/development/test-user.seed +77 -0
  35. data/db/seed/production/.keep +0 -0
  36. data/lib/aerogel/users.rb +22 -0
  37. data/lib/aerogel/users/auth.rb +67 -0
  38. data/lib/aerogel/users/omniauth-failure_endpoint_ex.rb +21 -0
  39. data/lib/aerogel/users/omniauth-password.rb +63 -0
  40. data/lib/aerogel/users/secure_password.rb +55 -0
  41. data/lib/aerogel/users/version.rb +5 -0
  42. data/locales/actions/en.yml +18 -0
  43. data/locales/actions/ru.yml +17 -0
  44. data/locales/auth/en.yml +22 -0
  45. data/locales/auth/ru.yml +22 -0
  46. data/locales/mailers/en.yml +69 -0
  47. data/locales/mailers/ru.yml +73 -0
  48. data/locales/models/en.yml +65 -0
  49. data/locales/models/ru.yml +64 -0
  50. data/locales/views/en.yml +122 -0
  51. data/locales/views/ru.yml +126 -0
  52. data/public/README.md +1 -0
  53. data/rake/README.md +3 -0
  54. data/views/mailers/user/account_activation.html.erb +3 -0
  55. data/views/mailers/user/account_activation.text.erb +3 -0
  56. data/views/mailers/user/email_confirmation.html.erb +3 -0
  57. data/views/mailers/user/email_confirmation.text.erb +3 -0
  58. data/views/mailers/user/password_reset.html.erb +3 -0
  59. data/views/mailers/user/password_reset.text.erb +3 -0
  60. data/views/user/activate_account.html.erb +3 -0
  61. data/views/user/activate_account_failure.html.erb +3 -0
  62. data/views/user/confirm_email.html.erb +3 -0
  63. data/views/user/confirm_email_failure.html.erb +3 -0
  64. data/views/user/edit.html.erb +6 -0
  65. data/views/user/index.html.erb +19 -0
  66. data/views/user/login.html.erb +3 -0
  67. data/views/user/login/_form.html.erb +28 -0
  68. data/views/user/login/_provider.html.erb +5 -0
  69. data/views/user/logout.html.erb +3 -0
  70. data/views/user/register.html.erb +10 -0
  71. data/views/user/register_success.html.erb +3 -0
  72. data/views/user/request_account_activation.html.erb +9 -0
  73. data/views/user/request_account_activation_success.html.erb +3 -0
  74. data/views/user/request_email_confirmation.html.erb +8 -0
  75. data/views/user/request_email_confirmation_success.html.erb +3 -0
  76. data/views/user/request_password_reset.html.erb +8 -0
  77. data/views/user/request_password_reset_success.html.erb +3 -0
  78. data/views/user/reset_password.html.erb +9 -0
  79. data/views/user/reset_password_success.html.erb +3 -0
  80. metadata +234 -0
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 38e3f2209b78bded716b72e0d6fc1c3c018dce08
4
+ data.tar.gz: 15d5df47c1147bab9404240b00aebf2416ba642f
5
+ SHA512:
6
+ metadata.gz: 2fe81054db3c0fb713768328bc76632b45f4a0663b85a7961dbfb0cd468e58dadd61f3f14572c3d95e578c45e001738548bd877a538ac4235b6cebca49f8a0ab
7
+ data.tar.gz: 897182d5dac0d2e39d9f672eb02f4b4111d76fd8dfccf38b504284f75a9aeaae7542ee92aceb86275b41215fbe50dac1cad0976ea0551091569808a4ef1e7962
@@ -0,0 +1,19 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
18
+ .ruby-version
19
+ .ruby-gemset
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in aerogel-module123.gemspec
4
+ gemspec
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2013 Alex Kukushkin
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,16 @@
1
+ # aerogel-users
2
+
3
+ User management for aerogel CMS.
4
+
5
+ Provides user models, helpers and routs for user registration, authentication,
6
+ roles and access management.
7
+
8
+ ## Usage
9
+
10
+ In your application's config.ru:
11
+ ```ruby
12
+ require 'aerogel/core'
13
+ require 'aerogel/users'
14
+
15
+ run Aerogel::Application.load
16
+ ```
@@ -0,0 +1 @@
1
+ require "bundler/gem_tasks"
@@ -0,0 +1,30 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'aerogel/users/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "aerogel-users"
8
+ spec.version = Aerogel::Users::VERSION
9
+ spec.authors = ["Alex Kukushkin"]
10
+ spec.email = ["alex@kukushk.in"]
11
+ spec.description = %q{User management for aerogel}
12
+ spec.summary = %q{User management for aerogel}
13
+ spec.homepage = ""
14
+ spec.license = "MIT"
15
+
16
+ spec.files = `git ls-files`.split($/)
17
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
+ spec.require_paths = ["lib"]
20
+
21
+ spec.add_dependency "aerogel-core", "~> 1.4"
22
+ spec.add_dependency "aerogel-forms", "~> 0.1"
23
+ spec.add_dependency "aerogel-mailer", "~> 1.1"
24
+ spec.add_dependency "aerogel-font_awesome"
25
+ spec.add_dependency "omniauth"
26
+ spec.add_dependency "bcrypt"
27
+
28
+ spec.add_development_dependency "bundler", "~> 1.3"
29
+ spec.add_development_dependency "rake"
30
+ end
@@ -0,0 +1,32 @@
1
+ # Returns +true+ is user allowed to access +path+ with operation +access+.
2
+ #
3
+ def can? path, access = Access::READ
4
+ applied_rules = Access.rules_for_path path
5
+
6
+ # no rules for the path mean the access is not restricted
7
+ return true if applied_rules.blank?
8
+
9
+ # there are rules for the path, but the user is not authenticated
10
+ return false unless current_user?
11
+
12
+ # check if any rule grants access to path/access/user roles.
13
+ applied_rules.each do |access_rule|
14
+ return true if access_rule.grants?( path, access, current_user.roles )
15
+ end
16
+
17
+ # no luck
18
+ return false
19
+ end
20
+
21
+
22
+ # Returns constructed link if READ access to +url+ is allowed, returns empty string otherwise.
23
+ #
24
+ def link_to_if_can( url, text = url, opts = {} )
25
+ link_to( url, text, opts ) if can?( url )
26
+ end
27
+
28
+
29
+ def on_access_denied( &block )
30
+ @on_access_denied_callback = block if block_given?
31
+ @on_access_denied_callback
32
+ end
@@ -0,0 +1,58 @@
1
+ # Redirects after authentication process to the most appropriate origin URL.
2
+ #
3
+ # Possible origins (URLs to redirect to) are tried in following order:
4
+ # explicit_origin
5
+ # request.env['omniauth.origin']
6
+ # request.params['origin']
7
+ # default_origin
8
+ #
9
+ def auth_redirect_to_origin( explicit_origin, default_origin = "/" )
10
+ url = explicit_origin || request.env['omniauth.origin'] || request.params['origin'] || default_origin
11
+ redirect url
12
+ end
13
+
14
+ def auth_login( user, opts = {} )
15
+ session[:user_id] = user.id
16
+ session[:remember_me] = opts[:remember_me].present?
17
+ user.authenticated!( opts )
18
+ auth_keepalive_session
19
+ end
20
+
21
+ def auth_logout
22
+ session[:user_id] = nil
23
+ session[:remember_me] = nil
24
+ session.options[:expire_after] = nil
25
+ end
26
+
27
+ def auth_keepalive_session
28
+ session.options[:expire_after] = 2592000 if session[:remember_me]
29
+ end
30
+
31
+ # Returns User object of the current authenticated user or +nil+.
32
+ #
33
+ def current_user
34
+ auth_keepalive_session
35
+ @current_user ||= ( session[:user_id] ? User.find( session[:user_id] ) : nil )
36
+ end
37
+
38
+ # Returns +true+ if user is authenticated, +false+ otherwise.
39
+ #
40
+ def current_user?
41
+ !current_user.nil?
42
+ end
43
+
44
+ # Gets or sets auth state.
45
+ # 'auth state' is a one-time used Hash used to store auth system data between requests.
46
+ #
47
+ def auth_state( value = nil )
48
+ if value
49
+ @auth_state = value
50
+ session[:auth_state] = value.to_json
51
+ return @auth_state
52
+ end
53
+ unless @auth_state
54
+ @auth_state = session[:auth_state].nil? ? {} : JSON.parse( session[:auth_state] )
55
+ session[:auth_state] = nil
56
+ end
57
+ @auth_state
58
+ end
@@ -0,0 +1,18 @@
1
+
2
+ # Returns A HREF link to a login using given provider.
3
+ #
4
+ def link_to_social_login( provider_key, text = nil, on_success = nil )
5
+ provider = Aerogel::Auth.providers[provider_key] || {}
6
+ text ||= provider[:name]
7
+ link_to url_to_social_login( provider_key, on_success ), text.to_s, title: "log in using #{provider[:name]}"
8
+ end
9
+
10
+
11
+ # Returns URL to a login using given provider.
12
+ #
13
+ def url_to_social_login( provider_key, on_success = nil )
14
+ provider = Aerogel::Auth.providers[provider_key] || {}
15
+ origin = on_success || params['on_success']
16
+ query_string = origin ? "?origin=#{origin}" : ''
17
+ "/auth/#{provider_key}#{query_string}"
18
+ end
@@ -0,0 +1,43 @@
1
+
2
+ mailer 'user/email_confirmation' do |user_email|
3
+ user = user_email.user
4
+ url = url_to( "/user/confirm_email",
5
+ fqdn: true,
6
+ email: user_email.email,
7
+ token: user_email.confirmation_token
8
+ )
9
+
10
+ to user_email.email
11
+ subject t.aerogel.users.mailers.email_confirmation.subject
12
+ locals full_name: h(user.full_name), url: url
13
+ end
14
+
15
+
16
+ mailer 'user/account_activation' do |user|
17
+ user_email = user.emails.first
18
+ url = url_to( "/user/activate_account",
19
+ fqdn: true,
20
+ email: user_email.email,
21
+ token: user_email.confirmation_token
22
+ )
23
+
24
+ to user_email.email
25
+ subject t.aerogel.users.mailers.account_activation.subject
26
+ locals full_name: h(user.full_name), url: url
27
+ end
28
+
29
+
30
+ mailer 'user/password_reset' do |authentication|
31
+ user_email = authentication.email
32
+ user = user_email.user
33
+ url = url_to( "/user/reset_password",
34
+ fqdn: true,
35
+ email: user_email.email,
36
+ token: authentication.password_reset_token
37
+ )
38
+
39
+ user_email = authentication.email
40
+ to user_email.email
41
+ subject t.aerogel.users.mailers.password_reset.subject
42
+ locals full_name: h(user.full_name), url: url
43
+ end
@@ -0,0 +1,15 @@
1
+
2
+ # Before serving any route check for access permissions.
3
+ #
4
+ before do
5
+ access = %w(GET HEAD).include?( request.request_method.upcase.to_s ) ? Access::READ : Access::WRITE
6
+ unless can? request.path_info, access
7
+ flash[:error] = t.aerogel.auth.actions.access_denied path: request.path_info, access: access
8
+
9
+ # on_access_denied callback redefines flash[:error] and raises redirect exception
10
+ on_access_denied.call( request.path_info, access ) if on_access_denied.present?
11
+
12
+ redirect "/"
13
+ end
14
+ end
15
+
@@ -0,0 +1,53 @@
1
+ # Authentication system routes
2
+ route :get, :post, "/auth/:provider/callback" do
3
+ # flash[:debug] = "params.inspect: #{params.inspect}"
4
+
5
+ user = User.find_by_authentication( params[:provider].to_sym, request.env['omniauth.auth']['uid'] )
6
+ if params[:provider] == 'password' && !user.activated?( request.env['omniauth.auth']['uid'] )
7
+ auth_state params.merge(
8
+ result: 'error',
9
+ error: 'account_not_activated'
10
+ )
11
+ auth_redirect_to_origin( params['on_failure'] )
12
+ halt
13
+ end
14
+
15
+ if user
16
+ # registered user
17
+ auth_login( user, remember_me: !!params['remember_me'], provider: params[:provider], uid: request.env['omniauth.auth']['uid'] )
18
+ logger.debug( "successfully authenticated: #{params.inspect} ")
19
+ logger.debug( "user: #{user} ")
20
+ auth_state result: :success
21
+ flash[:notice] = t.aerogel.auth.welcome full_name: user.full_name
22
+ auth_redirect_to_origin( params["on_success"] )
23
+ else
24
+ user = User.create_from_omniauth request.env['omniauth.auth']
25
+ unless user.save
26
+ flash[:error] = t.aerogel.auth.errors.create_from_omniauth errors: user.errors.inspect
27
+ auth_redirect_to_origin( params["on_failure"] )
28
+ end
29
+ auth_login( user, remember_me: !!params['remember_me'], provider: params[:provider], uid: request.env['omniauth.auth']['uid'] )
30
+ flash[:notice] = t.aerogel.auth.welcome_new_user full_name: user.full_name
31
+ redirect "/user/edit"
32
+ end
33
+ end
34
+
35
+ get "/auth/failure" do
36
+ logger.debug( "failed to authenticate: #{params.inspect} ")
37
+ flash[:debug] = "params.inspect: #{params.inspect}"
38
+ auth_state params.merge(
39
+ result: 'error',
40
+ error: 'invalid_credentials',
41
+ provider: params['provider'] || params['strategy']
42
+ )
43
+ auth_redirect_to_origin( params['on_failure'] )
44
+ end
45
+
46
+
47
+ # Retrieves auth_state
48
+ #
49
+ before do
50
+ auth_state
51
+ end
52
+
53
+
@@ -0,0 +1,192 @@
1
+
2
+ namespace '/user' do
3
+
4
+ before do
5
+ on_access_denied do |path|
6
+ unless current_user?
7
+ redirect "/user/login?on_success=#{path}"
8
+ end
9
+ end
10
+ end
11
+
12
+ get '/login' do
13
+ @error_message = nil
14
+ params['on_success'] ||= auth_state['on_success'] || '/'
15
+ params['on_failure'] ||= auth_state['on_failure'] || '/user/login'
16
+ params['email'] ||= auth_state['email']
17
+ if auth_state['result'] == 'error'
18
+ auth_state['provider'] ||= "i_think_its_password"
19
+ # error messages are looked up in the following order:
20
+ # aerogel.auth.<provider>.errors.<message>
21
+ # aerogel.auth.errors.<message>
22
+ # aerogel.auth.errors.unknown
23
+ @error_message = t.aerogel.auth.send( auth_state['provider'].to_sym ).errors.send(
24
+ auth_state['error'].to_sym,
25
+ default: [ "aerogel.auth.errors.#{auth_state['error']}".to_sym, :'aerogel.auth.errors.unknown' ],
26
+ provider: auth_state['provider'],
27
+ message: auth_state['error']
28
+ )
29
+ end
30
+ pass
31
+ end
32
+
33
+ get '/logout' do
34
+ auth_logout
35
+ flash.now[:notice] = t.aerogel.users.actions.logged_out
36
+ pass
37
+ end
38
+
39
+ #
40
+ # Account management routes
41
+ #
42
+ get '/register' do
43
+ @user_registration_form = UserRegistrationForm.new
44
+ pass
45
+ end
46
+
47
+ post '/register' do
48
+ @user_registration_form = UserRegistrationForm.new( params[:user_registration_form] )
49
+ pass unless @user_registration_form.valid?
50
+ user = User.create_from @user_registration_form
51
+ unless user.save
52
+ flash[:error] = t.aerogel.db.errors.failed_to_save name: user.model_name.human,
53
+ errors: user.errors.full_messages.join(', ')
54
+ pass
55
+ end
56
+ user.request_activation!
57
+ email 'user/account_activation', user
58
+ view "user/register_success"
59
+ end
60
+
61
+ get '/request_account_activation' do
62
+ @user_request_account_activation_form = UserRequestAccountActivationForm.new
63
+ pass
64
+ end
65
+
66
+ post '/request_account_activation' do
67
+ @user_request_account_activation_form = UserRequestAccountActivationForm.new(
68
+ params[:user_request_account_activation_form]
69
+ )
70
+ pass unless @user_request_account_activation_form.valid?
71
+ user = @user_request_account_activation_form.user
72
+ user.request_activation!
73
+ email 'user/account_activation', user
74
+ view 'user/request_account_activation_success'
75
+ end
76
+
77
+
78
+ get '/activate_account' do
79
+ begin
80
+ @user = User.activate!( params['email'], params['token'] )
81
+ pass
82
+ rescue StandardError => e
83
+ flash.now[:error] = e.to_s
84
+ view 'user/activate_account_failure'
85
+ end
86
+ end
87
+
88
+ get '/request_email_confirmation' do
89
+ @user_request_email_confirmation_form = UserRequestEmailConfirmationForm.new
90
+ pass
91
+ end
92
+
93
+ post '/request_email_confirmation' do
94
+ @user_request_email_confirmation_form = UserRequestEmailConfirmationForm.new(
95
+ params[:user_request_email_confirmation_form]
96
+ )
97
+ pass unless @user_request_email_confirmation_form.valid?
98
+ user_email = @user_request_email_confirmation_form.user_email
99
+ user_email.user.request_email_confirmation! @user_request_email_confirmation_form.email
100
+ email 'user/email_confirmation', user_email
101
+ view 'user/request_email_confirmation_success'
102
+ end
103
+
104
+ get '/confirm_email' do
105
+ begin
106
+ user = User.confirm_email!( params['email'], params['token'] )
107
+ pass
108
+ rescue StandardError => e
109
+ flash.now[:error] = e.to_s
110
+ view 'user/confirm_email_failure'
111
+ end
112
+ end
113
+
114
+ get '/request_password_reset' do
115
+ @user_request_password_reset_form = UserRequestPasswordResetForm.new
116
+ pass
117
+ end
118
+
119
+ post '/request_password_reset' do
120
+ @user_request_password_reset_form = UserRequestPasswordResetForm.new(
121
+ params[:user_request_password_reset_form]
122
+ )
123
+ pass unless @user_request_password_reset_form.valid?
124
+ user = @user_request_password_reset_form.user
125
+ authentication = user.request_password_reset! @user_request_password_reset_form.email
126
+ email 'user/password_reset', authentication
127
+ view 'user/request_password_reset_success'
128
+ end
129
+
130
+ get '/reset_password' do
131
+ @user_reset_password_form = UserResetPasswordForm.new(
132
+ email: params['email'],
133
+ password_reset_token: params['token']
134
+ )
135
+ pass
136
+ end
137
+
138
+ post '/reset_password' do
139
+ @user_reset_password_form = UserResetPasswordForm.new(
140
+ params[:user_reset_password_form]
141
+ )
142
+ pass unless @user_reset_password_form.valid?
143
+ begin
144
+ user = User.reset_password!(
145
+ @user_reset_password_form.email,
146
+ @user_reset_password_form.password_reset_token,
147
+ @user_reset_password_form.password,
148
+ @user_reset_password_form.password_confirmation
149
+ )
150
+ view 'user/reset_password_success'
151
+ rescue StandardError => e
152
+ flash.now[:error] = e.to_s
153
+ pass
154
+ end
155
+ end
156
+
157
+ #
158
+ # Edit user profile
159
+ #
160
+ get '/edit' do
161
+ @user = current_user
162
+ pass
163
+ end
164
+
165
+ post '/edit' do
166
+ @user = User.find( params[:id] )
167
+ unless @user
168
+ flash.now[:error] = t.error.messages.user_not_found
169
+ pass
170
+ end
171
+ unless @user.id == current_user.id
172
+ redirect '/user', error: "Access denied, user profile does not belong to you"
173
+ end
174
+ unless @user.update_attributes params[:user].except( :roles )
175
+ flash.now[:error] = t.aerogel.db.errors.failed_to_save name: User.model_name.human,
176
+ errors: @user.errors.full_messages.join(", ")
177
+ pass
178
+ end
179
+ redirect '/user'
180
+ end
181
+
182
+ #
183
+ # Common User controller routes
184
+ #
185
+ route :get, :post, ['', '/:action'] do
186
+ action = params[:action] || 'index'
187
+ view "user/#{action}"
188
+ end
189
+
190
+
191
+ end # namespace '/user'
192
+