I am working on an application that I am wanting to authenticate through a custom oauth provider I have setup on a different server.
I am trying to integrate this custom login using RefineryCMS. I have my routes setup, but for some reason it is still trying to follow the devise route built into RefineryCMS.
Routes.rb
devise_for :users, :controllers => { :omniauth_callbacks => "users/omniauth_callbacks" }
devise_scope :users do
match '/users/auth/:provider', :to => 'users/omniauth_callbacks#passthru'
end
Link for callback
<p><%= link_to "Sign in with Olympus", user_omniauth_callback_path(:olympus) %></p>
Olympus is the codename for the project for now.
Omniauth callback controller:
class Users::OmniauthCallbacksController < Devise::OmniauthCallbacksController
def olympus
auth = env["omniauth.auth"]
ap auth
end
def passthru
render :file => "#{Rails.root}/public/404.html", :status => 404, :layout => false
end
end
The log via rails s once the link is clicked on:
Started GET "/users/auth/olympus/callback" for 127.0.0.1 at 2011-08-12 07:52:09 -0500
Processing by Devise::OmniauthCallbacksController#failure as
SQL (0.7ms) SHOW TABLES
SQL (0.6ms) SHOW TABLES
Page Load (0.3ms) SELECT `pages`.`id`, `pages`.`depth`, `pages`.`parent_id`, `pages`.`lft`, `pages`.`rgt`, `pages`.`link_url`, `pages`.`menu_match`, page_translations.title as page_title FROM `pages` INNER JOIN `page_translations` ON `page_translations`.`page_id` = `pages`.`id` WHERE `pages`.`draft` = 0 AND `pages`.`show_in_menu` = 1 AND `page_translations`.`locale` = 'en' ORDER BY lft ASC
Slug Load (0.1ms) SELECT `slugs`.* FROM `slugs` WHERE (`slugs`.`sluggable_id` IN (1,3) and `slugs`.`sluggable_type` = 'Page' AND (`slugs`.`locale` = 'en')) ORDER BY id DESC
Slug Load (0.2ms) SELECT `slugs`.* FROM `slugs` WHERE (`slugs`.sluggable_id = 3 AND `slugs`.sluggable_type = 'Page') ORDER BY id DESC
SQL (1.3ms) describe `roles_users`
Role Load (0.1ms) SELECT `roles`.* FROM `roles` WHERE `roles`.`title` = 'Refinery' LIMIT 1
SQL (1.4ms) describe `roles_users`
User Load (0.2ms) SELECT * FROM `users` INNER JOIN `roles_users` ON `users`.id = `roles_users`.user_id WHERE (`roles_users`.role_id = 1 )
Redirected to http://localhost:3000/users/login
So its still trying to go through the Devise::OmniauthCallbacksController through RefineryCMS... Does anyone know how to get around this or maybe even override this devise controller?
I also would like to say that I am completely new to working with oauth providers/clients. I have worked with Facebook and Twitter but doing them from scratch is something I am new at. As well as working with Devise/Omniauth. So excuse my ignorance. Thanks!
Fixed routing issue by reading this article: http://groups.google.com/group/refinery-cms/browse_thread/thread/37917e227b26f5ca
The issue I am having now, is that it appears to not be connecting to my oauth provider to do the authenticating :(
Related
I have currently set my app so that on successful sign in the app redirects the user to their profile at localhost:3000/users/id however if I am the first user id => 1 and type users/2 I have full access to this profile. I have been trying to find how to stop this using devise. I'm pretty new to rails so I'm sure I'm missing something simple, I have used the before_filter :authenticate_user! but this is obviously just checking if a user is signed in, but doesn't limit access to other users' profiles. I have read a bit on CanCan but this seems a bit overkill for what I am trying to achieve. Any pointers much appreciated.
users_controller.rb
class UsersController < ApplicationController
before_filter :authenticate_user!
before_filter :user_authorization
def index
#users = User.all
end
def show
#user = User.find(current_user[:id])
end
private
def user_authorization
redirect_to(root_url) unless current_user.id == params[:id]
end
end
This is being reported from the server:
Started GET "/users/2" for 127.0.0.1 at 2012-06-24 13:00:38 +0200
Processing by UsersController#show as HTML
Parameters: {"id"=>"2"}
User Load (0.1ms) SELECT "users".* FROM "users" WHERE "users"."id" = 2 LIMIT 1
User Load (0.2ms) SELECT "users".* FROM "users" WHERE "users"."id" = ? LIMIT 1 [["id", 2]]
Redirected to http://localhost:3000/
Filter chain halted as :user_authorization rendered or redirected
Completed 302 Found in 20ms (ActiveRecord: 0.8ms)
In your controller:
class UsersController < ApplicationController
before_filter :validate_user, :only => :show
def show
#user = User.find(params[:id]
end
def validate_user
redirect_to courses_path unless current_user.id.to_s == params[:id]
end
end
Notice the current_user.id.to_s since current_user.id is an integer and params[:id] is a string.
In general, I'd say there are two approaches to solving this kind of problem:
Rolling your own code and implementing checks in your controllers (or potentially in your model classes), and
using a gem that enforces rules for you.
If you want to role-your-own, the simplest way would be to simply put checks in your controller that makes sure they get redirected if they try to look at a profile that isn't theirs. One way to do this using a before filter is this, though you'd want to adapt it for the behavior that makes sense for your app.
before_filter :validate_user
def validate_user
redirect_to home_path unless current_user and current_user.id == params[:id]
end
If you want to use a gem, then I'd recommend cancan as you've mentioned or another gem called Acts as Tenant. I've seen it used for similar things. But if all you want is to lock down the user profile, adding code to the controller probably works fine.
And voilĂ :
before_filter :user_authorization
private
def user_authorization
redirect_to(root_url) unless current_user.id == params[:id]
end
current_user is an helper that contains current logged user.
I have a rails 3 application using devise with the subscribers model. I would like the subscribers to update their account information in place using best_in_place gem, but I can't seem to get it to update the database. It will update on the screen, but not the database.
The error sees to suggest the subscriber existing is a bad thing, but it isn't since it would be impossible to update that subscriber if it didn't exist.
All I want to do is be able to allow the users to update their information in place from a the subscriber/accounts#show page that shows them their details.
my subscriber/accounts controller has this:
def update
#subscriber = Subscriber.find(current_subscriber.id)
#subscriber.update_without_password(params[:subscriber])
end
I always get these results in the server log:
Processing by Subscribers::AccountsController#update as JSON
Parameters: {"subscriber"=>{"firstname"=>"Aarof"}, "authenticity_token"=>"Sx6kEQy4NC56ovikMs/D9nVPGJt1q5jNCoFnNjFhDu8=", "id"=>"2"}
Subscriber Load (1.9ms) SELECT `subscribers`.* FROM `subscribers` WHERE `subscribers`.`id` = 2 LIMIT 1
CACHE (0.0ms) SELECT `subscribers`.* FROM `subscribers` WHERE `subscribers`.`id` = 2 LIMIT 1
(0.2ms) BEGIN
Subscriber Exists (0.4ms) SELECT 1 AS one FROM `subscribers` WHERE (`subscribers`.`email` = BINARY 'frostsquid#yahoo.com' AND `subscribers`.`id` != 2) LIMIT 1
(0.1ms) ROLLBACK
Redirected to http://localhost:3000/subscribers/accounts/2
Completed 302 Found in 12ms (ActiveRecord: 2.6ms)
my show page looks like this:
%p.subscriber-account-info
=best_in_place #subscriber, :firstname, :path => subscribers_account_path(#subscriber)
My routes for subscribers looks like:
subscribers_accounts GET /subscribers/accounts(.:format) subscribers/accounts#index
POST /subscribers/accounts(.:format) subscribers/accounts#create
new_subscribers_account GET /subscribers/accounts/new(.:format) subscribers/accounts#new
edit_subscribers_account GET /subscribers/accounts/:id/edit(.:format) subscribers/accounts#edit
subscribers_account GET /subscribers/accounts/:id(.:format) subscribers/accounts#show
PUT /subscribers/accounts/:id(.:format) subscribers/accounts#update
I just checked trying to update subscribers firstname using the console and received the same message that the subscriber exists and it rolled the transaction back. Is this because of something in Devise?
Maybe a little the answer, but do you respond to JSON? here sample code from our project:
def update
respond_to do |format|
resource_files_service.update(#resource_file, params[:resource_file], current_user)
format.html { }
format.json { respond_with_bip(#resource_file) }
end
I have a User model and a Job_Category model, the Job_Category model
belongs_to :user
The User model
has_many :job_categories, :dependent => :destroy
I have a Dashboard Controller and am trying to display all the Job_Categories for a specific logged in User.
class DashboardController < ApplicationController
before_filter :authenticate_user!
def index
#user = User.find(params[:id])
#job_categories = #user.job_categories
##job_categories = JobCategory.all
#respond_to do |format|
# format.html # index.html.erb
# format.xml { render :xml => #job_categories }
# end
end
However, when I am trying to display this I get the error 'Couldn't find User without an ID' . I see this in the log:
Processing by DashboardController#index as HTML
User Load (0.3ms) SELECT "users".* FROM "users" WHERE ("users"."id" = 2) LIMIT 1
Completed in 22ms
ActiveRecord::RecordNotFound (Couldn't find User without an ID):
app/controllers/dashboard_controller.rb:9:in `index'
First of all, you cannot try to find by params in console, because there ain't such thing.
Try this in console:
User.find(2)
(..default column where find method looks the value is primary key which I assume is id as usual)
Then another thing, if you're already authenticating user in before_filter, you might already have #current_user or something set, are you using some authentication plugin like devise or something? So there's no need to pass current users id to dashboard controller. Application controller already knows who this user is and your dashboard controller is a subclass of it.
And ensure your job_category table has user_id column too.
I have a route setup like this:
match '/:url' => 'subjects#show'
In my Subjects controller I use
#subject = Subject.where("url = ?", params[:url].downcase).first
instead of
#subject = Subject.find(params[:id])
and this works just fine. The problem is that none of my validation work for the Subject model.
validates :url, :uniqueness => true
This above validation does not work and I get this but only when the url already exists:
SQL (0.5ms) BEGIN
Subject Load (0.3ms) SELECT `subjects`.`id` FROM `subjects` WHERE (`subjects`.`url` = BINARY '78') LIMIT 1
SQL (0.2ms) ROLLBACK
SQL (0.2ms) BEGIN
CACHE (0.1ms) SELECT `subjects`.`id` FROM `subjects` WHERE (`subjects`.`url` = BINARY '78') LIMIT 1
SQL (0.1ms) ROLLBACK
I get forwarded to the existing record's subjects/show as if everything's great. Otherwise, when the url is unique, the exact same SQL query does not get rolled back and the record is created.
Any ideas how I should tackle this? Is this related to my custom subject routes? I'm pulling my hair out. Here's the rest of my routes:
match '/auth/:provider/callback' => 'authentications#create'
match '/about' => 'pages#about'
match '/dashboard' => 'subjects#index', :as => 'user_root'
get "pages/home"
get "pages/about"
resources :authentications
devise_for :admins
devise_for :users, :controllers => {:registrations => 'registrations'}
resources :subjects do
member do
get 'stats'
get 'comments'
get 'qrcode'
get 'download_qrcode'
end
end
resources :traits
resources :ratings
resources :assets
match '/:url/stats' => 'subjects#stats'
match '/:url/remove' => 'subjects#remove'
match '/:url/comments' => 'subjects#comments'
match '/:url/edit' => 'subjects#edit'
match '/:url' => 'subjects#show'
root :to => "pages#home"
I think your routes file is OK.
Usually ActiveRecord rollback selects when you already have duplicated entries in your table, like multiple null or blank entries.
If you set the uniqueness validation after populate the table and you don't set an index into your migration file, probably that's the problem.
In this case, you can do:
Insert index into your migration file:
class CreateSubjects < ActiveRecord::Migration
def change
...
end
add_index :subjects, :url, :unique => true
end
Redo migrations and seed the table.
If this doesn't work, please send your migration file and active_record model.
So after searching for a tagging gem for my rails app I found the amazing acts-as-taggable gem. Installing it and playing around I discovered that it keeps all the Tags inside a tag db which just holds the Tag.name without the context, instead the context is held in the :through relationship db ( taggings ). For most purposes I can see this being perfect. Except with my app I want to be able to offer the user the ability to tag based on an pre-existing tags ( eg not allow them to create their own ) and acts-as-taggable doesn't have the ability to search all tags within one context built in ( eg, if I were to present an auto-completion of the tag db I would have all the tags in my app included which is'nt what I want )
The method below is what I just fleshed out to see if it would work ( which it does ) but I wondered if I was missing something with acts-as-taggable. I mean I can't see anywhere that offers this kind method?
<% ActsAsTaggableOn::Tagging.find_all_by_context("tags").each do |tagging| %>
<%= tagging.tag %>
<% end %>
If for instance acts-as-taggable doesn't do this, is this the best way to do this? It feels a little non performant, Would I be better doing a custom SQL query instead of routing through acts-as-taggable?
If it helps at all heres a tail of my log:
Started GET "/users" for 127.0.0.1 at 2011-01-04 14:46:20 +0000
Processing by UsersController#index as HTML
SQL (0.5ms) SELECT name
FROM sqlite_master
WHERE type = 'table' AND NOT name = 'sqlite_sequence'
User Load (0.1ms) SELECT "users".* FROM "users"
ActsAsTaggableOn::Tagging Load (0.5ms) SELECT "taggings".* FROM "taggings" WHERE ("taggings"."context" = 'languages')
ActsAsTaggableOn::Tag Load (0.1ms) SELECT "tags".* FROM "tags" WHERE ("tags"."id" = 2) LIMIT 1
Rendered users/index.html.erb within layouts/application (10.4ms)
You could also use a statement like the following:
# Returns all the tags for the specified model/context with a count >= 1
#tags = YourModel.tag_counts_on(**context**)
Add limit and order:
# Get the top 5 tags by count
#tags = YourModel.tag_counts_on(**context**, :limit => 5, :order => "count desc")
Access the counts with the count attribute of the tags returned from tag_counts_on
tag.count
I believe there is the way:
User.tag_counts_on(:tags)
=> [#<ActsAsTaggableOn::Tag id: 1, name: "foo">,
#<ActsAsTaggableOn::Tag id: 2, name: "bar">,
#<ActsAsTaggableOn::Tag id: 3, name: "sushi">,
#<ActsAsTaggableOn::Tag id: 4, name: "pizza">]