Creating User with Rails API and JSON? - ruby-on-rails

So I'm testing out a very simple API in Rails to see if I can create a user from it locally using the Chrome plugin Postman (REST Client extension).
In my rails app, I've set up a folder/namespace for my API, and whenever I try to create my user, I get the following error:
Missing template api/v1/users/create, application/create with {:locale=>[:en], :formats=>[:json], :handlers=>[:erb, :builder, :raw, :ruby, :jbuilder, :coffee]}. Searched in: * "PATH/app/views"
I'm using Rails 4.0.1 and Ruby 2.0
I'm posting a screenshot below of what I'm posting:
module Api
module V1
class UsersController < ApplicationController
class User < ::User
# add any hacks
end
respond_to :json
def index
respond_with User.all
end
def show
respond_with User.find(params[:id])
end
def new
#user = User.new
end
def create
#user = User.create(user_params)
# respond_with(#user)
if #user.save
# render json: #user, status: :created, location: #user
redirect_to #user
end
end
private
def user_params
params.require(:user).permit(:name, :age, :location) if params[:user]
end
end
end
end
So based on my user_params, I should be able to create a new user, correct?
Please let me know if you need any additional info and I'll do my best to respond ASAP!
Thanks!

You can create user using API.
1) First you need to put proper resources in your routes.rb:
YourApp::Application.routes.draw do
namespace :api do
namespace :v1 do
resources :users
end
namespace :v2 do
# ... if needed
end
end
root to: 'users#index'
end
2) You need to create a RESTfull-style controller to process requests. Here how your action 'create' may be implemented.
def create
respond_with User.create(fio: params[:fio], phone: params[:phone], region: params[:region], updated_at: Time.now)
end
Example of 'create' with respond_to:
def create
# ...
respond_to do |format|
format.html {render text: "Your data was sucessfully loaded. Thanks"}
format.json {
User.create(... params ...)
render text: User.last.to_json # !
}
end
end
See documents about respond_with and respond_to if you need something special to respond.
Also can be helpful railscasts episodes about API building: #350 and #352
P.S. folder/namespace/v1/users_controller shall be the same as class name in your module Api
P.S.2 You can observe my app, where you can probably find something helpful (same as your app - simple API for records creating) - myApp
Example of users_controller (controllers/api/v1/users_controller.rb):
#encoding: utf-8
module Api
module V1
class UsersController < ApplicationController # Api::BaseController
before_filter :authenticate_user!, except: [:create, :index]
respond_to :json
def index
#respond_with
respond_to do |format|
format.html {render text: "Your data was sucessfully loaded. Thanks"}
format.json { render text: User.last.to_json }
end
end
def show
respond_with User.find(params[:id])
end
def create
respond_with User.create(access_token: params[:access_token], city: params[:city], created_at: Time.now, phone: params[:phone], region: params[:region], updated_at: Time.now)
end
def update
respond_with User.update(params[:id], params[:users])
end
def destroy
respond_with User.destroy(params[:id])
end
end
end
end

