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
Jérome Perrin
gitlab-ce
Commits
d7572472
Commit
d7572472
authored
Nov 30, 2016
by
Rémy Coutable
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Allow public access to some Project API endpoints
Signed-off-by:
Rémy Coutable
<
remy@rymai.me
>
parent
e91afc0d
Changes
5
Show whitespace changes
Inline
Side-by-side
Showing
5 changed files
with
315 additions
and
152 deletions
+315
-152
changelogs/unreleased/4269-public-api.yml
changelogs/unreleased/4269-public-api.yml
+4
-0
lib/api/helpers.rb
lib/api/helpers.rb
+5
-0
lib/api/projects.rb
lib/api/projects.rb
+18
-10
spec/requests/api/api_helpers_spec.rb
spec/requests/api/api_helpers_spec.rb
+53
-1
spec/requests/api/projects_spec.rb
spec/requests/api/projects_spec.rb
+235
-141
No files found.
changelogs/unreleased/4269-public-api.yml
0 → 100644
View file @
d7572472
---
title
:
Allow public access to some Project API endpoints
merge_request
:
7843
author
:
lib/api/helpers.rb
View file @
d7572472
...
@@ -141,6 +141,10 @@ module API
...
@@ -141,6 +141,10 @@ module API
unauthorized!
unless
current_user
unauthorized!
unless
current_user
end
end
def
authenticate_non_get!
authenticate!
unless
%w[GET HEAD]
.
include?
(
route
.
route_method
)
end
def
authenticate_by_gitlab_shell_token!
def
authenticate_by_gitlab_shell_token!
input
=
params
[
'secret_token'
].
try
(
:chomp
)
input
=
params
[
'secret_token'
].
try
(
:chomp
)
unless
Devise
.
secure_compare
(
secret_token
,
input
)
unless
Devise
.
secure_compare
(
secret_token
,
input
)
...
@@ -149,6 +153,7 @@ module API
...
@@ -149,6 +153,7 @@ module API
end
end
def
authenticated_as_admin!
def
authenticated_as_admin!
authenticate!
forbidden!
unless
current_user
.
is_admin?
forbidden!
unless
current_user
.
is_admin?
end
end
...
...
lib/api/projects.rb
View file @
d7572472
...
@@ -3,7 +3,7 @@ module API
...
@@ -3,7 +3,7 @@ module API
class
Projects
<
Grape
::
API
class
Projects
<
Grape
::
API
include
PaginationParams
include
PaginationParams
before
{
authenticate!
}
before
{
authenticate
_non_get
!
}
helpers
do
helpers
do
params
:optional_params
do
params
:optional_params
do
...
@@ -61,7 +61,7 @@ module API
...
@@ -61,7 +61,7 @@ module API
end
end
end
end
desc
'Get a
projects list
for authenticated user'
do
desc
'Get a
list of visible projects
for authenticated user'
do
success
Entities
::
BasicProjectDetails
success
Entities
::
BasicProjectDetails
end
end
params
do
params
do
...
@@ -70,15 +70,15 @@ module API
...
@@ -70,15 +70,15 @@ module API
use
:filter_params
use
:filter_params
use
:pagination
use
:pagination
end
end
get
do
get
'/visible'
do
projects
=
current_user
.
authorized_projects
projects
=
ProjectsFinder
.
new
.
execute
(
current_user
)
projects
=
filter_projects
(
projects
)
projects
=
filter_projects
(
projects
)
entity
=
params
[
:simple
]
?
Entities
::
BasicProjectDetails
:
Entities
::
ProjectWithAccess
entity
=
params
[
:simple
]
||
!
current_user
?
Entities
::
BasicProjectDetails
:
Entities
::
ProjectWithAccess
present
paginate
(
projects
),
with:
entity
,
user:
current_user
present
paginate
(
projects
),
with:
entity
,
user:
current_user
end
end
desc
'Get a
list of visible projects
for authenticated user'
do
desc
'Get a
projects list
for authenticated user'
do
success
Entities
::
BasicProjectDetails
success
Entities
::
BasicProjectDetails
end
end
params
do
params
do
...
@@ -87,8 +87,10 @@ module API
...
@@ -87,8 +87,10 @@ module API
use
:filter_params
use
:filter_params
use
:pagination
use
:pagination
end
end
get
'/visible'
do
get
do
projects
=
ProjectsFinder
.
new
.
execute
(
current_user
)
authenticate!
projects
=
current_user
.
authorized_projects
projects
=
filter_projects
(
projects
)
projects
=
filter_projects
(
projects
)
entity
=
params
[
:simple
]
?
Entities
::
BasicProjectDetails
:
Entities
::
ProjectWithAccess
entity
=
params
[
:simple
]
?
Entities
::
BasicProjectDetails
:
Entities
::
ProjectWithAccess
...
@@ -103,6 +105,8 @@ module API
...
@@ -103,6 +105,8 @@ module API
use
:pagination
use
:pagination
end
end
get
'/owned'
do
get
'/owned'
do
authenticate!
projects
=
current_user
.
owned_projects
projects
=
current_user
.
owned_projects
projects
=
filter_projects
(
projects
)
projects
=
filter_projects
(
projects
)
...
@@ -117,6 +121,8 @@ module API
...
@@ -117,6 +121,8 @@ module API
use
:pagination
use
:pagination
end
end
get
'/starred'
do
get
'/starred'
do
authenticate!
projects
=
current_user
.
viewable_starred_projects
projects
=
current_user
.
viewable_starred_projects
projects
=
filter_projects
(
projects
)
projects
=
filter_projects
(
projects
)
...
@@ -132,6 +138,7 @@ module API
...
@@ -132,6 +138,7 @@ module API
end
end
get
'/all'
do
get
'/all'
do
authenticated_as_admin!
authenticated_as_admin!
projects
=
Project
.
all
projects
=
Project
.
all
projects
=
filter_projects
(
projects
)
projects
=
filter_projects
(
projects
)
...
@@ -213,7 +220,8 @@ module API
...
@@ -213,7 +220,8 @@ module API
success
Entities
::
ProjectWithAccess
success
Entities
::
ProjectWithAccess
end
end
get
":id"
do
get
":id"
do
present
user_project
,
with:
Entities
::
ProjectWithAccess
,
user:
current_user
,
entity
=
current_user
?
Entities
::
ProjectWithAccess
:
Entities
::
BasicProjectDetails
present
user_project
,
with:
entity
,
user:
current_user
,
user_can_admin_project:
can?
(
current_user
,
:admin_project
,
user_project
)
user_can_admin_project:
can?
(
current_user
,
:admin_project
,
user_project
)
end
end
...
@@ -433,7 +441,7 @@ module API
...
@@ -433,7 +441,7 @@ module API
use
:pagination
use
:pagination
end
end
get
':id/users'
do
get
':id/users'
do
users
=
User
.
where
(
id:
user_project
.
team
.
users
.
map
(
&
:id
))
users
=
user_project
.
team
.
users
users
=
users
.
search
(
params
[
:search
])
if
params
[
:search
].
present?
users
=
users
.
search
(
params
[
:search
])
if
params
[
:search
].
present?
present
paginate
(
users
),
with:
Entities
::
UserBasic
present
paginate
(
users
),
with:
Entities
::
UserBasic
...
...
spec/requests/api/api_helpers_spec.rb
View file @
d7572472
...
@@ -47,7 +47,7 @@ describe API::Helpers, api: true do
...
@@ -47,7 +47,7 @@ describe API::Helpers, api: true do
end
end
def
error!
(
message
,
status
)
def
error!
(
message
,
status
)
raise
Exception
raise
Exception
.
new
(
"
#{
status
}
-
#{
message
}
"
)
end
end
describe
".current_user"
do
describe
".current_user"
do
...
@@ -290,4 +290,56 @@ describe API::Helpers, api: true do
...
@@ -290,4 +290,56 @@ describe API::Helpers, api: true do
handle_api_exception
(
exception
)
handle_api_exception
(
exception
)
end
end
end
end
describe
'.authenticate_non_get!'
do
%w[HEAD GET]
.
each
do
|
method_name
|
context
"method is
#{
method_name
}
"
do
before
do
expect_any_instance_of
(
self
.
class
).
to
receive
(
:route
).
and_return
(
double
(
route_method:
method_name
))
end
it
'does not raise an error'
do
expect_any_instance_of
(
self
.
class
).
not_to
receive
(
:authenticate!
)
expect
{
authenticate_non_get!
}.
not_to
raise_error
end
end
end
%w[POST PUT PATCH DELETE]
.
each
do
|
method_name
|
context
"method is
#{
method_name
}
"
do
before
do
expect_any_instance_of
(
self
.
class
).
to
receive
(
:route
).
and_return
(
double
(
route_method:
method_name
))
end
it
'calls authenticate!'
do
expect_any_instance_of
(
self
.
class
).
to
receive
(
:authenticate!
)
authenticate_non_get!
end
end
end
end
describe
'.authenticate!'
do
context
'current_user is nil'
do
before
do
expect_any_instance_of
(
self
.
class
).
to
receive
(
:current_user
).
and_return
(
nil
)
end
it
'returns a 401 response'
do
expect
{
authenticate!
}.
to
raise_error
'401 - {"message"=>"401 Unauthorized"}'
end
end
context
'current_user is present'
do
before
do
expect_any_instance_of
(
self
.
class
).
to
receive
(
:current_user
).
and_return
(
true
)
end
it
'does not raise an error'
do
expect
{
authenticate!
}.
not_to
raise_error
end
end
end
end
end
spec/requests/api/projects_spec.rb
View file @
d7572472
...
@@ -200,32 +200,43 @@ describe API::API, api: true do
...
@@ -200,32 +200,43 @@ describe API::API, api: true do
end
end
describe
'GET /projects/visible'
do
describe
'GET /projects/visible'
do
let
(
:public_project
)
{
create
(
:project
,
:public
)
}
shared_examples_for
'visible projects response'
do
it
'returns the visible projects'
do
get
api
(
'/projects/visible'
,
current_user
)
expect
(
response
).
to
have_http_status
(
200
)
expect
(
json_response
).
to
be_an
Array
expect
(
json_response
.
map
{
|
p
|
p
[
'id'
]
}).
to
contain_exactly
(
*
projects
.
map
(
&
:id
))
end
end
let!
(
:public_project
)
{
create
(
:project
,
:public
)
}
before
do
before
do
public_project
project
project
project2
project2
project3
project3
project4
project4
end
end
it
'returns the projects viewable by the user'
do
context
'when unauthenticated'
do
get
api
(
'/projects/visible'
,
user
)
it_behaves_like
'visible projects response'
do
let
(
:current_user
)
{
nil
}
expect
(
response
).
to
have_http_status
(
200
)
let
(
:projects
)
{
[
public_project
]
}
expect
(
json_response
).
to
be_an
Array
end
expect
(
json_response
.
map
{
|
project
|
project
[
'id'
]
}).
to
contain_exactly
(
public_project
.
id
,
project
.
id
,
project2
.
id
,
project3
.
id
)
end
end
it
'shows only public projects when the user only has access to those'
do
context
'when authenticated'
do
get
api
(
'/projects/visible'
,
user2
)
it_behaves_like
'visible projects response'
do
let
(
:current_user
)
{
user
}
let
(
:projects
)
{
[
public_project
,
project
,
project2
,
project3
]
}
end
end
expect
(
response
).
to
have_http_status
(
200
)
context
'when authenticated as a different user'
do
expect
(
json_response
).
to
be_an
Array
it_behaves_like
'visible projects response'
do
expect
(
json_response
.
map
{
|
project
|
project
[
'id'
]
}).
let
(
:current_user
)
{
user2
}
to
contain_exactly
(
public_project
.
id
)
let
(
:projects
)
{
[
public_project
]
}
end
end
end
end
end
...
@@ -528,8 +539,24 @@ describe API::API, api: true do
...
@@ -528,8 +539,24 @@ describe API::API, api: true do
end
end
describe
'GET /projects/:id'
do
describe
'GET /projects/:id'
do
before
{
project
}
context
'when unauthenticated'
do
before
{
project_member
}
it
'returns the public projects'
do
public_project
=
create
(
:project
,
:public
)
get
api
(
"/projects/
#{
public_project
.
id
}
"
)
expect
(
response
).
to
have_http_status
(
200
)
expect
(
json_response
[
'id'
]).
to
eq
(
public_project
.
id
)
expect
(
json_response
[
'description'
]).
to
eq
(
public_project
.
description
)
expect
(
json_response
.
keys
).
not_to
include
(
'permissions'
)
end
end
context
'when authenticated'
do
before
do
project
project_member
end
it
'returns a project by id'
do
it
'returns a project by id'
do
group
=
create
(
:group
)
group
=
create
(
:group
)
...
@@ -645,18 +672,17 @@ describe API::API, api: true do
...
@@ -645,18 +672,17 @@ describe API::API, api: true do
end
end
end
end
end
end
end
describe
'GET /projects/:id/events'
do
describe
'GET /projects/:id/events'
do
before
{
project_member2
}
shared_examples_for
'project events response'
do
it
'returns the project events'
do
context
'valid request'
do
member
=
create
(
:user
)
before
do
create
(
:project_member
,
:developer
,
user:
member
,
project:
project
)
note
=
create
(
:note_on_issue
,
note:
'What an awesome day!'
,
project:
project
)
note
=
create
(
:note_on_issue
,
note:
'What an awesome day!'
,
project:
project
)
EventCreateService
.
new
.
leave_note
(
note
,
note
.
author
)
EventCreateService
.
new
.
leave_note
(
note
,
note
.
author
)
end
it
'returns all events'
do
get
api
(
"/projects/
#{
project
.
id
}
/events"
,
current_user
)
get
api
(
"/projects/
#{
project
.
id
}
/events"
,
user
)
expect
(
response
).
to
have_http_status
(
200
)
expect
(
response
).
to
have_http_status
(
200
)
...
@@ -669,8 +695,22 @@ describe API::API, api: true do
...
@@ -669,8 +695,22 @@ describe API::API, api: true do
expect
(
last_event
[
'action_name'
]).
to
eq
(
'joined'
)
expect
(
last_event
[
'action_name'
]).
to
eq
(
'joined'
)
expect
(
last_event
[
'project_id'
].
to_i
).
to
eq
(
project
.
id
)
expect
(
last_event
[
'project_id'
].
to_i
).
to
eq
(
project
.
id
)
expect
(
last_event
[
'author_username'
]).
to
eq
(
user3
.
username
)
expect
(
last_event
[
'author_username'
]).
to
eq
(
member
.
username
)
expect
(
last_event
[
'author'
][
'name'
]).
to
eq
(
user3
.
name
)
expect
(
last_event
[
'author'
][
'name'
]).
to
eq
(
member
.
name
)
end
end
context
'when unauthenticated'
do
it_behaves_like
'project events response'
do
let
(
:project
)
{
create
(
:project
,
:public
)
}
let
(
:current_user
)
{
nil
}
end
end
context
'when authenticated'
do
context
'valid request'
do
it_behaves_like
'project events response'
do
let
(
:current_user
)
{
user
}
end
end
end
end
...
@@ -689,6 +729,58 @@ describe API::API, api: true do
...
@@ -689,6 +729,58 @@ describe API::API, api: true do
expect
(
response
).
to
have_http_status
(
404
)
expect
(
response
).
to
have_http_status
(
404
)
end
end
end
end
end
describe
'GET /projects/:id/users'
do
shared_examples_for
'project users response'
do
it
'returns the project users'
do
member
=
create
(
:user
)
create
(
:project_member
,
:developer
,
user:
member
,
project:
project
)
get
api
(
"/projects/
#{
project
.
id
}
/users"
,
current_user
)
expect
(
response
).
to
have_http_status
(
200
)
expect
(
json_response
).
to
be_an
Array
expect
(
json_response
.
size
).
to
eq
(
1
)
first_user
=
json_response
.
first
expect
(
first_user
[
'username'
]).
to
eq
(
member
.
username
)
expect
(
first_user
[
'name'
]).
to
eq
(
member
.
name
)
expect
(
first_user
.
keys
).
to
contain_exactly
(
*
%w[name username id state avatar_url web_url]
)
end
end
context
'when unauthenticated'
do
it_behaves_like
'project users response'
do
let
(
:project
)
{
create
(
:project
,
:public
)
}
let
(
:current_user
)
{
nil
}
end
end
context
'when authenticated'
do
context
'valid request'
do
it_behaves_like
'project users response'
do
let
(
:current_user
)
{
user
}
end
end
it
'returns a 404 error if not found'
do
get
api
(
'/projects/42/users'
,
user
)
expect
(
response
).
to
have_http_status
(
404
)
expect
(
json_response
[
'message'
]).
to
eq
(
'404 Project Not Found'
)
end
it
'returns a 404 error if user is not a member'
do
other_user
=
create
(
:user
)
get
api
(
"/projects/
#{
project
.
id
}
/users"
,
other_user
)
expect
(
response
).
to
have_http_status
(
404
)
end
end
end
describe
'GET /projects/:id/snippets'
do
describe
'GET /projects/:id/snippets'
do
before
{
snippet
}
before
{
snippet
}
...
@@ -950,35 +1042,37 @@ describe API::API, api: true do
...
@@ -950,35 +1042,37 @@ describe API::API, api: true do
let!
(
:public
)
{
create
(
:empty_project
,
:public
,
name:
"public
#{
query
}
"
)
}
let!
(
:public
)
{
create
(
:empty_project
,
:public
,
name:
"public
#{
query
}
"
)
}
let!
(
:unfound_public
)
{
create
(
:empty_project
,
:public
,
name:
'unfound public'
)
}
let!
(
:unfound_public
)
{
create
(
:empty_project
,
:public
,
name:
'unfound public'
)
}
shared_examples_for
'project search response'
do
|
args
=
{}
|
it
'returns project search responses'
do
get
api
(
"/projects/search/
#{
query
}
"
,
current_user
)
expect
(
response
).
to
have_http_status
(
200
)
expect
(
json_response
).
to
be_an
Array
expect
(
json_response
.
size
).
to
eq
(
args
[
:results
])
json_response
.
each
{
|
project
|
expect
(
project
[
'name'
]).
to
match
(
args
[
:match_regex
]
||
/.*query.*/
)
}
end
end
context
'when unauthenticated'
do
context
'when unauthenticated'
do
it
'returns authentication error'
do
it_behaves_like
'project search response'
,
results:
1
do
get
api
(
"/projects/search/
#{
query
}
"
)
let
(
:current_user
)
{
nil
}
expect
(
response
).
to
have_http_status
(
401
)
end
end
end
end
context
'when authenticated'
do
context
'when authenticated'
do
it
'returns an array of projects'
do
it_behaves_like
'project search response'
,
results:
6
do
get
api
(
"/projects/search/
#{
query
}
"
,
user
)
let
(
:current_user
)
{
user
}
expect
(
response
).
to
have_http_status
(
200
)
expect
(
json_response
).
to
be_an
Array
expect
(
json_response
.
size
).
to
eq
(
6
)
json_response
.
each
{
|
project
|
expect
(
project
[
'name'
]).
to
match
(
/.*query.*/
)}
end
end
end
end
context
'when authenticated as a different user'
do
context
'when authenticated as a different user'
do
it
'returns matching public projects'
do
it_behaves_like
'project search response'
,
results:
2
,
match_regex:
/(internal|public) query/
do
get
api
(
"/projects/search/
#{
query
}
"
,
user2
)
let
(
:current_user
)
{
user2
}
expect
(
response
).
to
have_http_status
(
200
)
expect
(
json_response
).
to
be_an
Array
expect
(
json_response
.
size
).
to
eq
(
2
)
json_response
.
each
{
|
project
|
expect
(
project
[
'name'
]).
to
match
(
/(internal|public) query/
)}
end
end
end
end
end
end
describe
'PUT /projects/:id
̈́
'
do
describe
'PUT /projects/:id'
do
before
{
project
}
before
{
project
}
before
{
user
}
before
{
user
}
before
{
user3
}
before
{
user3
}
...
...
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