diff --git a/Gemfile b/Gemfile index 0bd76cd0f..69440425c 100644 --- a/Gemfile +++ b/Gemfile @@ -77,6 +77,8 @@ gem 'bootstrap-kaminari-views', '0.0.5' gem "pd_x12", "~> 1.5.4" gem 'carrierwave-mongoid', '0.7.1', :require => 'carrierwave/mongoid' gem 'devise', '3.3.0' +# for account locking +gem 'devise_security_extension' gem "rsec", "~> 0.4.2" gem "mongoid_auto_increment", '0.1.2' gem 'american_date', '1.1.0' diff --git a/Gemfile.lock b/Gemfile.lock index 2e8782099..9d619af87 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -152,6 +152,9 @@ GEM railties (>= 3.2.6, < 5) thread_safe (~> 0.1) warden (~> 1.2.3) + devise_security_extension (0.10.0) + devise (>= 3.0.0, < 4.0) + railties (>= 3.2.6, < 5.0) diff-lcs (1.2.5) docile (1.1.5) equalizer (0.0.11) @@ -424,6 +427,7 @@ DEPENDENCIES database_cleaner (= 1.5.3) designmodo-flatuipro-rails! devise (= 3.3.0) + devise_security_extension edi_codec! edi_safe! factory_girl (= 4.5.0) diff --git a/app/models/user.rb b/app/models/user.rb index fbd7edd68..4ba44ab37 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -6,7 +6,9 @@ class User # Include default devise modules. Others available are: # :confirmable, :lockable, :timeoutable and :omniauthable devise :database_authenticatable, - :recoverable, :rememberable, :trackable, :validatable, :registerable + :recoverable, :rememberable, :trackable, :validatable, :registerable, + :session_limitable, # Limit number of sessions + :expirable # Setup accessible (or protected) attributes for your model attr_accessible :email, :password, :password_confirmation, :remember_me, :approved, :role, :updated_by @@ -26,6 +28,13 @@ class User ## Rememberable field :remember_created_at, :type => Time + ## Session Limitable + field :unique_session_id, type: String + + ## Expirable + field :last_activity_at, type: Time + field :expired_at, type: Time + ## Trackable field :sign_in_count, :type => Integer, :default => 0 field :current_sign_in_at, :type => Time @@ -56,6 +65,14 @@ class User before_save :ensure_authentication_token + def update_last_activity! + if respond_to?(:update_column) + self.update_column(:last_activity_at, Time.now.utc) + elsif defined? Mongoid + self.update_attribute(:last_activity_at, Time.now.utc) + end + end + def update_attributes_as(update_params, current_user = nil) params = update_params.dup if current_user diff --git a/config/application.rb b/config/application.rb index ad88afa9c..ac59161cd 100644 --- a/config/application.rb +++ b/config/application.rb @@ -1,3 +1,4 @@ +DEVISE_ORM = :mongoid require File.expand_path('../boot', __FILE__) # Pick the frameworks you want: diff --git a/config/initializers/devise.rb b/config/initializers/devise.rb index c94f07baf..c2c697f12 100644 --- a/config/initializers/devise.rb +++ b/config/initializers/devise.rb @@ -251,4 +251,13 @@ # When using omniauth, Devise cannot automatically set Omniauth path, # so you need to do it manually. For the users scope, it would be: # config.omniauth_path_prefix = '/my_engine/users/auth' + # ==> Configuration for :expirable + # Time period for account expiry from last_activity_at + user_account_lock_period = 60 + unless ENV['DEVISE_USER_INACTIVITY_LOCK_PERIOD_IN_DAYS'].blank? + period_in_days_env = ENV['DEVISE_USER_INACTIVITY_LOCK_PERIOD_IN_DAYS'] + num_days = period_in_days_env.to_i + user_account_lock_period = num_days if num_days.to_s == period_in_days_env + end + config.expire_after = user_account_lock_period.days end diff --git a/spec/controllers/users_controller_spec.rb b/spec/controllers/users_controller_spec.rb new file mode 100644 index 000000000..79afb4a5b --- /dev/null +++ b/spec/controllers/users_controller_spec.rb @@ -0,0 +1,35 @@ +require 'rails_helper' + +describe UsersController, :dbclean => :after_each do + + describe 'GET index' do + context "attempting to log in an expired user" do + let(:user) do + user_record = FactoryGirl.create(:user, :admin) + user_record.last_activity_at = Time.now - 180.days + user_record.save! + user_record + end + + it "can't access the endpoint" do + sign_in user + get :index + expect(response).to redirect_to("http://test.host/accounts/sign_in") + end + end + + context "attempting to log in an valid user" do + let(:user) do + user_record = FactoryGirl.create(:user, :admin) + user_record.save! + user_record + end + + it "can't access the endpoint" do + sign_in user + get :index + expect(response).to render_template :index + end + end + end +end