Welcome to mirror list, hosted at ThFree Co, Russian Federation.

gitlab.com/gitlab-org/gitlab-foss.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--app/controllers/application_controller.rb2
-rw-r--r--lib/gitlab/expiring_lock.rb52
-rw-r--r--lib/gitlab/ldap/access.rb6
-rw-r--r--lib/gitlab/user_access.rb5
4 files changed, 63 insertions, 2 deletions
diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb
index fb74919ea23..bc8019193ee 100644
--- a/app/controllers/application_controller.rb
+++ b/app/controllers/application_controller.rb
@@ -246,6 +246,8 @@ class ApplicationController < ActionController::Base
def ldap_security_check
if current_user && current_user.requires_ldap_check?
+ return unless Gitlab::LDAP::Access.try_lock_user(user)
+
unless Gitlab::LDAP::Access.allowed?(current_user)
sign_out current_user
flash[:alert] = "Access denied for your LDAP account."
diff --git a/lib/gitlab/expiring_lock.rb b/lib/gitlab/expiring_lock.rb
new file mode 100644
index 00000000000..ef77a24cf7d
--- /dev/null
+++ b/lib/gitlab/expiring_lock.rb
@@ -0,0 +1,52 @@
+module Gitlab
+ # This class implements a distributed self-expiring lock.
+ #
+ # [2] pry(main)> l = Gitlab::ExpiringLock.new('foobar', 5)
+ # => #<Gitlab::ExpiringLock:0x007ffb9d7cb7f8 @key="foobar", @timeout=5>
+ # [3] pry(main)> l.try_lock
+ # => true
+ # [4] pry(main)> l.try_lock # Only the first try_lock succeeds
+ # => false
+ # [5] pry(main)> l.locked?
+ # => true
+ # [6] pry(main)> sleep 5
+ # => 5
+ # [7] pry(main)> l.locked? # After the timeout the lock is released
+ # => false
+ #
+ class ExpiringLock
+ def initialize(key, timeout)
+ @key, @timeout = key, timeout
+ end
+
+ # Try to obtain the lock. Return true on succes,
+ # false if the lock is already taken.
+ def try_lock
+ # INCR does not change the key TTL
+ if redis.incr(redis_key) == 1
+ # We won the race to insert the key into Redis
+ redis.expire(redis_key, @timeout)
+ true
+ else
+ # Somebody else won the race
+ false
+ end
+ end
+
+ # Check if somebody somewhere locked this key
+ def locked?
+ !!redis.get(redis_key)
+ end
+
+ private
+
+ def redis
+ # Maybe someday we want to use a connection pool...
+ @redis ||= Redis.new(url: Gitlab::RedisConfig.url)
+ end
+
+ def redis_key
+ "gitlab:expiring_lock:#{@key}"
+ end
+ end
+end
diff --git a/lib/gitlab/ldap/access.rb b/lib/gitlab/ldap/access.rb
index da4435c7308..29347c05b7d 100644
--- a/lib/gitlab/ldap/access.rb
+++ b/lib/gitlab/ldap/access.rb
@@ -7,6 +7,12 @@ module Gitlab
class Access
attr_reader :provider, :user
+ LOCK_TIMEOUT = 600
+
+ def self.try_lock_user(user)
+ Gitlab::ExpiringLock.new("user_ldap_check:#{user.id}", LOCK_TIMEOUT).try_lock
+ end
+
def self.open(user, &block)
Gitlab::LDAP::Adapter.open(user.ldap_identity.provider) do |adapter|
block.call(self.new(user, adapter))
diff --git a/lib/gitlab/user_access.rb b/lib/gitlab/user_access.rb
index 4885baf9526..46ac5825fd1 100644
--- a/lib/gitlab/user_access.rb
+++ b/lib/gitlab/user_access.rb
@@ -3,8 +3,9 @@ module Gitlab
def self.allowed?(user)
return false if user.blocked?
- if user.requires_ldap_check?
- return false unless Gitlab::LDAP::Access.allowed?(user)
+ if user.requires_ldap_check? && Gitlab::LDAP::Access.try_lock_user(user)
+ return Gitlab::LDAP::Access.allowed?(user)
+ end
end
true