Multi-form in rails using Wicked Gem and Devise - ruby-on-rails

I am trying to set a multi-form login system using the Wicked gem. I have devise installed up and running correctly, when following these steps:http://railscasts.com/episodes/346-wizard-forms-with-wicked.
I'm not being redirected to the user_step_paths? everything is done as stated in the tutorial but, I'm guessing because I'm using devise i need to do it in a controller inherited by devise? my code is below for the controllers:
users_controller.rb
class UsersController < Devise::RegistrationsController
def new
#user = User.new
end
def create
#user = User.new(params[:sign_up])
if #user.save
session[:user_id] = #user.id
redirect_to user_steps_path
else
redirect_to new_user_registration_path
end
end
end
users_steps_controller.rb
class UserStepsController < ApplicationController
include Wicked::Wizard
steps :education, :social
def show
render_wizard
end
end
routes
get 'pages/home'
devise_for :users, :controllers => { :registrations => 'users'}
resources :user
resources :user_steps

1.Needed a update method in the controller and needed to define user in the show method:
def show
#user = current_user
render_wizard
end
def update
#user = current_user
#user.update_attributes(user_params)
render_wizard #user
end
2.Needed to generate the devise controllers:
rails generate devise:controllers [scope]
3.Update the registration_controller for devise
class Users::RegistrationsController < Devise::RegistrationsController
# before_filter :configure_sign_up_params, only: [:create]
# before_filter :configure_account_update_params, only: [:update]
# GET /resource/sign_up
def new
super
end
# POST /resource
def create
super
end
# GET /resource/edit
# def edit
# super
# end
# PUT /resource
def update
super
end
# The path used after sign up.
def after_sign_up_path_for(resource)
user_steps_path
end
# The path used after sign up for inactive accounts.
def after_inactive_sign_up_path_for(resource)
super(resource)
end
end
4.This controller is invalid, you need to use the generated controllers by devise:
class UsersController < Devise::RegistrationsController

Related

Rails: before_action :authenticate_admin! doesn't work

I'm doing this in routes.rb:
devise_for :admins, :skip => [:registrations], controllers: {sessions: 'admins/sessions'}
devise_scope :admin do
get "admins/home"=> "admins/sessions#home", :as => "admin_home"
end
But when I put the before_action :authenticate_admin! in the sessions controller and open the admins/home the login screen doesn't appear, no errors pop up neither. I get full access to the page without having to log in!
class Admins::SessionsController < Devise::SessionsController
before_action :configure_sign_in_params, only: [:create]
before_action :authenticate_admin!
def home
end
def new
super
end
def create
super
end
def destroy
super
end
protected
def configure_sign_in_params
devise_parameter_sanitizer.permit(:sign_in, keys: [:attribute])
end
end
Any ideas what I might be doing wrong?
Your defined route to create session in home action.So change home action
class Admins::SessionsController < Devise::SessionsController
before_action :configure_sign_in_params, only: [:create]
def home
self.resource = warden.authenticate!(auth_options)
set_flash_message!(:notice, :signed_in)
sign_in(resource)
respond_with resource, location: after_sign_in_path_for(resource)
end
def new
super
end
# override method here
def create
end
def destroy
super
end
protected
def configure_sign_in_params
devise_parameter_sanitizer.permit(:sign_in, keys: [:attribute])
end
end
NOTE: And put this in application controller
before_action :authenticate_admin!

ruby - run http://localhost:3000/admin/users but redirect_to http://localhost:3000/admin/login

I am new in Ruby on Rails.
I want to run http://localhost:3000/admin/users to see users index page.
But when I run this link, it guide me to http://localhost:3000/admin/login.
Is there something wrongs with my route setting?
Rails.application.routes.draw do
get 'users/new'
get 'users/show'
if Rails.env.development?
mount LetterOpenerWeb::Engine, at: '/letter_opener'
end
root to: 'helps#top'
# admin login
get 'admin/login', to: 'admin/login#index', as: 'admin/login'
get 'admin/logout', to: 'admin/login#logout'
post 'admin/login/login'
get 'admin', to: 'admin/projects#index', as: 'admin_top'
namespace :admin do
resources :users, only: %i(index new create)
resources :projects do
resources :project_users
resources :project_comments
end
resources :images
resources :categories
resources :campanies
end
end
users_controller.rb
class Admin::UsersController < AdminController
before_action :set_user, only: [:show, :edit, :update, :destroy]
def index
#users = User.all
end
def show
end
def new
#user = User.new
end
def edit
end
#Post /admin/projects
def create
#user = User.new(user_params)
if #user.save
flash[:notice] = 'User saved successfully'
redirect_to :back
else
flash[:alert] = #user.errors
binding.pry
render :new
end
end
def update
end
def destroy
end
private
def set_user
#user = User.find(params [:id])
end
def user_params
params.require(:user).permit(:campany_id, :name, :email, :password_digest, :profile, :prefecture_id, :address)
end
end
Thank you!
Your UsersControllers is under the admin namespace, that's to say you must be logged in order to access to this.
If you want to have access without validating the user is currently logged in, then you'll have to remove the constraint or verification to the controller or to make a new controller and index method which point to /admin/users but this time without the user verification.
That's:
# app/controllers/users_controller.rb
class UsersController < ApplicationController
...
def index
#users = User.all
end
...
end
# config/routes.rb
get '/users', to: 'users#index'
'/users' or '/admin/users' as you want to do it, but if you use the last one then any person must infer that's a restricted resource .

