I would like to use a web server based on Rails.
But I have no idea about how to check a user's identification.
For example, a user named Guest could only perform actions like GET and UPDATE on certain tables, while another user named Admin could perform all possible actions such as POST.
I am new to this area, but I heard there are some technicals like SQL injection could threaten the security of the web server.
So, could you tell me how to check the authentication and how to encrypt password entered by the user?
What you seem to be wanting is authentication and authorization.
For authentication:
https://github.com/plataformatec/devise
https://github.com/vpereira/authlogic
https://github.com/thoughtbot/clearance
For authorization:
https://github.com/be9/acl9
https://github.com/ryanb/cancan
This is strictly speaking out of my personal experience. I have tried all of the suggested authentication and authorization gems mentioned above, but I always came to the conclusion that its not more or less work to just write it yourself, especially when your requirements a very simple. Consider this:
class ApplicationController < ActionController::Base
before_filter :authentication
def authentication
redirect_to '/authentication_form' unless session[:logged_in]
end
def authentication_form
... render the form
end
def login
if params[:username] == 'adam' && params[:password] == 'eva'
session[:logged_in] = true
redirect_to '/restricted_area'
else
render :action => 'authentication_form'
end
end
end
class RestrictedController < ApplicationController
def index
... this action is now restricted
end
end
This is not complete, of course but it demonstrates how easy authentication can be with rails. Instead of checking users and passwords through controller code, you could query the database like this:
if User.find_by_name_and_password(params[:username], params[:password])
session[:logged_in] = true
...
For authorization you would have to save the users identity within the session hash which allows you to restrict access from within every action (provided the controller is a derived from ApplicationController)
I hope, this helps.
Related
I would like to protect a site with a very simple password only validation when user first visits site. I currently use http authentication, but that requires a username & password. I can hardcode password in back end. Basics of site: local sports league where we keep our stats and info about league. Simply trying to keep "riff-raff" out :)
I am a ruby on rails newbie, and am using this site as a way to learn. Any help out there would be appreciated!
You could do something cookie-based.
In your ApplicationController, you'd implement a method for determining if the cookie is present that states that the visitor has entered your password – if the cookie isn't present, then you'll redirect to your password page:
class ApplicationController < ActionController::Base
def require_password_verification
unless cookies[:visitor_password_verified]
return redirect_to <whatever your passwords#new path is>
end
end
end
The controller for your password page would look something like this:
class PasswordController < ApplicationController
def new
# Nothing needed here because all your #new view needs is a password field
end
def create
unless params[:password].present?
return redirect_back(fallback_location: root_path, alert: 'Password is required.')
end
if params[:password] == Rails.configuration.visitor_password
cookies[:visitor_password_verified] = true
redirect_to(root_path, notice: 'Password verified.')
else
cookies.delete(:visitor_password_verified)
redirect_back(fallback_location: root_path, alert: 'You've entered the wrong password.')
end
end
end
Your password would be stored in the application.rb file, like so:
config.visitor_password = '12345'
Normally, you would never store a password in this way because it's not secure at all but considering your use case, it's probably fine, since having a single password for everybody is already not secure. 😃 However, if you did want to step up the security a notch, I would recommend storing your password in an environment variable, and then you could set the password like so:
config.visitor_password = ENV['VISITOR_PASSWORD']
That way, at least your password isn't hard-coded and accessible to anybody who looks at your, assumedly public, repo.
And then you can require the "password has been entered" cookie for whatever views you want like so:
class LeagueStatsController < ApplicationController
before_action :require_password_verification
def index
# Whatever
end
end
If somebody hits your league_stats#index page, then it's going to check to make sure the visitor_password_verified cookie is present and true first. If it is, then they'll get through to the view. If it's not, they'll be redirected to your passwords#new page.
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 want to design an API in Rails that requires actions like Create, Update and Delete to be readonly for certain controllers, and open to the public for others (eg, comments on an article should be open but editing that article should require API authentication)
I know how to do the authentication part, what I don't know how to do is the "read only" part or the "you have permission to create a comment but not delete it" part.
Does any one have any resources, tips, tricks or github repositories that do this or something similar to this?
You are needing to do authorization. Look at Pundit for a scalable solution https://github.com/elabs/pundit
I had an app for a while that only needed a little bit of control as there were only a few methods on 2 controllers that were limited. For those i just created a before_filter and method to control the authorization.
The code below would allow everyone to do index and only allow users with a role attribute that has a value of "admin" to do any other action in the controller. You can also opt to raise an unauthorized error or raise an error message instead of redirecting. There are articles (probably books) written on the security side of the house for whether you should give users notice if they are not authorized to do something (which means they can infer that there is something there that someone can do at the uri)
SomeController < ApplicationController
before_filter check_authorized, except [:index]
def index
....stuff that everyone can do
end
def delete
....stuff only admin can do
end
private
def check_authorized
redirect_to root_path unless current_user.admin?
end
end
Of course you will need devise or a current_user method and a method on user that checks admin
class User < ActiveRecord::Base
def admin?
if self.role == "admin"
true
else
false
end
end
end
I'm new to rails and are have a pretty basic understanding of the Devise Gem. Besides the CRUD and views I'm not clear on what it provides that could help me for a AngularJs app talking to a Rails Json Api.
At the moment I'm hand rolling things ie. for security I have I exchange a HTTP Header token between client (js) and server. I'm also using the Railscast #250 for user authentication - but as I don't see how to apply the SessionController for a remote client.
Are there any strategies I could employ for authentication and managing session via a remote json API?
Thanks!
I personally wouldn't use devise for something like this because there's only a small part of it you'd be using anyways
Dont
You pretty much just don't use a session. All you need to do is pass in basic authentication each time, and in the application controller you determine if its valid, if not just send them back an auth error.
Example request: http://username:password#example.com/api/endpoint
class ApplicationController
before_filter :check_auth!
private
def check_auth!
username, password = ActionController::HttpAuthentication::Basic::user_name_and_password(request)
user = User.find_by(username: username)
if user && user.encrypted_password == SomeEncryptFunction(params[:password])
#current_user = user
else
raise "error"
end
end
end
But if you want to...
Then what you can do is update a DateTime field on the user when they first auth (which starts their session), then on subsequent calls they can just pass a token you give them that you you check for each time they sign in. You also check that only a certain amount of time has passed since they first authed, otherwise their session is invalid.
class SessionsController < ApplicationController
skip_before_filter :check_auth!
before_filter :login!
private
# Note: I don't remember the actual devise method for validating username + password
def login!
user = User.find_by(username: params[:username])
if user && user.valid_password(params[:password])
current_user = user
current_user.update_attributes(
authenticated_at: DateTime.now,
authentication_token: Devise.friendly_token
)
else
raise "error"
end
end
end
class ApplicationController
before_filter :check_auth!
private
def check_auth!
if valid_token(params[:token])
current_user = User.find_by(authentication_token: params[:token])
else
raise "error"
end
end
# Returns true if token belongs to a user and is recent enough
def valid_token(token)
user = User.find_by(authentication_token: params[:token])
user && user.authenticated_at < DateTime.now - 1.day
end
end
I am fairly new to Ruby On Rails and right now I am doing a simple app. In this app a user can create many items and I use devise for authentication. Ofcourse I want to make sure that you are the owner in order to delete items (Teams, Players etc) and the way I do it now is:
def destroy
#team = Team.find(params[:id])
if current_user.id == #team.user_id
#team.destroy
redirect_to(teams_url, :notice => 'The team was deleted.')
else
redirect_to root_path
end
end
Is this the best way? I was thinking about putting a method in the model but I am not sure I can access current_user from there. I was also thinking about a before_filer, something like:
before_filter :check_ownership, :only => [:destroy, :update]
I that case and if I want to code only one method for all objects (all objects this relates to have a "user_id"-field)
In my application controller I put:
before_filter :authorize
def authorize
false # Or use code here to check if user is admin or not
end
Then I override the authorize method in my actual controller to allow access to various actions.
You're looking for an authorization solution on top of your authentication (devise)
You can't access current user in the model no. I've had a fair amount of success using Makandra's Aegis for authorization. It allows you to create roles specify permissions attributed to each role. The docs are pretty good and I know it works fine with Devise, it's pretty agnostic that way, I've also used it with Clearance. It also passes an implicit "current_user" to your permissions so you can specify and check that your current user can take appropriate actions.