Devise Invitable - Restricting Access to Only invited users - ruby-on-rails

I might be searching the wrong key words, but I can't find a way to only restrict users that are invited to be allowed to create an account on my rails site (or maybe I'm using devise-invitable wrong). I assume, there should be a method I call in the before filter or flip the switch on the initializer/devise.rb
I tried in my users_controller.rb and had no luck using Ruby Doc as reference
before_filter: invited?
I have read the initializers/devise.rb and the readme and didn't have any luck.

I think that you should make custom filter for this purpose.
before_action :authenticate_user!
before_filter :restrict_only_invited_users
def restrict_only_invited_users
redirect_to :root if current_user.invitation_accepted_at.blank?
end

I figured out that I didnt think of the obvious. Restrict users via devise with the routes, not devise-invitable.
Used Solution #2 as a reference (see below)
Lets suppose that you don't want to allow to sign up but you want to allow to change password for registered users. Just paste this code in routes.rb:
devise_for :users, :skip => [:registrations]
as :user do
get 'users/edit' => 'devise/registrations#edit', :as => 'edit_user_registration'
put 'users/:id' => 'devise/registrations#update', :as => 'user_registration'
end
And then you can make such link in your view:
= link_to "Change your password", edit_user_registration_path
Notice: you will need to update default devise views accordingly, i.e. in app/views/devise/registrations/edit.html.erb change registration_path(resource_name) to user_registration_path(resource) (If using shared views for multiple models, you can use send("#{resource_name}_registration_path", resource))
Notice: If you are using rails 4.0+ you should be using patch instead of put for updates. You should change the method in the form_tag residing in app/views/devise/registrations/edit.html.erb and the routes.rb file.

Related

Devise multiple concurrent logins

