diff options
author | Jacob Vosmaer <contact@jacobvosmaer.nl> | 2016-03-09 19:11:24 +0100 |
---|---|---|
committer | Jacob Vosmaer <contact@jacobvosmaer.nl> | 2016-03-09 19:11:24 +0100 |
commit | acd9bc0213098a5626499021eb72595eb556f9be (patch) | |
tree | 5492c07f5e8f5695fd8c43120bda79a9e7286fdf /lib | |
parent | 3eb7ea49feaf9341b2eec6eb0c58ae2f0aa37864 (diff) | |
download | gitlab-ce-acd9bc0213098a5626499021eb72595eb556f9be.tar.gz |
Acquire lock before LDAP sync
Diffstat (limited to 'lib')
-rw-r--r-- | lib/gitlab/expiring_lock.rb | 52 | ||||
-rw-r--r-- | lib/gitlab/ldap/access.rb | 6 | ||||
-rw-r--r-- | lib/gitlab/user_access.rb | 5 |
3 files changed, 61 insertions, 2 deletions
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 |