How to add custom field to devise forgot my password? - ruby-on-rails

Let me preface this with I am a Java developer, but my team is developing an SSO microservice for our product in rails so I am learning as I go.
Currently I have it setup to send a confirmation of password reset when a user enters their email into this form:
<%= form_for(resource, as: resource_name, url: password_path(resource_name), html: { method: :post }) do |f| %>
<%= devise_error_messages! %>
<div class="row">
<div class="medium-12 small-12">
<div class="field">
<%= f.label :email, class: 'field-label' %>
<%= f.email_field :email, autofocus: true %>
</div>
</div>
</div>
<div class="row">
<div class="actions columns medium-12 small-12">
<%= f.submit "Send", id: "send-button" %>
</div>
</div>
<div class="row text-center align-center">
<div class="small-8 small-centered columns">
<br><p id="form-subheading">* For security reasons, we do NOT store your password. We will NEVER send your password via email.</p>
</div>
</div>
<% end %>
And it successfully passes through the following function in the class Users::PasswordsController < Devise::PasswordsController
# POST /resource/password
def create
super
end
The problem is now we need to add the following first_name and last_name fields to the form, and authenticate it against the User model before sending form to their email, but it is ignoring those parameters entirely. Clearly I need a check somewhere, but have no idea where to begin.
<div class="row">
<div class="medium-5 small-12">
<div class="field">
<%= f.label :first_name, class: 'field-label' %>
<%= f.text_field :first_name %>
</div>
</div>
<div class="medium-6 medium-offset-1 small-12">
<div class="field">
<%= f.label :last_name, class: 'field-label' %>
<%= f.text_field :last_name %>
</div>
</div>
</div>
I know they get sent to the backend because the params[:user] variable contains the information if I put a byebug statement in the function. I am just unsure how to validate the new information in the form.
Any help on building custom fields for this form would be appreciated.

So of course, after looking for an hour I post to SO, and I immediately find what I need in the documentation.
Turns out I needed to:
# config/initializers/devise.rb
config.reset_password_keys = [:email]
and change it to
config.reset_password_keys = [:email, :first_name, :last_name]

