summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorelijah <elijah@riseup.net>2015-03-11 01:12:23 -0700
committerelijah <elijah@riseup.net>2015-03-17 22:36:27 -0700
commit9266c3ac58404894539e25e514d8d8a6775c701f (patch)
tree3beaa846b37aecdf933763564710c2e1042c03a3
parenta777c4c677e8cbd4f91c66a29ee1ecb347c5b8ab (diff)
add support for rotating tokens and sessions databases, and for a special tmp db for test users.
-rw-r--r--DEPLOY.md5
-rw-r--r--Gemfile4
-rw-r--r--Gemfile.lock6
-rw-r--r--app/models/account.rb23
-rw-r--r--app/models/temporary_user.rb78
-rw-r--r--app/models/token.rb13
-rw-r--r--app/models/user.rb2
-rw-r--r--lib/tasks/leap_web_core_tasks.rake35
-rw-r--r--test/integration/api/srp_test.rb10
-rw-r--r--test/integration/api/tmp_user_test.rb19
-rw-r--r--test/test_helper.rb9
-rw-r--r--test/unit/tmp_user_test.rb29
-rw-r--r--test/unit/token_test.rb2
13 files changed, 208 insertions, 27 deletions
diff --git a/DEPLOY.md b/DEPLOY.md
index 23a54a4..05d97cb 100644
--- a/DEPLOY.md
+++ b/DEPLOY.md
@@ -25,8 +25,9 @@ run `cap deploy` to deploy to the server.
Please make sure your deploy includes the following files:
-* public/config/provider.json
-* config/couchdb.yml
+* `public/config/provider.json` -- provider bootstrap file.
+* `config/couchdb.yml` -- normal webapp couchdb configuration.
+* `config/couchdb.admin.yml` -- configuration used for rake tasks.
## Couch Security ##
diff --git a/Gemfile b/Gemfile
index 1444ff4..be0ed2f 100644
--- a/Gemfile
+++ b/Gemfile
@@ -6,8 +6,8 @@ require File.expand_path('../lib/gemfile_tools.rb', __FILE__)
gem "rails", "~> 3.2.21"
gem "couchrest", "~> 1.1.3"
gem "couchrest_model", "~> 2.0.0"
-unless ARGV.grep(/assets:precompile/)
- gem "couchrest_session_store", "~> 0.2.4"
+if ARGV.grep(/assets:precompile/).empty?
+ gem "couchrest_session_store", "= 0.3.0"
end
## AUTHENTICATION
diff --git a/Gemfile.lock b/Gemfile.lock
index 346155a..4de7e05 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -76,8 +76,8 @@ GEM
couchrest (~> 1.1.3)
mime-types (>= 1.15)
tzinfo (>= 0.3.22)
- couchrest_session_store (0.2.4)
- actionpack
+ couchrest_session_store (0.3.0)
+ actionpack (~> 3.0)
couchrest
couchrest_model
cucumber (1.3.17)
@@ -255,7 +255,7 @@ DEPENDENCIES
client_side_validations-simple_form
couchrest (~> 1.1.3)
couchrest_model (~> 2.0.0)
- couchrest_session_store (~> 0.2.4)
+ couchrest_session_store (= 0.3.0)
cucumber-rails
debugger
factory_girl_rails
diff --git a/app/models/account.rb b/app/models/account.rb
index e60a356..af470ed 100644
--- a/app/models/account.rb
+++ b/app/models/account.rb
@@ -20,7 +20,7 @@ class Account
user = nil
user = User.new(attrs)
user.save
- if user.persisted?
+ if !user.tmp? && user.persisted?
identity = user.identity
identity.user_id = user.id
identity.save
@@ -52,10 +52,12 @@ class Account
def destroy(destroy_identity=false)
return unless @user
- if destroy_identity == false
- Identity.disable_all_for(@user)
- else
- Identity.destroy_all_for(@user)
+ if !user.tmp?
+ if destroy_identity == false
+ Identity.disable_all_for(@user)
+ else
+ Identity.destroy_all_for(@user)
+ end
end
@user.destroy
end
@@ -84,12 +86,11 @@ class Account
end
def self.creation_problem?(user, identity)
- user.nil? ||
- !user.persisted? ||
- identity.nil? ||
- !identity.persisted? ||
- user.errors.any? ||
- identity.errors.any?
+ return true if user.nil? || !user.persisted? || user.errors.any?
+ if !user.tmp?
+ return true if identity.nil? || !identity.persisted? || identity.errors.any?
+ end
+ return false
end
# You can hook into the account lifecycle from different engines using
diff --git a/app/models/temporary_user.rb b/app/models/temporary_user.rb
new file mode 100644
index 0000000..87800ed
--- /dev/null
+++ b/app/models/temporary_user.rb
@@ -0,0 +1,78 @@
+#
+# For users with login '*test_user*', we don't want to store these documents in
+# the main users db. This is because we create and destroy a lot of test
+# users. This weirdness of using a different db for some users breaks a lot of
+# things, such as associations. However, this is OK for now since we don't need
+# those for running the frequent nagios tests.
+#
+# This module is included in user.rb. This will only work if it is included
+# after designs are defined, otherwise, the design definition will overwrite
+# find_by_login().
+#
+
+module TemporaryUser
+ extend ActiveSupport::Concern
+ include CouchRest::Model::DatabaseMethod
+
+ USER_DB = 'users'
+ TMP_USER_DB = 'tmp_users'
+ TMP_LOGIN = 'test_user'
+
+ included do
+ use_database_method :db_name
+
+ # since the original find_by_login is dynamically created with
+ # instance_eval, it appears that we also need to use instance eval to
+ # override it.
+ instance_eval <<-EOS, __FILE__, __LINE__ + 1
+ def find_by_login(*args)
+ if args.grep(/#{TMP_LOGIN}/).any?
+ by_login.database(tmp_database).key(*args).first()
+ else
+ by_login.key(*args).first()
+ end
+ end
+ EOS
+ end
+
+ module ClassMethods
+ def get(id, db = database)
+ super(id, db) || super(id, tmp_database)
+ end
+ alias :find :get
+
+ # calls db_name(TMP_LOGIN), then creates a CouchRest::Database
+ # from the name
+ def tmp_database
+ choose_database(TMP_LOGIN)
+ end
+
+ def db_name(login=nil)
+ if !login.nil? && login.include?(TMP_LOGIN)
+ TMP_USER_DB
+ else
+ USER_DB
+ end
+ end
+
+ # create the tmp db if it doesn't exist.
+ # requires admin access.
+ def create_tmp_database!
+ design_doc.sync!(tmp_database.tap{|db|db.create!})
+ end
+ end
+
+ #
+ # this gets called each and every time a User object needs to
+ # access the database.
+ #
+ def db_name
+ self.class.db_name(self.login)
+ end
+
+ # returns true if this User instance is stored in tmp db.
+ def tmp?
+ !login.nil? && login.include?(TMP_LOGIN)
+ end
+
+end
diff --git a/app/models/token.rb b/app/models/token.rb
index ff2ad12..4afd275 100644
--- a/app/models/token.rb
+++ b/app/models/token.rb
@@ -1,8 +1,15 @@
require 'digest/sha2'
class Token < CouchRest::Model::Base
+ def self.expires_after
+ APP_CONFIG[:auth] && APP_CONFIG[:auth][:token_expires_after]
+ end
- use_database :tokens
+ include CouchRest::Model::Rotation
+ rotate_database :tokens,
+ :every => 1.month,
+ :timestamp_field => :last_seen_at,
+ :timeout => self.expires_after.to_i # in minutes
belongs_to :user
@@ -23,10 +30,6 @@ class Token < CouchRest::Model::Base
self.find Digest::SHA512.hexdigest(token)
end
- def self.expires_after
- APP_CONFIG[:auth] && APP_CONFIG[:auth][:token_expires_after]
- end
-
def self.expired
return [] unless expires_after
by_last_seen_at.endkey(expires_after.minutes.ago)
diff --git a/app/models/user.rb b/app/models/user.rb
index 9ac7d3d..52e20dd 100644
--- a/app/models/user.rb
+++ b/app/models/user.rb
@@ -42,6 +42,8 @@ class User < CouchRest::Model::Base
view :by_created_at
end # end of design
+ include TemporaryUser # MUST come after designs are defined.
+
def self.login_starts_with(query)
self.by_login.startkey(query).endkey(query + "\ufff0")
end
diff --git a/lib/tasks/leap_web_core_tasks.rake b/lib/tasks/leap_web_core_tasks.rake
index ec6abac..e446f93 100644
--- a/lib/tasks/leap_web_core_tasks.rake
+++ b/lib/tasks/leap_web_core_tasks.rake
@@ -8,7 +8,6 @@ namespace :couchrest do
end
namespace :cleanup do
-
desc "Cleanup all expired session documents"
task :sessions => :environment do
# make sure this is the same as in
@@ -23,3 +22,37 @@ namespace :cleanup do
end
end
+namespace :db do
+ desc "Rotate the databases, as needed."
+ task :rotate => :environment do
+ #
+ # db rotation must be performed by admin, and since
+ # CouchRest::Session::Document is not a CouchRest::Model, we need to
+ # override the default config twice.
+ #
+
+ CouchRest::Model::Base.configure do |conf|
+ conf.environment = Rails.env
+ conf.connection_config_file = File.join(Rails.root, 'config', 'couchdb.admin.yml')
+ end
+ Token.rotate_database_now(:window => 1.day)
+
+ CouchRest::Session::Document.configure do |conf|
+ conf.environment = Rails.env
+ conf.connection_config_file = File.join(Rails.root, 'config', 'couchdb.admin.yml')
+ end
+ CouchRest::Session::Document.rotate_database_now(:window => 1.day)
+ end
+
+ desc "Delete and recreate temporary databases."
+ task :deletetmp => :environment do
+ # db deletion and creation must be performed by admin
+ CouchRest::Model::Base.configure do |conf|
+ conf.environment = Rails.env
+ conf.connection_config_file = File.join(Rails.root, 'config', 'couchdb.admin.yml')
+ end
+ User.tmp_database.recreate!
+ User.design_doc.sync!(User.tmp_database)
+ end
+
+end
diff --git a/test/integration/api/srp_test.rb b/test/integration/api/srp_test.rb
index 946450e..fbef47e 100644
--- a/test/integration/api/srp_test.rb
+++ b/test/integration/api/srp_test.rb
@@ -32,11 +32,11 @@ class SrpTest < RackTest
attr_reader :server_auth
- def register_user(login = "integration_test_user", password = 'srp, verify me!')
+ def register_user(login = "integration_test", password = 'srp, verify me!')
cleanup_user(login)
post 'http://api.lvh.me:3000/1/users.json',
user_params(login: login, password: password)
- @user = User.find_by_login(login)
+ assert(@user = User.find_by_login(login), 'user should have been created: %s' % last_response_errors)
@login = login
@password = password
end
@@ -101,4 +101,10 @@ class SrpTest < RackTest
SRP::Client.new(params.delete(:login) || @login, params)
end
end
+
+ def last_response_errors
+ JSON.parse(last_response.body)['errors']
+ rescue
+ ""
+ end
end
diff --git a/test/integration/api/tmp_user_test.rb b/test/integration/api/tmp_user_test.rb
new file mode 100644
index 0000000..4c1e659
--- /dev/null
+++ b/test/integration/api/tmp_user_test.rb
@@ -0,0 +1,19 @@
+require 'test_helper'
+require_relative 'srp_test'
+
+class TmpUserTest < SrpTest
+
+ setup do
+ register_user('test_user_'+SecureRandom.hex(5))
+ end
+
+ test "login with srp" do
+ authenticate
+ assert_nil server_auth["errors"]
+ assert_nil server_auth["error"]
+ assert_equal ["M2", "id", "token"], server_auth.keys
+ assert last_response.successful?
+ assert server_auth["M2"]
+ end
+
+end
diff --git a/test/test_helper.rb b/test/test_helper.rb
index 7959ddb..dfc6627 100644
--- a/test/test_helper.rb
+++ b/test/test_helper.rb
@@ -30,3 +30,12 @@ class ActiveSupport::TestCase
require 'i18n/missing_translations'
at_exit { I18n.missing_translations.dump }
end
+
+#
+# Create databases, since the temporary databases might not have been created
+# when `rake couchrest:migrate` was run.
+#
+
+Token.create_database! if Token.respond_to?(:create_database)
+CouchRest::Session::Document.create_database! if CouchRest::Session::Document.respond_to?(:create_database)
+User.create_tmp_database! if User.respond_to?(:create_tmp_database)
diff --git a/test/unit/tmp_user_test.rb b/test/unit/tmp_user_test.rb
new file mode 100644
index 0000000..55b117f
--- /dev/null
+++ b/test/unit/tmp_user_test.rb
@@ -0,0 +1,29 @@
+require 'test_helper'
+
+class TmpUserTest < ActiveSupport::TestCase
+
+ test "test_user saved to tmp_users" do
+ begin
+ assert User.ancestors.include?(TemporaryUser)
+
+ assert_difference('User.database.info["doc_count"]') do
+ normal_user = User.create!(:login => 'a'+SecureRandom.hex(5).downcase,
+ :password_verifier => 'ABCDEF0010101', :password_salt => 'ABCDEF')
+ refute normal_user.database.to_s.include?('tmp')
+ end
+
+ assert_difference('User.tmp_database.info["doc_count"]') do
+ tmp_user = User.create!(:login => 'test_user_'+SecureRandom.hex(5).downcase,
+ :password_verifier => 'ABCDEF0010101', :password_salt => 'ABCDEF')
+ assert tmp_user.database.to_s.include?('tmp')
+ end
+ ensure
+ begin
+ normal_user.destroy
+ tmp_user.destroy
+ rescue
+ end
+ end
+ end
+
+end
diff --git a/test/unit/token_test.rb b/test/unit/token_test.rb
index b143345..5468650 100644
--- a/test/unit/token_test.rb
+++ b/test/unit/token_test.rb
@@ -1,6 +1,6 @@
require 'test_helper'
-class ClientCertificateTest < ActiveSupport::TestCase
+class TokenTest < ActiveSupport::TestCase
include StubRecordHelper
setup do