redirect dose not return, so your create method will keep looking for template to render, and then find there is no matching template.
To fix, you need to explicitly return redirect
if #user.save
return redirect_to(#user)
end
You also need to pay attention to the default url of #user. It's better to assign a named path explicitly in this case, say redirect_to(user_path(#user))

Related

Devise and Knock gem current_user

I have an application that is using both Devise and Knock. It is using Devise to power the authentication for Active Admin and Knock gem is providing the authentication for my API's
The issue I have is that Knock can't seem to find current_user and I believe this is likely because I am using Devise in the same project.
I have the following setup:
Api Controller
class ApiController < ActionController::API
include Knock::Authenticable
end
User Controller (for API not ActiveAdmin)
module Api
module V1
class UsersController < ApiController
before_action :set_user, only: [:show, :update, :destroy]
# GET /users/1
def show
render json: #user
end
# POST /users
def create
#user = User.new(user_params)
if #user.save
render json: #user.id, status: :created
else
render json: #user.errors, status: :unprocessable_entity
end
end
# PATCH/PUT /users/1
def update
if #user.update(user_params)
render json: #user
else
render json: #user.errors, status: :unprocessable_entity
end
end
# DELETE /users/1
def destroy
#user.destroy
end
private
# Use callbacks to share common setup or constraints between actions.
def set_user
#user = User.find(params[:id])
end
# Only allow a trusted parameter "white list" through.
def user_params
params.require(:user).permit(:email, :password, :password_confirmation)
end
end
end
end
Auth Controller
module Api
module V1
class AuthController < ApiController
def auth
render json: { status: 200, user: current_user }
end
end
end
end
Current User in this Auth controller returns nothing however in another project I have, without devise, this will correctly return the user.
Is there a way to redefine what current_user is or assign it to something different for the purposes of using Knock?
Try this in your ApplicationController
# JWT: Knock defines it's own current_user method unless one is already
# defined. As controller class is cached between requests, this method
# stays and interferes with a browser-originated requests which rely on
# Devise's implementation of current_user. As we define the method here,
# Knock does not reimplement it anymore but we have to do its thing
# manually.
def current_user
if token
#_current_user ||= begin
Knock::AuthToken.new(token: token).entity_for(User)
rescue
nil
end
else
super
end
end
private
# JWT: No need to try and load session as there is none in an API request
def skip_session
request.session_options[:skip] = true if token
end
# JWT: overriding Knock's method to manually trigger Devise's auth.
# When there is no token we assume the request comes from the browser so
# has a session (potentially with warden key) attached.
def authenticate_entity(entity_name)
if token
super(entity_name)
else
current_user
end
end

Devise crud + namespaces: Pro level :)

