diff --git a/app/assets/javascripts/app/lib/app_post/auth.js.coffee b/app/assets/javascripts/app/lib/app_post/auth.js.coffee index 6d4225960..751288a58 100644 --- a/app/assets/javascripts/app/lib/app_post/auth.js.coffee +++ b/app/assets/javascripts/app/lib/app_post/auth.js.coffee @@ -2,6 +2,7 @@ class App.Auth @login: (params) -> App.Log.notice 'Auth', 'login', params + params.data['fingerprint'] = App.Browser.fingerprint() App.Ajax.request( id: 'login' type: 'POST' @@ -21,12 +22,15 @@ class App.Auth ) @loginCheck: -> + params = + fingerprint: App.Browser.fingerprint() App.Log.debug 'Auth', 'loginCheck' App.Ajax.request( id: 'login_check' async: false - type: 'GET' + type: 'POST' url: App.Config.get('api_path') + '/signshow' + data: JSON.stringify(params) success: (data, status, xhr) => # set login (config, session, ...) diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index 3a6b5e58a..68c72fb88 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -14,10 +14,10 @@ class ApplicationController < ActionController::Base :model_index_render skip_before_action :verify_authenticity_token - before_action :set_user, :session_update, :check_user_device + before_action :set_user, :session_update before_action :cors_preflight_check - after_action :set_access_control_headers + after_action :user_device_update, :set_access_control_headers after_action :trigger_events # For all responses in this controller, return the CORS access control headers. @@ -95,8 +95,8 @@ class ApplicationController < ActionController::Base session[:user_agent] = request.env['HTTP_USER_AGENT'] end - # check user device - def check_user_device + # user device recent action update + def user_device_update # return if we are in switch to user mode return if session[:switched_from_user_id] @@ -104,21 +104,46 @@ class ApplicationController < ActionController::Base # only if user_id exists return if !session[:user_id] - # only if write action + # only with user device + if !session[:user_device_id] + if params[:fingerprint] + return false if !user_device_log(current_user, 'session') + end + return + end + + # check if entry exists / only if write action return if request.method == 'GET' || request.method == 'OPTIONS' # only update if needed - return if session[:check_user_device_at] && session[:check_user_device_at] > Time.zone.now - 5.minutes - session[:check_user_device_at] = Time.zone.now + return if session[:user_device_update_at] && session[:user_device_update_at] > Time.zone.now - 5.minutes + session[:user_device_update_at] = Time.zone.now - user_device = UserDevice.add( + UserDevice.action( + session[:user_device_id], session[:user_agent], session[:remote_id], session[:user_id], ) - if user_device.id != session[:check_user_device_id] - session[:check_user_device_id] = user_device.id + end + + def user_device_log(user, type) + + # for sessions we need the fingperprint + if !params[:fingerprint] && type == 'session' + render json: { error: 'Need fingerprint param!' }, status: :unprocessable_entity + return false end + + # add defice if needed + user_device = UserDevice.add( + request.env['HTTP_USER_AGENT'], + request.remote_ip, + user.id, + params[:fingerprint], + type, + ) + session[:user_device_id] = user_device.id end def authentication_check_only(auth_param) @@ -130,7 +155,8 @@ class ApplicationController < ActionController::Base # already logged in, early exit if session.id && session[:user_id] - userdata = User.find( session[:user_id] ) + + userdata = User.find(session[:user_id]) current_user_set(userdata) return { @@ -143,11 +169,10 @@ class ApplicationController < ActionController::Base # check sso sso_userdata = User.sso(params) if sso_userdata + session[:persistent] = true current_user_set(sso_userdata) - session[:persistent] = true - return { auth: true } @@ -161,8 +186,9 @@ class ApplicationController < ActionController::Base next if !userdata - # set basic auth user to current user current_user_set(userdata) + user_device_log(userdata, 'basic_auth') + return { auth: true } @@ -180,8 +206,8 @@ class ApplicationController < ActionController::Base next if !userdata - # set token user to current user current_user_set(userdata) + user_device_log(userdata, 'token_auth') return { auth: true @@ -216,9 +242,6 @@ class ApplicationController < ActionController::Base return false end - # store current user id into the session - session[:user_id] = current_user.id - # return auth ok true end diff --git a/app/controllers/sessions_controller.rb b/app/controllers/sessions_controller.rb index 4c4d49e2e..f84a57c91 100644 --- a/app/controllers/sessions_controller.rb +++ b/app/controllers/sessions_controller.rb @@ -30,6 +30,9 @@ class SessionsController < ApplicationController # set session user current_user_set(user) + # log device + return if !user_device_log(user, 'session') + # log new session user.activity_stream_log( 'session started', user.id, true ) @@ -85,6 +88,9 @@ class SessionsController < ApplicationController # subsequent requests user = User.find( user_id ) + # log device + return if !user_device_log(user, 'session') + # auto population of default collections collections, assets = SessionHelper.default_collections(user) diff --git a/app/controllers/user_devices_controller.rb b/app/controllers/user_devices_controller.rb index e80db5115..c5b0a90bc 100644 --- a/app/controllers/user_devices_controller.rb +++ b/app/controllers/user_devices_controller.rb @@ -4,18 +4,18 @@ class UserDevicesController < ApplicationController before_action :authentication_check def index - devices = UserDevice.where(user_id: current_user.id).order('updated_at DESC') + devices = UserDevice.where(user_id: current_user.id).order('created_at DESC') devices_full = [] devices.each {|device| attributes = device.attributes - if device.location_details['city'] - attributes['location'] += ", #{device.location_details['city']}" + if device.location_details['city_name'] + attributes['location'] += ", #{device.location_details['city_name']}" end attributes.delete('created_at') attributes.delete('device_details') attributes.delete('location_details') - if session[:check_user_device_id] == device.id + if session[:user_device_id] == device.id attributes['current'] = true end devices_full.push attributes @@ -24,6 +24,7 @@ class UserDevicesController < ApplicationController end def destroy + # find device user_device = UserDevice.find_by(user_id: current_user.id, id: params[:id]) @@ -31,8 +32,8 @@ class UserDevicesController < ApplicationController if user_device SessionHelper.list.each {|session| next if !session.data['user_id'] - next if !session.data['check_user_device_id'] - next if session.data['check_user_device_id'] != user_device.id + next if !session.data['user_device_id'] + next if session.data['user_device_id'] != user_device.id SessionHelper.destroy( session.id ) } user_device.destroy diff --git a/app/models/user_device.rb b/app/models/user_device.rb index 1e9b5649a..a247a84d7 100644 --- a/app/models/user_device.rb +++ b/app/models/user_device.rb @@ -13,11 +13,37 @@ store device for user 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/44.0.2403.107 Safari/537.36', '172.0.0.1', user.id, + 'fingerprintABC123', + 'session', # session|basic_auth|token_auth|sso ) =end - def self.add(user_agent, ip, user_id) + def self.add(user_agent, ip, user_id, fingerprint, type) + + # get location info + location_details = Service::GeoIp.location(ip) + location = location_details['country_name'] + + # find device by fingerprint + if fingerprint + user_device = UserDevice.find_by( + user_id: user_id, + fingerprint: fingerprint, + location: location, + ) + return action(user_device.id, user_agent, ip, user_id) if user_device + end + + # for basic_auth|token_auth search for user agent + if type == 'basic_auth' || type == 'token_auth' + user_device = UserDevice.find_by( + user_id: user_id, + user_agent: user_agent, + location: location, + ) + return action(user_device.id, user_agent, ip, user_id) if user_device + end # get browser details browser = Browser.new(:ua => user_agent, :accept_language => 'en-us') @@ -37,21 +63,22 @@ store device for user name += browser[:name] end - # get location info - location = Service::GeoIp.location(ip) - country = location['country_name'] + # if not identified, use user agent + if name == 'Other, Other' + name = user_agent + browser[:name] = user_agent + end # check if exists - exists = self.find_by( - :user_id => user_id, + user_device = self.find_by( + user_id: user_id, os: browser[:plattform], browser: browser[:name], - location: country, + location: location, ) - if exists - exists.touch - return exists + if user_device + return action(user_device.id, user_agent, ip, user_id) if user_device end # create new device @@ -60,10 +87,45 @@ store device for user name: name, os: browser[:plattform], browser: browser[:name], - location: country, + location: location, device_details: browser, - location_details: location, + location_details: location_details, + user_agent: user_agent, + ip: ip, + fingerprint: fingerprint, ) + + end + +=begin + +log user device action + + UserDevice.action( + user_device_id, + 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/44.0.2403.107 Safari/537.36', + '172.0.0.1', + user.id, + ) + +=end + + def self.action(user_device_id, user_agent, ip, user_id) + user_device = UserDevice.find(user_device_id) + + # update location if needed + if user_device.ip != ip + user_device.ip = ip + location_details = Service::GeoIp.location(ip) + user_device.location_details = location_details + + location = location_details['country_name'] + user_device.location = location + end + + # update attributes + user_device.save + user_device end end diff --git a/config/routes/auth.rb b/config/routes/auth.rb index 96e284d87..6807b69b3 100644 --- a/config/routes/auth.rb +++ b/config/routes/auth.rb @@ -9,7 +9,7 @@ Zammad::Application.routes.draw do # sessions match api_path + '/signin', to: 'sessions#create', via: :post - match api_path + '/signshow', to: 'sessions#show', via: :get + match api_path + '/signshow', to: 'sessions#show', via: [:get, :post] match api_path + '/signout', to: 'sessions#destroy', via: [:get, :delete] match api_path + '/sessions/switch/:id', to: 'sessions#switch_to_user', via: :get diff --git a/db/migrate/20150818000001_update_user_devices2.rb b/db/migrate/20150818000001_update_user_devices2.rb new file mode 100644 index 000000000..f8a016424 --- /dev/null +++ b/db/migrate/20150818000001_update_user_devices2.rb @@ -0,0 +1,11 @@ +class UpdateUserDevices2 < ActiveRecord::Migration + def up + add_column :user_devices, :user_agent, :string, limit: 250, null: true + add_column :user_devices, :ip, :string, limit: 160, null: true + add_column :user_devices, :fingerprint, :string, limit: 160, null: true + add_index :user_devices, [:fingerprint] + add_index :user_devices, [:created_at] + UserDevice.reset_column_information + end + +end