How to resolve conflict in Devise mass assign errors - ruby

I have an application that was running just fine using Devise for authentication. I added CanCan and I am now having issues with Devise. Given the standard "encrypted_password" and "password_confirmation" attributes if they are not specified as attr_accessible then when the controller tries to do:
#user = User.new(params[:user])
I get the standard MassAssignment error. But if I add them to the attr_accessible list I can then create the user but it will then fail on password validation since the attr_accessible does not let Devise encrypt the password. I suspect the issue is something in my routes since I have both:
devise_for :users, :path_prefix => 'secure'
resources :users
The resources :users is there so that the admin and use standard CRUD type operations to manage CanCan.
Not sure if more detail is needed but this sounds like a core issue that I am stubbing my toe on.
Thank you

I found the problem. Someone had changed the model's attr_accessible list to have encrypted_password not password. Devise is looking for the password in the parameters and then creates the encrypted_password for the model.
Another newbeee programmer has learned a lesson :) So did I, double check changes made by newbes!
Thankyou

Related

Authorisation for Sinatra via CanCan

I am trying to get some auth mechanism working for my webapp (written in Sinatra). Currently I am playing around with sinatra-can which looks great. The issue I now face is that I need access to the specific model from the can method. Lets say I have a route that looks like that:
class ProjMgmt < Sinatra::Base
get '/edit/:project' do
project = Project.where(name: param[:project]).first
authorize! :edit, project
project.to_html
end
end
There are two models defined, Project and Manager. They are stored in a MongoDB (via mongoid, NO datamapper, ActiveRecord or so) and have a has_and_belongs_to_many relation, eg. the relations can be accessed via project.managers or manager.projects.
Now, only managers that have a relation to the particular project should be able to edit the project. What I want to have is something like that on authorize!:
class Ability
include CanCan::Ability
def initialize(user)
can :edit, project if project.managers.include? user
end
end
Obviously, that does not work since Ability does not know about any project.
Is there any nice approach to this? Must not necessarily be CanCan...
Try like this
def initialize(user)
can :edit, Project do |project|
project.managers.include? user
end
end

rails default actions are missing params in routing

Excuse me for my lame question, but i`m stuck.
Plain and simple in my routes.rb file i have:
resource :books do
resource :reviews
end
running rake routes | grep reviews gives me:
books_reviews POST /books/reviews(.:format) reviews#create
new_books_reviews GET /books/reviews/new(.:format) reviews#new
edit_books_reviews GET /books/reviews/edit(.:format) reviews#edit
GET /books/reviews(.:format) reviews#show
PUT /books/reviews(.:format) reviews#update
DELETE /books/reviews(.:format) reviews#destroy
My question is: where is the :id param in the show and edit actions? According to this tutorial: http://guides.rubyonrails.org/routing.html there should be "id" params in the routing, like this:
new_books_reviews GET /books/:id/reviews/new(.:format) reviews#new
edit_books_reviews GET /books/:id/reviews/edit(.:format) reviews#edit
GET /books/:id/reviews(.:format) reviews#show
On top of that where are the routes for show, update and destroy actions?
I think i`m missing something fundamental, because this really sucks. Running Rails 3.2.11
Thanks for the help
Try using resources instead of resource.
Rails thinks there's only one when you use the singular form and doesn't need the ID.
For more info see the "singular resource" section of the guide
Using resources over resource will make sure Rails accounts for many resources.
Your routes should look like this
resources :books do
resources :reviews
end

What is the correct way to setup database with DataMapper and Sinatra on production server?

