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
0
Merge Requests
0
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
Boxiang Sun
gitlab-ce
Commits
3ed96afc
Commit
3ed96afc
authored
Nov 21, 2016
by
tiagonbotelho
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
adds impersonator variable and makes sudo usage overall more clear
parent
8b379465
Changes
10
Hide whitespace changes
Inline
Side-by-side
Showing
10 changed files
with
390 additions
and
100 deletions
+390
-100
changelogs/unreleased/24537-reenable-private-token-with-sudo.yml
...ogs/unreleased/24537-reenable-private-token-with-sudo.yml
+5
-0
doc/api/users.md
doc/api/users.md
+50
-1
lib/api/entities.rb
lib/api/entities.rb
+3
-4
lib/api/helpers.rb
lib/api/helpers.rb
+10
-3
lib/api/session.rb
lib/api/session.rb
+2
-2
lib/api/users.rb
lib/api/users.rb
+8
-8
spec/fixtures/api/schemas/user/login.json
spec/fixtures/api/schemas/user/login.json
+37
-0
spec/fixtures/api/schemas/user/public.json
spec/fixtures/api/schemas/user/public.json
+79
-0
spec/requests/api/api_helpers_spec.rb
spec/requests/api/api_helpers_spec.rb
+129
-70
spec/requests/api/users_spec.rb
spec/requests/api/users_spec.rb
+67
-12
No files found.
changelogs/unreleased/24537-reenable-private-token-with-sudo.yml
0 → 100644
View file @
3ed96afc
---
title
:
Reenables /user API request to return private-token if user is admin and request
is made with sudo
merge_request
:
7615
author
:
doc/api/users.md
View file @
3ed96afc
...
...
@@ -291,7 +291,9 @@ Parameters:
-
`id`
(required) - The ID of the user
## Current user
## User
### For normal users
Gets currently authenticated user.
...
...
@@ -335,6 +337,53 @@ GET /user
}
```
### For admins
Parameters:
-
`sudo`
(required) - the ID of a user
```
GET /user
```
```
json
{
"id"
:
1
,
"username"
:
"john_smith"
,
"email"
:
"john@example.com"
,
"name"
:
"John Smith"
,
"state"
:
"active"
,
"avatar_url"
:
"http://localhost:3000/uploads/user/avatar/1/index.jpg"
,
"web_url"
:
"http://localhost:3000/john_smith"
,
"created_at"
:
"2012-05-23T08:00:58Z"
,
"is_admin"
:
false
,
"bio"
:
null
,
"location"
:
null
,
"skype"
:
""
,
"linkedin"
:
""
,
"twitter"
:
""
,
"website_url"
:
""
,
"organization"
:
""
,
"last_sign_in_at"
:
"2012-06-01T11:41:01Z"
,
"confirmed_at"
:
"2012-05-23T09:05:22Z"
,
"theme_id"
:
1
,
"color_scheme_id"
:
2
,
"projects_limit"
:
100
,
"current_sign_in_at"
:
"2012-06-02T06:36:55Z"
,
"identities"
:
[
{
"provider"
:
"github"
,
"extern_uid"
:
"2435223452345"
},
{
"provider"
:
"bitbucket"
,
"extern_uid"
:
"john_smith"
},
{
"provider"
:
"google_oauth2"
,
"extern_uid"
:
"8776128412476123468721346"
}
],
"can_create_group"
:
true
,
"can_create_project"
:
true
,
"two_factor_enabled"
:
true
,
"external"
:
false
,
"private_token"
:
"dd34asd13as"
}
```
## List SSH keys
Get a list of currently authenticated user's SSH keys.
...
...
lib/api/entities.rb
View file @
3ed96afc
...
...
@@ -22,7 +22,7 @@ module API
expose
:provider
,
:extern_uid
end
class
User
Full
<
User
class
User
Public
<
User
expose
:last_sign_in_at
expose
:confirmed_at
expose
:email
...
...
@@ -32,10 +32,9 @@ module API
expose
:can_create_project?
,
as: :can_create_project
expose
:two_factor_enabled?
,
as: :two_factor_enabled
expose
:external
expose
:private_token
,
if:
lambda
{
|
user
,
options
|
user
.
is_admin?
&&
options
[
:sudo_identifier
]
}
end
class
User
Login
<
UserFull
class
User
WithPrivateToken
<
UserPublic
expose
:private_token
end
...
...
@@ -290,7 +289,7 @@ module API
end
class
SSHKeyWithUser
<
SSHKey
expose
:user
,
using:
Entities
::
User
Full
expose
:user
,
using:
Entities
::
User
Public
end
class
Note
<
Grape
::
Entity
...
...
lib/api/helpers.rb
View file @
3ed96afc
...
...
@@ -44,11 +44,14 @@ module API
return
nil
end
identifier
=
sudo_identifier
()
identifier
=
sudo_identifier
# If the sudo is the current user do nothing
if
identifier
&&
!
(
@current_user
.
id
==
identifier
||
@current_user
.
username
==
identifier
)
if
identifier
# We check for private_token because we cannot allow PAT to be used
forbidden!
(
'Must be admin to use sudo'
)
unless
@current_user
.
is_admin?
forbidden!
(
'Private token must be specified in order to use sudo'
)
unless
private_token_used?
@impersonator
=
@current_user
@current_user
=
User
.
by_username_or_id
(
identifier
)
not_found!
(
"No user id or username for:
#{
identifier
}
"
)
if
@current_user
.
nil?
end
...
...
@@ -399,6 +402,10 @@ module API
links
.
join
(
', '
)
end
def
private_token_used?
private_token
==
@current_user
.
private_token
end
def
secret_token
Gitlab
::
Shell
.
secret_token
end
...
...
lib/api/session.rb
View file @
3ed96afc
module
API
class
Session
<
Grape
::
API
desc
'Login to get token'
do
success
Entities
::
User
Logi
n
success
Entities
::
User
WithPrivateToke
n
end
params
do
optional
:login
,
type:
String
,
desc:
'The username'
...
...
@@ -14,7 +14,7 @@ module API
return
unauthorized!
unless
user
return
render_api_error!
(
'401 Unauthorized. You have 2FA enabled. Please use a personal access token to access the API'
,
401
)
if
user
.
two_factor_enabled?
present
user
,
with:
Entities
::
User
Logi
n
present
user
,
with:
Entities
::
User
WithPrivateToke
n
end
end
end
lib/api/users.rb
View file @
3ed96afc
...
...
@@ -51,7 +51,7 @@ module API
users
=
users
.
external
if
params
[
:external
]
&&
current_user
.
is_admin?
end
entity
=
current_user
.
is_admin?
?
Entities
::
User
Full
:
Entities
::
UserBasic
entity
=
current_user
.
is_admin?
?
Entities
::
User
Public
:
Entities
::
UserBasic
present
paginate
(
users
),
with:
entity
end
...
...
@@ -66,7 +66,7 @@ module API
not_found!
(
'User'
)
unless
user
if
current_user
&&
current_user
.
is_admin?
present
user
,
with:
Entities
::
User
Full
present
user
,
with:
Entities
::
User
Public
elsif
can?
(
current_user
,
:read_user
,
user
)
present
user
,
with:
Entities
::
User
else
...
...
@@ -75,7 +75,7 @@ module API
end
desc
'Create a user. Available only for admins.'
do
success
Entities
::
User
Full
success
Entities
::
User
Public
end
params
do
requires
:email
,
type:
String
,
desc:
'The email of the user'
...
...
@@ -99,7 +99,7 @@ module API
end
if
user
.
save
present
user
,
with:
Entities
::
User
Full
present
user
,
with:
Entities
::
User
Public
else
conflict!
(
'Email has already been taken'
)
if
User
.
where
(
email:
user
.
email
).
...
...
@@ -114,7 +114,7 @@ module API
end
desc
'Update a user. Available only for admins.'
do
success
Entities
::
User
Full
success
Entities
::
User
Public
end
params
do
requires
:id
,
type:
Integer
,
desc:
'The ID of the user'
...
...
@@ -161,7 +161,7 @@ module API
user_params
.
delete
(
:provider
)
if
user
.
update_attributes
(
user_params
)
present
user
,
with:
Entities
::
User
Full
present
user
,
with:
Entities
::
User
Public
else
render_validation_error!
(
user
)
end
...
...
@@ -350,10 +350,10 @@ module API
resource
:user
do
desc
'Get the currently authenticated user'
do
success
Entities
::
User
Full
success
Entities
::
User
Public
end
get
do
present
current_user
,
with:
Entities
::
UserFull
,
sudo_identifier:
sudo_identifier
present
current_user
,
with:
@impersonator
?
Entities
::
UserWithPrivateToken
:
Entities
::
UserPublic
end
desc
"Get the currently authenticated user's SSH keys"
do
...
...
spec/fixtures/api/schemas/user/login.json
0 → 100644
View file @
3ed96afc
{
"type"
:
"object"
,
"required"
:
[
"id"
,
"username"
,
"email"
,
"name"
,
"state"
,
"avatar_url"
,
"web_url"
,
"created_at"
,
"is_admin"
,
"bio"
,
"location"
,
"skype"
,
"linkedin"
,
"twitter"
,
"website_url"
,
"organization"
,
"last_sign_in_at"
,
"confirmed_at"
,
"theme_id"
,
"color_scheme_id"
,
"projects_limit"
,
"current_sign_in_at"
,
"identities"
,
"can_create_group"
,
"can_create_project"
,
"two_factor_enabled"
,
"external"
,
"private_token"
],
"properties"
:
{
"$ref"
:
"full.json"
,
"private_token"
:
{
"type"
:
"string"
}
}
}
spec/fixtures/api/schemas/user/public.json
0 → 100644
View file @
3ed96afc
{
"type"
:
"object"
,
"required"
:
[
"id"
,
"username"
,
"email"
,
"name"
,
"state"
,
"avatar_url"
,
"web_url"
,
"created_at"
,
"is_admin"
,
"bio"
,
"location"
,
"skype"
,
"linkedin"
,
"twitter"
,
"website_url"
,
"organization"
,
"last_sign_in_at"
,
"confirmed_at"
,
"theme_id"
,
"color_scheme_id"
,
"projects_limit"
,
"current_sign_in_at"
,
"identities"
,
"can_create_group"
,
"can_create_project"
,
"two_factor_enabled"
,
"external"
],
"properties"
:
{
"id"
:
{
"type"
:
"integer"
},
"username"
:
{
"type"
:
"string"
},
"email"
:
{
"type"
:
"string"
,
"pattern"
:
"^[^@]+@[^@]+$"
},
"name"
:
{
"type"
:
"string"
},
"state"
:
{
"type"
:
"string"
,
"enum"
:
[
"active"
,
"blocked"
]
},
"avatar_url"
:
{
"type"
:
"string"
},
"web_url"
:
{
"type"
:
"string"
},
"created_at"
:
{
"type"
:
"date"
},
"is_admin"
:
{
"type"
:
"boolean"
},
"bio"
:
{
"type"
:
[
"string"
,
"null"
]
},
"location"
:
{
"type"
:
[
"string"
,
"null"
]
},
"skype"
:
{
"type"
:
"string"
},
"linkedin"
:
{
"type"
:
"string"
},
"twitter"
:
{
"type"
:
"string "
},
"website_url"
:
{
"type"
:
"string"
},
"organization"
:
{
"type"
:
[
"string"
,
"null"
]
},
"last_sign_in_at"
:
{
"type"
:
"date"
},
"confirmed_at"
:
{
"type"
:
[
"date"
,
"null"
]
},
"theme_id"
:
{
"type"
:
"integer"
},
"color_scheme_id"
:
{
"type"
:
"integer"
},
"projects_limit"
:
{
"type"
:
"integer"
},
"current_sign_in_at"
:
{
"type"
:
"date"
},
"identities"
:
{
"type"
:
"array"
,
"items"
:
{
"type"
:
"object"
,
"properties"
:
{
"provider"
:
{
"type"
:
"string"
,
"enum"
:
[
"github"
,
"bitbucket"
,
"google_oauth2"
]
},
"extern_uid"
:
{
"type"
:
[
"number"
,
"string"
]
}
}
}
},
"can_create_group"
:
{
"type"
:
"boolean"
},
"can_create_project"
:
{
"type"
:
"boolean"
},
"two_factor_enabled"
:
{
"type"
:
"boolean"
},
"external"
:
{
"type"
:
"boolean"
}
}
}
spec/requests/api/api_helpers_spec.rb
View file @
3ed96afc
...
...
@@ -153,85 +153,144 @@ describe API::Helpers, api: true do
end
end
it
"changes current user to sudo when admin"
do
set_env
(
admin
,
user
.
id
)
expect
(
current_user
).
to
eq
(
user
)
set_param
(
admin
,
user
.
id
)
expect
(
current_user
).
to
eq
(
user
)
set_env
(
admin
,
user
.
username
)
expect
(
current_user
).
to
eq
(
user
)
set_param
(
admin
,
user
.
username
)
expect
(
current_user
).
to
eq
(
user
)
end
context
'sudo usage'
do
context
'with admin'
do
context
'with header'
do
context
'with id'
do
it
'changes current_user to sudo'
do
set_env
(
admin
,
user
.
id
)
it
"throws an error when the current user is not an admin and attempting to sudo"
do
set_env
(
user
,
admin
.
id
)
expect
{
current_user
}.
to
raise_error
(
Exception
)
set_param
(
user
,
admin
.
id
)
expect
{
current_user
}.
to
raise_error
(
Exception
)
set_env
(
user
,
admin
.
username
)
expect
{
current_user
}.
to
raise_error
(
Exception
)
set_param
(
user
,
admin
.
username
)
expect
{
current_user
}.
to
raise_error
(
Exception
)
end
expect
(
current_user
).
to
eq
(
user
)
end
it
"throws an error when the user cannot be found for a given id"
do
id
=
user
.
id
+
admin
.
id
expect
(
user
.
id
).
not_to
eq
(
id
)
expect
(
admin
.
id
).
not_to
eq
(
id
)
set_env
(
admin
,
id
)
expect
{
current_user
}.
to
raise_error
(
Exception
)
it
'handles sudo to oneself'
do
set_env
(
admin
,
admin
.
id
)
set_param
(
admin
,
id
)
expect
{
current_user
}.
to
raise_error
(
Exception
)
end
expect
(
current_user
).
to
eq
(
admin
)
end
it
"throws an error when the user cannot be found for a given username"
do
username
=
"
#{
user
.
username
}#{
admin
.
username
}
"
expect
(
user
.
username
).
not_to
eq
(
username
)
expect
(
admin
.
username
).
not_to
eq
(
username
)
set_env
(
admin
,
username
)
expect
{
current_user
}.
to
raise_error
(
Exception
)
it
'throws an error when user cannot be found'
do
id
=
user
.
id
+
admin
.
id
expect
(
user
.
id
).
not_to
eq
(
id
)
expect
(
admin
.
id
).
not_to
eq
(
id
)
set_param
(
admin
,
username
)
expect
{
current_user
}.
to
raise_error
(
Exception
)
end
set_env
(
admin
,
id
)
it
"handles sudo's to oneself"
do
set_env
(
admin
,
admin
.
id
)
expect
(
current_user
).
to
eq
(
admin
)
set_param
(
admin
,
admin
.
id
)
expect
(
current_user
).
to
eq
(
admin
)
set_env
(
admin
,
admin
.
username
)
expect
(
current_user
).
to
eq
(
admin
)
set_param
(
admin
,
admin
.
username
)
expect
(
current_user
).
to
eq
(
admin
)
end
expect
{
current_user
}.
to
raise_error
(
Exception
)
end
end
it
"handles multiple sudo's to oneself"
do
set_env
(
admin
,
user
.
id
)
expect
(
current_user
).
to
eq
(
user
)
expect
(
current_user
).
to
eq
(
user
)
set_env
(
admin
,
user
.
username
)
expect
(
current_user
).
to
eq
(
user
)
expect
(
current_user
).
to
eq
(
user
)
set_param
(
admin
,
user
.
id
)
expect
(
current_user
).
to
eq
(
user
)
expect
(
current_user
).
to
eq
(
user
)
set_param
(
admin
,
user
.
username
)
expect
(
current_user
).
to
eq
(
user
)
expect
(
current_user
).
to
eq
(
user
)
end
context
'with username'
do
it
'changes current_user to sudo'
do
set_env
(
admin
,
user
.
username
)
expect
(
current_user
).
to
eq
(
user
)
end
it
'handles sudo to oneself'
do
set_env
(
admin
,
admin
.
username
)
expect
(
current_user
).
to
eq
(
admin
)
end
it
"throws an error when the user cannot be found for a given username"
do
username
=
"
#{
user
.
username
}#{
admin
.
username
}
"
expect
(
user
.
username
).
not_to
eq
(
username
)
expect
(
admin
.
username
).
not_to
eq
(
username
)
set_env
(
admin
,
username
)
expect
{
current_user
}.
to
raise_error
(
Exception
)
end
end
end
context
'with param'
do
context
'with id'
do
it
'changes current_user to sudo'
do
set_param
(
admin
,
user
.
id
)
expect
(
current_user
).
to
eq
(
user
)
end
it
'handles sudo to oneself'
do
set_param
(
admin
,
admin
.
id
)
expect
(
current_user
).
to
eq
(
admin
)
end
it
'handles sudo to oneself using string'
do
set_env
(
admin
,
user
.
id
.
to_s
)
expect
(
current_user
).
to
eq
(
user
)
end
it
'throws an error when user cannot be found'
do
id
=
user
.
id
+
admin
.
id
expect
(
user
.
id
).
not_to
eq
(
id
)
expect
(
admin
.
id
).
not_to
eq
(
id
)
it
"handles multiple sudo's to oneself using string ids"
do
set_env
(
admin
,
user
.
id
.
to_s
)
expect
(
current_user
).
to
eq
(
user
)
expect
(
current_user
).
to
eq
(
user
)
set_param
(
admin
,
id
)
set_param
(
admin
,
user
.
id
.
to_s
)
expect
(
current_user
).
to
eq
(
user
)
expect
(
current_user
).
to
eq
(
user
)
expect
{
current_user
}.
to
raise_error
(
Exception
)
end
end
context
'with username'
do
it
'changes current_user to sudo'
do
set_param
(
admin
,
user
.
username
)
expect
(
current_user
).
to
eq
(
user
)
end
it
'handles sudo to oneself'
do
set_param
(
admin
,
admin
.
username
)
expect
(
current_user
).
to
eq
(
admin
)
end
it
"throws an error when the user cannot be found for a given username"
do
username
=
"
#{
user
.
username
}#{
admin
.
username
}
"
expect
(
user
.
username
).
not_to
eq
(
username
)
expect
(
admin
.
username
).
not_to
eq
(
username
)
set_param
(
admin
,
username
)
expect
{
current_user
}.
to
raise_error
(
Exception
)
end
end
end
end
context
'with regular user'
do
context
'with env'
do
it
'changes current_user to sudo when admin and user id'
do
set_env
(
user
,
admin
.
id
)
expect
{
current_user
}.
to
raise_error
(
Exception
)
end
it
'changes current_user to sudo when admin and user username'
do
set_env
(
user
,
admin
.
username
)
expect
{
current_user
}.
to
raise_error
(
Exception
)
end
end
context
'with params'
do
it
'changes current_user to sudo when admin and user id'
do
set_param
(
user
,
admin
.
id
)
expect
{
current_user
}.
to
raise_error
(
Exception
)
end
it
'changes current_user to sudo when admin and user username'
do
set_param
(
user
,
admin
.
username
)
expect
{
current_user
}.
to
raise_error
(
Exception
)
end
end
end
end
end
...
...
spec/requests/api/users_spec.rb
View file @
3ed96afc
...
...
@@ -651,20 +651,75 @@ describe API::Users, api: true do
end
describe
"GET /user"
do
it
"returns current user"
do
get
api
(
"/user"
,
user
)
expect
(
response
).
to
have_http_status
(
200
)
expect
(
json_response
[
'email'
]).
to
eq
(
user
.
email
)
expect
(
json_response
[
'is_admin'
]).
to
eq
(
user
.
is_admin?
)
expect
(
json_response
[
'can_create_project'
]).
to
eq
(
user
.
can_create_project?
)
expect
(
json_response
[
'can_create_group'
]).
to
eq
(
user
.
can_create_group?
)
expect
(
json_response
[
'projects_limit'
]).
to
eq
(
user
.
projects_limit
)
expect
(
json_response
[
'private_token'
]).
to
be_blank
let
(
:personal_access_token
)
{
create
(
:personal_access_token
,
user:
user
)
}
let
(
:private_token
)
{
user
.
private_token
}
context
'with regular user'
do
context
'with personal access token'
do
it
'returns 403 without private token when sudo is defined'
do
get
api
(
"/user?private_token=
#{
personal_access_token
.
token
}
&sudo=
#{
user
.
id
}
"
)
expect
(
response
).
to
have_http_status
(
403
)
end
end
context
'with private token'
do
it
'returns 403 without private token when sudo defined'
do
get
api
(
"/user?private_token=
#{
private_token
}
&sudo=
#{
user
.
id
}
"
)
expect
(
response
).
to
have_http_status
(
403
)
end
end
it
'returns current user without private token when sudo not defined'
do
get
api
(
"/user"
,
user
)
expect
(
response
).
to
have_http_status
(
200
)
expect
(
response
).
to
match_response_schema
(
'user/public'
)
end
end
it
"returns 401 error if user is unauthenticated"
do
get
api
(
"/user"
)
expect
(
response
).
to
have_http_status
(
401
)
context
'with admin'
do
let
(
:user
)
{
create
(
:admin
)
}
context
'with personal access token'
do
it
'returns 403 without private token when sudo defined'
do
get
api
(
"/user?private_token=
#{
personal_access_token
.
token
}
&sudo=
#{
user
.
id
}
"
)
expect
(
response
).
to
have_http_status
(
403
)
end
it
'returns current user without private token when sudo not defined'
do
get
api
(
"/user?private_token=
#{
personal_access_token
.
token
}
"
)
expect
(
response
).
to
have_http_status
(
200
)
expect
(
response
).
to
match_response_schema
(
'user/public'
)
end
end
context
'with private token'
do
it
'returns current user with private token when sudo defined'
do
get
api
(
"/user?private_token=
#{
private_token
}
&sudo=
#{
user
.
id
}
"
)
expect
(
response
).
to
have_http_status
(
200
)
expect
(
response
).
to
match_response_schema
(
'user/login'
)
end
it
'returns current user without private token when sudo not defined'
do
get
api
(
"/user?private_token=
#{
private_token
}
"
)
expect
(
response
).
to
have_http_status
(
200
)
expect
(
response
).
to
match_response_schema
(
'user/public'
)
end
end
end
context
'with unauthenticated user'
do
it
"returns 401 error if user is unauthenticated"
do
get
api
(
"/user"
)
expect
(
response
).
to
have_http_status
(
401
)
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