BigW Consortium Gitlab
Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
G
gitlab-ce
Project
Overview
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
Registry
Registry
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Commits
Issue Boards
Open sidebar
Forest Godfrey
gitlab-ce
Commits
1e4b75ba
Commit
1e4b75ba
authored
Oct 12, 2017
by
Robert Speicher
Browse files
Options
Browse Files
Download
Plain Diff
Merge branch 'dm-api-authentication' into 'master'
Move all API authentication code to APIGuard See merge request gitlab-org/gitlab-ce!14528
parents
5d4e0733
025c6eea
Hide whitespace changes
Inline
Side-by-side
Showing
5 changed files
with
108 additions
and
105 deletions
+108
-105
oauth_access_token.rb
app/models/oauth_access_token.rb
+2
-0
api_guard.rb
lib/api/api_guard.rb
+92
-41
helpers.rb
lib/api/helpers.rb
+1
-51
helpers_spec.rb
spec/requests/api/helpers_spec.rb
+9
-9
read_user_shared_examples.rb
spec/support/api/scopes/read_user_shared_examples.rb
+4
-4
No files found.
app/models/oauth_access_token.rb
View file @
1e4b75ba
class
OauthAccessToken
<
Doorkeeper
::
AccessToken
belongs_to
:resource_owner
,
class_name:
'User'
belongs_to
:application
,
class_name:
'Doorkeeper::Application'
alias_method
:user
,
:resource_owner
end
lib/api/api_guard.rb
View file @
1e4b75ba
...
...
@@ -42,6 +42,38 @@ module API
# Helper Methods for Grape Endpoint
module
HelperMethods
def
find_current_user
user
=
find_user_from_private_token
||
find_user_from_oauth_token
||
find_user_from_warden
return
nil
unless
user
raise
UnauthorizedError
unless
Gitlab
::
UserAccess
.
new
(
user
).
allowed?
&&
user
.
can?
(
:access_api
)
user
end
def
private_token
params
[
PRIVATE_TOKEN_PARAM
]
||
env
[
PRIVATE_TOKEN_HEADER
]
end
private
def
find_user_from_private_token
token_string
=
private_token
.
to_s
return
nil
unless
token_string
.
present?
user
=
find_user_by_authentication_token
(
token_string
)
||
find_user_by_personal_access_token
(
token_string
)
raise
UnauthorizedError
unless
user
user
end
# Invokes the doorkeeper guard.
#
# If token is presented and valid, then it sets @current_user.
...
...
@@ -60,70 +92,89 @@ module API
# scopes: (optional) scopes required for this guard.
# Defaults to empty array.
#
def
doorkeeper_guard
(
scopes:
[])
access_token
=
find_access_token
return
nil
unless
access_token
case
AccessTokenValidationService
.
new
(
access_token
,
request:
request
).
validate
(
scopes:
scopes
)
when
AccessTokenValidationService
::
INSUFFICIENT_SCOPE
raise
InsufficientScopeError
.
new
(
scopes
)
when
AccessTokenValidationService
::
EXPIRED
raise
ExpiredError
def
find_user_from_oauth_token
access_token
=
find_oauth_access_token
return
unless
access_token
when
AccessTokenValidationService
::
REVOKED
raise
RevokedError
find_user_by_access_token
(
access_token
)
end
when
AccessTokenValidationService
::
VALID
User
.
find
(
access_token
.
resource_owner_id
)
end
def
find_user_by_authentication_token
(
token_string
)
User
.
find_by_authentication_token
(
token_string
)
end
def
find_user_by_private_token
(
scopes:
[])
token_string
=
(
params
[
PRIVATE_TOKEN_PARAM
]
||
env
[
PRIVATE_TOKEN_HEADER
]).
to_s
def
find_user_by_personal_access_token
(
token_string
)
access_token
=
PersonalAccessToken
.
find_by_token
(
token_string
)
return
unless
access_token
return
nil
unless
token_string
.
present?
find_user_by_access_token
(
access_token
)
end
user
=
find_user_by_authentication_token
(
token_string
)
||
find_user_by_personal_access_token
(
token_string
,
scopes
)
# Check the Rails session for valid authentication details
def
find_user_from_warden
warden
.
try
(
:authenticate
)
if
verified_request?
end
raise
UnauthorizedError
unless
user
def
warden
env
[
'warden'
]
end
user
# Check if the request is GET/HEAD, or if CSRF token is valid.
def
verified_request?
Gitlab
::
RequestForgeryProtection
.
verified?
(
env
)
end
private
def
find_oauth_access_token
return
@oauth_access_token
if
defined?
(
@oauth_access_token
)
def
find_user_by_authentication_token
(
token_string
)
User
.
find_by_authentication_token
(
token_string
)
end
token
=
Doorkeeper
::
OAuth
::
Token
.
from_request
(
doorkeeper_request
,
*
Doorkeeper
.
configuration
.
access_token_methods
)
return
@oauth_access_token
=
nil
unless
token
def
find_user_by_personal_access_token
(
token_string
,
scopes
)
access_token
=
PersonalAccessToken
.
active
.
find_by_token
(
token_string
)
return
unless
access_token
@oauth_access_token
=
OauthAccessToken
.
by_token
(
token
)
raise
UnauthorizedError
unless
@oauth_access_token
if
AccessTokenValidationService
.
new
(
access_token
,
request:
request
).
include_any_scope?
(
scopes
)
User
.
find
(
access_token
.
user_id
)
end
@oauth_access_token
.
revoke_previous_refresh_token!
@oauth_access_token
end
def
find_
access_token
return
@access_token
if
defined?
(
@access_token
)
def
find_
user_by_access_token
(
access_token
)
scopes
=
scopes_registered_for_endpoint
token
=
Doorkeeper
::
OAuth
::
Token
.
from_request
(
doorkeeper_request
,
*
Doorkeeper
.
configuration
.
access_token_methods
)
return
@access_token
=
nil
unless
token
case
AccessTokenValidationService
.
new
(
access_token
,
request:
request
).
validate
(
scopes:
scopes
)
when
AccessTokenValidationService
::
INSUFFICIENT_SCOPE
raise
InsufficientScopeError
.
new
(
scopes
)
when
AccessTokenValidationService
::
EXPIRED
raise
ExpiredError
@access_token
=
Doorkeeper
::
AccessToken
.
by_token
(
token
)
raise
UnauthorizedError
unless
@access_token
when
AccessTokenValidationService
::
REVOKED
raise
RevokedError
@access_token
.
revoke_previous_refresh_token!
@access_token
when
AccessTokenValidationService
::
VALID
access_token
.
user
end
end
def
doorkeeper_request
@doorkeeper_request
||=
ActionDispatch
::
Request
.
new
(
env
)
end
# An array of scopes that were registered (using `allow_access_with_scope`)
# for the current endpoint class. It also returns scopes registered on
# `API::API`, since these are meant to apply to all API routes.
def
scopes_registered_for_endpoint
@scopes_registered_for_endpoint
||=
begin
endpoint_classes
=
[
options
[
:for
].
presence
,
::
API
::
API
].
compact
endpoint_classes
.
reduce
([])
do
|
memo
,
endpoint
|
if
endpoint
.
respond_to?
(
:allowed_scopes
)
memo
.
concat
(
endpoint
.
allowed_scopes
)
else
memo
end
end
end
end
end
module
ClassMethods
...
...
lib/api/helpers.rb
View file @
1e4b75ba
...
...
@@ -3,8 +3,6 @@ module API
include
Gitlab
::
Utils
include
Helpers
::
Pagination
UnauthorizedError
=
Class
.
new
(
StandardError
)
SUDO_HEADER
=
"HTTP_SUDO"
.
freeze
SUDO_PARAM
=
:sudo
...
...
@@ -379,47 +377,16 @@ module API
private
def
private_token
params
[
APIGuard
::
PRIVATE_TOKEN_PARAM
]
||
env
[
APIGuard
::
PRIVATE_TOKEN_HEADER
]
end
def
warden
env
[
'warden'
]
end
# Check if the request is GET/HEAD, or if CSRF token is valid.
def
verified_request?
Gitlab
::
RequestForgeryProtection
.
verified?
(
env
)
end
# Check the Rails session for valid authentication details
def
find_user_from_warden
warden
.
try
(
:authenticate
)
if
verified_request?
end
def
initial_current_user
return
@initial_current_user
if
defined?
(
@initial_current_user
)
begin
@initial_current_user
=
Gitlab
::
Auth
::
UniqueIpsLimiter
.
limit_user!
{
find_current_user
}
rescue
APIGuard
::
UnauthorizedError
,
UnauthorizedError
rescue
APIGuard
::
UnauthorizedError
unauthorized!
end
end
def
find_current_user
user
=
find_user_by_private_token
(
scopes:
scopes_registered_for_endpoint
)
||
doorkeeper_guard
(
scopes:
scopes_registered_for_endpoint
)
||
find_user_from_warden
return
nil
unless
user
raise
UnauthorizedError
unless
Gitlab
::
UserAccess
.
new
(
user
).
allowed?
&&
user
.
can?
(
:access_api
)
user
end
def
sudo!
return
unless
sudo_identifier
return
unless
initial_current_user
...
...
@@ -479,22 +446,5 @@ module API
exception
.
status
==
500
end
# An array of scopes that were registered (using `allow_access_with_scope`)
# for the current endpoint class. It also returns scopes registered on
# `API::API`, since these are meant to apply to all API routes.
def
scopes_registered_for_endpoint
@scopes_registered_for_endpoint
||=
begin
endpoint_classes
=
[
options
[
:for
].
presence
,
::
API
::
API
].
compact
endpoint_classes
.
reduce
([])
do
|
memo
,
endpoint
|
if
endpoint
.
respond_to?
(
:allowed_scopes
)
memo
.
concat
(
endpoint
.
allowed_scopes
)
else
memo
end
end
end
end
end
end
spec/requests/api/helpers_spec.rb
View file @
1e4b75ba
...
...
@@ -222,13 +222,6 @@ describe API::Helpers do
expect
{
current_user
}.
to
raise_error
/401/
end
it
"returns a 401 response for a token without the appropriate scope"
do
personal_access_token
=
create
(
:personal_access_token
,
user:
user
,
scopes:
[
'read_user'
])
env
[
API
::
APIGuard
::
PRIVATE_TOKEN_HEADER
]
=
personal_access_token
.
token
expect
{
current_user
}.
to
raise_error
/401/
end
it
"leaves user as is when sudo not specified"
do
env
[
API
::
APIGuard
::
PRIVATE_TOKEN_HEADER
]
=
personal_access_token
.
token
expect
(
current_user
).
to
eq
(
user
)
...
...
@@ -238,18 +231,25 @@ describe API::Helpers do
expect
(
current_user
).
to
eq
(
user
)
end
it
"does not allow tokens without the appropriate scope"
do
personal_access_token
=
create
(
:personal_access_token
,
user:
user
,
scopes:
[
'read_user'
])
env
[
API
::
APIGuard
::
PRIVATE_TOKEN_HEADER
]
=
personal_access_token
.
token
expect
{
current_user
}.
to
raise_error
API
::
APIGuard
::
InsufficientScopeError
end
it
'does not allow revoked tokens'
do
personal_access_token
.
revoke!
env
[
API
::
APIGuard
::
PRIVATE_TOKEN_HEADER
]
=
personal_access_token
.
token
expect
{
current_user
}.
to
raise_error
/401/
expect
{
current_user
}.
to
raise_error
API
::
APIGuard
::
RevokedError
end
it
'does not allow expired tokens'
do
personal_access_token
.
update_attributes!
(
expires_at:
1
.
day
.
ago
)
env
[
API
::
APIGuard
::
PRIVATE_TOKEN_HEADER
]
=
personal_access_token
.
token
expect
{
current_user
}.
to
raise_error
/401/
expect
{
current_user
}.
to
raise_error
API
::
APIGuard
::
ExpiredError
end
end
...
...
spec/support/api/scopes/read_user_shared_examples.rb
View file @
1e4b75ba
...
...
@@ -27,10 +27,10 @@ shared_examples_for 'allows the "read_user" scope' do
stub_container_registry_config
(
enabled:
true
)
end
it
'returns a "40
1
" response'
do
it
'returns a "40
3
" response'
do
get
api_call
.
call
(
path
,
user
,
personal_access_token:
token
)
expect
(
response
).
to
have_http_status
(
40
1
)
expect
(
response
).
to
have_http_status
(
40
3
)
end
end
end
...
...
@@ -74,10 +74,10 @@ shared_examples_for 'does not allow the "read_user" scope' do
context
'when the requesting token has the "read_user" scope'
do
let
(
:token
)
{
create
(
:personal_access_token
,
scopes:
[
'read_user'
],
user:
user
)
}
it
'returns a "40
1
" response'
do
it
'returns a "40
3
" response'
do
post
api_call
.
call
(
path
,
user
,
personal_access_token:
token
),
attributes_for
(
:user
,
projects_limit:
3
)
expect
(
response
).
to
have_http_status
(
40
1
)
expect
(
response
).
to
have_http_status
(
40
3
)
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