I have an existing Rails 3 site. This site uses Devise to log in.
Now I need to add a subsite with a separate login. Let's say that it is at mysite.com/special. Everybody that go to /special need to sign in with a different set of credentials than the main site. These credentials are not valid for the main site but they must, in parallel, be able to log into the main site and stay logged in both.
To complicate the picture a bit, 'special' users are able to impersonate a subset of regular users. If they do impersonate, sign out of the impersonation and so forth, they must remain logged into the special site until they explicitly sign out there.
My initial idea was to use a separate session cookie for this entire subsite but I don't know how to do that. I tried setting something like this up but it is almost guesswork at this point.
scope '/special' do
devise_for :special_users, :controllers => { :sessions => "special/sessions" }, :skip => "registrations", :module => "special_users"
unauthenticated do
as :special_user do
root :to => 'special/sessions#new'
end
end
end
Unfortunately, I'm a bit new to Rails and this seems to be rather complicated. Is it even possible with Devise and if so, do anyone have any pointers as to how to go about it?
As it turns out, this is what scopes are for. Everything makes much more sense when you know what they call it ;) Anyway, Qumaras' comment above was actually on the right track), and the tutorials and wikis are generally good sources of information. One possible approach:
devise_for :spuser, :controllers => { :sessions => "sp/spusers/sessions"}
as :spuser do # as = equiv. to devise_scope
# add spuser specific items here, such as:
unauthenticated :spuser do
root :to => 'sp/spusers/sessions#new'
end
end
Then if you want to sign in another tab as a regular user, you could do this in a controller.
def impersonate
#user = User.first
sign_in(:user, #user)
redirect_to '/main'
end
And wire the impersonate action up in routes.rb
resources :regularcustomers do
member do
get :impersonate
end
end
and then you can call it in a view:
<%= link_to #customer.name, impersonate_regularcustomers_path(#customer), :target => 'impersonationtab' %>

How does one use a rails admin account outside of rails admin to edit user data?

I would like to create a new controller that will allow the admin user to edit models attached to users outside of rails admin. Here is a sample from my routes.rb:
devise_for :admins
mount RailsAdmin::Engine => '/admin', :as => 'rails_admin'
devise_for :dealers
devise_for :users
get "..." => "..."
etc
What do I need to do in both my controller and in routes.rb to make this possible? I'm fairly new to Rails. Thanks! I have a limited amount of time so making a rails-admin plugin is not feasible.
In your controller check if an admin is loged in using admin_signed_in?, and you can also access it by current_admin. So you just have to place conditions checking if it's and admin to enable or not those functionalities.
EDIT:
You can have something like this in your before_filter
before_filter :check_authentication
private
def check_authentication
authenticate_user! unless admin_signed_in?
end

Rails Devise after_sign_in_path_for(resource) method not functioning as expected

I want to redirect an inactive user to the registration path to collect some information. Here are two approaches I took but neither is working:
I overrode the devise after_sign_in_path method as follows (in application_controller.rb):
def after_sign_in_path_for(resource)
debugger
if(account_active)
return root_path;
else
return edit_user_registration_path(resource)
end
end
When I hooked the code upto debugger, I see that devise does call after_sign_in_path_for. Also, the correct url is being generated by this call:
(rdb:2) after_sign_in_path_for(resource)
"/users/edit.1"
However, when I look at the server logs, there is no attempt being made to redirect to "/users/edit.1" under any circumstances.
I have tried moving the above method to application_helper.rb, session_controller.rb (by extending Devise::SessionController) and session_helper.rb
The issue is that devise does call this method to retrieve the url but it never attempts the redirect. I checked the web server logs, and devise directly goes to the user_root url.
Here is the relevant devise configuration from routes.rb:
devise_for :users do
resource :registration,
only: [:new, :create, :edit, :update],
path: 'users',
path_names: { new: 'sign_up' },
controller: 'devise/registrations',
as: :user_registration do
get :cancel
end
root :to => "home#index"
end
match '/user' => "products#index", :as => 'user_root'
Any suggestions on what I should try?
Thanks,
Tabrez
Are you sure you want to redirect to /users/edit.1? Rails will pick that up as if you're trying to access the 1 mime-type instead of html.
The user registration path doesn't need an id, because it always belongs to the currently signed in user. This should be enough:
def after_sign_in_path_for(resource)
if account_active
root_path
else
edit_user_registration_path
end
end
Also, placing it in the ApplicationController is the right spot. If you have your own sessions controller, like Users::SessionsController, which inherits from Devise::SessionsController, than it can go in there too.
So either the account_active method doesn't do what you think it does, or you've screwed up the routes file. Try working with a more vanilla configuration in your routes to see if that is the case:
devise_for :users
PS. as a complete an utterly unrelated side note: please try to use Ruby coding conventions, like no semicolons when they're not needed, no parenthesis in if statements, 2-spaces indenting and no unneeded return statements.
This may not apply to you, but in my recent use of devise + active_admin, I ran into the same problems you are describing. I added the devise override while my development rails server was running, and assumed rails/devise would automatically pick up the method. Apparently not since the problem was solved when I restarted my server.
It seems that devise is cherry picking these methods off ApplicationController when it initializes, though I havent looked at the source.

How do you navigate to a users show page with devise?

Like the title says, I am using devise. I want to navigate to /users/1 or whatever the current_users path is. When I try link_to(current_user.name, user_path) I just get an error.
undefined local variable or method `user' for #<#:0x00000101d2dfc8>
Am I not using the right path? Do I need to route it or something?
Also, this goes with the first, how would i navigate to a different users show page(not the current user)
You will need to to three things to get the view you want because devise doesn't provide the standard user routes.
1) Create a route in config/routes.rb. Devise doesn't provide a standard route, and in my experience rake routes just confirms this. I've used:
match '/users/:id', :to => 'users#show', :as => :user, :via => :get
with mixed success
2) Create a user controller with the show section appropriately filled out
3) create a view file: user/show and add the parts you want to show.
I am not familiar with devise, but I'd be surprised if devise differed from Rails conventions. You need to pass in the user to the route helper: link_to(current_user.name, user_path(current_user))
What helped for me was to add the # before user in "routes.rb":
"match 'users/:id' => 'users#show', :as => #user"

Route depending if user is an artist

I'd like to set up my routes depending if the user is an artist. So for example, something like:
namespace 'dashboard' do
if current_user.is_artist
get '/settings', :to => 'users#edit', :as => 'account_settings'
put '/settings', :to => 'users#update', :as => 'account_settings'
delete '/settings', :to => 'users#destroy', :as => 'account_settings'
else
get '/settings', :to => 'artists#edit', :as => 'account_settings'
put '/settings', :to => 'artists#update', :as => 'account_settings'
delete '/settings', :to => 'artists#destroy', :as => 'account_settings'
end
end
Unfortunately, I cannot access current_user in routes.rb. However, the logic of the code above, explains what my intentions are.
Is there a way I can achieve this?
The routing table doesn't run at the context of a specific user. This is because it routes the request before reaching your session management code, so it can't tell who's the current user.
I can think of two solutions:
Render your settings links through some method that rewrites them if the user is an artist. This way 'artists' and 'users' get different URLs.
Add a before_filter to your UserSettings controller that catches requests from artists and redirects them to ArtistSettings (not sure of your internal structure, but you probably get the general idea).
from what you have it looks like you have set up a role called artists. all you have to do to make your current_user method work is: in your account controller you can set up a before filter like saying before_filter :check_user_role :only => :account_setting. then you would set your account_settings for the various roles and their various redirects_to call methods you have. i am working on a similar project and it works for me. i hope this helps you
You could create a piece of middleware that figures out the user on a level before it hits the actual application. This is what warden/devise are doing for example. Add the user object to the env variable and you can access it anywhere lower in the stack.
Your routing file simply determines which controller action is being invoked based on the URL. You can map both artists and users to a single controller, and place the logic for which screen to render in that controller by looking at the user. By default, controllers render a view that is based on the name of the controller and method, but you're free to override this and render whatever view you want. Use your route to dispatch requests to the appropriate edit/update/destroy actions, and put your user/artist logic into those actions to render either the users/edit.html.haml or the artists/edit.html.haml file...
As explained by Elad, the routing itself can not tell if the current user is an artist or not.
If you want to keep a single url with two differents behaviours and views, you may go with a single controller such as :
def settings
if current_user.is_artist
do something
render :action => "artist_settings"
else
do something else
render :action => "users_settings"
end
end

Resources