In your user model you can validate those fields. Something like this:
validates :first_name, presence: true
validates :last_name, presence: true
After sending the form, in the model they are validated. If the first_name and/or last_name isn't filled in, it will return to the form. Read this for more information about rails validations
Also in your controller you have to accept the params (if you don't already).
def user_params
params.require(:user).permit(:first_name, :last_name)
end
In your user controller you probably already have the def user_params, just add the first_name and last_name to it.
after that you can access those params like you normally would. (ex: in the create function #user = User.new(user_params) and you'll have #user.first_name.)

Related

Missing and inaccurate payload in Rails with ActiveModel and Forms

I have a model for Organisation like
class Organisation
include ActiveModel::Model
attr_accessor :orguid,
:title, :firstname, :lastname, :role, :telephone, :extension, :email,
:name, :branch, :address1, :address2, :address3, :city, :state, :country, :zip
end
In my controller I have the following actions:
# frozen_string_literal: true
require 'cgi'
require 'json'
class OrganisationsController < ApplicationController
include Secured
before_action :set_api, only: %i[dashboard create]
before_action :user_info, only: %i[dashboard register]
def dashboard
#registration = #api.registered?
end
def register
#organisation = Organisation.new
end
def create
organisation_params
register_data = params[:organisation].to_h
register_data['oruid'] = org_uid
#api.register(register_data)
end
private
def set_api
#api = CoreApi.new(org_uid)
end
def user_info
#user_info = session[:userinfo].to_h
end
def org_uid
CGI.escape(user_info['uid'])
end
def organisation_params
params.require(:organisation).permit!
end
end
in my register.html.erb I have:
<h1> Register Your Organisation</h1>
<%= form_with model: #organisation, url: org_register_path do |f| %>
<div class="container">
<h2>Your Details</h2>
<div class="form-row">
<div class="form-group col-md-2">
<%= f.label :title %>
<%= f.text_field :title, class: 'form-control' %>
</div>
<div class="form-group col-md-5">
<%= f.label :first_name %>
<%= f.text_field :firstname, class: 'form-control' %>
</div>
<div class="form-group col-md-5">
<%= f.label :last_name %>
<%= f.text_field :lastname, class: 'form-control' %>
</div>
</div>
<div class="form-row">
<div class="form-group col-md-12">
<%= f.label :role %>
<%= f.text_field :role, class: 'form-control' %>
</div>
</div>
<div class="form-row">
<div class="form-group col-md-4">
<%= f.label :telephone %>
<%= f.telephone_field :telephone, class: 'form-control' %>
</div>
<div class="form-group col-md-2">
<%= f.label :extension %>
<%= f.text_field :extension, class: 'form-control' %>
</div>
<div class="form-group col-md-6">
<%= f.label :email %>
<%= f.email_field :email, class: 'form-control', readonly:'', value: #user_info['info']['name'] %>
</div>
</div>
</div>
<div class="container">
<h2>Organisation Details</h2>
<div class="form-row">
<div class="form-group col-md-6">
<%= f.label :name %>
<%= f.text_field :name, class: 'form-control' %>
</div>
<div class="form-group col-md-6">
<%= f.label :branch %>
<%= f.text_field :branch, class: 'form-control' %>
</div>
</div>
<div class="form-row">
<div class="form-group col-md-12">
<%= f.label :address_line_1 %>
<%= f.text_field :address1, class: 'form-control' %>
</div>
</div>
<div class="form-row">
<div class="form-group col-md-12">
<%= f.label :address_line_2 %>
<%= f.text_field :address2, class: 'form-control' %>
</div>
</div>
<div class="form-row">
<div class="form-group col-md-12">
<%= f.label :address_line_3 %>
<%= f.text_field :address3, class: 'form-control' %>
</div>
</div>
<div class="form-row">
<div class="form-group col-md-4">
<%= f.label :city %>
<%= f.text_field :city, class: 'form-control' %>
</div>
<div class="form-group col-md-4">
<%= f.label :state %>
<%= f.text_field :state, class: 'form-control' %>
</div>
<div class="form-group col-md-4">
<%= f.label :country %>
<%= f.text_field :country, class: 'form-control' %>
</div>
</div>
<div class="form-row">
<div class="form-group col-md-2">
<%= f.label :zip %>
<%= f.text_field :zip, class: 'form-control' %>
</div>
</div>
</div>
<div class="container">
<div class="form-row">
<div class="form-group col-md-12">
<%= f.button :Register, class: 'btn btn-primary' %>
</div>
</div>
</div>
<% end %>
and finally the register method in my core_api.rb is this:
def register(data)
body = data.to_json
puts ">> >> >> >> #{body.class} :: #{body}"
options = { headers: { 'Content-Type' => 'application/json' }, body: body }
response = self.class.post('/organisations', options)
#puts ">>>>>>>>>>>> #{response}"
end
and finally my routes.rb file contains:
Rails.application.routes.draw do
get '/' => 'home#show'
get '/auth/auth0/callback' => 'auth0#callback'
get '/auth/failure' => 'auth0#failure'
get '/logout', to: 'logout#logout', as: 'logout'
get '/organisations/dashboard', to: 'organisations#dashboard', as: 'org_dashboard'
get '/organisations/register', to: 'organisations#register', as: 'org_register'
post '/organisations/register', to: 'organisations#create'
root 'home#show'
end
now when I run the server and submit the form in the logs I get:
>> >> >> >> String :: {"title":"","firstname":"","lastname":"","role":"","telephone":"","extension":"","email":"alijy3#yahoo.com","name":"we","branch":"we","address1":"we","address2":"","address3":"","city":"we","state":"","country":"we","zip":"","oruid":"auth0%7C5e5388493d670c11be833bca","contact_id":0}
which to me looks like a proper json. But, since the api response was continually unsuccessful I intercepted the outgoing post with Postman to see what payload is being sent. To my surprise the payload is not flat json, but comes out like this:
I have 2 problems with this:
The api accepts items like address1, address2, city, etc. I believe I should send those rather than the currently showing organisation[address1], organisation[address2], etc.
The second problem is that I add the orguid after the form is submitted and before calling/posting to the api. But, although I can see it in the log messages, I don't see the orguid in the postman payload in any form.
I don't have any database on the server. Everything is fetch/posted/saved through the api. I've been reading about how to work with Activemodel and forms for a while and I haven't managed to get this resolved yet. Any help or explanation would be much appreciated.
No offense but this is a train wreck. You don't need to break every rails convention just because you're not using ActiveRecord in this specific case.
Start off by using ActiveModel::Attributes#attribute instead of Ruby's built in attr_accessor.
class Organisation
include ActiveModel::Model
include ActiveModel::Attributes
[:orguid, :title, :firstname, :lastname, :role, :telephone,
:extension, :email, :name, :branch,
:address1, :address2, :address3, :city, :state, :country, :zip]
.each do |name|
attribute name
end
# #todo write validations!
end
This creates attributes that act like ActiveRecord attributes and you can serialize the model properly with #organization.as_json.
Then lets just start fresh on that controller as there is just too much smell for it to be worth salvaging.
# routes.rb
resources :organisations, only: [:new, :create]
class OganizationsController < ApplicationController
# GET /organizations/new
def new
#organization = Organization.new
end
# POST /organizations
def create
# You never manually parse out incoming params - thats Rack's job.
# also since you have a model - USE IT!
#organization = Organization.new(organization_params) do |o|
o.orguid = org_uid
end
# validate the user input before you send it to an external API
if #organization.valid? && #api.register(#organization)
redirect_to '/somewhere'
else
render :new
end
end
private
# use monads here instead of callbacks!
def user_info
# Rails will serialize/deserialize hashes automatically
# from the session
session[:userinfo]
end
def org_uid
# Have no clue what the heck you're doing with CGI escape.
#org_uid ||= user_info['uid']
end
def api
#api ||= CoreApi.new(org_uid)
end
def organization_params
# You don't have any reason to use 'permit!' and give
# yourself a potential mass assignment vunerablity
params.require(:organization)
.permit(
:title, :firstname, :lastname, :role, :telephone,
:extension, :email, :name, :branch,
:address1, :address2, :address3, :city,
:state, :country, :zip
)
end
end
Rename the view /organizations/new.html.rb. At this point you should be able to stub out the API and do an integration test with valid and invalid input.
That whole session[:userinfo] thing still smells really bad - if you are taking the response from OAuth and shoving it into the session your setting yourself up for a really bad time as that can cause cookie overflows. Also in general in Rails if you're ever manually casting/serializing then its a really good sign that your doing something very wrong.
Have no clue really whats going on in your CoreApi class but if you are using HTTParty you should not do ANY manual JSON encoding.
# #fixme name is way to generic.
class CoreApi
include HTTParty
format :json # sets content type and encodes the content
# ...
def register(organization)
response = self.class.post('/organisations', #organization.as_json)
if response.success?
true
else
#organization.errors.add(:base, 'Could not be registered')
false
end
end
end

Create Devise User From A Different Form and Controller - Ruby on Rails

We have two models, a 'Devise User' and an 'Influencer'. An Influencer is a User, as such it must have a User (from the db standpoint). A User can be multiple other things. Thus, we want to have the ability to sign up a User without being an Influencer and we want to sign up a User when they want to sign up as an Influencer.
I have a form like so:
influencers/new.html.erb
<%= form_for #influencer do |i| %>
<%= i.fields_for(resource, as: resource_name, url: registration_path(resource_name)) do |u| %>
<div id="registration_fields">
<%= render 'devise/registrations/registration_fields', f: u %>
</div>
<% end %>
<div class='field'>
<%= i.label :twitter_handle %><br/>
<%= i.text_field :twitter_handle %>
</div>
<div class='field'>
<%= i.label :short_bio %><br/>
<%= i.text_area :short_bio %>
</div>
/views/devise/registrations/_registration_fields
<%= devise_error_messages! %>
<div class="field">
<%= f.label :first_name %> <br />
<%= f.text_field :first_name %>
</div>
<div class="field">
<%= f.label :last_name %> <br />
<%= f.text_field :last_name %>
</div>
<div class="field">
<%= f.label :email %><br />
<%= f.email_field :email, autofocus: true %>
</div>
<div class="field">
<%= f.label :password %>
<% if #minimum_password_length %>
<em>(<%= #minimum_password_length %> characters minimum)</em>
<% end %><br />
<%= f.password_field :password, autocomplete: "off" %>
</div>
<div class="field">
<%= f.label :password_confirmation %><br />
<%= f.password_field :password_confirmation, autocomplete: "off" %>
</div>
We've modified the ApplicationHelper and 'new' method so that it could render this Devise form without problems. Unfortunately, we are stuck as to how to properly make the 'create' method for our InfluencersController.
This is the hash we receive:
Parameters: {..., "influencer"=>{"user"=>{"first_name"=>"buddy", "last_name"=>"king", "email"=>"bdking#gmail.com", "password"=>"[FILTERED]", "password_confirmation"=>"[FILTERED]"}, "twitter_handle"=>"#bdking", "short_bio"=>"None"}, "commit"=>"Join as influencer"
Essentially we want to Devise to handle the user information while we handle the influencer information. We have tried calling the Devise::RegistrationsController.new.create method from within InfluencersController#create. However, this poses its own difficulties (even with multiple hacks we reach different problems such as, missing '#response' or missing 'response.env' or missing 'devise.mappings').
With that said, We believe that inheriting will allow us to call 'super' in the create function. However, we do not want to have InfluencersController inherit from Devise::RegistrationsController since this controller is not by any means a true Devise controller.
Is there any way we could get around this?
I would use the tried and true pattern of users and roles.
Basically your User class is in charge of authentication (identity) and the user has many Roles which can be used for authorization (permissions).
class User < ActiveRecord::Base
has_many :roles
def has_role?(role)
roles.where(name: role)
end
end
class Role < ActiveRecord::Base
belongs_to :user
validates_uniqueness_of :name, scope: :user_id
end
So an "influencer" is really just a user with a Role(name: "influencer") attached to it. The real power and flexibility is that it makes it trivial to implement granting/revoking roles from a web GUI.
And you don't have to mess around with how Devise/Warden handle authentication to support multiple classes which can get really messy.
Best part is that the Rolify gem makes it really trivial to set up.
If you need to setup a specific endpoint (influences/registrations) to register "influencers" you can simply override the build_resource method in the Devise controller:
class InfluencerRegistrationsController < Devise::RegistrationsController
def resource_class
User
end
def build_resource
super
self.resource.roles.new(name: :influencer)
end
end

Rails model associations and relationships

I'm new in rails and currently trying to create an app but I can't seem to make it work. Here's my setup in model.
class User < ActiveRecord::Base
has_one :doctor, dependent: :destroy
accepts_nested_attributes :doctor
end
class Doctor < ActiveRecord::Base
belongs_to :user, :dependent => :destroy
end
In my users_controller, here's my code:
class UsersController < ApplicationController
def show
#user = current_user
# render text: #user.inspect
end
def new
#user = User.new
#user.build_doctor`
end
def create
# binding.pry
#user = User.new(user_params)
if #user.save
sign_in #user
redirect_to dashboard_path
else
render 'new'
end
end
private
def user_params
params.require(:user).permit(:role, :lastname, :firstname, :middlename, :email, :password, :password_confirmation, :doctor_attributes => [:institution, :license_number])
end
end
And my view:
<ul id="cbp-bislideshow" class="cbp-bislideshow">
<li>
<%= image_tag "blur1.jpg" %>
</li>
<li>
<%= image_tag "blur2.jpg" %>
</li>
</ul>
<% provide(:title, 'Sign Up') %>
<%= form_for(#user) do |f| %>
<div class="sign-up-wrapper divided-wrapper cf">
<div class="left-section">
<h3 class="section-title">JOIN US AND LET'S CHANGE THINGS</h3>
<div class="row">
<div class="w49 pull-left">
<%= f.text_field :firstname, class: "input-text personal ", placeholder: 'FIRSTNAME' %>
</div>
<div class="w49 pull-right">
<%= f.text_field :lastname, class: "input-text personal", placeholder: 'LASTNAME' %>
</div>
<%= f.hidden_field :role, value: :doctor %>
</div>
<div class="row">
<%# f.text_field :specialization, class: "input-text personal ", placeholder: 'SPECIALIZATION' %>
</div>
<%= f.fields_for :doctors do |p| %>
<div class="row">
<%= p.text_field :institution, class: "input-text personal ", placeholder: 'INSTITUTION' %>
</div>
<div class="row">
<%= p.text_field :license_number, class: "input-text personal ", placeholder: 'LICENSE NUMBER' %>
</div>
<% end %>
<span class="remind bottom-message"> ONCE INSIDE DON'T FORGET TO UPDATE YOUR PROFILE WITH MORE DETAILS </span>
</div>
<div class="right-section">
<h3 class="section-title"></h3>
<div class="row">
<%= f.text_field :email, class: "input-text personal ", placeholder: 'EMAIL' %>
</div>
<div class="row">
<%= f.password_field :password, class: "input-text personal ", placeholder: 'PASSWORD' %>
</div>
<div class="row">
<%= f.password_field :password_confirmation, class: "input-text personal ", placeholder: 'CONFIRM PASSWORD' %>
</div>
<div class="row cf">
<%= f.submit class: 'btn-join btn', value: 'JOIN NOW' %>
</div>
<div class="row">
SIGN UP WITH FACEBOOK / TWITTER ACCOUNT?
</div>
</div>
</div>
<% end %>
Everytimie I execute these pieces, only the user model gets populated but not the doctors table? Is there something wrong on my code?
EDIT
Changed doctors_attributes to doctor_attributes
changed #user.doctor.build`to #user.build_doctor
In the logs. I saw this error ---> Unpermitted parameters: doctors
So in theory, I think we know what's the problem, but I don't know how to fix this in the strong_parameters. Haven't tried a strong_parameter with accepted_nested_attributes_for in rails yet and this is my first time. Any solution?
In the fields_for, replace :doctors with :doctor. Remember that you're doing a 1 to 1 relationship.
In your user_params model, the attribute doctors_attributes should be doctor_attributes since it's a has_one relationship. If it was a has_many relationship, it would be doctors_attributes. The part before _attributes would be whatever the association is named.
Another note: If you want to be able to update the doctor from the user form, you should also include the id in the doctor_attributes array. Though now that I think of it, it might only be a requirement on has_many nested associations. I've never tried doing a has_one without including the id.

Can't make 'username' attr accessible in User model (Rails 4 - Devise)

I decided after creating a User model through Devise to add a column for a username. I realized that I'd need to make it accessible so that the database would update correctly when a user submits the new user form. I then realized that since this is Rails 4 I have to do that through the controller, and Devise doesn't make a controller for my User model available. So I followed the instructions at the Devise page and created my own custom controller and changed the route to use it. Here is my custom controller, which I placed in apps/controllers/users:
class Users::RegistrationsController < Devise::RegistrationsController
def new
super
end
def create
super
end
def edit
super
end
private
def user_params
params.require(:user).permit(:username, :email, :password, :password_confirmation)
end
end
I then modified routes.db with:
devise_for :users, :controllers => { :registrations => "users/registrations" }.
My form looks like this:
<%= form_for(resource, :as => resource_name, :url => registration_path(resource_name), :html => {:class => 'form-horizontal'}) do |f| %>
<%= devise_error_messages! %>
<div class="control-group">
<div class="control-label">
<%= f.label :username %>
</div>
<div class="controls">
<%= f.text_field :username %>
</div>
</div>
<div class="control-group">
<div class="control-label">
<%= f.label :email %>
</div>
<div class="controls">
<%= f.email_field :email, :autofocus => true %>
</div>
</div>
<div class="control-group">
<div class="control-label">
<%= f.label :password %>
</div>
<div class="controls">
<%= f.password_field :password %>
</div>
</div>
<div class="control-group">
<div class="control-label">
<%= f.label :password_confirmation %>
</div>
<div class="controls">
<%= f.password_field :password_confirmation %>
</div>
</div>
<div class="control-group">
<div class="controls">
<%= f.submit "Sign up" %>
</div>
</div>
<div class="control-group">
<div class="controls">
Already have an account? <%= render "devise/shared/links" %>
</div>
</div>
<% end %>
I copied the /devise/registrations' views to/users/registrations` per the instructions at the Devise page.
I'm not getting any errors but for whatever reason when I test the form it writes everything to the database except the username. I can go into the rails console and create users with usernames but it just won't happen with the form. My goal is to get the following line of code in application.html.erb to work:
Logged in as <%= current_user.username %>
But it never works and the failure is reflected in the console, which keeps showing the username as "nil."
Anyone know what I'm doing wrong? Where is the mistake?? I ran into a similar problem with my Post model with certain things not updating but I was able to fix it by changing which attributes were accessible in PostController. Thanks for any help!
Looking at the registration controller of devise, it looks like it is not using user_params. Try changing user_params to sign_up_params.

Why is my password always nil?

I'm trying to create a register form in Ruby on Rails.
My form current form looks like this:
<%= form_for #util, :remote => true do |f| %>
<div class="register-form">
<div class=form-name>
<span>Name </span>
<span><%= f.text_field :name %></span>
</div>
<div class=form-surname>
<span>Surname </span>
<span><%= f.text_field :surname %></span>
</div>
<div class=form-email>
<span>E-mail </span>
<span><%= f.text_field :email %></span>
</div>
<div class=form-password>
<span>Password </span>
<span><%= f.text_field :password %></span>
</div>
<div class="submit">
<%= f.submit "Register" %>
</div>
</div>
<% end %>
My controller for creating it looks like this:
def create
#user = User.new(params[:util])
#user.password = encrypt_password(#user.password)
if #user.save!
respond_to do |format|
format.json { render :partial => "partials/form.html.erb", :task => #task }
end
end
end
When I check the variables during debug I can see that the #user.password comes nil, but if I check it manually as #user.password it gives me the content that I input.
When I try to save the new user in the database it always has a nil password.
Any sugestion on why the password always comes nil even if the params and encrypt are correct?
Edit:
Thanks for the help with the code and the password security field issue. I had forgotten about it, after taking a look into the model after the replies I noticed something off:
class User < ActiveRecord::Base
attr_accessible :email, :name, :password, :surname
has_and_belongs_to_many :projects
attr_accessor :password
After removing attr_accessor I managed to acess the password and save in the DB without issues.
Please change:
<%= f.text_field :password %>
To:
<%=f.password_field :password%>
Also:
#user.password = encrypt_password(#user.password)`
should be something like:
#user.password = encrypt_password(params[:util][:password])

Resources