Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
G
gitlab-ce
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
1
Merge Requests
1
Analytics
Analytics
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Commits
Issue Boards
Open sidebar
nexedi
gitlab-ce
Commits
1192fb33
Commit
1192fb33
authored
Jan 19, 2022
by
Pavel Shutsin
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Simplify ApplicationRateLimiter
Removed unused methods and declarations
parent
39422b7b
Changes
5
Hide whitespace changes
Inline
Side-by-side
Showing
5 changed files
with
20 additions
and
48 deletions
+20
-48
app/services/concerns/rate_limited_service.rb
app/services/concerns/rate_limited_service.rb
+7
-12
lib/api/helpers/rate_limiter.rb
lib/api/helpers/rate_limiter.rb
+1
-1
lib/gitlab/application_rate_limiter.rb
lib/gitlab/application_rate_limiter.rb
+2
-23
spec/services/concerns/rate_limited_service_spec.rb
spec/services/concerns/rate_limited_service_spec.rb
+9
-11
spec/services/issues/create_service_spec.rb
spec/services/issues/create_service_spec.rb
+1
-1
No files found.
app/services/concerns/rate_limited_service.rb
View file @
1192fb33
...
@@ -17,7 +17,7 @@ module RateLimitedService
...
@@ -17,7 +17,7 @@ module RateLimitedService
end
end
def
log_request
(
request
,
current_user
)
def
log_request
(
request
,
current_user
)
rate_limiter
.
class
.
log_request
(
request
,
"
#{
key
}
_request_limit"
.
to_sym
,
current_user
)
rate_limiter
.
log_request
(
request
,
"
#{
key
}
_request_limit"
.
to_sym
,
current_user
)
end
end
private
private
...
@@ -26,20 +26,19 @@ module RateLimitedService
...
@@ -26,20 +26,19 @@ module RateLimitedService
end
end
class
RateLimiterScopedAndKeyed
class
RateLimiterScopedAndKeyed
attr_reader
:key
,
:opts
,
:rate_limiter
_klass
attr_reader
:key
,
:opts
,
:rate_limiter
def
initialize
(
key
:,
opts
:,
rate_limiter
_klass
:)
def
initialize
(
key
:,
opts
:,
rate_limiter
:)
@key
=
key
@key
=
key
@opts
=
opts
@opts
=
opts
@rate_limiter
_klass
=
rate_limiter_klass
@rate_limiter
=
rate_limiter
end
end
def
rate_limit!
(
service
)
def
rate_limit!
(
service
)
evaluated_scope
=
evaluated_scope_for
(
service
)
evaluated_scope
=
evaluated_scope_for
(
service
)
return
if
feature_flag_disabled?
(
evaluated_scope
[
:project
])
return
if
feature_flag_disabled?
(
evaluated_scope
[
:project
])
rate_limiter
=
new_rate_limiter
(
evaluated_scope
)
if
rate_limiter
.
throttled?
(
key
,
**
opts
.
merge
(
scope:
evaluated_scope
.
values
,
users_allowlist:
users_allowlist
))
if
rate_limiter
.
throttled?
raise
RateLimitedError
.
new
(
key:
key
,
rate_limiter:
rate_limiter
),
_
(
'This endpoint has been requested too many times. Try again later.'
)
raise
RateLimitedError
.
new
(
key:
key
,
rate_limiter:
rate_limiter
),
_
(
'This endpoint has been requested too many times. Try again later.'
)
end
end
end
end
...
@@ -59,20 +58,16 @@ module RateLimitedService
...
@@ -59,20 +58,16 @@ module RateLimitedService
def
feature_flag_disabled?
(
project
)
def
feature_flag_disabled?
(
project
)
Feature
.
disabled?
(
"rate_limited_service_
#{
key
}
"
,
project
,
default_enabled: :yaml
)
Feature
.
disabled?
(
"rate_limited_service_
#{
key
}
"
,
project
,
default_enabled: :yaml
)
end
end
def
new_rate_limiter
(
evaluated_scope
)
rate_limiter_klass
.
new
(
key
,
**
opts
.
merge
(
scope:
evaluated_scope
.
values
,
users_allowlist:
users_allowlist
))
end
end
end
prepended
do
prepended
do
attr_accessor
:rate_limiter_bypassed
attr_accessor
:rate_limiter_bypassed
cattr_accessor
:rate_limiter_scoped_and_keyed
cattr_accessor
:rate_limiter_scoped_and_keyed
def
self
.
rate_limit
(
key
:,
opts
:,
rate_limiter
_klass
:
::
Gitlab
::
ApplicationRateLimiter
)
def
self
.
rate_limit
(
key
:,
opts
:,
rate_limiter:
::
Gitlab
::
ApplicationRateLimiter
)
self
.
rate_limiter_scoped_and_keyed
=
RateLimiterScopedAndKeyed
.
new
(
key:
key
,
self
.
rate_limiter_scoped_and_keyed
=
RateLimiterScopedAndKeyed
.
new
(
key:
key
,
opts:
opts
,
opts:
opts
,
rate_limiter
_klass:
rate_limiter_klass
)
rate_limiter
:
rate_limiter
)
end
end
end
end
...
...
lib/api/helpers/rate_limiter.rb
View file @
1192fb33
...
@@ -5,7 +5,7 @@ module API
...
@@ -5,7 +5,7 @@ module API
# == RateLimiter
# == RateLimiter
#
#
# Helper that checks if the rate limit for a given endpoint is throttled by calling the
# Helper that checks if the rate limit for a given endpoint is throttled by calling the
# Gitlab::ApplicationRateLimiter
class
. If the action is throttled for the current user, the request
# Gitlab::ApplicationRateLimiter
module
. If the action is throttled for the current user, the request
# will be logged and an error message will be rendered with a Too Many Requests response status.
# will be logged and an error message will be rendered with a Too Many Requests response status.
# See app/controllers/concerns/check_rate_limit.rb for Rails controllers version
# See app/controllers/concerns/check_rate_limit.rb for Rails controllers version
module
RateLimiter
module
RateLimiter
...
...
lib/gitlab/application_rate_limiter.rb
View file @
1192fb33
# frozen_string_literal: true
# frozen_string_literal: true
module
Gitlab
module
Gitlab
# This
class
implements a simple rate limiter that can be used to throttle
# This
module
implements a simple rate limiter that can be used to throttle
# certain actions. Unlike Rack Attack and Rack::Throttle, which operate at
# certain actions. Unlike Rack Attack and Rack::Throttle, which operate at
# the middleware level, this can be used at the controller or API level.
# the middleware level, this can be used at the controller or API level.
# See CheckRateLimit concern for usage.
# See CheckRateLimit concern for usage.
class
ApplicationRateLimiter
module
ApplicationRateLimiter
InvalidKeyError
=
Class
.
new
(
StandardError
)
InvalidKeyError
=
Class
.
new
(
StandardError
)
def
initialize
(
key
,
**
options
)
@key
=
key
@options
=
options
end
def
throttled?
self
.
class
.
throttled?
(
key
,
**
options
)
end
def
threshold_value
options
[
:threshold
]
||
self
.
class
.
threshold
(
key
)
end
def
interval_value
self
.
class
.
interval
(
key
)
end
class
<<
self
class
<<
self
# Application rate limits
# Application rate limits
#
#
...
@@ -201,9 +184,5 @@ module Gitlab
...
@@ -201,9 +184,5 @@ module Gitlab
scoped_user
.
username
.
downcase
.
in?
(
users_allowlist
)
scoped_user
.
username
.
downcase
.
in?
(
users_allowlist
)
end
end
end
end
private
attr_reader
:key
,
:options
end
end
end
end
spec/services/concerns/rate_limited_service_spec.rb
View file @
1192fb33
...
@@ -6,11 +6,10 @@ RSpec.describe RateLimitedService do
...
@@ -6,11 +6,10 @@ RSpec.describe RateLimitedService do
let
(
:key
)
{
:issues_create
}
let
(
:key
)
{
:issues_create
}
let
(
:scope
)
{
[
:project
,
:current_user
]
}
let
(
:scope
)
{
[
:project
,
:current_user
]
}
let
(
:opts
)
{
{
scope:
scope
,
users_allowlist:
->
{
[
User
.
support_bot
.
username
]
}
}
}
let
(
:opts
)
{
{
scope:
scope
,
users_allowlist:
->
{
[
User
.
support_bot
.
username
]
}
}
}
let
(
:rate_limiter_klass
)
{
::
Gitlab
::
ApplicationRateLimiter
}
let
(
:rate_limiter
)
{
::
Gitlab
::
ApplicationRateLimiter
}
let
(
:rate_limiter_instance
)
{
rate_limiter_klass
.
new
(
key
,
**
opts
)
}
describe
'RateLimitedError'
do
describe
'RateLimitedError'
do
subject
{
described_class
::
RateLimitedError
.
new
(
key:
key
,
rate_limiter:
rate_limiter
_instance
)
}
subject
{
described_class
::
RateLimitedError
.
new
(
key:
key
,
rate_limiter:
rate_limiter
)
}
describe
'#headers'
do
describe
'#headers'
do
it
'returns a Hash of HTTP headers'
do
it
'returns a Hash of HTTP headers'
do
...
@@ -26,7 +25,7 @@ RSpec.describe RateLimitedService do
...
@@ -26,7 +25,7 @@ RSpec.describe RateLimitedService do
request
=
instance_double
(
Grape
::
Request
)
request
=
instance_double
(
Grape
::
Request
)
user
=
instance_double
(
User
)
user
=
instance_double
(
User
)
expect
(
rate_limiter
_klass
).
to
receive
(
:log_request
).
with
(
request
,
"
#{
key
}
_request_limit"
.
to_sym
,
user
)
expect
(
rate_limiter
).
to
receive
(
:log_request
).
with
(
request
,
"
#{
key
}
_request_limit"
.
to_sym
,
user
)
subject
.
log_request
(
request
,
user
)
subject
.
log_request
(
request
,
user
)
end
end
...
@@ -34,7 +33,7 @@ RSpec.describe RateLimitedService do
...
@@ -34,7 +33,7 @@ RSpec.describe RateLimitedService do
end
end
describe
'RateLimiterScopedAndKeyed'
do
describe
'RateLimiterScopedAndKeyed'
do
subject
{
described_class
::
RateLimiterScopedAndKeyed
.
new
(
key:
key
,
opts:
opts
,
rate_limiter
_klass:
rate_limiter_klass
)
}
subject
{
described_class
::
RateLimiterScopedAndKeyed
.
new
(
key:
key
,
opts:
opts
,
rate_limiter
:
rate_limiter
)
}
describe
'#rate_limit!'
do
describe
'#rate_limit!'
do
let
(
:project_with_feature_enabled
)
{
create
(
:project
)
}
let
(
:project_with_feature_enabled
)
{
create
(
:project
)
}
...
@@ -49,13 +48,12 @@ RSpec.describe RateLimitedService do
...
@@ -49,13 +48,12 @@ RSpec.describe RateLimitedService do
let
(
:rate_limited_service_issues_create_feature_enabled
)
{
nil
}
let
(
:rate_limited_service_issues_create_feature_enabled
)
{
nil
}
before
do
before
do
allow
(
rate_limiter_klass
).
to
receive
(
:new
).
with
(
key
,
**
evaluated_opts
).
and_return
(
rate_limiter_instance
)
stub_feature_flags
(
rate_limited_service_issues_create:
rate_limited_service_issues_create_feature_enabled
)
stub_feature_flags
(
rate_limited_service_issues_create:
rate_limited_service_issues_create_feature_enabled
)
end
end
shared_examples
'a service that does not attempt to throttle'
do
shared_examples
'a service that does not attempt to throttle'
do
it
'does not attempt to throttle'
do
it
'does not attempt to throttle'
do
expect
(
rate_limiter
_instance
).
not_to
receive
(
:throttled?
)
expect
(
rate_limiter
).
not_to
receive
(
:throttled?
)
expect
(
subject
.
rate_limit!
(
service
)).
to
be_nil
expect
(
subject
.
rate_limit!
(
service
)).
to
be_nil
end
end
...
@@ -63,7 +61,7 @@ RSpec.describe RateLimitedService do
...
@@ -63,7 +61,7 @@ RSpec.describe RateLimitedService do
shared_examples
'a service that does attempt to throttle'
do
shared_examples
'a service that does attempt to throttle'
do
before
do
before
do
allow
(
rate_limiter
_instance
).
to
receive
(
:throttled?
).
and_return
(
throttled
)
allow
(
rate_limiter
).
to
receive
(
:throttled?
).
and_return
(
throttled
)
end
end
context
'when rate limiting is not in effect'
do
context
'when rate limiting is not in effect'
do
...
@@ -134,7 +132,7 @@ RSpec.describe RateLimitedService do
...
@@ -134,7 +132,7 @@ RSpec.describe RateLimitedService do
end
end
before
do
before
do
allow
(
RateLimitedService
::
RateLimiterScopedAndKeyed
).
to
receive
(
:new
).
with
(
key:
key
,
opts:
opts
,
rate_limiter
_klass:
rate_limiter_klass
).
and_return
(
rate_limiter_scoped_and_keyed
)
allow
(
RateLimitedService
::
RateLimiterScopedAndKeyed
).
to
receive
(
:new
).
with
(
key:
key
,
opts:
opts
,
rate_limiter
:
rate_limiter
).
and_return
(
rate_limiter_scoped_and_keyed
)
end
end
context
'bypasses rate limiting'
do
context
'bypasses rate limiting'
do
...
@@ -173,12 +171,12 @@ RSpec.describe RateLimitedService do
...
@@ -173,12 +171,12 @@ RSpec.describe RateLimitedService do
end
end
before
do
before
do
allow
(
RateLimitedService
::
RateLimiterScopedAndKeyed
).
to
receive
(
:new
).
with
(
key:
key
,
opts:
opts
,
rate_limiter
_klass:
rate_limiter_klass
).
and_return
(
rate_limiter_scoped_and_keyed
)
allow
(
RateLimitedService
::
RateLimiterScopedAndKeyed
).
to
receive
(
:new
).
with
(
key:
key
,
opts:
opts
,
rate_limiter
:
rate_limiter
).
and_return
(
rate_limiter_scoped_and_keyed
)
end
end
context
'and applies rate limiting'
do
context
'and applies rate limiting'
do
it
'raises an RateLimitedService::RateLimitedError exception'
do
it
'raises an RateLimitedService::RateLimitedError exception'
do
expect
(
rate_limiter_scoped_and_keyed
).
to
receive
(
:rate_limit!
).
with
(
subject
).
and_raise
(
RateLimitedService
::
RateLimitedError
.
new
(
key:
key
,
rate_limiter:
rate_limiter
_instance
))
expect
(
rate_limiter_scoped_and_keyed
).
to
receive
(
:rate_limit!
).
with
(
subject
).
and_raise
(
RateLimitedService
::
RateLimitedError
.
new
(
key:
key
,
rate_limiter:
rate_limiter
))
expect
{
subject
.
execute
}.
to
raise_error
(
RateLimitedService
::
RateLimitedError
)
expect
{
subject
.
execute
}.
to
raise_error
(
RateLimitedService
::
RateLimitedError
)
end
end
...
...
spec/services/issues/create_service_spec.rb
View file @
1192fb33
...
@@ -17,7 +17,7 @@ RSpec.describe Issues::CreateService do
...
@@ -17,7 +17,7 @@ RSpec.describe Issues::CreateService do
expect
(
described_class
.
rate_limiter_scoped_and_keyed
.
key
).
to
eq
(
:issues_create
)
expect
(
described_class
.
rate_limiter_scoped_and_keyed
.
key
).
to
eq
(
:issues_create
)
expect
(
described_class
.
rate_limiter_scoped_and_keyed
.
opts
[
:scope
]).
to
eq
(
%i[project current_user external_author]
)
expect
(
described_class
.
rate_limiter_scoped_and_keyed
.
opts
[
:scope
]).
to
eq
(
%i[project current_user external_author]
)
expect
(
described_class
.
rate_limiter_scoped_and_keyed
.
rate_limiter
_klass
).
to
eq
(
Gitlab
::
ApplicationRateLimiter
)
expect
(
described_class
.
rate_limiter_scoped_and_keyed
.
rate_limiter
).
to
eq
(
Gitlab
::
ApplicationRateLimiter
)
end
end
end
end
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment