Suppress password change email when accepting invitation with Devise Invitable - ruby-on-rails

I have enabled password change notifications using
config.send_password_change_notification = true
However, currently when a user accepts an invitation triggered by Devise Invitable they are receiving a password change email.
e.g.
Started PUT "/users/invitation" for 127.0.0.1 at 2017-10-20 16:14:41 +0100
Processing by UsersController::InvitationsController#update as HTML
Parameters: {"utf8"=>"✓", "authenticity_token"=>"9gDHF+Vm6oxGPILonaQe7NSnhytoQGsOBm0eVEMziSS6J93UFnoHSwouyV9NezleulmstfcNW8Axr/nJajBBYw==", "user"=>{"invitation_token"=>"98usw1XW4w31UuCd_DzQ", "full_name"=>"example", "password"=>"[FILTERED]", "password_confirmation"=>"[FILTERED]"}, "commit"=>"Set my password"}
User Load (1.0ms) SELECT "users".* FROM "users" WHERE "users"."invitation_token" = $1 ORDER BY "users"."id" ASC LIMIT $2 [["invitation_token", "0a0787a3270a097c22a1272f49040c5c11d67b2cb222059d88d85f35e95c8d78"], ["LIMIT", 1]]
(0.2ms) BEGIN
SQL (0.6ms) UPDATE "users" SET "invitation_token" = $1, "encrypted_password" = $2, "full_name" = $3, "invitation_accepted_at" = $4, "updated_at" = $5 WHERE "users"."id" = $6 [["invitation_token", nil], ["encrypted_password", "$2a$11$qQKCx4FWQS2ARyiiGf8zdeAn7XLBa0clbWuv1cH1c1cXWbF65VMd6"], ["full_name", "example"], ["invitation_accepted_at", "2017-10-20 16:14:41.323768"], ["updated_at", "2017-10-20 16:14:41.324814"], ["id", 9]]
DeviseMailer#password_change: processed outbound mail in 1.7ms
Sent mail to example#example.com (195.9ms)
Is it possible to suppress the notification if the user is accepting an invitation but continue to send it for other password change events?
Environment info:
Rails 5.1.4
devise (4.3.0, 4.2.1)
devise_invitable (1.7.2)

thank you for pointing me in the right direction, here is my take on this
def send_password_change_notification
super unless accepting_invitation?
end
accepting_invitation? is a variable set on the beginning of the process
As a bonus, it bugged me that when you ask for a password recover it would prompt you with a success message and don't do anything, with this it would prompt with an error message.
[:en, :errors, :messages, :invitation_active] on your locales or just use default :not_found
def send_reset_password_instructions
if invited_to_sign_up?
self.errors.add :email, :invitation_active
else
super
end
end

OK, figured this out.
Add the following to app/models/user.rb
def send_devise_notification(notification, *args)
unless(notification == :password_change && sign_in_count.zero?)
super
end
end

Related

Cannot access user model from Devise sessions controller: [merit] no target_obj found on Rule#applies?

