I use Devise and I want to do my logout action.
What I want to do is, that when I log out, I want to create a own JSON object to return. At this time, after I logt out, I get all my root articles.
How can I write my own destory action like I have found the create action?
class SessionsController < Devise::SessionsController
def create
resource = warden.authenticate!(:scope => resource_name, :recall => :failure)
return sign_in_and_redirect(resource_name, resource)
end
def sign_in_and_redirect(resource_or_scope, resource=nil)
scope = Devise::Mapping.find_scope!(resource_or_scope)
resource ||= resource_or_scope
sign_in(scope, resource) unless warden.user(scope) == resource
return render :json => {:success => true, :redirect => stored_location_for(scope) || after_sign_in_path_for(resource)}
end
def failure
return render:json => {:success => false, :errors => ["Login failed."]}
end
end
And my Routes in routes.rb
devise_for :users, :controllers => {:session => "sessions"} do
get "/users/sing_out" => "devise/sessions#destroy"
end
this is the destroy method of the sessions-controller.
you should be able to customize it to your needs. i think that it would be wiser to add another action and implementing your custom behavior there, as this will be less likely to cause unexpected errors with upgrading devise in the future.
# DELETE /resource/sign_out
def destroy
redirect_path = after_sign_out_path_for(resource_name)
signed_out = (Devise.sign_out_all_scopes ? sign_out : sign_out(resource_name))
set_flash_message :notice, :signed_out if signed_out
# We actually need to hardcode this as Rails default responder doesn't
# support returning empty response on GET request
respond_to do |format|
format.any(*navigational_formats) { redirect_to redirect_path }
format.all do
method = "to_#{request_format}"
text = {}.respond_to?(method) ? {}.send(method) : ""
render :text => text, :status => :ok
end
end
end
Related
I am migrating my Rails 4 app (still using protected attributes gem) to Rails 5.1.4. In the course of this action, I need to rewrite a lot of code to replace protected attributes with strong parameters.
I am currently stuck on one specific controller where my RSpec tests fail, and I don't know how to implement the controller and test logic such that things are correct and tests pass.
The app has an admin backend where users can add (and thus upload) photos to an album. The respective Admin::PhotosController handles the photos of an album.
Here's the relevant exerpt from my app:
def create
# #organizer_account is set by an before_filter
#album = #organizer_account.albums.find_by_id(params[:album_id])
#photo = #album.photos.new(photo_params)
#photo.organizer_account_id = #organizer_account.id
authorize! :create, #photo
respond_to do |format|
if #photo.save
format.html {
render :json => [#photo.to_jq_file].to_json, :content_type => 'text/html', :layout => false
}
format.json {
files = [ #photo.to_jq_file ]
render :json => {:files => [#photo.to_jq_file] }, :status => :created, :location => admin_album_photo_path(#album, #photo)
}
else
format.html {
render action: "new"
}
format.json {
render json: #photo.errors, status: :unprocessable_entity
}
end
end
end
I have defined the following strong parameters:
private
def photo_params
params.require(:photo).permit(:id, :album_id, :organizer_account_id, :file)
end
The failing RSpec test is as follows:
require 'spec_helper'
describe Admin::PhotosController, :type => :controller do
render_views
describe "post 'create'" do
describe "with valid parameters" do
before(:each) do
#organizer_account = FactoryBot.create(:organizer_account)
#user = FactoryBot.create(:user)
#user.organizer_account_id = #organizer_account.id
#user.add_role :admin, #organizer_account
#user.save
sign_in #user
#album = #organizer_account.albums.create(:title => "Album 1")
#photo_attrs = FactoryBot.attributes_for(:photo)
request.env["HTTP_REFERER"] = new_admin_album_path
controller.request.host = #organizer_account.subdomain + ".lvh.me"
end
it "should create a new photo record", :focus => true do
lambda {
post :create, params: {:photo => #photo_attrs, :album_id => #album.id }
}.should change(#organizer_account.albums.find_by_id(#album.id).photos, :count).by(1)
end
end
end
end
I strongly assume that the issue is in parameters are a) passed
post :create, params: {:photo => #photo_attrs, :album_id => #album.id }
and then processed
#photo = #album.photos.new(photo_params)
While the params hash passed by the test has all the required entries
params: {"photo"=><ActionController::Parameters {"file"=>[#<ActionDispatch::Http::UploadedFile:0x00000010dd7560 #tempfile=#<Tempfile:C:/Users/PATRIC~1/AppData/Local/Temp/RackMultipart20180520-11424-avge07.gif>, #original_filename="image6.gif", #content_type="image/gif", #headers="Content-Disposition: form-data; name=\"photo[file][]\"; filename=\"image6.gif\"\r\nContent-Type: image/gif\r\nContent-Length: 46844\r\n">]} permitted: false>, "album_id"=>"1561", "controller"=>"admin/photos", "action"=>"create"}
the photo_params is empty:
photo_params: {}
Update #1: Definition of factory for photo
FactoryBot.define do
factory :photo, :class => Photo do
file Rack::Test::UploadedFile.new(Rails.root + 'spec/fixtures/photos/apfelkuchen.jpg', "image/jpg")
end
end
Update #2: Photo model with file attachment and image processing config
class Photo < ActiveRecord::Base
require 'rmagick'
include Magick
belongs_to :album
belongs_to :organizer_account
before_destroy { |photo| photo.file.destroy }
validates :album_id, :presence => true
validates :organizer_account_id, :presence => true
has_attached_file :file,
# The following tyles and convert options lead to breaking RSpec tests. If commented, RSpec tests pass.
:styles => {
:mini => "50x50#",
:thumb => "160x160#",
:large => "1200x1200>"
},
:convert_options => {
:mini => "-quality 75 -strip",
:thumb => "-quality 75 -strip"
}
validates :file, :presence => true
end
i want to rebuild an app which is a typical rails 3.2 mvc app into a API + Frontend (Backbone) only. As I have no experience in building APIs in rails including authenticatin:
What's the best way to authenticate with devise using backbone? Using auth_tokens?
How should I make he API? Just printing out JSON or use a gem like Grape?
thanks in advance!
I can explain you the way i do this :
First, i install a standard rails application with devise. After that, i create my own session controller :
class SessionsController < ApplicationController
def authenticate
# this method logs you in and returns you a single_access_token token for authentication.
#user = User.find_for_authentication(:email => params[:user][:email])
if #user && #user.valid_password?(params[:user][:password])
render :json => {:user => {:email => #user.email, :id => #user.id, :firsname => #user.firstname, :lastname => #user.lastname, :team_id => #user.team_id, :singleAccessToken => #user.generate_access_token}}
else
render :json => {:errors => ["Nom d'utilisateur ou mot de passe invalide"]}, :status => 401
end
end
end
As you can see, i send a request to this url with the json looking like :
{
user => {
email => "myemail#toto.com",
password => "monpass"
}
}
And my controller return me the json with user data if every thing is fine, or an error. On json with user, i return an access_token used on next requests to check that the user is allowed to request. I made this filters in my application controller :
class ApplicationController < ActionController::Base
protect_from_forgery
protected
def user_access_token
request.headers["HTTP_X_USER_ACCESS_TOKEN"] || request.headers["HTTP_USER_ACCESS_TOKEN"]
end
def current_user
if token = user_access_token
#user ||= User.find_by_access_token(token)
end
end
def require_user
unless current_user
render :json => {:error => "Invalid Access Token"}, :status => 401
end
end
def require_owner
unless current_user && current_user == object.user
render :json => {:error => "Unauthorized"}
end
end
end
As you can see, on each next request, i will add the access_token in html header on key : HTTP_USER_ACCESS_TOKEN
So, i can check if the user is allowed to make the request.
To make an API, you can use the Rails API gem as see here :
http://railscasts.com/episodes/348-the-rails-api-gem
Good luck.
I have a controller I created for Devise that I want to use to add a few more pages inside /devise/registration/
class RegistrationsController < Devise::RegistrationsController
skip_before_filter :detect_account_ended
def import
#title = "Import customers from Quickbooks"
end
def categories
#title = "Edit Categories"
#categories = current_user.categories
respond_to do |format|
format.html # index.html.erb
format.json { render json: #categories }
end
end
protected
def after_update_path_for(resource)
edit_user_registration_path
end
end
I created the routes
devise_for :users,
:path_names => {
:sign_up => "register",
:destroy => "logout" },
:controllers => {
:registrations => :registrations } do
get '/users/sign_out' => 'devise/sessions#destroy'
get '/users/import' => 'devise/registrations#import'
get '/users/categories' => 'devise/registrations#categories'
end
And the views. The views show up correctly but the objects I pass from the registrations controller are not going to the views. How do I fix this so that these objects are getting passed to my views?
Try access them directly into view like this:
current_user.categories
As current_user is available in views too, as for the #title, you don't have to pass it as a variable, as you can add the text you need directly into view too.
At each step in my checkout process, an order is updated via a PUT request. However, one of the states has a form that submits to a third party which redirects back to my site, calling the update method with GET (no control over this).
Why does my respond_with code appear to be totally ignored and I get a Missing Template checkout/update error? It should be hitting #edit.
CheckoutController.rb
before_filter :load_order
def update
if #order.update_attributes(params[:order])
#order.next
end
respond_with(#order, :location => checkout_state_url(#order.state))
end
routes.rb
match '/checkout/update/:state' => 'checkout#update', :as => :update_checkout
match '/checkout/:state' => 'checkout#edit', :as => :checkout_state
match '/checkout' => 'checkout#edit', :state => 'client_details', :as => :checkout
It looks like respond_with does different things depending upon the HTTP verb and whether the resource has errors. See here and here.
The following code worked for me:
def update
if #order.update_attributes(params[:order]) && #order.next
respond_with(#order) { |format| format.html { redirect_to checkout_state_url(#order.state) } }
else
respond_with(#order) { |format| format.html { render :edit } }
end
end
I am refactoring my access_controller into a sessions_controller and can't seem to get my destroy action working properly.
Logging in seems to work fine, but I am unable to log out of a session. Here is the link I have for logging out:
<%= link_to("Logout", :controller => "sessions", :action => 'destroy') %>
routes.rb
resources :sessions
sessions_controller.rb
class SessionsController < ApplicationController
def new
end
def create
...
end
def destroy
session[:user_id] = nil
flash[:notice] = "You are now logged out"
redirect_to root_url
end
end
When I click "Logout" I get redirected to "/sessions/destroy" with a message of "The action 'show' could not be found for SessionsController". The destroy actions seems to want an id, but I don't need to pass in an id, I just want to run the action.
Ah, I found the answer here: http://railscasts.com/episodes/250-authentication-from-scratch
I need to set up my routes as follows:
get "log_out" => "sessions#destroy", :as => "log_out"
get "log_in" => "sessions#new", :as => "log_in"
resources :sessions