I'm using FB and G+ for authentication. How do I make the first user that is registered an admin? I've looked at a few answers like this one, but I get errors (like "undefined method `update_attribute' for nil:NilClass" for the previous linked answer) and they are mostly old and for devise gem.
users_controller
def update
#user = User.find(params[:id])
if #user.update(user_params)
redirect_to #user
else
render 'edit'
end
end
private
def user_params
params.require(:user).permit(:admin, :banned)
end
This should be done in the db/seeds.rb file which you can execute with rake db:seed. The linking with Facebook and G+ will have to be done after the fact. I would not depend on any solution that automatically promotes the first user to admin status. That should only ever done explicitly.
Example seeds.rb:
# This example is using Devise, however it can easily be adapted to whatever
# setup you require as there is nothing "devise" specific here.
user = User.find_or_create_by(email: "Admin Email")
user.password = "Admin Password"
user.admin = true
user.save
Another tool I use is by registering a user normally and then manually promoting them to Admin status via a tool like rails console.
EDIT
If you still wish to pursue your method (which I consider unsafe) then you can use ActiveRecord hooks like so:
class User < ActiveRecord::Base
# relations and stuff
before_save :check_to_make_admin
private
def check_to_make_admin
# Only the first user should be made admin
if User.count == 0
self.admin = true
end
end
end
It's important to note that I strongly discourage this approach and recommned using the seed method which was created for purposes like this.
Related
So in my ruby on rails project, I want to track how many times a user has logged in. I am using devise for authentication. Right now, I think what I have is sufficient for my current project, I am just having an issue incrementing the login_count column from my db.
Application.rb snippet:
def after_sign_in_path_for(resource)
if current_user.login_count == 0
new_user_profile_path(user_id: current_user.id)
else
root_path
end
User.user_id.increment_counter(:login_count, 1)
end
Right now User.user_id.increment_counter(:login_count,1) gives me undefined method `to_model' for 1:Integer Did you mean? to_yaml.
You’d be much better off providing a custom SessionsController and modifying the create action to perform the actions you require.
Use the devise generator to create controllers for you to work with, if you haven’t already. Then in your sessions_controller.rb add something like this:
def create
super do |resource|
# at this point, resource is your logged in user (or whatever model)
resource.class.increment_counter(:login_count, resource.id)
end
end
I'm working on google authentication for a rails app. Currently using the omniauth-google-oauth2 gem to implement Google auth. I've managed to have users sign in using google. However, I'd also like users to be able to sign up using google. My problem is that I've matched the google callback URL to a particular controller action (sessions#create).
Is it possible to choose between 2 redirect URIs based on whether users are signing in or signing up? Currently, my only idea is to create new google client credentials to be used for sign up, I hope there is a better way.
You don't need to have 2 redirect uris, you just need to do some more work when receiving the callback. For instance:
class SessionsController < ApplicationController
...
def create
email = auth_hash['info']['email'] # assuming your omniauth hash is auth_hash and you're requiring the email scope
#user = User.find_by(email: email) if !email.blank? # assuming your user model is User
if #user
login_user(#user) # use your login method
elsif !email.blank?
#user = User.new(name: auth_hash['info']['name'], email: email)
unless #user.save!(validate: false) # validate false because I'm enforcing passwords on devise - hence I need to allow passwordless register here)
# deal with error on saving
end
else
# deal with no found user and no email
end
end
protected
def auth_hash
request.env['omniauth.auth']
end
end
I've written all steps but the creation process can be shortened to:
#user = User.create_with(name: auth_hash['info']['name']).find_or_initialize_by(email: email)
#user.save! if #user.new_record?
if #user
login_user(#user)
else
# deal with no user
end
Nonetheless, you can't be sure the user is going to give you scope access to the email, so personally I think the first version, even if a bit lengthier is more robust. Then on the shorter version there's also the problem of, if #user is false, why is so? And will require you to add more logic to figure out why is that, whereas in the first one it's much easier to apply the correct response to each situation.
I am looking to set a secondary password by which I can authenticate a user for a login as from admin. The reason for this work around is the front end is a single page application.
Each user has been given a unique login_as string. now I need to configure Devise to compare the login_as if the password fails.
Any help is appreciated. I am of course open to an alternative solution if there is a better way.
Thanks.
This post from Duncan Robertson was very helpful in solving my issue. I essentially created an override strategy and called it in the devise.rb file. I had some concern regarding tampering with a large user base but it has proved successful. By adding a column to users named ":signin_as" and then setting it to a default unique string with a rake I then had what I needed to fallback on if the initial sign in failed.
the override strategy (config/initializers/auth_override.rb)
module Devise
module Strategies
class AuthOverride < Authenticatable
def custom_auth(user, signin_as)
if user[:signin_as] == signin_as
return true
else
return false
end
end
def authenticate!
user = User.find_by_email(email)
if user
if user.valid_password?(params[:password])
success!(user)
elsif custom_auth(user, params[:password])
success!(user)
else
fail
end
else
fail
end
end
end
end
end
including the strategy in devise (config/initializers/devise.rb)
config.warden do |manager|
manager.default_strategies(:scope => :user).unshift :auth_override
end
while doing admin work, i'd like to disable user logins --
is there some way to use devise for this -- I don't THINK this
is suitable for rolify -- because this is a temporary disablement --
thanks in advance for any help,
rick
Back-End
If you wanted to create a "maintenance" mode, you'll be best doing something like this:
#app/models/user.rb
class User < ActiveRecord::Base
end
#app/models/admin.rb
class Admin < User
def maintainance!
self.toggle! :maintainance
end
end
This will need a maintenance column in the users table, and you'll have to add a type column in the users table, too.
You could get away with keeping this in the User model, however, you'd need some conditions to determine whether the user is an admin. Since you didn't specify how you're differentiating, above is how we do it.
--
You'd be able to call it like this:
#app/controllers/users_controller.rb
class SettingsController < ApplicationController
before_action :authenticate_user!
def maintenance
current_user.maintenance! #-> toggles so you'll just be able to call this as you need.
end
end
#config/routes.rb
resources :settings, only: [] do
put :maintenance #-> url.com/settings/maintenance (considering current_user present)
end
This will allow you to set the "maintenance" mode through your user settings area. If you don't have one, you'll be able to use the above code to get it working.
Front-End
With the backend in place, you'll be able to then manage the front-end.
To do this, you'll need a helper to determine if any user has set the "maintenance" mode...
#app/helpers/application_helper.rb
class ApplicationHelper
def maintenance_mode?
Admin.exists? maintenance: true
end
end
This will allow you to use this helper to determine whether you should allow Devise to accept logins or not:
#app/views/devise/sessions/new.html.erb
<% unless maintenance_mode? %>
... devise form ...
<% end %>
The helper will execute a DB request, but keeping it in the devise areas only (IE it's not "site wide") should make it okay.
#app/controllers/devise/sessions_controller.rb
class SessionsController < Devise::SessionsController
before_action :check_maintenance
private
def check_maintenance
redirect_to root_path, notice: "Sorry, maintenance mode is in effect; no logins." if maintenance_mode?
end
end
This will prevent any controller-based actions from firing.
Finally, if you want to get rid of any logged-in users, you'll need to do something quirky, like resetting the sessions or something similar:
How can I reset all devise sessions so every user has to login again?
Devise force signout
Here's what I'd do:
1. Create a method for your User model. It could be something like active, or able_to_login.
2. Set this attribute to :boolean.
3. Use rails console. Use the console to set the active method to true or false, enabling or disabling your users to access your application:
user = User.all
user.each do |u|
u.active = false # or
u.able_to_login = false
u.save
end
I don't think this is the best method, but it should work without installing another gem or heavy code.
In your /models/user.rb add this method
def active_for_authentication?
super && is_admin?
end
def is_admin?
# returns true if user is admin
end
This is the "Devise way" of doing this :)
Using Devise gem :-
in my application i have two type user
1) Company
2) Employee
i have two separate model for both user and i want to give single sign-in page for both user
i already generate different views for both user using devise but then also i need single sign-in.
so how it is possible please help me out...!!!!!!!!!
i try this:-
controllers/compnaies/sessions_controller.rb
def create
#employee = Employee.new
#company = Company.new
if #company.email_exist?(params[:company][:email])
super
elsif #employee.email_exist?(params[:company][:email])
params[:employee]=params[:company]
redirect_to employee_session_path(params)
end
end
employee_session_path is the path of employees's sessions create method but it always call new method of employees's sessions
controllers/employees/sessions_controller.rb
def create
super
end
email_exist? method check the email id in both model which is provided by any of user
in my model:
company.rb
def email_exist?(email)
if Company.find_by(email: email)
return true
end
end
employee.rb
def email_exist?(email)
if Employee.find_by(email: email)
return true
end
end
Thanks in advance...!!!!!!!!
CanCan and Rolify seems to be the combination stack you are looking for to solve your problems here are the link to the github pages:
Rolify https://github.com/RolifyCommunity/rolify
CanCan https://github.com/ryanb/cancan and the Railscast http://railscasts.com/episodes/192-authorization-with-cancan
Let me know if you need any help