A lot of the questions you're asking are very application specific.
What constitutes secure is ultimately only something you can answer,
and on an application by application basis. That being said, I'll try
to talk through some of the answers and questions you might want to
consider asking.
First, storing username and password in the code is fine. As long as
no-one untrusted has access to the code. And it has scalability
issues if you want more than one admin. And it requires a code edit
and a reboot to change the password. But for all those limitations,
it's as secure as the source code. How secure that is, depends on
things like directory permissions for the deployment, and is way
beyond the scope of sinatra.
But to get beyond this very simple method of authentication, since the
rest of your email suggests you want a richer authentication and
authorisation model. One which supports multiple authenticated users,
with differing permissions through roles. It also seems that the
admin(s) is responsible for signing up new users, rather than the more
usual (for many public facing webapps) 'anyone can sign up'. But all
these are doable, though they might require a certain amount of code
writing by yourself. I'm not aware, off-hand, of a sinatra solution
for role-based authorisation.
So, first, the user model. This should ideally store the password
salted+hashed. Probably using BCrypt (see http://is.gd/eWa11 for some
reasons why this is a good idea). That way, if the application
database (or backup) is compromised, people's passwords arn't spilled
all over for the world to see. As an aside, I believe Padrino by
default stores user passwords in the DB with reversible encryption,
which I don't particularly agree with. I'd much rather get a password
reset email than my original emailed back to me. And it seems a
little unlikely that only a database would get breached, while the
application code stays safe. But, whichever. The ability to email
passwords back, and have admins look at passwords, might be
appropriate for your application.
Next, creating the admin user. This is another one where what is
appropriate is up to you. For some deployments, first account is
admin will work fine. Depending on what other configuration is
possible via the web interface, you might even want to consider
something like an 'install' action, which guides the admin through
setting up their account, and configuring other details about the
site. For others, you might want to create the first user via some
kind of system task, run from the console. I'd suggest against
creating directly from the database console, since if you do you'll
have to manually hash the password, and set whichever other fields are
appropriate.
At that point, it's then mostly up to you to set things up
appropriately with regards to the permissions used to set up accounts.
Many of the prebuilt auth solutions you mentioned have a way of
checking if a/which user is signed in. By checking those credentials
in your 'sign up' action, you can deny guests the right to set up an
account or not, as you please.
I hope my writings on the subject help,
Regards,
Jon
class MyApp < Sinatra::Base
def login_required!
if session[:user].nil?
redirect '/login'
end
end
# A nice helper to have... returns a User object, or nil
def current_user
if session[:user]
User.find(session[:user])
end
end
get '/admin' do
login_required!
haml :admin
end
# Ask for username/password
get '/login' do
haml :login
end
post '/login' do
user = params[:user]
password = params[:password]
u = User.find(:first, :conditions => {:username => user})
if u.authenticate?(password)
session[:user] = u.id
else
haml :login
end
end
end
class User < ActiveRecord::Base
# fields needed are:
# username
# encrypted_password
def authenticate?(password)
# don't wanna look up how to actually call bcrypt. Also, if you want to be
# super secure, add a per-user salt... like their created_at attribute
self.encrypted_password == bcrypt(password)
end
# Other useful stuff, like changing password:
def password=(password)
self.encrypted_password = bcrypt(password)
end
end
> --
> You received this message because you are subscribed to the Google Groups "sinatrarb" group.
> To post to this group, send email to sina...@googlegroups.com.
> To unsubscribe from this group, send email to sinatrarb+...@googlegroups.com.
> For more options, visit this group at http://groups.google.com/group/sinatrarb?hl=en.
>
BCrypt actually includes a salt (randomly generated) in the hash which
is stored in the DB. So you don't have to worry about that.
class User < ActiveRecord::Base
# fields needed are:
# username
# encrypted_password
def authenticate?(password)
# bcrypt overrides == for an instance to allow this to work.
BCrypt::Password.new(self.encrypted_password) == password
end
# Other useful stuff, like changing password:
def password=(password)
self.encrypted_password = BCrypt::Password.create(password)
end
end
Just to add the correct BCrypt calls. Thanks for actually putting some code
to the stuff I was talking about :)
Regards
Jon
On 5 September 2010 19:08, Chris Schneider