rails-identity 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (90) hide show
  1. checksums.yaml +7 -0
  2. data/MIT-LICENSE +20 -0
  3. data/Rakefile +37 -0
  4. data/app/assets/javascripts/rails_identity/application.js +13 -0
  5. data/app/assets/javascripts/rails_identity/sessions.js +2 -0
  6. data/app/assets/javascripts/rails_identity/users.js +2 -0
  7. data/app/assets/stylesheets/rails_identity/application.css +15 -0
  8. data/app/assets/stylesheets/rails_identity/sessions.css +4 -0
  9. data/app/assets/stylesheets/rails_identity/users.css +4 -0
  10. data/app/controllers/rails_identity/application_controller.rb +200 -0
  11. data/app/controllers/rails_identity/sessions_controller.rb +108 -0
  12. data/app/controllers/rails_identity/users_controller.rb +168 -0
  13. data/app/helpers/rails_identity/application_helper.rb +19 -0
  14. data/app/helpers/rails_identity/sessions_helper.rb +4 -0
  15. data/app/helpers/rails_identity/users_helper.rb +4 -0
  16. data/app/jobs/rails_identity/sessions_cleanup_job.rb +13 -0
  17. data/app/mailers/application_mailer.rb +4 -0
  18. data/app/mailers/rails_identity/user_mailer.rb +14 -0
  19. data/app/models/rails_identity/session.rb +44 -0
  20. data/app/models/rails_identity/user.rb +48 -0
  21. data/app/views/layouts/mailer.html.erb +5 -0
  22. data/app/views/layouts/mailer.text.erb +1 -0
  23. data/app/views/layouts/rails_identity/application.html.erb +14 -0
  24. data/app/views/rails_identity/user_mailer/email_verification.html.erb +12 -0
  25. data/app/views/rails_identity/user_mailer/email_verification.text.erb +13 -0
  26. data/app/views/rails_identity/user_mailer/password_reset.html.erb +14 -0
  27. data/app/views/rails_identity/user_mailer/password_reset.text.erb +15 -0
  28. data/config/routes.rb +7 -0
  29. data/db/migrate/20160323210013_create_rails_identity_users.rb +13 -0
  30. data/db/migrate/20160323210017_create_rails_identity_sessions.rb +12 -0
  31. data/db/migrate/20160401223433_add_reset_token_to_users.rb +5 -0
  32. data/db/migrate/20160411215917_add_verification_token_to_users.rb +10 -0
  33. data/db/migrate/20160414145851_add_api_key_to_users.rb +5 -0
  34. data/lib/rails_identity/engine.rb +9 -0
  35. data/lib/rails_identity/version.rb +3 -0
  36. data/lib/rails_identity.rb +52 -0
  37. data/lib/tasks/rails_identity_tasks.rake +4 -0
  38. data/test/controllers/rails_identity/sessions_controller_test.rb +192 -0
  39. data/test/controllers/rails_identity/users_controller_test.rb +253 -0
  40. data/test/dummy/README.rdoc +28 -0
  41. data/test/dummy/Rakefile +6 -0
  42. data/test/dummy/app/assets/javascripts/application.js +13 -0
  43. data/test/dummy/app/assets/stylesheets/application.css +15 -0
  44. data/test/dummy/app/controllers/application_controller.rb +5 -0
  45. data/test/dummy/app/helpers/application_helper.rb +2 -0
  46. data/test/dummy/app/views/layouts/application.html.erb +14 -0
  47. data/test/dummy/bin/bundle +3 -0
  48. data/test/dummy/bin/rails +4 -0
  49. data/test/dummy/bin/rake +4 -0
  50. data/test/dummy/bin/setup +29 -0
  51. data/test/dummy/config/application.rb +26 -0
  52. data/test/dummy/config/boot.rb +5 -0
  53. data/test/dummy/config/database.yml +25 -0
  54. data/test/dummy/config/environment.rb +5 -0
  55. data/test/dummy/config/environments/development.rb +41 -0
  56. data/test/dummy/config/environments/production.rb +79 -0
  57. data/test/dummy/config/environments/test.rb +42 -0
  58. data/test/dummy/config/initializers/assets.rb +11 -0
  59. data/test/dummy/config/initializers/backtrace_silencers.rb +7 -0
  60. data/test/dummy/config/initializers/cookies_serializer.rb +3 -0
  61. data/test/dummy/config/initializers/filter_parameter_logging.rb +4 -0
  62. data/test/dummy/config/initializers/inflections.rb +16 -0
  63. data/test/dummy/config/initializers/mime_types.rb +4 -0
  64. data/test/dummy/config/initializers/session_store.rb +3 -0
  65. data/test/dummy/config/initializers/wrap_parameters.rb +14 -0
  66. data/test/dummy/config/locales/en.yml +23 -0
  67. data/test/dummy/config/routes.rb +4 -0
  68. data/test/dummy/config/secrets.yml +22 -0
  69. data/test/dummy/config.ru +4 -0
  70. data/test/dummy/db/development.sqlite3 +0 -0
  71. data/test/dummy/db/schema.rb +42 -0
  72. data/test/dummy/db/test.sqlite3 +0 -0
  73. data/test/dummy/log/development.log +215 -0
  74. data/test/dummy/log/test.log +280622 -0
  75. data/test/dummy/public/404.html +67 -0
  76. data/test/dummy/public/422.html +67 -0
  77. data/test/dummy/public/500.html +66 -0
  78. data/test/dummy/public/favicon.ico +0 -0
  79. data/test/dummy/tmp/cache/A5C/3F0/rails-identity-0.0.1-session-1 +0 -0
  80. data/test/fixtures/rails_identity/sessions.yml +36 -0
  81. data/test/fixtures/rails_identity/users.yml +24 -0
  82. data/test/integration/navigation_test.rb +10 -0
  83. data/test/jobs/rails_identity/sessions_cleanup_job_test.rb +9 -0
  84. data/test/mailers/previews/rails_identity/user_mailer_preview.rb +6 -0
  85. data/test/mailers/rails_identity/user_mailer_test.rb +9 -0
  86. data/test/models/rails_identity/session_test.rb +26 -0
  87. data/test/models/rails_identity/user_test.rb +54 -0
  88. data/test/rails_identity_test.rb +7 -0
  89. data/test/test_helper.rb +33 -0
  90. metadata +297 -0
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: f8999b0f579047f638bec461d6b469bc46b85319
4
+ data.tar.gz: 3412103b78681075aef4c59610db1a2bb17db352
5
+ SHA512:
6
+ metadata.gz: 120410b35e89556e779f13cae65aad0c68c57e0d3353094297ad8b7eb1e32c5ced2ae6ebd6bf5b6dbe158b587e8bcb08a0d73ae0ab5993c77e48d2c529dd3658
7
+ data.tar.gz: efe61e96f68ceefd497de74573a5b75c8d8ddcbe6a5c5273aec7bbd4dc6d071c4a98ba91dd060ed5bafe4170d9ffe6f81220b0b2234773a6d04334f6c715ccf2
data/MIT-LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright 2016
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/Rakefile ADDED
@@ -0,0 +1,37 @@
1
+ begin
2
+ require 'bundler/setup'
3
+ rescue LoadError
4
+ puts 'You must `gem install bundler` and `bundle install` to run rake tasks'
5
+ end
6
+
7
+ require 'rdoc/task'
8
+
9
+ RDoc::Task.new(:rdoc) do |rdoc|
10
+ rdoc.rdoc_dir = 'rdoc'
11
+ rdoc.title = 'RailsIdentity'
12
+ rdoc.options << '--line-numbers'
13
+ rdoc.rdoc_files.include('README.rdoc')
14
+ rdoc.rdoc_files.include('lib/**/*.rb')
15
+ end
16
+
17
+ APP_RAKEFILE = File.expand_path("../test/dummy/Rakefile", __FILE__)
18
+ load 'rails/tasks/engine.rake'
19
+
20
+
21
+ load 'rails/tasks/statistics.rake'
22
+
23
+
24
+
25
+ Bundler::GemHelper.install_tasks
26
+
27
+ require 'rake/testtask'
28
+
29
+ Rake::TestTask.new(:test) do |t|
30
+ t.libs << 'lib'
31
+ t.libs << 'test'
32
+ t.pattern = 'test/**/*_test.rb'
33
+ t.verbose = false
34
+ end
35
+
36
+
37
+ task default: :test
@@ -0,0 +1,13 @@
1
+ // This is a manifest file that'll be compiled into application.js, which will include all the files
2
+ // listed below.
3
+ //
4
+ // Any JavaScript/Coffee file within this directory, lib/assets/javascripts, vendor/assets/javascripts,
5
+ // or any plugin's vendor/assets/javascripts directory can be referenced here using a relative path.
6
+ //
7
+ // It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the
8
+ // compiled file.
9
+ //
10
+ // Read Sprockets README (https://github.com/rails/sprockets#sprockets-directives) for details
11
+ // about supported directives.
12
+ //
13
+ //= require_tree .
@@ -0,0 +1,2 @@
1
+ // Place all the behaviors and hooks related to the matching controller here.
2
+ // All this logic will automatically be available in application.js.
@@ -0,0 +1,2 @@
1
+ // Place all the behaviors and hooks related to the matching controller here.
2
+ // All this logic will automatically be available in application.js.
@@ -0,0 +1,15 @@
1
+ /*
2
+ * This is a manifest file that'll be compiled into application.css, which will include all the files
3
+ * listed below.
4
+ *
5
+ * Any CSS and SCSS file within this directory, lib/assets/stylesheets, vendor/assets/stylesheets,
6
+ * or any plugin's vendor/assets/stylesheets directory can be referenced here using a relative path.
7
+ *
8
+ * You're free to add application-wide styles to this file and they'll appear at the bottom of the
9
+ * compiled file so the styles you add here take precedence over styles defined in any styles
10
+ * defined in the other CSS/SCSS files in this directory. It is generally better to create a new
11
+ * file per style scope.
12
+ *
13
+ *= require_tree .
14
+ *= require_self
15
+ */
@@ -0,0 +1,4 @@
1
+ /*
2
+ Place all the styles related to the matching controller here.
3
+ They will automatically be included in application.css.
4
+ */
@@ -0,0 +1,4 @@
1
+ /*
2
+ Place all the styles related to the matching controller here.
3
+ They will automatically be included in application.css.
4
+ */
@@ -0,0 +1,200 @@
1
+ module RailsIdentity
2
+
3
+ ##
4
+ # The root application controller class in rails-identity.
5
+ #
6
+ class ApplicationController < ActionController::Base
7
+ include ApplicationHelper
8
+
9
+ # This is a catch-all.
10
+ rescue_from StandardError do |exception|
11
+ # :nocov:
12
+ logger.error exception.message
13
+ render_error 500, "Unknown error occurred: #{exception.message}"
14
+ # :nocov:
15
+ end
16
+
17
+ # Most actions require a session token. If token is invalid, rescue the
18
+ # exception and throw an HTTP 401 response.
19
+ rescue_from Errors::InvalidTokenError do |exception|
20
+ logger.error exception.message
21
+ render_error 401, "Invalid token"
22
+ end
23
+
24
+ # Some actions require a resource object via id. If no such object
25
+ # exists, throw an HTTP 404 response.
26
+ rescue_from Errors::ObjectNotFoundError do |exception|
27
+ logger.error exception.message
28
+ render_error 404, exception.message
29
+ end
30
+
31
+ # The request is authenticated but not authorized for the specified
32
+ # action. Throw an HTTP 401 response.
33
+ #
34
+ rescue_from Errors::UnauthorizedError do |exception|
35
+ logger.error exception.message
36
+ render_error 401, "Unauthorized request"
37
+ end
38
+
39
+ ##
40
+ # Renders a generic OPTIONS response. The actual controller must
41
+ # override this action if desired to have specific OPTIONS handling
42
+ # logic.
43
+ #
44
+ def options()
45
+ # echo back access-control-request-headers
46
+ if request.headers["Access-Control-Request-Headers"]
47
+ response["Access-Control-Allow-Headers"] = request.headers["Access-Control-Request-Headers"]
48
+ end
49
+ render body: "", status: 200
50
+ end
51
+
52
+ protected
53
+
54
+ ##
55
+ # Helper method to get the user object in the request context. There
56
+ # are two ways to specify the user id--one in the routing or the auth
57
+ # context. Only admin can actually specify the user id in the routing.
58
+ #
59
+ # A Errors::UnauthorizedError is raised if the authenticated user
60
+ # is not authorized for the specified user information.
61
+ #
62
+ # A Errors::ObjectNotFoundError is raised if the specified user cannot
63
+ # be found.
64
+ #
65
+ def get_user(fallback: true)
66
+ user_id = params[:user_id]
67
+ logger.debug("Attempting to get user #{user_id}")
68
+ if !user_id.nil? && user_id != "current"
69
+ @user = find_object(User, params[:user_id]) # will throw error if nil
70
+ unless authorized?(@user)
71
+ raise Errors::UnauthorizedError, "Not authorized to access user #{user_id}"
72
+ end
73
+ elsif fallback || user_id == "current"
74
+ @user = @auth_user
75
+ else
76
+ # :nocov:
77
+ raise Errors::ObjectNotFoundError, "User #{user_id} does not exist"
78
+ # :nocov:
79
+ end
80
+ end
81
+
82
+ ##
83
+ # Finds an object by model and UUID and throws an error (which will be
84
+ # caught and re-thrown as an HTTP error.)
85
+ #
86
+ # A Errors::ObjectNotFoundError is raised if specified to do so when
87
+ # the object could not be found using the uuid.
88
+ #
89
+ def find_object(model, uuid, error: Errors::ObjectNotFoundError)
90
+ logger.debug("Attempting to get #{model.name} #{uuid}")
91
+ obj = model.find_by_uuid(uuid)
92
+ if obj.nil? && !error.nil?
93
+ raise error, "#{model.name} #{uuid} cannot be found"
94
+ end
95
+ return obj
96
+ end
97
+
98
+ ##
99
+ # Attempt to get a token for the session. Token must be specified in query
100
+ # string or part of the JSON object.
101
+ #
102
+ # A Errors::InvalidTokenError is raised if the JWT is malformed or not
103
+ # valid against its secret.
104
+ #
105
+ def get_token(required_role: Roles::PUBLIC)
106
+ token = params[:token]
107
+
108
+ # Attempt to decode token w/o secret first to see if well-formed and
109
+ # not expired.
110
+ begin
111
+ decoded = JWT.decode token, nil, false
112
+ rescue JWT::DecodeError => e
113
+ logger.error("Token decode error: #{e.message}")
114
+ raise Errors::InvalidTokenError, "Invalid token: #{token}"
115
+ end
116
+
117
+ # At this point, we know that the token is not expired and
118
+ # well formatted. Find out if the payload is well defined.
119
+ payload = decoded[0]
120
+ if payload.nil?
121
+ logger.error("Token payload is nil: #{token}")
122
+ raise Errors::InvalidTokenError, "Invalid token payload: #{token}"
123
+ end
124
+
125
+ user_uuid = payload["user_uuid"]
126
+ session_uuid = payload["session_uuid"]
127
+ if user_uuid.nil? || session_uuid.nil?
128
+ logger.error("User UUID or session UUID is nil")
129
+ raise Errors::InvalidTokenError, "Invalid token payload content: #{token}"
130
+ end
131
+ logger.debug("Token well formatted for user #{user_uuid}, session #{session_uuid}")
132
+
133
+ # Look up the cache. If present, use it and skip the verification.
134
+ @auth_session = Rails.cache.fetch("#{CACHE_PREFIX}-session-#{session_uuid}")
135
+ if @auth_session.nil?
136
+ logger.debug("Cache miss. Try database.")
137
+ auth_user = User.find_by_uuid(user_uuid)
138
+ if auth_user.nil? || auth_user.role < required_role
139
+ raise Errors::InvalidTokenError, "Well-formed but invalid user token: #{token}"
140
+ end
141
+ @auth_session = Session.find_by_uuid(session_uuid)
142
+ if @auth_session.nil?
143
+ raise Errors::InvalidTokenError, "Well-formed but invalid session token: #{token}"
144
+ end
145
+ JWT.decode token, @auth_session.secret, true
146
+ logger.debug("Token well formatted and verified. Set cache.")
147
+ Rails.cache.write("#{CACHE_PREFIX}-session-#{session_uuid}", @auth_session)
148
+ end
149
+ @auth_user = @auth_session.user
150
+ @token = @auth_session.token
151
+ end
152
+
153
+ ##
154
+ # Requires a token.
155
+ #
156
+ def require_token
157
+ logger.debug("Requires a token")
158
+ get_token
159
+ end
160
+
161
+ ##
162
+ # Accepts a token if present. If not, it's still ok.
163
+ #
164
+ def accept_token()
165
+ logger.debug("Accepts a token")
166
+ begin
167
+ get_token()
168
+ rescue StandardError => e
169
+ logger.error("Suppressing error: #{e.message}")
170
+ end
171
+ end
172
+
173
+ ##
174
+ # Requires an admin session. All this means is that the session is
175
+ # issued for an admin user (role == 1000).
176
+ #
177
+ def require_admin_token
178
+ logger.debug("Requires an admin token")
179
+ get_token(required_role: Roles::ADMIN)
180
+ end
181
+
182
+ ##
183
+ # Determines if the user is authorized for the object.
184
+ #
185
+ def authorized?(obj)
186
+ logger.debug("Checking to see if authorized to access object")
187
+ if !@auth_user
188
+ # :nocov:
189
+ return false
190
+ # :nocov:
191
+ elsif @auth_user.role >= Roles::ADMIN
192
+ return true
193
+ elsif obj.is_a? User
194
+ return obj == @auth_user
195
+ else
196
+ return obj.user == @auth_user
197
+ end
198
+ end
199
+ end
200
+ end
@@ -0,0 +1,108 @@
1
+ require_dependency "rails_identity/application_controller"
2
+
3
+ module RailsIdentity
4
+
5
+ ##
6
+ # This class is sessions controller that performs CRD on session objects.
7
+ # Note that a token includes its session ID. Use "current" to look up a
8
+ # session in the current context.
9
+ #
10
+ class SessionsController < ApplicationController
11
+
12
+ prepend_before_action :require_token, except: [:create, :options]
13
+ before_action :get_session, only: [:show, :destroy]
14
+ before_action :get_user, only: [:index]
15
+
16
+ ##
17
+ # Lists all sessions that belong to the specified or authenticated user.
18
+ #
19
+ def index
20
+ @sessions = Session.where(user: @user)
21
+ expired = []
22
+ active = []
23
+ @sessions.each do |session|
24
+ if session.expired?
25
+ expired << session.uuid
26
+ else
27
+ active << session
28
+ end
29
+ end
30
+ SessionsCleanupJob.perform_later(*expired)
31
+ render json: active, except: [:secret]
32
+ end
33
+
34
+ ##
35
+ # This action is essentially the login action. Note that get_user is not
36
+ # triggered for this action because we will look at username first. That
37
+ # would be the "normal" way to login. The alternative would be with the
38
+ # token based authentication. If the latter doesn't make sense, just use
39
+ # the username and password approach.
40
+ #
41
+ def create
42
+ @user = User.find_by_username(session_params[:username])
43
+ if (@user && @user.authenticate(session_params[:password])) || get_user()
44
+ raise Errors::UnauthorizedError unless @user.verified
45
+ @session = Session.new(user: @user)
46
+ if @session.save
47
+ render json: @session, except: [:secret], status: 201
48
+ else
49
+ # :nocov:
50
+ render_errors 400, @session.full_error_messages
51
+ # :nocov:
52
+ end
53
+ else
54
+ render_error 401, "Invalid username or password"
55
+ end
56
+ end
57
+
58
+ ##
59
+ # Shows a session information.
60
+ #
61
+ def show
62
+ render json: @session, except: [:secret]
63
+ end
64
+
65
+ ##
66
+ # Deletes a session.
67
+ #
68
+ def destroy
69
+ if @session.destroy
70
+ render body: "", status: 204
71
+ else
72
+ # :nocov:
73
+ render_error 500, "Something went wrong. Oops!"
74
+ # :nocov:
75
+ end
76
+ end
77
+
78
+ private
79
+
80
+ ##
81
+ # Get the specified or current session.
82
+ #
83
+ # An Errors::ObjectNotFoundError is raised if the session does not
84
+ # exist (or deleted due to expiration).
85
+ #
86
+ # An Errors::UnauthorizedError is raised if the authenticated user
87
+ # does not have authorization for the specified session.
88
+ #
89
+ def get_session
90
+ session_id = params[:id]
91
+ if session_id == "current"
92
+ session_id = @auth_session.id
93
+ end
94
+ @session = find_object(Session, session_id)
95
+ if !authorized?(@session)
96
+ raise Errors::UnauthorizedError
97
+ elsif @session.expired?
98
+ @session.destroy
99
+ raise Errors::ObjectNotFoundError
100
+ end
101
+ end
102
+
103
+ def session_params
104
+ params.permit(:username, :password)
105
+ end
106
+
107
+ end
108
+ end
@@ -0,0 +1,168 @@
1
+ require_dependency "rails_identity/application_controller"
2
+
3
+ module RailsIdentity
4
+
5
+ ##
6
+ # Users controller that performs CRUD on users.
7
+ #
8
+ class UsersController < ApplicationController
9
+
10
+ # All except user creation requires a session token. Note that reset
11
+ # token is also a legit session token, so :require_token will suffice.
12
+ prepend_before_action :require_token, only: [:show, :destroy]
13
+ prepend_before_action :accept_token, only: [:update, :create]
14
+ prepend_before_action :require_admin_token, only: [:index]
15
+
16
+ # Some actions must have a user specified.
17
+ before_action :get_user, only: [:show, :destroy]
18
+
19
+ ##
20
+ # List all users (but only works for admin user).
21
+ #
22
+ def index
23
+ @users = User.all
24
+ render json: @users, except: [:password_digest]
25
+ end
26
+
27
+ ##
28
+ # Creates a new user. This action does not require any auth although it
29
+ # is optional.
30
+ #
31
+ def create
32
+ @user = User.new(user_params)
33
+ if @user.save
34
+ render json: @user, except: [:verification_token, :reset_token, :password_digest], status: 201
35
+ UserMailer.email_verification(@user).deliver_later
36
+ else
37
+ render_errors 400, @user.errors.full_messages
38
+ end
39
+ end
40
+
41
+ ##
42
+ # Renders a user data.
43
+ #
44
+ def show
45
+ render json: @user, except: [:password_digest], methods: [:role]
46
+ end
47
+
48
+ ##
49
+ # Patches the user. Some overloading operations here. There are five
50
+ # notable ways to update a user.
51
+ #
52
+ # - Issue a reset token
53
+ # If params has :issue_reset_token set to true, the action will
54
+ # issue a reset token for the user and returns 204. Yes, 204 No
55
+ # Content. TODO: in the future, the action will trigger an email.
56
+ # - Reset the password
57
+ # Two ways to reset password:
58
+ # - Provide the old password along with the new password and
59
+ # confirmation.
60
+ # - Provide the reset token as the auth token.
61
+ # - Issue a verification token
62
+ # - Change other data
63
+ #
64
+ def update
65
+ if params[:issue_reset_token] || params[:issue_verification_token]
66
+ # For issuing a reset token, one does not need an auth token. so do
67
+ # not authorize the request.
68
+ raise Errors::UnauthorizedError unless params[:id] == "current"
69
+ get_user_for_token()
70
+ raise Errors::UnauthorizedError unless params[:username] == @user.username
71
+ if params[:issue_reset_token]
72
+ update_token(:reset_token)
73
+ else
74
+ update_token(:verification_token)
75
+ end
76
+ else
77
+ get_user()
78
+ if params[:password]
79
+ if params[:old_password]
80
+ raise Errors::UnauthorizedError unless @user.authenticate(params[:old_password])
81
+ else
82
+ raise Errors::UnauthorizedError unless @token == @user.reset_token
83
+ end
84
+ end
85
+ update_user(user_params)
86
+ end
87
+ end
88
+
89
+ ##
90
+ # Deletes a user.
91
+ #
92
+ def destroy
93
+ if @user.destroy
94
+ render body: '', status: 204
95
+ else
96
+ # :nocov:
97
+ render_error 500, "Something went wrong!"
98
+ # :nocov:
99
+ end
100
+ end
101
+
102
+ protected
103
+
104
+ ##
105
+ # This method normally updates the user using permitted params.
106
+ #
107
+ def update_user(update_user_params)
108
+ if @user.update_attributes(update_user_params)
109
+ render json: @user, except: [:password_digest]
110
+ else
111
+ render_errors 400, @user.errors.full_messages
112
+ end
113
+ end
114
+
115
+ ##
116
+ # This method updates user with a new reset token. Only used for this
117
+ # operation.
118
+ #
119
+ def update_token(kind)
120
+ @user.issue_token(kind)
121
+ @user.save
122
+ if kind == :reset_token
123
+ UserMailer.password_reset(@user).deliver_later
124
+ else
125
+ UserMailer.email_verification(@user).deliver_later
126
+ end
127
+ render body: '', status: 204
128
+ end
129
+
130
+ private
131
+
132
+ ##
133
+ # This overrides the application controller's get_user method. Since
134
+ # resource object of this users controller is user, the id is
135
+ # specified in :id param.
136
+ #
137
+ def get_user
138
+ if params[:id] == "current"
139
+ raise Errors::UnauthorizedError if @auth_user.nil?
140
+ params[:id] = @auth_user.uuid
141
+ end
142
+ @user = find_object(User, params[:id])
143
+ raise Errors::UnauthorizedError unless authorized?(@user)
144
+ return @user
145
+ end
146
+
147
+ ##
148
+ # For issuing a new reset or for re-issuing a verification token, use
149
+ # this method to get user.
150
+ #
151
+ def get_user_for_token
152
+ @user = User.find_by_username(params[:username])
153
+ raise Errors::ObjectNotFoundError if @user.nil?
154
+ return @user
155
+ end
156
+
157
+ def user_params
158
+ # Only ADMIN can assign the attribute role. The attribute value will
159
+ # be ignored if the user is not an ADMIN.
160
+ if @auth_user.try(:role).try(:>=, Roles::ADMIN)
161
+ params.permit(:username, :password, :password_confirmation, :role, :verified)
162
+ else
163
+ params.permit(:username, :password, :password_confirmation, :verified)
164
+ end
165
+ end
166
+
167
+ end
168
+ end
@@ -0,0 +1,19 @@
1
+ module RailsIdentity
2
+ module ApplicationHelper
3
+
4
+ ##
5
+ # Renders a single error.
6
+ #
7
+ def render_error(status, msg)
8
+ render json: {errors: [msg]}, status: status
9
+ end
10
+
11
+ ##
12
+ # Renders multiple errors
13
+ #
14
+ def render_errors(status, msgs)
15
+ render json: {errors: msgs}, status: status
16
+ end
17
+
18
+ end
19
+ end
@@ -0,0 +1,4 @@
1
+ module RailsIdentity
2
+ module SessionsHelper
3
+ end
4
+ end
@@ -0,0 +1,4 @@
1
+ module RailsIdentity
2
+ module UsersHelper
3
+ end
4
+ end
@@ -0,0 +1,13 @@
1
+ module RailsIdentity
2
+ class SessionsCleanupJob < ActiveJob::Base
3
+ queue_as :default
4
+
5
+ def perform(*args)
6
+ # Do something later
7
+ args.each do |uuid|
8
+ session = Session.find_by_uuid(uuid)
9
+ session.destroy()
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,4 @@
1
+ class ApplicationMailer < ActionMailer::Base
2
+ default from: RailsIdentity::MAILER_EMAIL
3
+ layout 'mailer'
4
+ end
@@ -0,0 +1,14 @@
1
+ module RailsIdentity
2
+ class UserMailer < ApplicationMailer
3
+
4
+ def email_verification(user)
5
+ @user = user
6
+ mail(to: @user.username, subject: "[rails-identity] Email Confirmation")
7
+ end
8
+
9
+ def password_reset(user)
10
+ @user = user
11
+ mail(to: @user.username, subject: "[rails-identity] Password Reset")
12
+ end
13
+ end
14
+ end