I have
class CustomSessionsController < Devise::SessionsController
def create
#user = resource # needed for Merit
super
end
protected
def after_sign_in_path_for(resource)
#user = resource # needed for Merit
resource.update_streak
super
And
grant_on 'custom_sessions#create', badge: :streak, level: 3, temporary: true, model_name: 'User' do |user|
puts user.inspect
user.streak.count >= 3
end
But it gives the error
[merit] no target_obj found on Rule#applies?
And I can't access the model and it doesn't grant the badge or log the user. What is wrong? I followed the guide.
https://github.com/merit-gem/merit/wiki/How-to-grant-badges-on-user-using-Devise
It's doing something.
Processing by CustomSessionsController#create as HTML
Parameters: {"utf8"=>"√", "authenticity_token"=>"gqUQjF9hfzJdQqxAAQJxv7bi+kZYwuv1NWtOP0YhkbjHKwnfa5WAb/CkRZ5c+Xi5yVlnJ2v774w3XLhTa1b1sQ==", "user"=>{"email"=>"student#gmail.com", "password"=>"[FILTERED]", "remember_me"=>"0"}, "commit"=>"Log in"}
User Load (6.0ms) SELECT "users".* FROM "users" WHERE "users"."email" = $1 ORDER BY "users"."id" ASC LIMIT 1 [["email", "student#gmail.com"]]
(7.0ms) BEGIN
SQL (3.0ms) UPDATE "users" SET "last_sign_in_at" = $1, "current_sign_in_at" = $2, "sign_in_count" = $3, "updated_at" = $4 WHERE "users"."id" = $5 [["last_sign_in_at", "2018-08-09 05:38:58.345271"], ["current_sign_in_at", "2018-08-10 01:40:51.644592"], ["sign_in_count", 15], ["updated_at", "2018-08-10 01:40:51.668609"], ["id", 3]]
(25.0ms) COMMIT
Streak Load (21.0ms) SELECT "streaks".* FROM "streaks" WHERE "streaks"."user_id" = $1 LIMIT 1 [["user_id", 3]]
Redirected to http://localhost:3000/
(1.0ms) BEGIN
SQL (7.0ms) INSERT INTO "merit_actions" ("user_id", "action_method", "target_model", "target_data", "created_at", "updated_at") VALUES ($1, $2, $3, $4, $5, $6) RETURNING "id" [["user_id", 3], ["action_method", "create"], ["target_model", "custom_sessions"], ["target_data", "--- \n...\n"], ["created_at", "2018-08-10 01:40:53.539847"], ["updated_at", "2018-08-10 01:40:53.539847"]]
(8.0ms) COMMIT
Merit::Action Load (6.0ms) SELECT "merit_actions".* FROM "merit_actions" WHERE "merit_actions"."processed" = $1 [["processed", "f"]]
(3.0ms) BEGIN
SQL (2.0ms) UPDATE "merit_actions" SET "processed" = $1, "updated_at" = $2 WHERE "merit_actions"."id" = $3 [["processed", "t"], ["updated_at", "2018-08-10 01:40:53.581875"], ["id", 17]]
(20.0ms) COMMIT
User Load (2.0ms) SELECT "users".* FROM "users" WHERE "users"."id" IS NULL LIMIT 1
[merit] no target_obj found on Rule#applies?
CACHE (0.0ms) SELECT "users".* FROM "users" WHERE "users"."id" IS NULL LIMIT 1
CACHE (0.0ms) SELECT "users".* FROM "users" WHERE "users"."id" IS NULL LIMIT 1
[merit] no target_obj found on Rule#applies?
CACHE (0.0ms) SELECT "users".* FROM "users" WHERE "users"."id" IS NULL LIMIT 1
Completed 302 Found in 2567ms (ActiveRecord: 293.2ms)
Merit 2.4, Rails 4.2.
I tried
grant_on 'custom_sessions#create', badge: :streak, level: 3, temporary: true do
puts current_user.inspect
current_user.streak.count >= 3
end
But it gave
[merit] no target found: uninitialized constant CustomSession. base_target_finder.rb:13:in 'find'
error NameError (undefined local variable or method 'current_user'
I tried
grant_on 'custom_sessions#create', badge: :streak, level: 3, temporary: true, to: :itself do |user|
puts user.inspect
user.streak.count >= 3
end
def create
#custom_session = resource # needed for Merit
def after_sign_in_path_for(resource)
#custom_session = resource # needed for Merit
But it gave
[merit] no target found: uninitialized constant CustomSession. C:/ruby23/lib/ruby/gems/2.3.0/gems/merit-2.4.0/lib/merit/base_target_finder.rb:13:in `find'
true
Completed 500 Internal Server Error in 2181ms (ActiveRecord: 177.1ms)
NoMethodError (undefined method `streak' for true:TrueClass):
app/models/merit/badge_rules.rb:43:in `block in initialize'
I got it working with
grant_on 'custom_sessions#create', badge: :streak, level: 3, temporary: true, model_name: 'User', to: :itself do |user|
def create
super
#custom_session = resource # needed for Merit
But I don't know why because the /users/sign_in path does not have an :id parameter.
https://github.com/merit-gem/merit#how-merit-finds-the-target-object
Merit would fetch the Article object from the database, found by the :id param sent in that update action.

Custom redirect based on user role in rails 5 with devise

Its been some time since I've worked with rails and I am jumping into a "project" for a company I work for..
Essentially what I am trying to do is redirect a user who logs in to the app to a specific page upon login..
For example an admin user will be redirected to an admin dashboard, an owner will be redirected to an owner dashboard and a driver to a driver dashboard..
Ive done this in the past using a single view and then filling it with elseif statements. But I found that to make my code look clunky and slow the app down.
I cant seem to find any docs (probably because I've been out of the game so long) on how to redirect to a specific url based on a user role.. Is this even possible? if so would someone be willing to share some resources as I am reaally struggling in this department.
Thanks in advance!
EDIT 1: Implemented ApplicationController method to find user role and redirect to appropriate page.
so I have added a few lines of code to my ApplicationController
class ApplicationController < ActionController::Base
protect_from_forgery with: :exception
def after_sign_in_path_for(resource)
if current_user.role == "dispatch"
dashboard_dispatch_path
else
root_path
end
end
end
when I try to load the page, the url in the browser changes to the proper url, however I get the following error:
the stack trace from rails server:
Started POST "/users/sign_in" for 127.0.0.1 at 2018-04-01 12:10:58 -0600
Processing by Devise::SessionsController#create as HTML
Parameters: {"utf8"=>"✓", "authenticity_token"=>"LJSAbgb3spFs0kEgFMYa40m2TZmZvH7weq53ciuGZAO07SUBgTHdxYTH0+MjRuYZIi+9++zIjnJP2rllVws5DA==", "user"=>{"email"=>"swixxxx#xxxxxltd.com", "password"=>"[FILTERED]", "remember_me"=>"0"}, "commit"=>"Log in"}
User Load (0.8ms) SELECT "users".* FROM "users" WHERE "users"."email" = $1 ORDER BY "users"."id" ASC LIMIT $2 [["email", "swixxxx#xxxxxxltd.com"], ["LIMIT", 1]]
(0.2ms) BEGIN
SQL (0.6ms) UPDATE "users" SET "current_sign_in_at" = $1, "sign_in_count" = $2, "updated_at" = $3 WHERE "users"."id" = $4 [["current_sign_in_at", "2018-04-01 18:10:58.835548"], ["sign_in_count", 2], ["updated_at", "2018-04-01 18:10:58.836544"], ["id", 2]]
(1.0ms) COMMIT
Redirected to http://localhost:3000/dashboard/dispatch
Completed 302 Found in 150ms (ActiveRecord: 2.6ms)
Started GET "/dashboard/dispatch" for 127.0.0.1 at 2018-04-01 12:10:58 -0600
ArgumentError - wrong number of arguments (given 3, expected 0):
app/controllers/dashboard_controller.rb:5:in `dispatch'
Started POST "/__better_errors/cda7f553a5c61c4b/variables" for 127.0.0.1 at 2018-04-01 12:10:58 -0600
EDIT 2: adds Controller Code:
class DashboardController < ApplicationController
def admin
end
def dispatch
end
def owner
end
def driver
end
def client
end
def guest
end
end

Downcase before save is nil is Rails

Rails 5.0.0.rc1
I want to have the username to downcase before save in rails 5.0.0.rc1 but some how username is nil:
user.rb:
before_save :downcase_username
def downcase_username
self.username.downcase!
end
Html:
<form action="/users" method="post">
<input type="text" name="user[username]" id="user_username" />
</form>
undefined method `downcase!' for nil:NilClass
I'm using React and I can create a user with a username no problem. I could have the username set to toLowerCase but I choose not to.
Why username is nil?
Edit (.jsx):
handleName: function(e){
this.setState({name: e.target.value});
}
<form action="/users" method="post">
<input type="text" name="user[username]" id="user_username"
value={this.state.name} onChange={this.handleName} placeholder="username" />
<button className="button small radius" type="submit">Register</button>
</form>
Devise controller:
def create
super do |resource|
resource.user[username] = params[:username]
resource.registration_id = params[:registration_id]
resource.save!
end
end
Logs:
Processing by Users::RegistrationsController#create as HTML
Parameters: {"user"=>{"username"=>"Test", "email"=>"test#example.com", "password"=>"[FILTERED]"}, "registration_id"=>"2"}
Unpermitted parameter: username
(0.2ms) BEGIN
User Exists (0.3ms) SELECT 1 AS one FROM "users" WHERE "users"."email" = $1 LIMIT $2 [["email", "test#example.com"], ["LIMIT", 1]]
(0.1ms) ROLLBACK
Completed 500 Internal Server Error in 159ms (ActiveRecord: 0.7ms)
Logs 2: # Data now saves but still getting downcase is nil
Started POST "/users" for ::1 at 2016-06-05 17:18:10 +0100
Processing by Users::RegistrationsController#create as HTML
Parameters: {"user"=>{"username"=>"Foo", "email"=>"foo#example.edu", "password"=>"[FILTERED]"}, "registration_id"=>"2"}
(0.1ms) BEGIN
User Exists (0.4ms) SELECT 1 AS one FROM "users" WHERE "users"."email" = $1 LIMIT $2 [["email", "foo#example.edu"], ["LIMIT", 1]]
SQL (1.1ms) INSERT INTO "users" ("email", "encrypted_password", "created_at", "updated_at", "username") VALUES ($1, $2, $3, $4, $5) RETURNING "id" [["email", "foo#example.edu"], ["encrypted_password", "$2a$11$TrrnP4391MeEMvn8e2JwMesU5JS0vGELx0.8eUVO.B4sDPZVdUFMy"], ["created_at", 2016-06-05 16:18:10 UTC], ["updated_at", 2016-06-05 16:18:10 UTC], ["username", "foo"]]
(0.9ms) COMMIT
(0.1ms) BEGIN
(0.1ms) ROLLBACK
Completed 500 Internal Server Error in 168ms (ActiveRecord: 5.5ms)
Update your method on user.rb to as per below:
def downcase_username
self.usernname = self.username.try(:downcase)
end
application_controller.rb
class ApplicationController < ActionController::Base
protected
def configure_permitted_parameters
# from new devise -v 4.1.1 gem
# devise_parameter_sanitizer.permit(:sign_up, keys: [:username])
# devise_parameter_sanitizer.permit(:account_update, keys: [:username])
#older version
devise_parameter_sanitizer.for(:sign_up) << [:username]
devise_parameter_sanitizer.for(:account_update) << [:username]
end
end

Devise authentication generating bad SQL

I have a small inherited rails project that uses devise to authenticate.
Recently it has started making an incorrect query to the database if the user enters an invalid password, as set out below. Previously it works as expected. I must have changed something, but I do not know what.
With a VALID password
When the user logs in with a VALID password, the console log shows similar to this
Started POST "/users/sign_in" for 192.168.2.30 at 2015-07-13 08:13:39 -0400
Processing by Users::SessionsController#create as HTML
Parameters: {"utf8"=>"✓", "authenticity_token"=>"A_Long_Authenticity_Token_Goes_Here", "user"=>{"email"=>"m.mouse#disney.com", "password"=>"[FILTERED]", "remember_me"=>"0"}, "commit"=>"Sign in"}
User Load (0.8ms) SELECT "users".* FROM "users" WHERE "users"."email" = $1 ORDER BY "users"."id" ASC LIMIT 1 [["email", "m.mouse#disney.com"]]
(0.3ms) BEGIN
SQL (0.6ms) UPDATE "users" SET "last_sign_in_at" = $1, "current_sign_in_at" = $2, "sign_in_count" = $3, "updated_at" = $4 WHERE "users"."id" = $5 [["last_sign_in_at", "2015-07-10 21:17:12.592611"], ["current_sign_in_at", "2015-07-13 12:13:39.359997"], ["sign_in_count", 1000], ["updated_at", "2015-07-13 12:13:39.363621"], ["id", 22]]
(17.6ms) COMMIT
and the system carries on as normal.
With an INvalid password
When the user attempts to log in with an INVALID password, the console log shows similar to this
Started POST "/users/sign_in" for 192.168.2.30 at 2015-07-13 07:44:55 -0400
Processing by Users::SessionsController#create as HTML
Parameters: {"utf8"=>"✓", "authenticity_token"=>"A_Long_Authenticity_Token_Goes_Here", "user"=>{"email"=>"m.mouse#disney.com", "password"=>"[FILTERED]", "remember_me"=>"0"}, "commit"=>"Sign in"}
User Load (53.0ms) SELECT "users".* FROM "users" WHERE "users"."email" = $1 ORDER BY "users"."id" ASC LIMIT 1 [["email", "m.mouse#disney.com"]]
Completed 401 Unauthorized in 288ms (ActiveRecord: 53.4ms)
Processing by Users::SessionsController#new as HTML
Parameters: {"utf8"=>"✓", "authenticity_token"=>"A_Long_Authenticity_Token_Goes_Here", "user"=>{"email"=>"m.mouse#disney.com", "password"=>"[FILTERED]", "remember_me"=>"0"}, "commit"=>"Sign in"}
User Load (2.2ms) SELECT "users".* FROM "users" WHERE "email"."email" = 'm.mouse#disney.com' AND "email"."password" = 'ThisIsAnInvalidPassword' AND "email"."remember_me" = '0' ORDER BY "users"."id" ASC LIMIT 1
PG::UndefinedTable: ERROR: missing FROM-clause entry for table "email"
LINE 1: SELECT "users".* FROM "users" WHERE "email"."email" = 'm.mou....
: SELECT "users".* FROM "users" WHERE "email"."email" = 'm.mouse#disney.com' AND "email"."password" = 'ThisIsAnInvalidPassword' AND "email"."remember_me" = '0' ORDER BY "users"."id" ASC LIMIT 1
Completed 500 Internal Server Error in 19ms (ActiveRecord: 3.3ms)
<< Output standard rails error page >>
So far as I understand this, the system tries to read the user table as expected, but no row is found. Devise munges this into a 401 Unauthorized response. The system is then attempting to redirect back to the login page somehow using Users::SessionsController#new
The system then tries a completely new query trying to look up the user using a half formatted query. The query tries to include a table called email that does not exist in the database; the query syntax is not correct either.
Database: postgres
Rails: 2.1.2
Devise gem: 3.5.1 according to bundle show
There is no Users::SessionsController#create def, so presumably using the underlying devise version
There is a Users::SessionsController#new as follows
def new
if (Rails.env.development? || Rails.env.test?) && params[:user]
user = User.where(email: params[:user]).first
sign_in :user, user
redirect_to dashboard_home_path
else
super
end
end
The environment is development
Nothing appears to be being written to the sessions table whether the login is successful or not.
Where does that second malformed query even come from, and why does devise try to use/call Users::SessionsController#new after an invalid login attempt anyway?
Thanks in advance

How do I pass commentable into mailer?

I have this in the Model:
after_create do |comment|
CommentMailer.comment_email(self).deliver
end
This in CommentMailer:
class CommentMailer < ActionMailer::Base
helper ActionView::Helpers::UrlHelper
include CommentHelper
helper :comment
def comment_email(user, comment, commentable)
mail(to: user.email,
subject: "You have left a comment",
from: "comments#lumeo.com",
bcc: "brian#lumeo.com")
end
end
And this in CommentHelper:
module CommentHelper
def find_commentable
#comment = Comment.find(params[:comment])
params.each do |name, value|
if name =~ /(.+)_id$/
return $1.classify.constantize.find(value)
end
end
nil
end
end
I'm getting this error:
Started POST "/requests/6/comments" for 127.0.0.1 at 2012-11-30 17:28:55 -0800
Processing by CommentsController#create as HTML
Parameters: {"utf8"=>"✓", "authenticity_token"=>"R62NH5/EE34FPapEqy7mfpa0wKz18GtSdhH8MGYq2Ec=", "comment"=>{"content"=>"post", "show"=>"true"}, "commit"=>"Create Comment", "request_id"=>"6"}
User Load (0.3ms) SELECT "users".* FROM "users" WHERE "users"."id" = 2 ORDER BY users.created_at DESC LIMIT 1
Request Load (0.3ms) SELECT "requests".* FROM "requests" WHERE "requests"."id" = $1 LIMIT 1 [["id", "6"]]
CACHE (0.0ms) SELECT "requests".* FROM "requests" WHERE "requests"."id" = $1 LIMIT 1 [["id", "6"]]
(0.1ms) BEGIN
SQL (0.4ms) INSERT INTO "comments" ("commentable_id", "commentable_type", "content", "created_at", "show", "updated_at", "user_id") VALUES ($1, $2, $3, $4, $5, $6, $7) RETURNING "id" [["commentable_id", 6], ["commentable_type", "Request"], ["content", "post"], ["created_at", Sat, 01 Dec 2012 01:28:55 UTC +00:00], ["show", true], ["updated_at", Sat, 01 Dec 2012 01:28:55 UTC +00:00], ["user_id", 2]]
(0.2ms) ROLLBACK
Completed 500 Internal Server Error in 136ms
ArgumentError (wrong number of arguments (1 for 3)):
app/mailers/comment_mailer.rb:5:in `comment_email'
app/models/comment.rb:27:in `block in <class:Comment>'
app/controllers/comments_controller.rb:22:in `create'
Looks like simple typos.
Line 7, as noted in the exception:
commentable = #comment.commentable
So, the issues:
You're calling #comment.commentabe, but #comment is nil
Hence the error: undefined method 'commentable' for nil:NilClass
#comment is nil in your mailer method because you're passing it in as comment NOT #comment, yet you're trying to reference it as #comment.
Also, why are you passing in commentable as a parameter, but on line 7 you're setting commentable again - this is redundant? Just use the already available commentable variable that you're passing in as a param. In fact, you seem to be doing this with several variables, yet I can't tell (because you don't show the mailer template) whether or not you're actually using them.
It could be that you could use something simpler like:
So, this should (probably) work:
def comment_email(user, comment, commentable)
mail(to: user.email,
subject: "You have left a comment",
from: "comments#lumeo.com",
bcc: "brian#lumeo.com")
end
If you post your mail template (so I can see what the body of the email looks like) I can help you get the variables into the template.

Resources