end
def getuserid(token)
- token=sqlescape(token)
- if (token=~/^(.+)\+(.+)$/) then
- return ActiveRecord::Base.connection.select_value("SELECT id FROM users WHERE active=1 AND email='#{$1}' AND pass_crypt=MD5('#{$2}')")
+ if (token =~ /^(.+)\+(.+)$/) then
+ user = User.authenticate(:username => $1, :password => $2)
else
- return ActiveRecord::Base.connection.select_value("SELECT id FROM users WHERE active=1 AND token='#{token}'")
+ user = User.authenticate(:token => token)
end
+
+ return user ? user.id : nil;
end
class ApplicationController < ActionController::Base
def authorize_web
- @user = User.find_by_token(session[:token])
+ if session[:user]
+ @user = User.find(session[:user])
+ elsif session[:token]
+ @user = User.authenticate(:token => session[:token])
+ session[:user] = @user.id
+ end
end
def require_user
if username.nil?
@user = nil # no authentication provided - perhaps first connect (client should retry after 401)
elsif username == 'token'
- @user = User.authenticate_token(passwd) # preferred - random token for user from db, passed in basic auth
+ @user = User.authenticate(:token => passwd) # preferred - random token for user from db, passed in basic auth
else
- @user = User.authenticate(username, passwd) # basic auth
+ @user = User.authenticate(:username => username, :password => passwd) # basic auth
end
# handle authenticate pass/fail
- if @user
- # user exists and password is correct ... horray!
- if @user.methods.include? 'lastlogin' # note last login
- @session['lastlogin'] = user.lastlogin
- @user.last.login = Time.now
- @user.save()
- @session["User.id"] = @user.id
- end
- else
+ unless @user
# no auth, the user does not exist or the password was wrong
response.headers["Status"] = "Unauthorized"
response.headers["WWW-Authenticate"] = "Basic realm=\"#{realm}\""
response.headers['Error'] = message
end
+private
+
# extract authorisation credentials from headers, returns user = nil if none
- private
def get_auth_data
if request.env.has_key? 'X-HTTP_AUTHORIZATION' # where mod_rewrite might have put it
authdata = request.env['X-HTTP_AUTHORIZATION'].to_s.split
lastfile='-1'
if params['token']
- token=sqlescape(params['token'])
+ user=User.authenticate(:token => params[:token])
sql="SELECT gps_points.latitude*0.000001 AS lat,gps_points.longitude*0.000001 AS lon,gpx_files.id AS fileid,UNIX_TIMESTAMP(gps_points.timestamp) AS ts "+
- " FROM gpx_files,gps_points,users "+
+ " FROM gpx_files,gps_points "+
"WHERE gpx_files.id=gpx_id "+
- " AND gpx_files.user_id=users.id "+
- " AND token='#{token}' "+
+ " AND gpx_files.user_id=#{user.id} "+
" AND (gps_points.longitude BETWEEN #{xminr} AND #{xmaxr}) "+
" AND (gps_points.latitude BETWEEN #{yminr} AND #{ymaxr}) "+
" AND (gps_points.timestamp IS NOT NULL) "+
def save
@title = 'create account'
@user = User.new(params[:user])
- @user.set_defaults
if @user.save
+ token = @user.tokens.create
flash[:notice] = "User was successfully created. Check your email for a confirmation note, and you\'ll be mapping in no time :-)<br>Please note that you won't be able to login until you've received and confirmed your email address."
- Notifier::deliver_signup_confirm(@user)
+ Notifier::deliver_signup_confirm(@user, token)
redirect_to :action => 'login'
else
render :action => 'new'
def lost_password
@title = 'lost password'
if params[:user] and params[:user][:email]
- user = User.find_by_email(params['user']['email'])
+ user = User.find_by_email(params[:user][:email])
if user
- user.token = User.make_token
- user.save
- Notifier::deliver_lost_password(user)
+ token = user.tokens.create
+ Notifier::deliver_lost_password(user, token)
flash[:notice] = "Sorry you lost it :-( but an email is on its way so you can reset it soon."
else
flash[:notice] = "Couldn't find that email address, sorry."
def reset_password
@title = 'reset password'
if params['token']
- user = User.find_by_token(params['token'])
- if user
- pass = User.make_token(8)
+ token = UserToken.find_by_token(params[:token])
+ if token
+ pass = OSM::make_token(8)
+ user = token.user
user.pass_crypt = pass
user.pass_crypt_confirmation = pass
user.active = true
- user.save
+ user.save!
+ token.destroy
Notifier::deliver_reset_password(user, pass)
flash[:notice] = "Your password has been changed and is on its way to your mailbox :-)"
else
if params[:user]
email = params[:user][:email]
pass = params[:user][:password]
- u = User.authenticate(email, pass)
- if u
- u.token = User.make_token
- u.timeout = 1.day.from_now
- u.save
- session[:token] = u.token
+ user = User.authenticate(:username => email, :password => pass)
+ if user
+ session[:user] = user.id
if params[:referer]
redirect_to params[:referer]
else
redirect_to :controller => 'site', :action => 'index'
end
return
- elsif User.authenticate(email, pass, false)
+ elsif User.authenticate(:username => email, :password => pass, :invalid => true)
flash[:notice] = "Sorry, your account is not active yet.<br>Please click on the link in the account confirmation email to activate your account."
else
flash[:notice] = "Sorry, couldn't log in with those details."
def logout
if session[:token]
- u = User.find_by_token(session[:token])
- if u
- u.token = User.make_token
- u.timeout = Time.now
- u.save
+ token = UserToken.find_by_token(session[:token])
+ if token
+ token.destroy
end
+ session[:token] = nil
end
- session[:token] = nil
+ session[:user] = nil
if params[:referer]
redirect_to params[:referer]
else
end
def confirm
- @user = User.find_by_token(params[:confirm_string])
- if @user && @user.active == 0
+ token = UserToken.find_by_token(params[:confirm_string])
+ if token and !token.user.active?
+ @user = token.user
@user.active = true
- @user.token = User.make_token
- @user.timeout = 1.day.from_now
- @user.save
+ @user.save!
+ token.destroy
flash[:notice] = 'Confirmed your account, thanks for signing up!'
- session[:token] = @user.token
+ session[:user] = @user.id
redirect_to :action => 'account', :display_name => @user.display_name
else
flash[:notice] = 'Something went wrong confirming that user.'
class Notifier < ActionMailer::Base
- def signup_confirm( user )
+ def signup_confirm( user, token )
@recipients = user.email
@from = 'abuse@openstreetmap.org'
@subject = '[OpenStreetMap] Confirm your email address'
- @body['url'] = "http://#{SERVER_URL}/user/confirm?confirm_string=#{user.token}"
+ @body['url'] = "http://#{SERVER_URL}/user/confirm?confirm_string=#{token.token}"
end
- def lost_password( user )
+ def lost_password( user, token )
@recipients = user.email
@from = 'abuse@openstreetmap.org'
@subject = '[OpenStreetMap] Password reset request'
- @body['url'] = "http://#{SERVER_URL}/user/reset_password?email=#{user.email}&token=#{user.token}"
+ @body['url'] = "http://#{SERVER_URL}/user/reset_password?email=#{user.email}&token=#{token.token}"
end
def reset_password(user, pass)
has_many :messages, :foreign_key => :to_user_id
has_many :new_messages, :class_name => "Message", :foreign_key => :to_user_id, :conditions => "message_read = 0"
has_many :friends
+ has_many :tokens, :class_name => "UserToken"
+ has_many :preferences, :class_name => "UserPreference"
validates_confirmation_of :pass_crypt, :message => 'Password must match the confirmation password'
validates_uniqueness_of :display_name, :allow_nil => true
before_save :encrypt_password
- def set_defaults
+ def after_initialize
self.creation_time = Time.now
- self.timeout = Time.now
- self.token = User.make_token()
end
def encrypt_password
self.pass_crypt = Digest::MD5.hexdigest(pass_crypt) unless pass_crypt_confirmation.nil?
end
- def self.authenticate(email, passwd, active = true)
- find(:first, :conditions => [ "email = ? AND pass_crypt = ? AND active = ?", email, Digest::MD5.hexdigest(passwd), active])
- end
-
- def self.authenticate_token(token)
- find(:first, :conditions => [ "token = ? ", token])
- end
-
- def self.make_token(length=30)
- chars = 'abcdefghijklmnopqrtuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'
- confirmstring = ''
+ def self.authenticate(options)
+ if options[:username] and options[:password]
+ user = find(:first, :conditions => ["email = ? OR display_name = ?", options[:username], options[:username]])
+ user = nil unless user.pass_crypt == Digest::MD5.hexdigest(options[:password])
+ elsif options[:token]
+ token = UserToken.find(:first, :include => :user, :conditions => ["user_tokens.token = ?", options[:token]])
+ user = token.user if token
+ end
- length.times do
- confirmstring += chars[(rand * chars.length).to_i].chr
+ if user
+ user = nil unless user.active? or options[:inactive]
end
- return confirmstring
- end
+ token.update_attribute(:expiry, 1.week.from_now) if token and user
+
+ return user
+ end
def to_xml
doc = OSM::API.new.get_xml_doc
--- /dev/null
+class UserPreference < ActiveRecord::Base
+ belongs_to :user
+end
--- /dev/null
+class UserToken < ActiveRecord::Base
+ belongs_to :user
+
+ def after_initialize
+ self.token = OSM::make_token() if self.token.blank?
+ self.expiry = 1.week.from_now if self.expiry.blank?
+ end
+end
<% else %>
<%= render :partial => 'search', :locals => { :onopen => "resizeMap();", :onclose => "resizeMap();" } %>
+<% session[:token] = @user.tokens.create.token unless session[:token] %>
+
<% if params['mlon'] and params['mlat'] %>
<% lon = params['mlon'] %>
<% lat = params['mlat'] %>
fo.addVariable('lat',lat);
fo.addVariable('long',lon);
fo.addVariable('scale',sc);
- fo.addVariable('token','<%= @user.token %>');
+ fo.addVariable('token','<%= session[:token] %>');
fo.write("map");
}
--- /dev/null
+class UserEnhancements < ActiveRecord::Migration
+ def self.up
+ add_column "diary_entries", "latitude", :double
+ add_column "diary_entries", "longitude", :double
+ add_column "diary_entries", "language", :string, :limit => 3
+
+ create_table "user_preferences", innodb_table do |t|
+ t.column "user_id", :bigint, :limit => 20, :null => false
+ t.column "k", :string, :null => false
+ t.column "v", :string, :null => false
+ end
+
+ add_primary_key "user_preferences", ["user_id", "k"]
+
+ create_table "user_tokens", innodb_table do |t|
+ t.column "id", :bigint, :limit => 20, :null => false
+ t.column "user_id", :bigint, :limit => 20, :null => false
+ t.column "token", :string, :null => false
+ t.column "expiry", :datetime, :null => false
+ end
+
+ add_primary_key "user_tokens", ["id"]
+ add_index "user_tokens", ["token"], :name => "user_tokens_token_idx", :unique => true
+ add_index "user_tokens", ["user_id"], :name => "user_tokens_user_id_idx"
+
+ change_column "user_tokens", "id", :bigint, :limit => 20, :null => false, :options => "AUTO_INCREMENT"
+
+ User.find(:all, :conditions => "token is not null").each do |user|
+ UserToken.create(:user_id => user.id, :token => user.token, :expiry => 1.week.from_now)
+ end
+
+ remove_column "users", "token"
+ remove_column "users", "timeout"
+ remove_column "users", "within_lon"
+ remove_column "users", "within_lat"
+ add_column "users", "nearby", :integer, :default => 50
+ add_column "users", "pass_salt", :string
+
+ User.update_all("nearby = 50");
+ end
+
+ def self.down
+ remove_column "users", "pass_salt"
+ remove_column "users", "nearby"
+ add_column "users", "within_lat", :double
+ add_column "users", "within_lon", :double
+ add_column "users", "timeout", :datetime
+ add_column "users", "token", :string
+
+ drop_table "user_tokens"
+
+ drop_table "user_preferences"
+
+ remove_column "diary_entries", "language"
+ remove_column "diary_entries", "longitude"
+ remove_column "diary_entries", "latitude"
+ end
+end
rescue Exception
return nil
end
+
+ # Construct a random token of a given length
+ def self.make_token(length = 30)
+ chars = 'abcdefghijklmnopqrtuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'
+ token = ''
+
+ length.times do
+ token += chars[(rand * chars.length).to_i].chr
+ end
+
+ return token
+ end
end