I'm working on a application which contains the soundcloud api.
i have the user login and model and i would like to append the soundcloud_id and token to the existing user but i cannot somehow update the users record.
what do i do wrong?
soundcloud controller
class SoundcloudController < ApplicationController
def connect
# create client object with app credentials
client = Soundcloud.new(:client_id => ENV["SOUNDCLOUD_CLIENT_ID"],
:client_secret => ENV["SOUNDCLOUD_CLIENT_SECRET"],
:redirect_uri => "http://localhost:3000/soundcloud/oauth-callback",
:response_type => 'code')
# redirect user to authorize URL
redirect_to client.authorize_url(:grant_type => 'authorization_code', :scope => 'non-expiring', :display => 'popup')
end
def connected
# create client object with app credentials
client = Soundcloud.new(:client_id => ENV["SOUNDCLOUD_CLIENT_ID"],
:client_secret => ENV["SOUNDCLOUD_CLIENT_SECRET"],
:redirect_uri => "http://localhost:3000/soundcloud/oauth-callback")
# exchange authorization code for access token
access_token = client.exchange_token(:code => params[:code])
client = Soundcloud.new(:access_token => access_token["access_token"])
# make an authenticated call
soundcloud_user = client.get('/me')
unless User.where(:soundcloud_user_id => soundcloud_user["id"]).present?
#User.create_from_soundcloud(soundcloud_user, access_token)
UsersController.add_soundcloud_account(soundcloud_user, access_token)
end
sign_in_user = User.where(:soundcloud_user_id => soundcloud_user["id"])
#create user sessions
#session[:user_id] = sign_in_user.first.id
redirect_to root_url, notice: "Signed in!"
end
def destroy
end
end
user controller
class UsersController < ApplicationController
def new
#user = User.new
end
#create a user and redirect to home
def create
#user = User.new(user_params)
if #user.save
session[:user_id] = #user.id
redirect_to '/'
else
redirect_to '/signup'
end
end
def self.add_soundcloud_account(soundcloud_user, access_token)
#current_user ||= User.find(session[:user_id])
#current_user.soundcloud_user_id = soundcloud_user["id"]
#current_user.soundcloud_access_token = access_token["access_token"]
end
private
def user_params
params.require(:user).permit(:first_name, :last_name, :email, :password)
end
end
You need to call save on the #current_user and pass in the session information to the method:
def self.add_soundcloud_account(user_id, soundcloud_user, access_token)
#current_user ||= User.find(user_id)
#current_user.soundcloud_user_id = soundcloud_user["id"]
#current_user.soundcloud_access_token = access_token["access_token"]
#current_user.save
end
It is called like this:
UsersController.add_soundcloud_account(session[:user_id], soundcloud_user, access_token)
However I am not sure the add_soundcloud_account method belongs in a controller. I would prefer to see it in a Service or maybe just in the User model.
Related
While implementing what I thought was a simple signup/login system for a Ruby on Rails app, results haven't matched what tutorials have shown.
I'm trying to use bcrypt for authentication and PostgreSQL for the database.
I continually get 'ActionController::ParameterMissing (param is missing or the value is empty: name): ', even though it will show name as being input. '"users"=>{"name"=>"asdf", "password"=>"Qq!1asdfasdf", "password_confirmation"=>"Qq!1asdfasdf"}, "commit"=>"Submit"} (0.1ms)
output from the console when attempting to sign in
users controller
class UsersController < ApplicationController
def new
end
def create
user = User.new(
name: params[:name],
password: params[:password],
password_confirmation: params[:password_confirmation])
if user.save
session[:user_id] = user.id
redirect_to '/'
else
redirect_to '/signup'
end
end
private
end
The table
class UsersController < ApplicationController
def new
end
def create
user = User.new(
name: params[:name],
password: params[:password],
password_confirmation: params[:password_confirmation])
if user.save
session[:user_id] = user.id
redirect_to '/'
else
redirect_to '/signup'
end
end
private
end
and the signup form
class UsersController < ApplicationController
def new
end
def create
user = User.new(
name: params[:name],
password: params[:password],
password_confirmation: params[:password_confirmation])
if user.save
session[:user_id] = user.id
redirect_to '/'
else
redirect_to '/signup'
end
end
private
end
the user model
class User < ActiveRecord::Base
PASSWORD_FORMAT = /\A
(?=.{10,}) # Must contain 10 or more characters
(?=.*\d) # Must contain a digit
(?=.*[a-z]) # Must contain a lower case character
(?=.*[A-Z]) # Must contain an upper case character
(?=.*[[:^alnum:]]) # Must contain a symbol
/x
#formatting for password
USERNAME_FORMAT = /\A[a-z0-9A-Z\-_]{2,15}\z/ #Can contain lowercase and upercase letters, numbers, - and _, must be between 2 and 15 length
#username formatting
validates :name,
:presence => true,
:uniqueness => true,
:format => USERNAME_FORMAT
validates :password,
:presence => true,
:format => PASSWORD_FORMAT,
:confirmation => true,
:on => create
has_secure_password
end
I've tried troubleshooting, all similar questions haven't yielded an answer or fix.
EDIT: More clarity on issue
You need to use rails Strong Parameter like the following
class UsersController < ApplicationController
def new
end
def create
user = User.new(user_params)
if user.save
session[:user_id] = user.id
redirect_to root_path
else
redirect_to new_user_path
end
end
private
def user_params
params.require(:user).permit(:name, :password, :password_confirmation)
end
end
I think it's a problem of passing data between your form and your controller.
In your logs your parameters for user looks like: "users"=>{"name"=> ...} but it should be "user"
To pass data between your controller and your view, you need to use instance variable such as #user to make the new instance of User available in the view. (source)
In that way your controller should be:
def new
#user = User.new
end
def create
#user = User.new(user_params)
if #user.save
session[:user_id] = #user.id
redirect_to '/'
else
redirect_to '/signup'
end
end
private
def user_params
params.require(:user).permit(:name, :password, :password_confirmation)
end
(with strong parameters like #fool-dev explained)
Then in your view, use this #user to pass the parameters to the controller:
<%= form_for #user do |f| %>
//...the form
<% end %>
I'm developing an Ruby on Rails webapp and I'm trying to use LDAP authentication to authenticate my users, I have the connection set up and working to the LDAP, but now I can't find any examples or documentation online on how to write code to authenticate users against my LDAP on Ruby on Rails
I'm using: Ruby v2.2 and Rails v5.0.3 and the gem I'm using to connect to ldap is gem 'net-ldap', '~> 0.16.0'
This is my login form at the moment, authenticating with a sqlserver DB, but I want it to authenticate against my LDAP DB :
class SessionsController < ApplicationController
def new
end
def create
user = User.find_by_NumeroEmpregado(params[:NumeroEmpregado])
if user && user.authenticate(params[:password])
session[:user_id] = user.id
redirect_to '/'
else
flash[:error] = "Erro! \nNĂºmero de Empregado e/ou password incorrecto(a)"
redirect_to '/login'
end
end
def destroy
session[:user_id] = nil
redirect_to '/index/new'
end
end
users_controller.rb
class UsersController < ApplicationController
def new
end
def create
user = User.new(user_params)
if user.save
session[:user_id] = user.id
redirect_to '/'
else
flash[:error] = "Erro! \nNenhum dos campos pode ser deixado em branco"
redirect_to '/signup'
end
end
private
def user_params
params.require(:user).permit(:NumeroEmpregado, :nome, :password, :password_confirmation)
end
end
How can I reformulate this code into authenticating with my LDAP DB?
You could create a service that handles that process:
app/services/authenticate_user.rb
class AuthenticateUser
def initialize(user, password)
#user = user
#password = password
end
def call
user_is_valid?
end
private
def user_is_valid?
ldap = Net::LDAP.new
ldap.host = your_server_ip_address
ldap.port = 389
ldap.auth(#user, #password)
ldap.bind
end
end
Then use it in your controller:
class SessionsController < ApplicationController
def new
end
def create
username = params[:NumeroEmpregado]
password = params[:password]
name = "Some Name" # Change "Some Name" to set the correct name
if AuthenticateUser.new(username, password).call
user = User.create_with(nome: name).find_or_create_by(NumeroEmpregado: username)
session[:user_id] = user.id
redirect_to '/'
else
flash[:error] = "Erro! \nNĂºmero de Empregado e/ou password incorrecto(a)"
redirect_to '/login'
end
end
def destroy
session[:user_id] = nil
redirect_to '/index/new'
end
end
AuthenticateUser.new(user, password).call will return true when valid user and password are provided, and will return false otherwise.
This is a basic example covering only the LDAP authentication, you will need to adapt it for your specific needs, including exception handling.
I set up a basic sign in, login, sign out format for a rails app and I was going to give it a function so that if a person forgot their password they could get a email back. when I went click on submit password reset I got
Missing host to link to! Please provide the :host parameter, set default_url_options[:host], or set :only_path to true
Extracted source (around line #3):
1: To reset your password click the URL below.
2:
3: <%= edit_password_reset_url(#user.password_reset_token) %>
4:
5: If you did not request your password to be reset please ignore this email and your password will stay as it is.
Rails.root: /Users/cheatermoves/nightclass/mainproject/project
Application Trace | Framework Trace | Full Trace
app/views/user_mailer/password_reset.text.erb:3:in `_app_views_user_mailer_password_reset_text_erb__3613112772785486465_70118994937040'
app/mailers/user_mailer.rb:6:in `password_reset'
app/models/user.rb:17:in `send_password_reset'
app/controllers/password_resets_controller.rb:7:in `create'
just completed rails cast 250 and was doing 274. Everything was fine until I got this problem.
here is my controllers. Password resets:
class PasswordResetsController < ApplicationController
def new
end
def create
user = User.find_by_email(params[:email])
user.send_password_reset if user
redirect_to root_url, :notice => "Email sent with password reset instructions."
end
end
sessions:
class SessionsController < ApplicationController
def new
end
def create
user = User.find_by_email(params[:email])
if user && user.authenticate(params[:password])
if params[:remember_me]
cookies.permanent[:auth_token] = user.auth_token
else
cookies[:auth_token] = user.auth_token
end
redirect_to root_url, :notice => "Logged in!"
else
flash.now.alert = "Invalid email or password"
render "new"
end
end
def destroy
cookies.delete(:auth_token)
redirect_to root_url, :notice => "Logged out!"
end
end
users:
class UsersController < ApplicationController
def new
#user = User.new
end
def create
#user = User.new(params[:user])
if #user.save
session[:user_id] = #user.id
redirect_to root_url, notice: "Thank you for signing up!"
else
render "new"
end
end
end
and application:
class ApplicationController < ActionController::Base
protect_from_forgery
private
def current_user
#current_user ||= User.find_by_auth_token( cookies[:auth_token]) if cookies[:auth_token]
end
helper_method :current_user
end
in environments/development.rb I have
config.action_mailer.default_url_options = { :host => "localhost:3000" }
here is my user model
class User < ActiveRecord::Base
attr_accessible :name, :email, :password, :password_confirmation
has_secure_password
validates_presence_of :password, :on => :create
before_create { generate_token(:auth_token) }
def generate_token(column)
begin
self[column] = SecureRandom.urlsafe_base64
end while User.exists?(column => self[column])
end
def send_password_reset
generate_token(:password_reset_token)
self.password_reset_sent_at = Time.zone.now
save!
UserMailer.password_reset(self).deliver
end
end
user_mailer.rb file
class UserMailer < ActionMailer::Base
default from: "from#example.com"
def password_reset(user)
#user = user
mail :to => user.email, :subject => "Password Reset"
end
end
anyone know what the problem is and how to fix it? I'm using rails 3.2.14 if anyone is wondering. Thanks!
Didn't read the part where you said that you already set your
config.default_url_options[:host] = "localhost:3000"
my bad, sorry
I am using Mongodb as database in rails and i got error when using /auth/linkedin/callback
NoMethodError in AuthenticationsController#create undefined method []' for nil:NilClass Rails.root: /home/prem/Music/heronhrm Application Trace | Framework Trace | Full Trace app/models/user.rb:57:in apply_omniauth' app/controllers/authentications_controller.rb:19:in `create'
Also when i remove self.email = omniauth['user_info']['email'] if email.blank? from usermodel then the validation errors arises in
users/sign_up Email can't be blank
I want to implement for twitter,linkdin and facebook.
my authentication.rb
class Authentication
include Mongoid::Document
belongs_to :user
field :user_id, :type => String
field :provider, :type => String
field :uid, :type => String
def self.find_by_provider_and_uid(provider, uid)
where(provider: provider, uid: uid).first
end
end
my user model is like this
def apply_omniauth(omniauth)
self.email = omniauth['user_info']['email'] if email.blank?
authentications.build(:provider => omniauth['provider'], :uid => omniauth['uid'])
end
def password_required?
(authentications.empty? || !password.blank?) && super
end
My authentications controller is like this
class AuthenticationsController < ApplicationController
def index
#authentications = current_user.authentications if current_user
end
def create
omniauth = request.env["omniauth.auth"]
authentication = Authentication.find_by_provider_and_uid(omniauth['provider'], omniauth['uid'])
if authentication
flash[:notice] = "Signed in successfully."
sign_in_and_redirect(:user, authentication.user)
elsif current_user
current_user.authentications.create!(:provider => omniauth['provider'], :uid => omniauth['uid'])
flash[:notice] = "Authentication successful."
redirect_to authentications_url
else
user = User.new
user.apply_omniauth(omniauth)
if user.save
flash[:notice] = "Signed in successfully."
sign_in_and_redirect(:user, user)
else
session[:omniauth] = omniauth.except('extra')
redirect_to new_user_registration_url
end
end
end
def destroy
#authentication = current_user.authentications.find(params[:id])
#authentication.destroy
flash[:notice] = "Successfully destroyed authentication."
redirect_to authentications_url
end
protected
# This is necessary since Rails 3.0.4
# See https://github.com/intridea/omniauth/issues/185
# and http://www.arailsdemo.com/posts/44
def handle_unverified_request
true
end
end
My registration controller is like this
class RegistrationsController < Devise::RegistrationsController
def create
super
session[:omniauth] = nil unless #user.new_record?
end
private
def build_resource(*args)
super
if session[:omniauth]
#user.apply_omniauth(session[:omniauth])
#user.valid?
end
end
end
Inside your app/models/authentication.rb add this
def self.find_by_provider_and_uid(provider, uid)
where(provider: provider, uid: uid).first
end
Did you add this in your model? If not added then add this and then try
key :provider, String
key :uid, String
What are the best practices to implement a "Remember Me" function in Rails 3 application ?
I store session information (session id + user id) in the database when user logs in, and I don't want to use any plugins at this moment.
Any pointers or code samples will be much appreciated.
You can just set the expiration on a signed cookie to accomplish this. (Signed cookies are tamper-proof just like the Rails-provided session cookie.)
class SessionsController < ApplicationController
def create
...
user = User.authenticate(params[:email_address], params[:password])
if params[:remember_me]
cookies.signed[:user_id] = { value: user.id, expires: 2.weeks.from_now }
else
# expires at the end of the browser session
cookies.signed[:user_id] = user.id
end
end
def destroy
cookies.delete :user_id
end
end
class ApplicationController < ActionController::Base
...
def current_user
User.find(cookies.signed[:user_id])
end
end
Railscasts has an episode on achieving this as well as well as a great HOWTO on implementing those features via BDD with RSpec and Capybara.
I store session information (session id + user id) in the database when user logs in
I believe that's one approach and the casts above does the same with cookies by issuing each User account a unique authentication token.
Have been reading the Rails tutorial book and it has an implementation for Remember Me
You can check for some hints (The implementation may be different from yours)
http://ruby.railstutorial.org/book/ruby-on-rails-tutorial#sec:remember_me
This is how I implemented remember_me (the below snippet is from my example Rails app on authentication):
class SessionsController < ApplicationController
skip_before_filter :login_required, :only => [:new, :create]
def new
end
def create
#current_user = User.authenticate(params[:email], params[:password])
if #current_user
#current_user.track_on_login(request)
if params[:remember_me]
cookies[:remember_token] = { :value => #current_user.remember_token, :expires => 24.weeks.from_now }
else
cookies[:remember_token] = #current_user.remember_token
end
redirect_to dashboard_url, :notice => "Logged in successfully."
else
flash.now[:alert] = "Invalid login or password."
render 'new'
end
end
def destroy
current_user.track_on_logout
current_user.reset_remember_token_and_save # can't rely on the 'save_current_user_if_dirty' after_filter here
cookies.delete(:remember_token)
reset_session
redirect_to root_url, :notice => "You have been logged out."
end
end
Just an example without salt:
class ApplicationController < ActionController::Base
protected
def signin!(user_id)
return unless user_id
#current_user = User.find(user_id)
self.session_user_id = #current_user.id
self.permanent_user_id = #current_user.id if session[:accept_remember_me]
end
def signout!
self.session_user_id = nil
self.permanent_user_id = nil
session[:accept_remember_me] = nil
#current_user = nil
end
def remember_me
session[:accept_remember_me] = true
end
private
def permanent_user_id
cookies.signed[:permanent_user_id]
end
def permanent_user_id= value
cookies.permanent.signed[:permanent_user_id] = value
end
def session_user_id
session[:user_id]
end
def session_user_id= value
session[:user_id] = value
end
end