From the DataMapper document, I think there are at least four functions need to be called to have database setup:
DataMapper.setup(:default, 'sqlite:///path/to/project.db')
DataMapper.finalize
DataMapper.auto_migrate!
DataMapper.auto_upgrade!
In many DataMapper+Sinatra tutorials I learned that auto_migrate! and auto_upgrade! are not supposed to be called every time the app is loaded on production server. But in the meanwhile many examples call these functions in the main ruby file of the sinatra app, say app.rb, without additional check. And some examples do not call finalize at all. So far I am confused and I am not sure what to do on the production server.
Take this following simple app.rb for example, I have some questions:
Where and when should finalize be called?
When deploying the app first time there is no db file on the production server, how do I have it automatically created? Or do I have to create the project.db file manually?
Since the auto_upgrade! is wrapped in :development block, it won't be called on production server. How am I supposed to upgrade database when I add or remove columns in it?
require 'sinatra'
require 'data_mapper'
configure do
DataMapper.setup :default, "sqlite3://#{Dir.pwd}/project.db"
end
class Book
include DataMapper::Resource
property :id, Serial
property :title, Text
belongs_to :author
end
class Author
include DataMapper::Resource
property :id, Serial
property :name, Text
has n, :books
end
configure :development do
DataMapper.auto_upgrade!
end
get '/:id' do
#author = Author.get params[:id]
erb :list_author_and_his_books # The template has nothing to do with this question, ignore it
end
get '/new' do
# Some code for user to input book or author details
end
get '/create' do
# Some code to create book or author in db
end
Thanks for reading this long post :D
Where and when should finalize be called?
From http://rdoc.info/github/datamapper/dm-core/DataMapper#finalize-class_method
This method should be called after loading all models and plugins.
When deploying the app first time there is no db file on the production server, how do I have it automatically created? Or do I have to create the project.db file manually?
It depends on your hosting arrangement, but the main thing to do is put the running of migrations in a Rake task and run them when the app is deployed. If you're using Sqlite, this would create the database (although on some hosts you are not allowed to update the file system). I don't think it's a good idea to use Sqlite for a production database, but that's your decision.
Since the auto_upgrade! is wrapped in :development block, it won't be called on production server. How am I supposed to upgrade database when I add or remove columns in it?
Use a Rake task. After each deployment you'd run the "db:migrate:up" (or whatever you'd called it) task and it would run the latest migrations. You might get a few ideas from Padrino's Rake tasks for DataMapper

How to implement client-server API and authorisation in Ruby (Rails/Sinatra)? [closed]

