I'm trying to make an app in Rails 4. I have been trying for the last 3 years (except for 10 days), to get devise to work.
I'm trying to follow this tutorial: http://sourcey.com/rails-4-omniauth-using-devise-with-twitter-facebook-and-linkedin/
Please don't recommend other tutorials / gem documentation. I have tried at least 30 other tutorials and the gem documentation is full of errors and components that I don't understand.
My current problem is that when I get to the finish sign up step in this tutorial, the form asks me for my email address.
The users controller has a finish signup method as:
def finish_signup
# authorize! :update, #user
if request.patch? && params[:user] #&& params[:user][:email]
if #user.update(user_params)
#user.skip_reconfirmation!
# sign_in(#user, :bypass => true)
# redirect_to root_path, notice: 'Your profile was successfully updated.'
# redirect_to [#user, #user.profile || #user.build_profile]
sign_in_and_redirect(#user, :bypass => true)
else
#show_errors = true
end
end
end
When I try this, I get this error:
undefined method `match' for {:host=>"localhost", :port=>3000}:Hash
The error points at this line:
<div class="intpol3"><%= link_to 'Confirm my account', confirmation_url(#resource, confirmation_token: #token) %></div>
My development environment is set up to include all the config details for my email sender.
When I try the same step in production mode, I get this error:
ActionView::Template::Error (No route matches {:action=>"show", :controller=>"profiles", :id=>nil} missing required keys: [:id]):
It's looking for a profile id because I have an after_create action in my user model as:
after_create :gen_profile
def gen_profile
Profile.create(user: self) # Associations must be defined correctly for this syntax, avoids using ID's directly.
# Profile.save
end
My other issue with this tutorial is that the fields in the identity table aren't being populated.
I'd love to find someone that has successfully implemented this tutorial or can see how to make this work.
My code is:
gemfile
gem 'devise', '3.4.1'
gem 'devise_zxcvbn'
gem 'omniauth'
gem 'omniauth-oauth2', '1.3.1'
gem 'omniauth-google-oauth2'
gem 'omniauth-facebook'
gem 'omniauth-twitter'
gem 'omniauth-linkedin-oauth2'
gem 'google-api-client', require: 'google/api_client'
routes
devise_for :users, #class_name: 'FormUser',
:controllers => {
:registrations => "users/registrations",
# :omniauth_callbacks => "users/authentications"
:omniauth_callbacks => 'users/omniauth_callbacks'
}
# get '/auth/:provider/callback' => 'users/authentications#create'
# get '/authentications/sign_out', :to => 'users/authentications#destroy'
# PER SOURCEY TUTORIAL ----------
match '/users/:id/finish_signup' => 'users#finish_signup', via: [:get, :patch], :as => :finish_signup
resources :users do
resources :profiles, only: [:new, :create]
end
user.rb
class User < ActiveRecord::Base
TEMP_EMAIL_PREFIX = 'change#me'
TEMP_EMAIL_REGEX = /\Achange#me/
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable,
:confirmable, :lockable,
# :zxcvbnable,
:omniauthable, :omniauth_providers => [:facebook, :linkedin, :twitter, :google_oauth2 ]
# --------------- associations
has_many :authentications, :dependent => :delete_all
has_one :profile
has_many :identities
# --------------- scopes
# --------------- validations
# validates_presence_of :first_name, :last_name
validates_uniqueness_of :email
# per sourcey tutorial - how do i confirm email registrations are unique?
# this is generating an error about the options in the without function -- cant figure out the solution
validates_format_of :email, :without => TEMP_EMAIL_REGEX, on: :update
# --------------- class methods
# sourcey tutorial
def self.find_for_oauth(auth, signed_in_resource = nil)
# Get the identity and user if they exist
identity = Identity.find_for_oauth(auth)
# If a signed_in_resource is provided it always overrides the existing user
# to prevent the identity being locked with accidentally created accounts.
# Note that this may leave zombie accounts (with no associated identity) which
# can be cleaned up at a later date.
user = signed_in_resource ? signed_in_resource : identity.user
# p '11111'
# Create the user if needed
if user.nil?
# p 22222
# Get the existing user by email if the provider gives us a verified email.
# If no verified email was provided we assign a temporary email and ask the
# user to verify it on the next step via UsersController.finish_signup
email_is_verified = auth.info.email && (auth.info.verified || auth.info.verified_email)
email = auth.info.email if email_is_verified # take out this if stmt for chin yi's solution
user = User.where(:email => email).first if email
# Create the user if it's a new registration
if user.nil?
# p 33333
user = User.new(
# at least one problem with this is that each provider uses different terms to desribe first name/last name/email. See notes on linkedin above
first_name: auth.info.first_name,
last_name: auth.info.last_name,
email: email ? email : "#{TEMP_EMAIL_PREFIX}-#{auth.uid}-#{auth.provider}.com",
#username: auth.info.nickname || auth.uid,
password: Devise.friendly_token[0,20])
# fallback for name fields - add nickname to user table
# debugger
# if email_is_verified
user.skip_confirmation!
# end
# user.skip_confirmation!
user.save!
end
end
# Associate the identity with the user if needed
if identity.user != user
identity.user = user
identity.save!
end
user
end
def email_verified?
self.email && TEMP_EMAIL_REGEX !~ self.email
end
users controller
class UsersController < ApplicationController
before_action :set_user, only: [:index, :show, :edit, :update, :finish_signup, :destroy]
# i added finish_signup to the set_user action (not shown in tutorial)
def index
# if params[:approved] == "false"
# #users = User.find_all_by_approved(false)
# else
#users = User.all
# end
end
# GET /users/:id.:format
def show
# authorize! :read, #user
end
# GET /users/:id/edit
def edit
# authorize! :update, #user
end
# PATCH/PUT /users/:id.:format
def update
# authorize! :update, #user
respond_to do |format|
if #user.update(user_params)
sign_in(#user == current_user ? #user : current_user, :bypass => true)
format.html { redirect_to #user, notice: 'Your profile was successfully updated.' }
format.json { head :no_content }
else
format.html { render action: 'edit' }
format.json { render json: #user.errors, status: :unprocessable_entity }
end
end
end
# GET/PATCH /users/:id/finish_signup
def finish_signup
# authorize! :update, #user
if request.patch? && params[:user] #&& params[:user][:email]
if #user.update(user_params)
#user.skip_reconfirmation!
# sign_in(#user, :bypass => true)
# redirect_to root_path, notice: 'Your profile was successfully updated.'
# redirect_to [#user, #user.profile || #user.build_profile]
sign_in_and_redirect(#user, :bypass => true)
else
#show_errors = true
end
end
end
# DELETE /users/:id.:format
def destroy
# authorize! :delete, #user
#user.destroy
respond_to do |format|
format.html { redirect_to root_url }
format.json { head :no_content }
end
end
private
def set_user
#user = User.find(params[:id])
end
def user_params
# params.require(:user).permit(policy(#user).permitted_attributes)
accessible = [ :first_name, :last_name, :email, :avatar ] # extend with your own params
accessible << [ :password, :password_confirmation ] unless params[:user][:password].blank?
# accessible << [:approved] if user.admin
params.require(:user).permit(accessible)
end
end
omniauth callbacks controller
class Users::OmniauthCallbacksController < Devise::OmniauthCallbacksController
def self.provides_callback_for(provider)
class_eval %Q{
def #{provider}
#user = User.find_for_oauth(env["omniauth.auth"], current_user)
if #user.persisted?
sign_in_and_redirect #user, event: :authentication
set_flash_message(:notice, :success, kind: "#{provider}".capitalize) if is_navigational_format?
else
session["devise.#{provider}_data"] = env["omniauth.auth"]
redirect_to new_user_registration_url
end
end
}
end
# , current_user has been deleted from the end of line 51
#come back to put current_user into fidn by oauth so i can link other accounts - i have added this back for the purpose of solving the current problem
# puts current_user.inspect
# sign_in_and_redirect [#user, #user.profile || #user.build_profile]
# sign_in_and_redirect_user(:user, event: :authentication)
[:twitter, :facebook, :linkedin, :google_oauth2].each do |provider|
provides_callback_for provider
end
def after_sign_in_path_for(resource)
if resource.email_verified?
super resource
else
finish_signup_path(resource)
end
end
end
registrations controller
class Users::RegistrationsController < Devise::RegistrationsController
protected
def after_sign_up_path_for(resource)
profile_path(resource)
end
private
def user_params
params.require(:user).permit(:first_name, :last_name, :email, :password )
end
end
Identity.rb
class Identity < ActiveRecord::Base
belongs_to :user
validates_presence_of :uid, :provider
validates_uniqueness_of :uid, :scope => :provider
def self.find_for_oauth(auth)
find_or_create_by(uid: auth.uid, provider: auth.provider)
end
end
Identities controller
class IdentitiesController < ApplicationController
before_action :set_identity, only: [:show, :edit, :update, :destroy]
before_action :authenticate_user!
# GET /identities
# GET /identities.json
def index
#identities = Identity.all
end
# GET /identities/1
# GET /identities/1.json
def show
end
# GET /identities/new
def new
#identity = Identity.new
end
# GET /identities/1/edit
def edit
end
# POST /identities
# POST /identities.json
def create
#identity = Identity.new(identity_params)
respond_to do |format|
if #identity.save
format.html { redirect_to #identity, notice: 'Identity was successfully created.' }
format.json { render :show, status: :created, location: #identity }
else
format.html { render :new }
format.json { render json: #identity.errors, status: :unprocessable_entity }
end
end
end
# PATCH/PUT /identities/1
# PATCH/PUT /identities/1.json
def update
respond_to do |format|
if #identity.update(identity_params)
format.html { redirect_to #identity, notice: 'Identity was successfully updated.' }
format.json { render :show, status: :ok, location: #identity }
else
format.html { render :edit }
format.json { render json: #identity.errors, status: :unprocessable_entity }
end
end
end
# DELETE /identities/1
# DELETE /identities/1.json
def destroy
#identity.destroy
respond_to do |format|
format.html { redirect_to identities_url, notice: 'Identity was successfully destroyed.' }
format.json { head :no_content }
end
end
private
# Use callbacks to share common setup or constraints between actions.
def set_identity
#identity = Identity.find(params[:id])
end
# Never trust parameters from the scary internet, only allow the white list through.
def identity_params
params[:identity]
end
end
devise mailer - confirmation
<div class="intpol3"><%= link_to 'Confirm my account', confirmation_url(#resource, confirmation_token: #token) %></div>
SUMMARY OF CURRENT PROBLEMS:
In development mode: There is a problem with the link to the confirmation token. I can't find any materials that indicate why this might arise. The error is:(ActionView::Template::Error (undefined method `match' for {:host=>"localhost", :port=>3000}:Hash):
In production mode, there is an error with user looking for profile id. The error message is: ActionView::Template::Error (No route matches {:action=>"show", :controller=>"profiles", :id=>nil} missing required keys: [:id]):
My profiles routes are:
resources :profiles, only: [:show, :edit, :update, :destroy]
resources :users do
resources :profiles, only: [:new, :create]
end
None of the fields in the identity model are populating. They are all showing as nil.
THINGS DONE DIFFERENTLY THAN AS SHOWN IN THE TUTORIAL:
I also allow email sign up
I add 'finish_sign_up' to the set_user before action in the users controller
I add g+ strategy (which means my gems are slightly different)
My new user method does not use raw info. It uses oauth processed info.
My redirect in the finish sign up method is slightly different, although I've commented that out and gone back to the way it is set out in the tutorial to try to get this working (although the above problems are repeating).
I'm going crazy trying to solve these problems. I'd say 3 years is way too long to be stuck on this problem. If anyone can help, I'd pay it forward 10x and then some. Thank you.
<div class="intpol3"><%= link_to 'Confirm my account',
confirmation_url(#resource, confirmation_token: #token) %></div>
Try resource instead of #resource. AFAIK it's only a helper_method, NOT an instance variable.
I think that will solve your problem fully in production. Since #resource is not the same as resource, it hasn't been set, and you're basically calling confirmation_url(nil, confirmation_token: #token), and that nil is getting passed through to the error message.
In development, there appears to be an additional issue, which most probably has to do with how you've configured config.action_mailer.default_url_options in config/environments/development.rb and most likely is raising the exception in ActionDispatch::Http::Url.build_host_url. I suspect you have something like:
config.action_mailer.default_url_options[:host] = { host: 'localhost', port: 9000 }
Change that to:
config.action_mailer.default_url_options[:host] = 'localhost:9000'
And see if that solves everything. If I'm wrong about how config.action_mailer.default_url_options is configured, please paste your config/environments/development.rb AND a full stack trace from your development error so we can help you further.
Related
I am trying to make an app in Rails 4.
I want to use Pundit for authorisations. I also use Devise for authentication and Rolify for role management.
I have a user model and am making my first policy, following along with this tutorial:
https://www.youtube.com/watch?v=qruGD_8ry7k
I have a users controller with:
class UsersController < ApplicationController
before_action :set_user, only: [:index, :show, :edit, :update, :destroy]
def index
if params[:approved] == "false"
#users = User.find_all_by_approved(false)
else
#users = User.all
end
end
# GET /users/:id.:format
def show
# authorize! :read, #user
end
# GET /users/:id/edit
def edit
# authorize! :update, #user
end
# PATCH/PUT /users/:id.:format
def update
# authorize! :update, #user
respond_to do |format|
if #user.update(user_params)
sign_in(#user == current_user ? #user : current_user, :bypass => true)
format.html { redirect_to #user, notice: 'Your profile was successfully updated.' }
format.json { head :no_content }
else
format.html { render action: 'edit' }
format.json { render json: #user.errors, status: :unprocessable_entity }
end
end
end
# GET/PATCH /users/:id/finish_signup
def finish_signup
# authorize! :update, #user
if request.patch? && params[:user] #&& params[:user][:email]
if #user.update(user_params)
#user.skip_reconfirmation!
sign_in(#user, :bypass => true)
redirect_to #user, notice: 'Your profile was successfully updated.'
else
#show_errors = true
end
end
end
# DELETE /users/:id.:format
def destroy
# authorize! :delete, #user
#user.destroy
respond_to do |format|
format.html { redirect_to root_url }
format.json { head :no_content }
end
end
private
def set_user
#user = User.find(params[:id])
end
def user_params
params.require(:user).permit(policy(#user).permitted_attributes)
# accessible = [ :first_name, :last_name, :email ] # extend with your own params
# accessible << [ :password, :password_confirmation ] unless params[:user][:password].blank?
# accessible << [:approved] if user.admin
# params.require(:user).permit(accessible)
end
end
And this is my first go at the User policy.
class UserPolicy < ApplicationPolicy
def initialize(current_user, user)
#current_user = current_user
#user = user
end
def index?
#current_user.admin?
end
def show?
#current_user.admin?
end
def edit?
#current_user.admin?
end
def update?
#current_user.admin?
end
def finish_signup?
#current_user = #user
end
def destroy?
return false if #current_user == #user
#current_user.admin?
end
private
def permitted_attributes
accessible = [ :first_name, :last_name, :email ] # extend with your own params
accessible << [ :password, :password_confirmation ] unless params[:user][:password].blank?
accessible << [:approved] if user.admin
params.require(:user).permit(accessible)
end
end
My questions are:
The tutorial shows something called attr_reader. I have started learning rails from rails 4 so I don't know what these words mean. I think it has something to do with the old way of whitelisting user params in the controller, so I think I don't need to include this in my user policy. Is that correct?
is it right that i have to initialise the user model the way I have above (or is that only the case in models other than user, since I'm initialising current_user, it might already get the user initialised?
is it necessary to move the strong params to the policy, or will this work if I leave them in the controller?
The tutorial shows something called attr_reader. I have started learning rails from rails 4 so I don't know what these words mean. I think it has something to do with the old way of whitelisting user params in the controller, so I think I don't need to include this in my user policy. Is that correct?
No, it is very important.
attr_reader creates instance variables and corresponding methods that return the value of each instance variable. - From Ruby Official Documentation
Basically if you do
class A
attr_reader :b
end
a = A.new
you can do a.b to access b instance variable. It is important because in every policies you might allow read access of instance variables. #current_user and #user is instance variable.
is it right that i have to initialise the user model the way I have above (or is that only the case in models other than user, since I'm initialising current_user, it might already get the user initialised?
You have to initialise it manually. Currently, the way you did it is correctly. Good.
is it necessary to move the strong params to the policy, or will this work if I leave them in the controller?
It is the matter of choice. It will work even if you kept it into controller. Move to policy only if you want to whitelist attributes in quite complex way.
Note: device , pundit and rolify gem works good but there share some of the same functionality so be careful and consistence what to do with what.
For example, You can use devise_for :users , :students , :teachers which will give 3 different links to login the respective resources. You can do lot of things with it. You can further authenticate the urls as per the resources with authenticate method. Check https://github.com/plataformatec/devise/wiki/How-To:-Define-resource-actions-that-require-authentication-using-routes.rb This sort of thing can also be done with pundit with policies and rolify with roles.
I tried asking this question - and didn't find any help.
http://stackoverflow.com/questions/33493369/rails-devise-omniauth-problems-with-setup
I gave up trying to solve the problem and made an entirely new app with nothing else in it so that I could try to reduce the scope for other errors interfering.
In my new app, I now get an error when I click on the new registration/new user link.
I have followed the rails casts tutorials (having tried about 20 others) to get this setup. 1.5 years in and I am still struggling.
The problem is with the way the strategies are defined in the controller. I have 4 strategies (twitter, Facebook, google and linkedin) and am I am currently getting a different error for each of them when I try clicking on the links to create a new registration with those accounts:
For Twitter:
Unknown action
The action 'twitter' could not be found for Users::AuthenticationsController
For Facebook:
Given URL is not permitted by the Application configuration: One or more of the given URLs is not permitted by the App's settings. It must match the Website URL or Canvas URL, or the domain must be a subdomain of one of the App's domains.
This error is because I can't figure out Facebook's documentation. It shows how to use js to login with Facebook where I am trying to use gems in Rails. I'm not worried about this error. I think it has something to do with my website not being defined as local host, although I can't find anywhere to but a callback url in Facebook's developer console. My question doesn't relate to this problem, I'll find help to sort this out another time.
For LinkedIn:
Safari can’t open the page “https://www.linkedin.com/uas/oauth2/authorization?client_id=LINKEDIN_DEV_ID&redirect_uri=http%3A%2F%2Flocalhost%3A3000%2Fusers%2Fauth%2Flinkedin%2Fcallback&response_type=code&scope=r_basicprofile+r_emailaddress&state=3fef961c10410464cd5b0ca49b25112ce53fb65f1a3c794f”.
The error is: “cannot decode raw data” (NSURLErrorDomain:-1015)
I have an oauth2 callback defined as:
http://localhost:3000/users/auth/linkedin/callback
I don't know what's broken here.
For Google:
translation missing: en.devise.authentications.user.failure
The new registrations view just refreshes when i click this link and an error message says the above. I don't know what's causing this error either.
Each of the errors is different.
I have a folder in my controllers folder called users. Inside that I have two controllers as follows:
Authentications controller:
class Users::AuthenticationsController < Devise::OmniauthCallbacksController
before_action :set_authentication, only: [:destroy]
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
sign_in_and_redirect_user(:user, authentication.user.profile)
elsif current_user
current_user.authentications.create!(:provider => omniauth['provider'], :uid => omniauth['uid'])
redirect_to user.profile_url
else
user = User.new
user.omniauth(omniauth)
if user.save!
sign_in_and_redirect_user(:user, user.profile)
else
session[:omniauth] = omniauth.except('extra')
redirect_to new_user_registration_url
end
end
end
def destroy
#authentication.destroy
respond_to do |format|
format.html { redirect_to root_path }
format.json { head :no_content }
end
end
private
# Use callbacks to share common setup or constraints between actions.
def set_authentication
#authentication = current_user.authentications.find(params[:id])
end
end
Registrations controller:
class Users::RegistrationsController < Devise::RegistrationsController
#before_filter :check_permissions , :only => [ :new, :create, :cancel ]
#skip_before_filter :require_no_authentication
# before_action :configure_permitted_parameters, if: :devise_controller?
def check_permissions
authorize! :create, resource
end
def index
if params[:approved] == "false"
#users = User.find_all_by_approved(false)
else
#users = User.all
end
end
def create
super
session[:omniauth] = nil unless #user.new_record?
end
# THIS IS A SUGGESTION FROM SITEPOINT TUTORIAL
# protected
# def configure_permitted_parameters
# devise_parameter_sanitizer.for(:sign_up) << [:first_name, :last_name]
# end
private
def user_params
params.require(:user).permit(:first_name, :last_name, :email, :password )
end
def build_resource(*args)
super
if session[:omniauth]
#user.apply_omniauth(session[:omniauth])
#user.valid?
end
end
end
User.rb has
devise
:omniauthable, :omniauth_providers => [:facebook, :linkedin, :twitter, :google_oauth2 ]
has_many :authentications, :dependent => :delete_all
def apply_omniauth(omniauth)
self.email = auth['extra']['raw_info']['email']
authentications.build(:provider => omniauth['provider'], :uid => omniauth['uid'], :token => auth['credentials']['token'])
end
def password_required?
(authentications.empty? || !password.blank?) && super
end
In my routes.rb I have:
devise_for :users,
:controllers => {
:registrations => "users/registrations",
:omniauth_callbacks => "users/authentications"
# :omniauth_callbacks => 'users/omniauth_callbacks',
}
get '/auth/:provider/callback' => 'users/authentications#create'
get '/sign_out', :to => 'users/authentications#destroy'
In my omniauth.rb I have:
require 'omniauth-facebook'
require 'omniauth-google-oauth2'
require 'omniauth-twitter'
OmniAuth.config.logger = Rails.logger
Rails.application.config.middleware.use OmniAuth::Builder do
provider :twitter, ENV['TWITTER_KEY'], ENV['TWITTER_SECRET']
end
Rails.application.config.middleware.use OmniAuth::Builder do
provider :facebook, ENV['FACEBOOK_ID'], ENV['FACEBOOK_KEY'],
:scope => 'public_profile', info_fields: 'id,first_name,last_name,link,email',
:display => 'popup',
:client_options => {:ssl => {:ca_file => '/usr/lib/ssl/certs/ca-certificates.crt'}}
end
Rails.application.config.middleware.use OmniAuth::Builder do
provider :google_oauth2, ENV['YT_CLIENT_ID'], ENV['YT_CLIENT_SECRET'],
scope: 'profile', image_aspect_ratio: 'square', image_size: 48,
# {name: "google_login", approval_prompt: ''},
access_type: 'online'
#ENV["GOOGLE_APP_ID"], ENV["GOOGLE_APP_SECRET"]
# {
# :name => "google",
# :scope => "userinfo.email, userinfo.profile, plus.me, http://gdata.youtube.com",
# :prompt => "select_account",
# :image_aspect_ratio => "square",
# :image_size => 50
# }
end
Rails.application.config.middleware.use OmniAuth::Builder do
if Rails.env == 'production'
key = ENV["LINKEDIN_PRODUCTION_KEY"]
secret = ENV["LINKEDIN_PRODUCTION_SECRET"]
else
key = "LINKEDIN_DEV_ID"
secret = "LINKEDIN_DEV_KEY"
end
provider :linkedin, key, secret,
:scope => "r_basicprofile r_emailaddress",
:field => ["id", "email-address", "first-name", "last-name" ],
:client_options => {:ssl => {:ca_file => '/usr/lib/ssl/certs/ca-certificates.crt'}}
end
In my new registration/session views, I have:
<% if devise_mapping.omniauthable? %>
<%= link_to icon('facebook', id: 'facebookauth'), user_omniauth_authorize_path(:facebook) %>
<%= link_to icon('google', id: 'googleauth'), user_omniauth_authorize_path(:google_oauth2) %>
<%= link_to icon('linkedin', id: 'linkedinauth'), user_omniauth_authorize_path(:linkedin) %>
<%= link_to icon('twitter', id: 'twitterauth'), user_omniauth_authorize_path(:twitter) %>
<% end %>
I think it has something to do with not having named strategies the authentications controller. In other tutorials, I have set up the following (now commented out of the controller because there is a method called apply_omniauth used now).
# def facebook
# # #user = User.find_for_facebook_oauth(request.env["omniauth.auth"])
# # if #user.persisted?
# # # #user.send_admin_mail
# # # #user.send_user_welcome_mail
# # sign_in #user, :event => :authentication #this will throw if #user is not activated
# # if #user.profile
# # redirect_to profile_path(#user.profile)
# # else
# # redirect_to new_profile_path
# # end
# # # sign_in_and_redirect #user, :event => :authentication #this will throw if #user is not activated
# # # set_flash_message(:notice, :success, :kind => "Facebook") if is_navigational_format?
# # else
# # session["devise.facebook_data"] = request.env["omniauth.auth"]
# # redirect_to root_path
# # end
# # end
# # def linkedin
# # #user = User.find_for_linkedin_oauth(request.env["omniauth.auth"])
# # if #user.persisted?
# # # #user.send_admin_mail
# # # #user.send_user_welcome_mail
# # sign_in #user, :event => :authentication
# # if #user.profile
# # redirect_to profile_path(#user.profile)
# # else
# # redirect_to new_profile_path
# # end
# # # set_flash_message(:notice, :success, :kind => "LinkedIn") if is_navigational_format?
# # else
# # session["devise.linkedin_data"] = request.env["omniauth.auth"]
# # redirect_to root_path
# # end
# # end
# # def twitter
# # begin
# # #user = User.from_omniauth(request.env['omniauth.auth'])
# # session[:user_id] = #user.id
# # flash[:success] = "Welcome, #{#user.name}!"
# # rescue
# # flash[:warning] = "There was an error while trying to authenticate you..."
# # end
# # redirect_to new_profile_path #root_path
# # end
# # #
# def google_oauth2
# # You need to implement the method below in your model (e.g. app/models/user.rb)
# #user = User.find_for_google_oauth2(request.env["omniauth.auth"], current_user)
# if #user.persisted?
# sign_in #user, :event => :authentication #this will throw if #user is not activated
# if #user.profile
# redirect_to profile_path(#user.profile)
# flash[:notice] = I18n.t "devise.omniauth_callbacks.success", :kind => "Google"
# else
# redirect_to new_profile_path
# end
# else
# session["devise.google_data"] = request.env["omniauth.auth"]
# redirect_to new_user_registration_url
# end
# end
Before I completely ditch the rails cast setup, is there a way I can point the current structure towards the named providers using the code I already have?
ANOTHER ATTEMPT:
So I ditched the RailsCast. I tried to follow this tutorial:
http://willschenk.com/setting-up-devise-with-twitter-and-facebook-and-other-omniauth-schemes-without-email-addresses/
I'm confused by the reference to adding a new 'class' called FormUser. I made a new model called form_user.rb and added the content as described. I'm using Rails 4. I don't know what atto-accessor means, but I have it in my file as demonstrated. I don't have a corresponding controller. I also don't have associations defined in my user model (which I think this belongs to).
Anyway, I have followed the setup and now get this error:
TypeError
superclass mismatch for class OmniauthCallbacksController
It's the same for each of the strategies defined in my app.
Has anyone seen this particular error & have any tips for how to resolve it?
The only difference between my setup and the tutorial is that in my controllers, I have a folder called users, in that, I have a registrations controller and an omniauth_callbacks controller. My routes have been adjusted to reflect this layer.
devise_for :users, class_name: 'FormUser',
:controllers => {
:registrations => "users/registrations",
# :omniauth_callbacks => "users/authentications"
:omniauth_callbacks => 'users/omniauth_callbacks',
}
get '/auth/:provider/callback' => 'users/authentications#create'
get '/authentications/sign_out', :to => 'users/authentications#destroy'
devise_scope :user do
get '/users/auth/:provider/upgrade' => 'users/omniauth_callbacks#upgrade', as: :user_omniauth_upgrade
get '/users/auth/:provider/setup', :to => 'users/omniauth_callbacks#setup'
end
If anyone else is tearing their hair out trying to figure this out - I don't have the answers for how to do it, but I do know that the Railscasts are so old they are not useful. There are several parts of the code above that no longer match the gem config. Following those tutorials will not help you. Over the past 1.5 years I've tried to follow at least 20 different tutorials and am yet to find one that I can get to work. Errors in the rails cast include the redirect (doesnt require reference to 'user' any more amongst many others. Give up if you're relying on the Rails Cast. I'd really appreciate if anyone has any insights into where to find a CURRENT tutorial.
I've been working on a social link aggregator site and have run into a wall with regards to admin permissions.
Currently all users can edit and destroy their own posts (admins included) but an error is thrown when an admin tries to delete a post created by another user.
While I'm fairly well versed in ruby my rails knowledge is severely lacking so it may well be something extremely simple staring me in the face.
When attempting to edit a post the error is: undefined method `model_name' for TrueClass:Class
When attempting to delete a post the error is: undefined method `destroy' for true:TrueClass
Here are relevant parts of my links_controller.rb:
def update
respond_to do |format|
if #link.update(link_params)
format.html { redirect_to #link, notice: 'Link was successfully updated.' }
format.json { render :show, status: :ok, location: #link }
else
format.html { render :edit }
format.json { render json: #link.errors, status: :unprocessable_entity }
end
end
end
def destroy
#link.destroy
respond_to do |format|
format.html { redirect_to links_url, notice: 'Link was successfully destroyed.' }
format.json { head :no_content }
end
end
def authorized_user
#link = current_user.links.find_by(id: params[:id]) || current_user.admin?
redirect_to links_path, notice: "Not authorized to edit this link" if #link.nil?
end
The user model:
class User < ActiveRecord::Base
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable
has_many :links
has_many :posts
acts_as_voter
validates :twitter, :twitter => { :format => :username_with_at, allow_blank: true }
validates :avatar_url, :url => { allow_blank: true }
end
And the admin model:
class Admin < ActiveRecord::Base
devise :database_authenticatable, :trackable, :timeoutable, :lockable
end
If you need any more information I'll be happy to provide it, alternatively if any of the code I've posted is irrelevant to the issue let me know and I'll remove it from the post.
#link will be true if current_user is admin, it's not correct, #link should be an instance of Link
#link = current_user.links.find_by(id: params[:id]) || current_user.admin?
You should use an authorization gem to check permission such as action_access, cancan, cancancan, pundit,...
problem is the following line
#link = current_user.links.find_by(id: params[:id]) || current_user.admin?
here if the user is admin then the value of #link will be 'true'
so #link.destroy becomes true.destroy which is not a valid function
undefined method `model_name' for TrueClass:Class
This basically means you've populated a variable with true instead of an object. You know this already; it should direct you towards what to look for to identify the problem:
#link = current_user.links.find_by(id: params[:id]) || current_user.admin?
The error here is that you're calling current_user.admin?. Because your initial argument is returning false, Ruby is referring to the last. the admin? method must be returning true.
--
Authorization
The fix for this is something called Authorization.
You'll probably know this already; in the sense of Rails, authorization is not handled very well at all at the moment. The best implementations have been CanCan by Ryan Bates (of Railscasts fame).
In short, what you need is some sort of system which examines who you are, and allows you to perform an action based on your credentials.
If you handled this manually, you'd do this:
#link = current_user.links.find_by(id: params[:id]) || (Link.find(params[:id]) if current_user.admin?) #-> not tested!!!
If you used something something like CanCan (now updated to CanCanCan) you could use:
#app/controllers/your_controller....
def destroy
#link = Link.find params[:id]
#link.destroy if can? :destroy, #link
end
#app/models/ability.rb
class Ability
include CanCan::Ability
def initialize(user)
user ||= User.new # guest user (not logged in)
can :manage, :all if user.has_role? :admin
can [:update, :destroy], Link, user_id: user.id
end
end
I create new user. After creating should received confirmation mail. but when i open mailcatcher(http://127.0.0.1:1080/)... nothing!
i use mailcatcher v. 0.5.12, Rails 3.2.22
In development.rb added:
# Don't care if the mailer can't send
config.action_mailer.delivery_method = :smtp
config.action_mailer.smtp_settings = { :address => "localhost", :port => 1025 }
config.action_mailer.raise_delivery_errors = true
What is wrong with settings? How check error? Please, help, thank you.
UPDATE
Confirmationcontroller:
class ConfirmationsController < Devise::ConfirmationsController
def new
super
resource.login = current_user.email if user_signed_in?
end
protected
def after_resending_confirmation_instructions_path_for(resource_name)
if user_signed_in?
home_path
else
new_session_path(resource_name)
end if is_navigational_format?
end
# The path used after confirmation.
def after_confirmation_path_for(resource_name, resource)
'/profile'
end
end
userscontrollers.rb:
# encoding: utf-8
class UsersController < ApplicationController
before_filter :require_admin, only: [:add_moderator, :remove_moderator, :destroy]
before_filter :require_moderator, only: [:edit, :update, :paid_on, :paid_off,
:ban, :unban]
before_filter :authenticate_user!, except: [:new, :create]
before_filter :load_user, only: [:show, :photos, :videos, :audios,
:buy_rating, :do_buy_rating,
:add_moderator, :remove_moderator,
:edit, :update, :paid_on, :paid_off, :ban, :unban, :destroy,
:add_funds, :mute, :unmute]
layout :determine_layout
def new
#user = User.new
#invite = Invite.find_by_code(session[:invite]) if session[:invite].present?
#user.email = #invite.email if #invite
end
def create
#user = User.new(params[:user])
raise ActiveRecord::RecordInvalid.new(#user) unless verify_recaptcha(model: #user, message: 'message')
#invite = Invite.find_by_code(session[:invite]) if session[:invite].present?
User.transaction do
#user.save!
#user.current_password = #user.password
#user.theme_ids = params[:user][:theme_ids]
#user.group_ids = [114, 130]
#user.save!
end
if #invite
#invite.new_user = #user
#invite.use!
end
redirect_to #user
rescue ActiveRecord::RecordInvalid
render :new
end
.........................................................
.........................................................
private
def determine_layout
return 'welcome' if %w(new create).include?(params[:action])
return 'dating' if params[:action]=='search'
'inner'
end
end
registrationscontroller.rb:
# encoding: utf-8
class RegistrationsController < Devise::RegistrationsController
def create
if verify_recaptcha
super
else
flash.delete :recaptcha_error
build_resource
resource.valid?
resource.errors.add(:base, :invalid_recaptcha)
# clean_up_passwords(resource)
render :new
end
rescue ActiveRecord::RecordNotUnique
render :new
end
def update
redirect_to '/settings'
end
# def update
# # required for settings form to submit when password is left blank
# if params[:user][:password].blank?
# params[:user].delete("password")
# params[:user].delete("current_password")
# end
#
# #user = User.find(current_user.id)
# if update_user
# set_flash_message :notice, :updated
# # Sign in the user bypassing validation in case his password changed
# sign_in #user, :bypass => true
# redirect_to after_update_path_for(#user)
# else
# render "edit"
# end
# end
def edit
redirect_to '/settings'
end
def destroy
current_password = params[:user].delete(:current_password)
if resource.valid_password?(current_password)
resource.mark_as_deleted!
render inline: "$('body').fadeOut(3000, function() { document.location = 'http://ya.ru'; })"
else
render inline: "$.flash.error('error')"
end
# Devise.sign_out_all_scopes ? sign_out : sign_out(resource_name)
# set_flash_message :notice, :destroyed if is_navigational_format?
# respond_with_navigational(resource){ redirect_to after_sign_out_path_for(resource_name) }
end
protected
def after_sign_up_path_for(resource)
'/profile'
end
end
Code was without Devise.mail_confirmation_instructions(#user).deliver. I realized, now all in fine
I have an issue with associating two models in my rails application: Users & Profiles. An individual user profile should be created after a new user signs up. After signing up the user, saving data into the actual profile model is not successful. I can not get it to work. Please find a detailed description below.
Here is my setup:
I use Rails 4.0.0.rc2 and ruby 2.0.0p195.
Both models are associated like this:
profile.rb
class Profile < ActiveRecord::Base
belongs_to :user
end
user.rb
has_one :profile, dependent: :destroy
accepts_nested_attributes_for :profile
before_create :build_profile
As I use the devise gemI have created a registrationscontrollerto change the after_sign_up_path:
class RegistrationsController < Devise::RegistrationsController
protected
def after_sign_up_path_for(resource)
new_user_profile_path(:user_id => #user)
end
end
Whenever I sign up a new user the actual sign up works fine, the user is subsequently being directed to http://localhost:3000/users/42/profile/new for example. However, when I then enter the data into the profile form fields and click on submit I get the following error:
No route matches [POST] "/users/profile"
Although one could expect a routing error, you will notice a different error when looking at the actual domain:
http://localhost:3000/users//profile
In case you still want to have a look at my routes.rb please do (relevant excerpt):
devise_for :users, :controllers => { :registrations => "registrations" }
devise_scope :user do
get 'signup', :to => "devise/registrations#new", as: :signup
get 'login', :to => "devise/sessions#new", as: :login
get 'logout', :to => "devise/sessions#destroy", as: :logout
end
resources :users do
resource :profile
end
However, as noted above I don't really have a routing issue. It rather seems like I have an issue with the current user_id not being properly shown in the domain, which can be related to either my actual profile form or the new action on the profiles controller.
I start my profile form on new.html.erb like this:
<%= form_for #profile, url: user_profile_path(#user) do |f| %>
My profiles_controller.rblooks like this:
class ProfilesController < ApplicationController
before_action :set_profile, only: [:show, :edit, :update, :destroy]
# GET /profiles
# GET /profiles.json
def index
#profiles = Profile.all
end
# GET /profiles/1
# GET /profiles/1.json
def show
end
# GET /profiles/new
def new
#profile = Profile.new
end
# GET /profiles/1/edit
def edit
end
# POST /profiles
# POST /profiles.json
def create
#profile = Profile.new(profile_params)
respond_to do |format|
if #profile.save
format.html { redirect_to #profile, notice: 'Profile was successfully created.' }
format.json { render action: 'show', status: :created, location: #profile }
else
format.html { render action: 'new' }
format.json { render json: #profile.errors, status: :unprocessable_entity }
end
end
end
# PATCH/PUT /profiles/1
# PATCH/PUT /profiles/1.json
def update
respond_to do |format|
if #profile.update(profile_params)
format.html { redirect_to #profile, notice: 'Profile was successfully updated.' }
format.json { head :no_content }
else
format.html { render action: 'edit' }
format.json { render json: #profile.errors, status: :unprocessable_entity }
end
end
end
# DELETE /profiles/1
# DELETE /profiles/1.json
def destroy
#profile.destroy
respond_to do |format|
format.html { redirect_to profiles_url }
format.json { head :no_content }
end
end
private
# Use callbacks to share common setup or constraints between actions.
def set_profile
##profile = Profile.find(params[:id])
#profile = current_user.profile
end
# Never trust parameters from the scary internet, only allow the white list through.
def profile_params
params.require(:profile).permit(:photo, :address, :zip, :city, :state, :country, :telephone, :experience, :levels, :ages, :travel, :teachinglocation, :onlineteaching, :quotation, :aboutme, :subjects, :specialties, :lessondetails, :equipment)
end
end
What do I do wrong? How can I properly ensure that a newly signed up user can properly save his profile data?
It would be so great, if you could help me out.
It appears the Profile controller is not configured with the correct url - as can be seen by the missing User ID information.
You can see all currently defined paths by running the command rake routes from the command line.
I am a beginner at RESTful design and Rails, so do not consider the following expert advice.
Try changing the location of your redirect_to in the Profile create method:
# POST /profiles
# POST /profiles.json
def create
...
format.html { redirect_to user_profile_path(#profile.user, #profile), notice: 'Profile was successfully created.' }
...
If this works, the path should also be updated in the update and delete methods. As well as the format.json sections.
For additional info about this topic see:
2.9 Creating Paths and URLs From Objects
http://guides.rubyonrails.org/routing.html