Devise | autogenerated user password does not show up in email sent - ruby

I am trying to set up an email where a referrer will fill in some information in a form about a referee. The referee will receive an email to subscribe to my services with 2 different messages depending if he is already existing in the database or not (ghost status). But when the email is sent, the password is not automatically generated. Why ?
Email sent:
You have been registered as a teacher on XX.
Please confirm your account
Confirm my account
and connect with these credentials log in:<br />
login: bb#ee.fr<br />
password:
My controller:
def new_teacher_registered(teacher, user = nil)
#teacher = teacher
#user = user
#password = user.generate_password
mail(from: 'XX', to: teacher.email, bcc: "YY", subject: 'You have been registered on XYZ')
end
My view:
<% if #user.ghost? %>
You have been registered as a teacher on XXX.
Please confirm your account
<%= link_to "Confirm my account",
confirmation_url(#user, I18n.locale, :confirmation_token => #user.confirmation_token) %>
and connect with these credentials log in:<br />
login: <%= #user.email %> <br />
password: <%= #password %>
<%else%>
You have been registered on XX by <%= #teacher.studios.first.user.contact.location.name %>
And I have the generate_password method in my User controller
class User < ActiveRecord::Base
.....
def generate_password
unless self.encrypted_password
#password = Devise.friendly_token.first(8)
if self.update(password: password, password_confirmation: password)
#password
else
false
end
end
end
......
end

Instead of sending them an email with a password (not a best practice), why not send them a link where they can create their own password on your secure site?
The below example will create a reset link for them to click and give them a temporary password so that devise is happy, and then send an email with the link(the raw token).
raw_token, hashed_token = Devise.token_generator.generate(User, :reset_password_token)
self.reset_password_token = hashed_token
self.reset_password_sent_at = Time.now.utc
self.password = Devise.friendly_token.first(8)
self.save
UserMailer.add_to_website(course, self, raw_token).deliver_later

Related

Password Authentication using BCrypt

I'm trying to build a very simple login page with username and password authentication using BCrypt.
There are two features, the first is to create a username and password, and the second is authenticate using login page. (create a new user, login with that user)
Below is the controller and User model code for creating a username and password. View model is not included, but it includes a simple form asking the user to create a username, password and confirm password
Controller:
get '/new_user' do
erb :new_user
end
post '/new_user' do
if #password == #password_confirm
new_user = User.new(username: params[:username])
new_user.password = params[:password]
new_user.insert_user
redirect '/index'
else
redirect '/new_user'
end
end
Model:
def initialize(params = {})
#username = params.fetch(:username, "test")
#password = params.fetch(:password, "test")
end
def password=(new_password)
#password = BCrypt::Password.create(new_password)
#db_password = BCrypt::Password.new(#password)
end
def insert_user
db = SQLite3::Database.open("helper_database")
db_results_as_hash = true
db.execute("INSERT INTO users (username, password) VALUES (?,?)", [#username, #db_password])
end
In the above, a new instance is created to pass username and password. If password and confirm password match, the password method will create an encrypted password, and the insert method will insert username and the encrypted password into the database using simple SQL command.
After the username and encrypted password have been inserted into the database. I want to use the login page to validate. I create a new User instance, passing the username and password params to the authentication method shown below. The method will look up the password corresponding the params username and evaluate the params password against the encrypted password.
Below is the controller and User model for Login
Controller:
get '/login' do
erb :login
end
post '/login' do
#user = User.new(username: params[:username], password: params[:password])
if #user.authenticate()
redirect '/index'
else
erb :login
end
end
Model:
def authenticate
db = SQLite3::Database.open("helper_database")
db.results_as_hash = true
password = db.execute("SELECT password FROM users WHERE username = '#{#username}'")
password = password[0]["password"]
password = BCrypt::Password.new(password)
#password == password
end
Above, I'm attempting to retrieve the encrypted password (string format) from SQL, convert to a Bcrypt object and validate the password against it. Theoretically, the method should return true assuming the entered password is correct, but it returns false.
What might the issue be?
You don’t appear to be hashing the incoming password when you authenticate. This is because you’re doing #password == password where #password is the plain-text String password (from User#initialize from post '/login').
You should flip the comparison so it uses BCrypt::Password#==: password == #password.

Ruby on Rails - Make view accessible to certain users

In my application I have models File & FileAccess. User A can upload files and give access to many other users (by other users asking for access).
My Models:
class File < ActiveRecord::Base
belongs_to :user
has_many :file_accesses
class FileAccess < ActiveRecord::Base
belongs_to :user
belongs_to :file
My FilesController:
class FilesController < ApplicationController
def accepted_users
FileAccess.create(user_id: params[:user_id], file_id: params[:file_id], accept: true)
end
My routes.rb:
get 'i/:user_id/:file_id', to: 'files#accepted_users', as: :file_access_accepted
My View:
= link_to "Give Access", file_access_accepted_path(#file, other_user.id)
Other users can ask for access & user A can select users that they want to give file access to by clicking on Give Access button.
In my FilesController I have a access_file action & view:
class FilesController < ApplicationController
def access_file
redirect_to #file, alert: "You don't have access to this page!" if #file.user != current_user
end
Currently this view/page is only viewable to File owner and if user is not the file owner, they'll be send back with a alert.
How can I achieve it so this page/view is accessible to File owner AND all other users that have been accepted accept: true by the file owner.
Try this one
def access_file
if #file.user != current_user && !#file.file_accesses.map(&:user).include?(current_user)
redirect_to #file, alert: "You don't have access to this page!"
end
end
or
def access_file
if #file.user != current_user && !FileAccess.exists?(file: #file, user: current_user)
redirect_to #file, alert: "You don't have access to this page!"
end
end
The latter should be just one SQL query

Using attr_encrypted with DataMapper and Sinatra

I'm quite new with DataMapper and Sinatra and especially attr_encrypted. What I want is to store my passwords encrypted and then be able to search for a user by username and password. I read the documentation of attr_encrypted, but I still don't get what to do :(
Can you please give my some example of a project using these two technologies or tell how to change my code to work :(
My User Class:
class User
include DataMapper::Resource
attr_encryptor :password, :key => 'secret key'
property :id, Serial
property :encrypted_password, Text
end
When I save a User, I do it this way:
username = params[:username]
password = params[:password]
user = User.new(:username => username, :encrypted_password => password)
user.save
which is saving the original password, not the encrypted one.
And I have no idea how to search for users when the password is encrypted :(
Now it is something like this:
#user = User.all(:username => username, :password => password)
Please excuse me for the newbie quiestion, but I really can't quite understand it :(
Thank you very much in advance!
You need to add the attr_encryptor line after you have specified the Data Mapper properties. This prevents DataMapper simply replacing the encrypted_password accessors with its own:
class User
include DataMapper::Resource
property :id, Serial
property :encrypted_password, Text
# this line moved down from above
attr_encryptor :password, :key => 'secret key'
end
and then create the user with:
user = User.new(:username => username, :password => password)
Are you sure you want to search for a User based on an encrypted password? Normally you would find the user based on e.g. username and then just check the password matches.
If you do want to do this, you will have to either recreate the encrypted password in your code and search with that (you’ll need to check the docs to see how encryption is done):
User.all(:username => username, :encrypted_password => encrypt(password))
Alternatively fetch all matching users and filter them in your code:
User.all(:username => name).select {|u| u.password == password}
Your encrypted password is :password, so you have to do
User.new(:username => username, :password => password)
To find an user by username and password, you should just do
User.first(:username => username, :password => password)
Anyway you could avoid that gem using instead bcrypt doing a thing like this.

Accessing more than one active records in action controller in rails 3

suppose I have 2 models: 1. user and 2. userProfile
we have one-to-one relationship between 2 models.
Then,I would like to create Usercontroller and create user with fields such as username and password in user and other details like address, mobile number,etc in userprofile.
user_id is a foreign key in user_profile
Then I created new.html.erb in user a view that render all above fields in a same form.
Then how I can save the data in two different tables user and userprofile respectively.
what I tried is
1. has_one :user_profile in user.rb and belongs_to :user in user_profile.rb
2. accepts_nested_attributes_for :user_profile in user.rb
user_controller:
def create
#user = User.new(params[:user])
if #user.save
#userprofile = #user.create_user_profile(params[:user_profile])
redirect_to root_url, :notice => "Signed up!"
else
render "new"
end
end
3. Then I got error the only userid is created in user_profile and also update run to make user_id nil which can't be! so raising error:
Unknown column 'user_profiles.' in 'where clause': UPDATE `user_profile`
SET `user_id` = NULL WHERE `user_profiles`.`` IS NULL
so what's wrong and how to troubleshoot this?
If you're using accepts_nested_fields_for then you don't need to manage saving the UserProfile records yourself. ActiveRecord will take care of it for you.
To get this to work you need to:
Make sure you have accepts_nested_fields_for :user_profile in your User model
Update your app/views/users/_form.html.erb to include form elements for the UserProfile, e.g.:
<%= f.fields_for :user_profile, #user.user_profile do |profile_form| %>
<%= profile_form.text_field :address %>
<%= profile_form.text_field :mobile_number %>
<% end %>
Update your UsersController#new action to build the UserProfile for the User, e.g.
def new
#user = User.new
#user.build_user_profile
(...)
end
For more information see the Rails API docs covering Active Record Nested Attributes.

App with Accounts and Users - Devise user can't create additional users

Using Rails 3.1.3 with Devise 1.5.3. My app has accounts and users. Each account can have multiple users. A user can have one role, "user" or "account_admin". Devise signup routes to accounts#new. The account and an initial account_admin user are created simultaneously. Got this working as described here (although things have evolved some since then).
An account_admin signs should be able to create additional users in that account. That's where I'm running into trouble: instead of creating new users, it's just redirecting to the user_path (users#show) page with the message "You are already signed in." (user_path is my overridden after_sign_in_path.)
This article asks a similar question. The answer suggests a separate namespace for admins, but since admins are part of my regular app I don't think that applies.
I've defined a complete UsersController. According to the log, GET "/users/new" renders from my "UsersController#new". However POST "/users" is intercepted by Devise and rendered from "Devise::RegistrationsController#create".
config/routes.rb
devise_for :users
devise_scope :user do
get "signup", :to => "accounts#new"
get "signin", :to => "devise/sessions#new"
get "signout", :to => "devise/sessions#destroy"
end
resources :users
resources :accounts
app/controllers/users_controller.rb
class UsersController < ApplicationController
before_filter :authenticate_user!
load_and_authorize_resource # CanCan
...
def new
# CanCan: #user = User.new
end
def create
# CanCan: #user = User.new(params[:user])
#user.skip_confirmation! # confirm immediately--don't require email confirmation
if #user.save
flash[:success] = "User added and activated."
redirect_to users_path # list of all users
else
render 'new'
end
end
...
end
I've tried overriding the Devise controller, thinking I could tell it to use my users#create action if the user is already signed in. The log tells me it is using my controller ("Processing by RegistrationsController#create as HTML"), but it doesn't seem to execute its code. I've commented out my custom actions and just left in the logger lines, but I don't get my logging messages. And in spite of super being commented out, the behavior doesn't change--it still redirects with "You are already signed in."
config/routes.rb
devise_for :users, :controllers => {:registrations => "registrations"}
app/controllers/registrations_controller.rb
class RegistrationsController < Devise::RegistrationsController
def new
logger.info "Custom RegistrationsController: new"
super
end
def create
logger.info "Custom RegistrationsController: create"
# super unless user_signed_in?
# render "users#create"
end
def update
super
end
end
What am I missing? How can I let the account_admin user create additional users?
The main issue is that Devise was intercepting the POST "/users" (and a few other routes). Found this workaround to allow my Users controller handle those routes: change the devise_for to skip registrations, then add back in the routes for which Devise normally defines aliases:
routes.rb
devise_for :users, :skip => [:registrations]
devise_scope :user do
get "signup", :to => "accounts#new"
get "signin", :to => "devise/sessions#new"
get "signout", :to => "devise/sessions#destroy"
get "cancel_user_registration", :to => "devise/registrations#cancel"
post "user_registration", :to => "users#create"
get "new_user_registration", :to => "accounts#new"
get "edit_user_registration", :to => "users#edit"
end
resources :users
resources :accounts
Never figured out why the Devise controller override wasn't working.
A user on this thread pointed out the devise_invitable gem which might be an interesting alternative.
This is how I ended up getting this to work with my setup (Devise tied to my User model -- no separate Editor/Admin model):
class UsermakersController < ApplicationController
before_action :authenticate_user!
def new
#user = User.new
end
def create
#user = User.new(user_params)
respond_to do |format|
if #user.save
format.html { redirect_to users_path, notice: 'User was successfully created.' }
format.json { render :show, status: :created, location: #user }
else
format.html { render users_path }
format.json { render json: #user.errors, status: :unprocessable_entity }
end
end
end
private
def user_params
userParams = params.require(:user).permit(:name, :email)
end
end
From here, when you want to add a new User as another User, simply call the Usermakers#new url in your form, e.g., I have this at the bottom of my Users index page:
<%= link_to 'Add User', new_usermaker_path, id: 'new_user_link' %>
And the Usermakers form looks like:
= simple_form_for #user, url: usermakers_path, html: {multipart: true} do |f|
=f.error_notification errors: true
= f.input :name
= f.input :email
%p
= f.button :submit, 'Create User', class: 'button'
Of course, you'd need to add a dummy new.html.erb file which simply renders _form.html.erb to use this.
Just add the new and create methods to your routes.rb file (whether by resources :usermakers, or more specific routes) and you should be good to go. Hope this helps!

Resources