As it currently stands, this question is not a good fit for our Q&A format. We expect answers to be supported by facts, references, or expertise, but this question will likely solicit debate, arguments, polling, or extended discussion. If you feel that this question can be improved and possibly reopened, visit the help center for guidance.
Closed 10 years ago.
I need a suggestion on how to implement "client-server" web apps in ruby. Any guides and best practices are highly appreciated. I'm interested both in Ruby ways and gems required as it is a desired platform, and in general ways and logic to implement such things.
I'm not an excellent ruby programmer or a skilled system designer with years of expirience sadly so I really need your help as I still hope this thing would shine in the end.
The current look of the application should be like this:
DB + Auth DB <-> API App <-> Other Apps:
DB - a database or set of databases with one DB for group of users (regions).
Auth DB - one more DB with personal users' data and login information, probably single even if main databases would be split between regions.
API App - this thing maintains all the logic around data, access control and translations probably to have one translations base for all the apps.
Other Apps - bunch of different applications, tied with API, this could be data providers that pokes API with some data about some user, and UI of different kinds. All of the apps could not have own storage of user-related info and work with the data only through the API.
API App: looks like the best tool to make it is Sinatra. The questions are:
How to organize API in the clear way? Rails makes nice REST path settings and folders structure for models and controllers. Any tips or gems to improve API building expirience?
How to maintain access? Warden isn't looks like a good option as API clients would be the web apps themselves. So I need some kind of auth token. How could it be done? Some sort of custom OAuth provider? The thing is, I don't like the idea to store and pass session cookies through the API, some sort of access token passed with each request. This also
Other Apps: mainly web-based UIs. The logical selection for this part is Rails. The main question is how to implement client-side auth check. Devise is cool, but is it possible to make it work with token, or is it more suitable tool?
Okay, this is going to be a bit longer:
If you are already familiar with Rails, you can have a look at the Rails API gem. The project strives to remove additional cruft from Rails which is not needed for a JSON-based RESTful API.
This may sound smooth, but it has it's downsides. First and foremost, you have the readd everything that is needed for basic rails functionality, that you may be accustomed to, e.g. respond_to. This can be a bit tricky, but is pretty straightforward when you find out which rails module originally provided the functionality usually bundled in Rails within the ActionController::Base.
That being said, let me give you an example of what i did for a small API project I did last week out of curiosity:
Initial Situation
We have a mainly finished Rails App. It all works fine, but it is basically monolithic. Everything is served by the Rails Framework. Luckily for us, all the model logic is bundled into a gem called core. the application essentially lets logged in customers create products which are than searchable through an end user view.
The goal was to provide a RESTful API for this, which can handle concurrency and larger data files (i.e. CSV, XLS) a little bit more efficiently.
Introducing Rails API
The design goal let me to the Rails API gem. The basic installation works like in Rails, except the script is called rails-api, i.e.:
rails-api new jsonapi
The advantage for me here was that I could use the core from the other application, but nothing would stop me from just introducing my own models to the jsonapi application.
That being said, you can do all the standard Rails goodies, like routing, etc. It follows the same convention. then again, standard routes initially react to JSON only, which can be a bit confusing at times.
Let me give you an example of the API Controller that handles products:
class ProductsController < ApplicationController
include ActionController::HttpAuthentication::Token
before_filter :find_product, :except => [:create, :index]
def index
render :json => #products
end
def create
#product = product.new params[:product]
if #product.save
render :json => #product, :status => :created
else
render :json => #product.errors, :status => :unprocessable_entity
end
end
def show
render :json => #product
end
def update
if #product.update_attributes params[:product]
render :json => #product, :status => :ok
else
render :json => #product.errors
end
end
def destroy
if #product.destroy
render :json => #product, :status => :ok
else
render :json => {:note => I18n.t("messages.deletion_impossible")}, :status => :unprocessable_entity
end
end
protected
def find_product
#product = Product.find params[:id]
end
end
It is nothing special. The only thing to note is the second line where ActionController::HttpAuthentication::Token is included explicitly. This is so that your aPI may be secured by HTTP Token. If you want to know more about securing an API, I suggest Ryan Bates' Guide on Railscasts.
In essential, you provide a before filter in the ApplicationController like this:
class ApplicationController < ActionController::API
include ActionController::HttpAuthentication::Token::ControllerMethods
[...]
before_filter :restrict_access
[...]
def restrict_access
authenticate_or_request_with_http_token do |token, options|
# see if key is valid.
end
end
end
Again, note the second line, you have to include the ControllerMethods manually, otherwise no controller will know about authenticate_or_request_with_http_token.
Extension
You may know extend the API based on the Rails conventions. It works exactly the same way, with the exception that some stuff is intentionally missing by default. I suggest adding JBuilder (Railscast), if you need more flexibility in your JSON Templates.
Great, but what about clients?
Personally, there is a lot of choice when it comes to clients. Ultimately I find that it comes down to what you like most. I can personally recommend a small node.js layer on top of the Rails API which then gets a single page application based on backbone.js in front of it. You can also try AngularJS if you like. You can also build another Rails App around it and call the API from within your controller actions.
It also depends on what platform you want to target - a native application for iOS/Android comes to mind.
The choice I made was node.js + backbone. It currently makes the most sense for me at the time and for the project. The node layer essentially holds the Token necessary to communicate with the API and the backbone application has a small library to talk with the node layer. It can however be a double edged sword, depending on how complex your API will be. For a small example this seems to be fine, but there can be a lot of code duplication just to put the calls from the backbone application through to the Rails API.
Authentication
For authentication you can make customer based API-Keys (Tokens) and then limit the controller logic to only accept data operations which are allowed with that key. You could than manage the session via the node layer. Edit: This is authorization, not authentication. Nothing actually stops you from using Authlogic with Rails API - i have not tested it, but it should work.
I confess that i have not finished this part yet - I hope others can answer this architectural question :-)
I hope I could provide some insights.
P.S.: If you want to test your API i highly recommend httpie (It's awesome!)

Ruby: how to uninstall Devise?

I have installed Devise and now want to remove it, including all the files it has generated. How do I do that?
I'm looking at solving the same problem today and since this is not answered, giving it a go =)
Models
Devise generates a User model if you installed by default.
Remove the lines under devise. This is how mine looks like.
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable
In attr_accessible, you may remove email, :password, password_confirmation and remember_me if you no longer need them.
Views
A default Devise install doesn't generate views in your app folder. If you generated overriding views for Devise, you may remove them by running rails destroy devise:views (Rails 3).
Generally, all the views are stored in app/views/devise.
Controllers
By default, Devise doesn't generate any controllers too. If you did any overrides, they are most likely known as registrations_controller. Search your project for controllers that inherit Devise::RegistrationsController class.
Also, if you followed Devise's wiki and monkey-ed around to add redirect methods etc, look out for methods such as after_sign_in_path_for, store_location etc that are for redirecting users.
Migrations
If you installed Devise via its generators, look out for a migration create_users. If you don't need it anymore, use drop_table :users in a migration to get rid of it.
I'll assume most people would want to keep their User model. If you're using Devise < 2.0, the migrations are done by helpers. Once you remove Devise from the Gemfile, Rails will not understand the helpers below and throw errors, for instance, when you're trying to rerun these migrations on another box. These helpers are:
t.database_authenticatable
t.recoverable
t.rememberable
t.trackable
t.encryptable
t.confirmable
t.lockable
t.token_authenticatable # => becomes t.string :authentication_token
For the exact columns, the below is reference to the columns generated by Devise.
https://github.com/plataformatec/devise/wiki/How-To:-Upgrade-to-Devise-2.0-migration-schema-style
The guide above lists the fields generated by Devise using the helpers. You should be able to look through the list and your model (e.g. calling User in console), generate a migration that removes those columns.
BUT...
It's a little unfortunate that for consistency, we have to convert the migration to not use helpers using the guide above then generate a migration to remove them. This is for migration history consistency, otherwise anyone running the migrations might try to call the non-existent helpers. Also, your migration to remove the fields will also expect the fields to be present.
Alternatively, it might be a good time to squash the migrations and rely on schema.rb / structure.sql for the schema's to-date state. Even after deleting migrations, you can always recreate your development DB anytime using rake db:schema:load.
Initializers and Locale
Remove devise.rb in config/initializers and devise.en.yml in config/locales.
Routes
Remove any devise_for lines. These will raise errors after the removal of the gem.
Gem File
Yaay. All dome, remove the line gem 'devise' from your gemfile.
Use the generator to remove configuration files as well (step 2), so the whole process would be (referencing previous answers):
Remove the table: rake db:rollback VERSION=<insert the version number of the migration>
Remove the configuration: rails destroy devise:install
Remove your User model: rails destroy devise User (replace 'User' with the name of your model)
Remove references to devise in your routes.rb, gemfile, controller files, and view files like the following, if you used them (once again replacing 'user' with your model name):
devise_for (routes.rb)
gem 'devise' (gemfile)
before_action :authenticate_user! (controllers)
user_signed_in? (controllers, views)
current_user (controllers, views)
user_session (controllers, views)
In my case I had two models User and Admin and I am sticking with Devise, but I had a name collision issue with ActiveAdmin that requires me to remove the Admin model. But because there were so many references to Admin in devise, I had to take the steps below. I think it answers the original question above as well, though. I believe the correct way to do this is:
1.Find the devise migration for the user model and roll it back [IMPORTANT: IF you DON'T want to remove the user table associated with Devise, then SKIP THIS STEP]:
rake db:rollback VERSION=<insert the version number of the migration>
example:
rake db:rollback VERSION:20110430031806
2.Run this command to remove Devise and associated files.
rails destroy devise Admin (if Admin is the name of the model with user accounts).
This produces this output:
invoke active_record
remove db/migrate/20110430031806_devise_create_admins.rb
remove app/models/admin.rb
invoke test_unit
remove test/unit/admin_test.rb
remove test/fixtures/admins.yml
route devise_for :admins
3.To completely remove Devise, you need to remove all references to it in your models, controllers and views. This is manual work. The answer above provides good details for finding this cruft, but was incomplete for my purposes. I hope this helps someone else.
I found daemonsy's reply to be very helpful. Here are a few other things to consider as you do this.
Replacing Devise
If you are going to replace Devise with your own authentication, I recommend this Railscast: Authentication from Scratch (revised) (subscription required, but it's the best $9/mo you can spend).
And this Railscast (no subscription required) can help with a forgot password link and "remember me" option (things Devise offers out of the box, but that you can build pretty easily yourself): Remember Me & Reset Password
Tests
Before you do this, I recommend running all your tests to make sure they're passing.
After you remove Devise, your authentication-dependent tests will probably fail, so plan to spend some time fixing failing tests. This is a good thing because it will help you see what stuff "broke" when you removed Devise.
Make sure you check your test helpers as well. Most of my helpers were in /spec/spec_helper.rb. In fact, most of my failing tests began passing once I updated the methods in spec_helper.rb (eg, "login_user").
This worked for me!
1: rails d devise User This deletes the model and the routes.
2: rails d devise:install , This destroys devise.rb and
devise.en.yml files.
3: create a migration eg: rails g migration drop_user_table. I
used drop_table :users , force: :cascade, force: :cascade; takes
care of
PG::DependentObjectsStillExist: ERROR:
that occurs if other tables depend on the user table.

Resources