Authorization settings using pundit gem rails

I'm new at rails so bear with me pls. My problem is so specific. I'm creating a User blog, where they could put any posts. So Users has a blogs, and blogs has posts. So when user create a blog, all posts in his blog should be written by him. Other users can't write not on their blogs.
post_controller.rb
class PostsController < ApplicationController
before_action :authenticate_user!
before_action :authorize_user!, only: [:edit, :update, :destroy]
expose :blog
expose :post
def show
end
def new
end
def edit
end
def create
post.user = current_user
post.save
respond_with post, location: user_blog_path(post.blog.user, post.blog)
end
def update
post.update(post_params)
respond_with post, location: user_blog_path(post.blog.user, post.blog)
end
def destroy
post.destroy
respond_with post, location: user_blog_path(post.blog.user, post.blog)
end
private
def authorize_user!
authorize(post, :authorized?)
end
def post_params
params.require(:post).permit(:title, :content, :user_id, :blog_id)
end
end
Here i'm using pundit to authorize user, when they update or destroy posts (users can update or destroy only their own posts) and it works perfectly.
views/posts/new
.row
.columns
h2 = title("New post")
.row
.medium-5.columns
= simple_form_for post do |f|
= f.error_notification
.form-inputs
= f.input :title
= f.input :content
= f.hidden_field :blog_id, value: blog.id
.form-actions
= f.button :submit
Here i'm using the hidden form to set the blog_id which I take from params. Http link looks like http://localhost:3000/posts/new?blog_id=6. The problem is that each user can copy this link to create the post( and they are not the blog owners).
post_policy.rb
class PostPolicy < ApplicationPolicy
def authorized?
record.user == user
end
end
How should I check the blog's owner before post creating? Maybe I have a wrong way to create posts like this(using hidden form).
Link to create new post
= link_to 'New Post', new_post_path(blog_id: blog.id)
I hope, it will work for you
application_controller.rb
class ApplicationController
include Pundit
after_action :verify_authorized, except: :index
after_action :verify_policy_scoped, only: :index
before_action :authenticate_admin_user!
helper_method :current_user
def pundit_user
current_admin_user
end
def current_user
#current_user ||= User.find(current_admin_user.id)
end
end
posts_controller.rb
class PostsController < ApplicationController
before_action :set_blog
def new
authorize(Post)
end
def edit
#post = #blog.posts.find(params[:id])
authorize(#post)
end
def index
#posts = policy_scope(#blog.posts)
end
private
def set_blog
#blog = current_user.blogs.find(params[:blog_id])
end
end
post_policy.rb
class PostPolicy < ApplicationPolicy
def show?
true
end
def index?
true
end
def new?
create?
end
def create?
true
end
def edit?
update?
end
def update?
scope_include_object?
end
def destroy?
scope_include_object?
end
class Scope < Scope
def resolve
scope.joins(:blog).where(blogs: { admin_user_id: user.id })
end
end
def scope_include_object?
scope.where(id: record.id).exists?
end
end
routes.rb
Rails.application.routes.draw do
devise_for :admin_users
resources :blogs do
resources :posts
end
end

Pundit policy_scope error: undefined method `admin?' for nil:NilClass

Running into something I don't understand with Pundit,
Using Rails 4.2.5.1, Pundit 1.1.0 with Devise for authentication.
I'm trying to use a policy scope for the BlogController#Index action.
If user is admin, display all posts (drafts, published)
If user is standard, display posts marked published only
If no user / user not logged in, display posts marked published only
Getting an error:
undefined method `admin?' for nil:NilClass
Live shell reveals:
>> user
=> nil
# ApplicationController
class ApplicationController < ActionController::Base
include Pundit
rescue_from Pundit::NotAuthorizedError, with: :user_not_authorized
# Prevent CSRF attacks by raising an exception.
# For APIs, you may want to use :null_session instead.
protect_from_forgery with: :exception
private
def user_not_authorized
flash[:error] = "You are not authorized to perform this action."
redirect_to(request.referrer || root_path)
end
end
# BlogController
# == Schema Information
#
# Table name: blogs
#
# id :integer not null, primary key
# title :string default(""), not null
# body :text default(""), not null
# published :boolean default("false"), not null
# created_at :datetime not null
# updated_at :datetime not null
#
class BlogsController < ApplicationController
before_action :set_blog, only: [:show, :edit, :update, :destroy]
before_action :authenticate_user!, except: [:index, :show]
after_action :verify_authorized, except: [:index, :show]
after_action :verify_policy_scoped, only: [:index]
def index
#blogs = policy_scope(Blog)
authorize #blog
end
def show
end
def new
#blog = Blog.new
authorize #blog
end
def edit
authorize #blog
end
def create
#blog = Blog.new(blog_params)
#blog.user = current_user if user_signed_in?
authorize #blog
if #blog.save
redirect_to #blog, notice: "Blog post created."
else
render :new
end
end
def update
authorize #blog
if #blog.update(blog_params)
redirect_to #blog, notice: "Blog updated."
else
render :edit
end
end
def destroy
authorize #blog
#blog.destroy
redirect_to blogs_url, notice: "Blog post deleted."
end
private
def set_blog
#blog = Blog.friendly.find(params[:id])
end
def blog_params
params.require(:blog).permit(*policy(#blog|| Blog).permitted_attributes)
end
end
# Application Policy
class ApplicationPolicy
attr_reader :user, :record
def initialize(user, record)
#user = user
#record = record
end
def index?
false
end
def show?
scope.where(:id => record.id).exists?
end
def create?
false
end
def new?
create?
end
def update?
false
end
def edit?
update?
end
def destroy?
false
end
def scope
Pundit.policy_scope!(user, record.class)
end
class Scope
attr_reader :user, :scope
def initialize(user, scope)
#user = user
#scope = scope
end
def resolve
scope
end
end
end
# Blog Policy
class BlogPolicy < ApplicationPolicy
class Scope < Scope
def resolve
if user.admin?
scope.all
else
scope.where(published: true)
end
end
end
def new?
user.admin?
end
def index?
true
end
def update?
user.admin?
end
def create?
user.admin?
end
def destroy?
user.admin?
end
def permitted_attributes
if user.admin?
[:title, :body]
end
end
end
In the Pundit BlogPolicy scope I've created:
class Scope < Scope
def resolve
if user.admin?
scope.order('id DESC')
else
scope.where('published: true')
end
end
end
If I log in as an admin user it works fine.
I'm able to view all blog posts.
If I log in as a standard user it works.
Standard user sees blog posts that are marked published.
If I'm not logged in where user is nil I get an error:
NoMethodError at /blog
undefined method `admin?' for nil:NilClass
I can add another clause elsif user.nil? before user.admin? or a case when statement but I thought if the user is not an admin it should just display what is in the else block?
# This seems wrong?
class Scope < Scope
def resolve
if user.nil?
scope.where('published: true')
elsif user.admin?
scope.all
else
scope.where('published: true')
end
end
end
Any pointers much appreciated
You can use try:
if user.try(:admin?)
# do something
end
http://api.rubyonrails.org/v4.2.5/classes/Object.html#method-i-try
This happens because there is no user when you are not logged in. (Probably to user variable nil value is assigned somewhere, so you are trying to call admin? method on nil object)
If you use ruby 2.3.0 or newer you had better use safe navigation
if user&.admin?
scope.order('id DESC')
else
scope.where('published: true')
end
If you user other ruby version
if user.try(:admin?)
scope.order(id: :desc)
else
scope.where(published: true)
end

how to define the admin_user to delete posts? Ruby on rails app

I am trying to get admins to be able to delete postings by users.
This is the code I'm using inside post.html.erb
<% if current_user.admin? %>
<%= link_to "delete", post, method: :delete %>
<% end %>
This is what I have inside the controller
class PostsController < ApplicationController
before_filter :signed_in_user
before_filter :admin_user, only: :destroy
before_filter :correct_user, only: :destroy
def destroy
#post.destroy
redirect_to root_path
end
private
def correct_user
#post = current_user.posts.find_by_id(params[:id])
redirect_to root_path if #post.nil?
end
I'm able to get it working for correct_user, but not for admin. I get this error message
undefined local variable or method `admin_user' for #<PostsController:0x5fda230>
I tried to def admin_user as user.id ==1
private
def admin_user?
current_user && current_user.id == 1
end
But I'm experiencing the same error message.
before_filter :admin_user, only: :destroy
class ApplicationController
def admin_user?
if current_user && current_user.id == 1
return true
else
redirect_to root_url, :error => "Admin only"
return false
end
end
end
Since admin_user? is called from any controller so put it in application controller
class ApplicationController < ActionController::Base
def admin_user?
unless current_user && current_user.admin?
redirect_to root_url
return false
end
end
end
class PostsController < ApplicationController
before_filter :signed_in_user
# before_filter :admin_user?, only: :destroy
before_filter :load_post, :only: :destroy
def destroy
#post.destroy
redirect_to root_url
end
private
def load_post
#post = current_user.admin? ? Post.find(params[:id]) : current_user.posts.find(params[:id])
end
end
UPD:
As I understood from comments you want users to be able to destroy their own posts and admin to be able to destroy any posts. In this case you don't have to check is user admin in before_filter, e.g. remove this line:
before_filter :admin_user?, only: :destroy

Resources