I have Devise Admin & Devise User;
I want to use namespaces;
What I want to achieve:
only devise admin can create devise user
registerable for user is not deleted so that he can edit only page
user can see only current_user/show page
What I have
routes:
Rails.application.routes.draw do root :to => 'dashboard#index'
devise_for :users, controllers: { registrations: 'user_registrations' }
devise_for :admins, controllers: { registrations: 'admin_registrations' }
get 'dashboard/index'
namespace :admin do
root 'dashboard#index'
resources :users
end
user_registration_controller:
class UserRegistrationsController < Devise::RegistrationsController
end
users_controller:
class UsersController < ApplicationController
def index
#users = User.all
end
def show
#user = User.find(params[:id])
end
def new
#user = User.new
end
def edit
end
def create
#user = User.new(user_params)
respond_to do |format|
if #guest.save
format.html { redirect_to users_path }
else
format.html { render :new }
end
end
end
def update
respond_to do |format|
if #user.update(user_params)
format.html { redirect_to #user }
else
format.html { render :edit }
end
end
end
def destroy
user = User.find(params[:id])
user.destroy
redirect_to users_path
end
private
def set_user
#user = User.find(params[:id])
end
def user_params
params.require(:user).permit(:email, :password, :password_confirmation)
end
end
+ I have user views as they would be in a normal scaffold.
=> With this setup, anyone can create a user
Any ideas how to solve the questions on top?..
Don't use separate user classes with Devise, use roles instead. Devise is only really made to authenticate a single class, while you can hack it into using two classes its a mess. You have to override all the logic of serializing/desearializing users from the session among other things so that devise knows if it should load the Admin or User class.
Its also a bad solution since you are push down a authorization problem into the authentication layer. Devise's job is to verify that the user is who she/he claims to be, which is no small feat. Authorization, on the other hand is rules about what a user can do. "Only admins can create users" is a clear cut authorization rule.
The simplest possible role based authorization would be something like this:
class AddRoleToUser < ActiveRecord::Migration
def change
add_column :users, :role, :integer, default: 0
add_index :users, :role
end
end
class User
# ...
enum role: [:visitor, :admin]
end
We use an enum which is a single bitmask column to store the users role. Declaring it as an enum column also gives us a few methods for free:
user.visitor?
user.admin?
user.admin!
So lets create a basic authorization check:
def create
unless current_user.admin?
redirect_to root_path, status: 401, error: 'You are not authorized to perform this action' and return
end
# ...
end
But we don't want to repeat that every time we want to authorize, so lets clean it up:
class AuthorizationError < StandardError; end
class ApplicationController
rescue_from AuthorizationError, with: :deny_access!
private
def authorize_admin!
raise AuthorizationError, unless current_user.admin?
end
def deny_access!
redirect_to root_path,
status: 401,
error: 'You are not authorized to perform this action'
end
end
So then we can setup the controller with a filter to check the authorization before the action is performed:
class UsersController < ApplicationController
before_action :authorize_admin!, except: [:show]
# ...
end
However instead of reinventing the wheel you might want to have a look at Pundit or CanCanCan which are solid authorization libraries with great communities. You also might want to look at Rolify.

Rails 4 with Pundit

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.

How do I let users edit the account their logged into in ruby on different pages?

I get the following error, Couldn't find User with 'id'=
I have this in my Users_Controller,
def edit
#user = #signed_in_user
end
This is in my routes.rb,
root 'welcome#welcome'
get 'login' => 'sessions#login', :as => :login
get 'profile' => 'users#profile', :as => :profile
post 'logging/user' => 'sessions#create'
get 'logout' => 'sessions#destroy', :as => :logout
get 'about' => 'about'
resources :users
get 'register' => 'users#new', :as => :register
get 'edit' => 'users#edit', :as => :edit
This is in my application_controller.rb,
class ApplicationController < ActionController::Base
protect_from_forgery with: :exception
before_action :set_user
protected
def set_user
unless session[:user_id] == nil
#signed_in_user = User.find(session[:user_id])
end
end
end
This is in my Users_Controller
Here is my code from my User_Controller on creating the account
class UsersController < ApplicationController
before_action :set_user, only: [:show, :edit, :update, :destroy]
# GET /users
# GET /users.json
def index
#users = User.all
end
def profile
#user = User.find(session[:user_id]) unless session[:user_id] == ""
redirect_to login_path, notice: "You're not logged in" unless #user
end
# GET /users/1
# GET /users/1.json
def show
end
# GET /users/new
def new
#user = User.new
end
# GET /users/1/edit
def edit
#user = #signed_in_user
end
# POST /users
# POST /users.json
def create
#user = User.new(user_params)
respond_to do |format|
if #user.save
format.html { redirect_to #user, notice: 'User was successfully created.' }
format.json { render :show, status: :created, location: #user }
else
format.html { render :new }
format.json { render json: #user.errors, status: :unprocessable_entity }
end
end
end
# PATCH/PUT /users/1
# PATCH/PUT /users/1.json
def update
respond_to do |format|
if #user.update(user_params)
format.html { redirect_to #user, notice: 'User was successfully updated.' }
format.json { render :show, status: :ok, location: #user }
else
format.html { render :edit }
format.json { render json: #user.errors, status: :unprocessable_entity }
end
end
end
# DELETE /users/1
# DELETE /users/1.json
def destroy
#user.destroy
respond_to do |format|
format.html { redirect_to users_url, notice: 'User was successfully destroyed.' }
format.json { head :no_content }
end
end
private
# Use callbacks to share common setup or constraints between actions.
def set_user
#user = User.find(params[:id])
end
# Never trust parameters from the scary internet, only allow the white list through.
def user_params
params.require(:user).permit(:name, :password, :password_confirmation, :email, :age)
end
end
And this is the link that I use for my HTML,
<li role="presentation"><%= link_to 'Edit', edit_path %></li>
So, to start, a good practice when you get an 'Couldn't find' message is to check what instance variables are in your view.
So in a view, just type: <%= #user_id %> and see if anything shows up on your page, thus indicating if any user is even present! The other problem is that your instance variable might be <%= user.id %> but I am not sure as I can't see your code and how the user is stored in the database.
Second if you run rake routes, you generally find that the edit path will have a URI pattern like: "/edit(.:format)", meaning the route need "edit_path(#user.id)" rather than just "edit_path".
Let me know if this leads you anywhere or you have further questions and I hope I can answer them!
===========
Additional info:
Well without more code to look at, I would provide a few more suggestions...The goal is to have the <%= #user.id %> (or user_id) show up on the page somehow, thus telling you it is available.
The set_user method is an instance method, not a class method. To make it a class method, try def self.set_user. This invokes the method of the instance on the controller, thus making it a class method.
Make sure you have a session object to use. In the routes, it looks like post logging/user might be creating the session, but I am not sure.
Keep the edit_path(#user.id) or however the id is stored for the user as the route rather than just edit_path. I am pretty sure if you run 'rake routes', it will tell you that an additional variable needs to be passed for the link to work
Use the gem byebug Here is the link: https://github.com/deivid-rodriguez/byebug. You get this error while the edit page or where ever you are getting the error write in the action byebug. As you have mentioned in the console it shows a arrow pointing at a specific line in the application, the last line should appear as this (byebug), here write the variable in which you are getting the user id. If we take an example of your application controller in the set_user method:
def set_user
byebug
unless session[:user_id] == nil
#signed_in_user = User.find(session[:user_id])
end
end
In the console after (byebug) write session[:user_id] so this will give you the value of the session[:user_id]. So if this is null then you have a problem here or just follow the same procedure to check anywhere else.
Also there is one more thing you can do to learn is just create a new project or use the existing one and generate a scaffold which will give you options of show, edit, index. It will generate all the views, controller code, migration and everything. You can do that like this:
rails generate scaffold User email:string password:string
You can add more fields if you want. And then in your application just visit http://localhost:[port_no]/users which will by default take you to index page where you can add new users, edit existing ones. This will teach you about everything. It would be like a reference code for you. Read more at: http://guides.rubyonrails.org/command_line.html#rails-generate
And being more specific to users only there is a gem named Devise which will give you all the required things like sign_in, sign_up, session_management for users. Hope these things help you with your issue.
Edit:
Here is a very good tutorial link which will help you: https://www.railstutorial.org/book/updating_and_deleting_users#sec-updating_users

Ckeditor in Rails, can't set authentication session-driven

I have a Ckeditor textarea in Rails 4, using gem "ckeditor". All works fine, and it's placed in an administration interface. So, when I click 'browse server' to upload the assets, it links to the url:
http://localhost:3000/ckeditor/pictures?CKEditor=skill_description&CKEditorFuncNum=1&langCode=es
The problem is that I don't want anybody to be able to access this page, only the administrator. So I use the gem cancan (supported by the ckeditor gem) to do it.
class Ability
include CanCan::Ability
def initialize(user)
can :access, :ckeditor # needed to access Ckeditor filebrowser
can [:access, :read, :create, :destroy], Ckeditor::Picture
can [:access, :read, :create, :destroy], Ckeditor::AttachmentFile
end
end
The problem is that the logic for cancan goes in a Model, so I can't get sessions there. If a make a before_filter in ApplicationController, somehow the app doesn't pass through it when going to the previous url. I think that with cancan the best way is to create a User model and create a is_admin field, but that's no what I want at this moment. Any ideas for how to handle this?
I found a solution for this issue.
Create a controller under controller with name ckeditor/pictures_controller.rb.
class Ckeditor::PicturesController < Ckeditor::ApplicationController
before_action: something
def index
#pictures = Ckeditor.picture_adapter.find_all(ckeditor_pictures_scope)
#pictures = Ckeditor::Paginatable.new(#pictures).page(params[:page])
respond_to do |format|
format.html { render :layout => #pictures.first_page? }
end
end
def create
#picture = Ckeditor.picture_model.new
respond_with_asset(#picture)
end
def destroy
#picture.destroy
respond_to do |format|
format.html { redirect_to pictures_path }
format.json { render :nothing => true, :status => 204 }
end
end
protected
def find_asset
#picture = Ckeditor.picture_adapter.get!(params[:id])
end
def authorize_resource
model = (#picture || Ckeditor.picture_model)
#authorization_adapter.try(:authorize, params[:action], model)
end
end
Or you can find the ckeditor controller in Ruby2.1.0\lib\ruby\gems\x.x.x\gems\ckeditor-x.x.x\app\controllers\ckeditor. In there, you can custom it.

Resources