Various updates to the user management, including the creation of a
preferences table and moving tokens into a tokens table so that a user can have more than one.
This commit is contained in:
parent
b61e4f77e8
commit
0a8c26e596
11 changed files with 151 additions and 74 deletions
|
@ -736,12 +736,13 @@ def array2tag(a)
|
||||||
end
|
end
|
||||||
|
|
||||||
def getuserid(token)
|
def getuserid(token)
|
||||||
token=sqlescape(token)
|
if (token =~ /^(.+)\+(.+)$/) then
|
||||||
if (token=~/^(.+)\+(.+)$/) then
|
user = User.authenticate(:username => $1, :password => $2)
|
||||||
return ActiveRecord::Base.connection.select_value("SELECT id FROM users WHERE active=1 AND email='#{$1}' AND pass_crypt=MD5('#{$2}')")
|
|
||||||
else
|
else
|
||||||
return ActiveRecord::Base.connection.select_value("SELECT id FROM users WHERE active=1 AND token='#{token}'")
|
user = User.authenticate(:token => token)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
return user ? user.id : nil;
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -3,7 +3,12 @@
|
||||||
class ApplicationController < ActionController::Base
|
class ApplicationController < ActionController::Base
|
||||||
|
|
||||||
def authorize_web
|
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
|
end
|
||||||
|
|
||||||
def require_user
|
def require_user
|
||||||
|
@ -16,21 +21,13 @@ class ApplicationController < ActionController::Base
|
||||||
if username.nil?
|
if username.nil?
|
||||||
@user = nil # no authentication provided - perhaps first connect (client should retry after 401)
|
@user = nil # no authentication provided - perhaps first connect (client should retry after 401)
|
||||||
elsif username == 'token'
|
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
|
else
|
||||||
@user = User.authenticate(username, passwd) # basic auth
|
@user = User.authenticate(:username => username, :password => passwd) # basic auth
|
||||||
end
|
end
|
||||||
|
|
||||||
# handle authenticate pass/fail
|
# handle authenticate pass/fail
|
||||||
if @user
|
unless @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
|
|
||||||
# no auth, the user does not exist or the password was wrong
|
# no auth, the user does not exist or the password was wrong
|
||||||
response.headers["Status"] = "Unauthorized"
|
response.headers["Status"] = "Unauthorized"
|
||||||
response.headers["WWW-Authenticate"] = "Basic realm=\"#{realm}\""
|
response.headers["WWW-Authenticate"] = "Basic realm=\"#{realm}\""
|
||||||
|
@ -58,8 +55,9 @@ class ApplicationController < ActionController::Base
|
||||||
response.headers['Error'] = message
|
response.headers['Error'] = message
|
||||||
end
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
# extract authorisation credentials from headers, returns user = nil if none
|
# extract authorisation credentials from headers, returns user = nil if none
|
||||||
private
|
|
||||||
def get_auth_data
|
def get_auth_data
|
||||||
if request.env.has_key? 'X-HTTP_AUTHORIZATION' # where mod_rewrite might have put it
|
if request.env.has_key? 'X-HTTP_AUTHORIZATION' # where mod_rewrite might have put it
|
||||||
authdata = request.env['X-HTTP_AUTHORIZATION'].to_s.split
|
authdata = request.env['X-HTTP_AUTHORIZATION'].to_s.split
|
||||||
|
|
|
@ -46,12 +46,11 @@ class SwfController < ApplicationController
|
||||||
lastfile='-1'
|
lastfile='-1'
|
||||||
|
|
||||||
if params['token']
|
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 "+
|
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 "+
|
"WHERE gpx_files.id=gpx_id "+
|
||||||
" AND gpx_files.user_id=users.id "+
|
" AND gpx_files.user_id=#{user.id} "+
|
||||||
" AND token='#{token}' "+
|
|
||||||
" AND (gps_points.longitude BETWEEN #{xminr} AND #{xmaxr}) "+
|
" AND (gps_points.longitude BETWEEN #{xminr} AND #{xmaxr}) "+
|
||||||
" AND (gps_points.latitude BETWEEN #{yminr} AND #{ymaxr}) "+
|
" AND (gps_points.latitude BETWEEN #{yminr} AND #{ymaxr}) "+
|
||||||
" AND (gps_points.timestamp IS NOT NULL) "+
|
" AND (gps_points.timestamp IS NOT NULL) "+
|
||||||
|
|
|
@ -10,11 +10,11 @@ class UserController < ApplicationController
|
||||||
def save
|
def save
|
||||||
@title = 'create account'
|
@title = 'create account'
|
||||||
@user = User.new(params[:user])
|
@user = User.new(params[:user])
|
||||||
@user.set_defaults
|
|
||||||
|
|
||||||
if @user.save
|
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."
|
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'
|
redirect_to :action => 'login'
|
||||||
else
|
else
|
||||||
render :action => 'new'
|
render :action => 'new'
|
||||||
|
@ -64,11 +64,10 @@ class UserController < ApplicationController
|
||||||
def lost_password
|
def lost_password
|
||||||
@title = 'lost password'
|
@title = 'lost password'
|
||||||
if params[:user] and params[:user][:email]
|
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
|
if user
|
||||||
user.token = User.make_token
|
token = user.tokens.create
|
||||||
user.save
|
Notifier::deliver_lost_password(user, token)
|
||||||
Notifier::deliver_lost_password(user)
|
|
||||||
flash[:notice] = "Sorry you lost it :-( but an email is on its way so you can reset it soon."
|
flash[:notice] = "Sorry you lost it :-( but an email is on its way so you can reset it soon."
|
||||||
else
|
else
|
||||||
flash[:notice] = "Couldn't find that email address, sorry."
|
flash[:notice] = "Couldn't find that email address, sorry."
|
||||||
|
@ -81,13 +80,15 @@ class UserController < ApplicationController
|
||||||
def reset_password
|
def reset_password
|
||||||
@title = 'reset password'
|
@title = 'reset password'
|
||||||
if params['token']
|
if params['token']
|
||||||
user = User.find_by_token(params['token'])
|
token = UserToken.find_by_token(params[:token])
|
||||||
if user
|
if token
|
||||||
pass = User.make_token(8)
|
pass = OSM::make_token(8)
|
||||||
|
user = token.user
|
||||||
user.pass_crypt = pass
|
user.pass_crypt = pass
|
||||||
user.pass_crypt_confirmation = pass
|
user.pass_crypt_confirmation = pass
|
||||||
user.active = true
|
user.active = true
|
||||||
user.save
|
user.save!
|
||||||
|
token.destroy
|
||||||
Notifier::deliver_reset_password(user, pass)
|
Notifier::deliver_reset_password(user, pass)
|
||||||
flash[:notice] = "Your password has been changed and is on its way to your mailbox :-)"
|
flash[:notice] = "Your password has been changed and is on its way to your mailbox :-)"
|
||||||
else
|
else
|
||||||
|
@ -106,19 +107,16 @@ class UserController < ApplicationController
|
||||||
if params[:user]
|
if params[:user]
|
||||||
email = params[:user][:email]
|
email = params[:user][:email]
|
||||||
pass = params[:user][:password]
|
pass = params[:user][:password]
|
||||||
u = User.authenticate(email, pass)
|
user = User.authenticate(:username => email, :password => pass)
|
||||||
if u
|
if user
|
||||||
u.token = User.make_token
|
session[:user] = user.id
|
||||||
u.timeout = 1.day.from_now
|
|
||||||
u.save
|
|
||||||
session[:token] = u.token
|
|
||||||
if params[:referer]
|
if params[:referer]
|
||||||
redirect_to params[:referer]
|
redirect_to params[:referer]
|
||||||
else
|
else
|
||||||
redirect_to :controller => 'site', :action => 'index'
|
redirect_to :controller => 'site', :action => 'index'
|
||||||
end
|
end
|
||||||
return
|
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."
|
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
|
else
|
||||||
flash[:notice] = "Sorry, couldn't log in with those details."
|
flash[:notice] = "Sorry, couldn't log in with those details."
|
||||||
|
@ -128,14 +126,13 @@ class UserController < ApplicationController
|
||||||
|
|
||||||
def logout
|
def logout
|
||||||
if session[:token]
|
if session[:token]
|
||||||
u = User.find_by_token(session[:token])
|
token = UserToken.find_by_token(session[:token])
|
||||||
if u
|
if token
|
||||||
u.token = User.make_token
|
token.destroy
|
||||||
u.timeout = Time.now
|
|
||||||
u.save
|
|
||||||
end
|
end
|
||||||
|
session[:token] = nil
|
||||||
end
|
end
|
||||||
session[:token] = nil
|
session[:user] = nil
|
||||||
if params[:referer]
|
if params[:referer]
|
||||||
redirect_to params[:referer]
|
redirect_to params[:referer]
|
||||||
else
|
else
|
||||||
|
@ -144,14 +141,14 @@ class UserController < ApplicationController
|
||||||
end
|
end
|
||||||
|
|
||||||
def confirm
|
def confirm
|
||||||
@user = User.find_by_token(params[:confirm_string])
|
token = UserToken.find_by_token(params[:confirm_string])
|
||||||
if @user && @user.active == 0
|
if token and !token.user.active?
|
||||||
|
@user = token.user
|
||||||
@user.active = true
|
@user.active = true
|
||||||
@user.token = User.make_token
|
@user.save!
|
||||||
@user.timeout = 1.day.from_now
|
token.destroy
|
||||||
@user.save
|
|
||||||
flash[:notice] = 'Confirmed your account, thanks for signing up!'
|
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
|
redirect_to :action => 'account', :display_name => @user.display_name
|
||||||
else
|
else
|
||||||
flash[:notice] = 'Something went wrong confirming that user.'
|
flash[:notice] = 'Something went wrong confirming that user.'
|
||||||
|
|
|
@ -1,17 +1,17 @@
|
||||||
class Notifier < ActionMailer::Base
|
class Notifier < ActionMailer::Base
|
||||||
|
|
||||||
def signup_confirm( user )
|
def signup_confirm( user, token )
|
||||||
@recipients = user.email
|
@recipients = user.email
|
||||||
@from = 'abuse@openstreetmap.org'
|
@from = 'abuse@openstreetmap.org'
|
||||||
@subject = '[OpenStreetMap] Confirm your email address'
|
@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
|
end
|
||||||
|
|
||||||
def lost_password( user )
|
def lost_password( user, token )
|
||||||
@recipients = user.email
|
@recipients = user.email
|
||||||
@from = 'abuse@openstreetmap.org'
|
@from = 'abuse@openstreetmap.org'
|
||||||
@subject = '[OpenStreetMap] Password reset request'
|
@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
|
end
|
||||||
|
|
||||||
def reset_password(user, pass)
|
def reset_password(user, pass)
|
||||||
|
|
|
@ -7,6 +7,8 @@ class User < ActiveRecord::Base
|
||||||
has_many :messages, :foreign_key => :to_user_id
|
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 :new_messages, :class_name => "Message", :foreign_key => :to_user_id, :conditions => "message_read = 0"
|
||||||
has_many :friends
|
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_confirmation_of :pass_crypt, :message => 'Password must match the confirmation password'
|
||||||
validates_uniqueness_of :display_name, :allow_nil => true
|
validates_uniqueness_of :display_name, :allow_nil => true
|
||||||
|
@ -18,34 +20,31 @@ class User < ActiveRecord::Base
|
||||||
|
|
||||||
before_save :encrypt_password
|
before_save :encrypt_password
|
||||||
|
|
||||||
def set_defaults
|
def after_initialize
|
||||||
self.creation_time = Time.now
|
self.creation_time = Time.now
|
||||||
self.timeout = Time.now
|
|
||||||
self.token = User.make_token()
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def encrypt_password
|
def encrypt_password
|
||||||
self.pass_crypt = Digest::MD5.hexdigest(pass_crypt) unless pass_crypt_confirmation.nil?
|
self.pass_crypt = Digest::MD5.hexdigest(pass_crypt) unless pass_crypt_confirmation.nil?
|
||||||
end
|
end
|
||||||
|
|
||||||
def self.authenticate(email, passwd, active = true)
|
def self.authenticate(options)
|
||||||
find(:first, :conditions => [ "email = ? AND pass_crypt = ? AND active = ?", email, Digest::MD5.hexdigest(passwd), active])
|
if options[:username] and options[:password]
|
||||||
end
|
user = find(:first, :conditions => ["email = ? OR display_name = ?", options[:username], options[:username]])
|
||||||
|
user = nil unless user.pass_crypt == Digest::MD5.hexdigest(options[:password])
|
||||||
def self.authenticate_token(token)
|
elsif options[:token]
|
||||||
find(:first, :conditions => [ "token = ? ", token])
|
token = UserToken.find(:first, :include => :user, :conditions => ["user_tokens.token = ?", options[:token]])
|
||||||
end
|
user = token.user if token
|
||||||
|
|
||||||
def self.make_token(length=30)
|
|
||||||
chars = 'abcdefghijklmnopqrtuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'
|
|
||||||
confirmstring = ''
|
|
||||||
|
|
||||||
length.times do
|
|
||||||
confirmstring += chars[(rand * chars.length).to_i].chr
|
|
||||||
end
|
end
|
||||||
|
|
||||||
return confirmstring
|
if user
|
||||||
end
|
user = nil unless user.active? or options[:inactive]
|
||||||
|
end
|
||||||
|
|
||||||
|
token.update_attribute(:expiry, 1.week.from_now) if token and user
|
||||||
|
|
||||||
|
return user
|
||||||
|
end
|
||||||
|
|
||||||
def to_xml
|
def to_xml
|
||||||
doc = OSM::API.new.get_xml_doc
|
doc = OSM::API.new.get_xml_doc
|
||||||
|
|
3
app/models/user_preference.rb
Normal file
3
app/models/user_preference.rb
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
class UserPreference < ActiveRecord::Base
|
||||||
|
belongs_to :user
|
||||||
|
end
|
8
app/models/user_token.rb
Normal file
8
app/models/user_token.rb
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
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
|
|
@ -5,6 +5,8 @@
|
||||||
<% else %>
|
<% else %>
|
||||||
<%= render :partial => 'search', :locals => { :onopen => "resizeMap();", :onclose => "resizeMap();" } %>
|
<%= render :partial => 'search', :locals => { :onopen => "resizeMap();", :onclose => "resizeMap();" } %>
|
||||||
|
|
||||||
|
<% session[:token] = @user.tokens.create.token unless session[:token] %>
|
||||||
|
|
||||||
<% if params['mlon'] and params['mlat'] %>
|
<% if params['mlon'] and params['mlat'] %>
|
||||||
<% lon = params['mlon'] %>
|
<% lon = params['mlon'] %>
|
||||||
<% lat = params['mlat'] %>
|
<% lat = params['mlat'] %>
|
||||||
|
@ -34,7 +36,7 @@
|
||||||
fo.addVariable('lat',lat);
|
fo.addVariable('lat',lat);
|
||||||
fo.addVariable('long',lon);
|
fo.addVariable('long',lon);
|
||||||
fo.addVariable('scale',sc);
|
fo.addVariable('scale',sc);
|
||||||
fo.addVariable('token','<%= @user.token %>');
|
fo.addVariable('token','<%= session[:token] %>');
|
||||||
fo.write("map");
|
fo.write("map");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
58
db/migrate/004_user_enhancements.rb
Normal file
58
db/migrate/004_user_enhancements.rb
Normal file
|
@ -0,0 +1,58 @@
|
||||||
|
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
|
12
lib/osm.rb
12
lib/osm.rb
|
@ -391,4 +391,16 @@ module OSM
|
||||||
rescue Exception
|
rescue Exception
|
||||||
return nil
|
return nil
|
||||||
end
|
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
|
end
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue