97424ea544
`User#starred_projects` doesn't perform any visibility checks. This has a couple of problems: 1. It assumes a user can always view all of their starred projects in perpetuity (project not changed to private, access revoked, etc.). 2. It assumes that we'll only ever allow a user to star a project they can view. This is currently the case, but bugs happen. Add `User#viewable_starred_projects` to filter the starred projects by those the user either has explicit access to, or are public or internal. Then use that in all places where we list the user's starred projects.
429 lines
15 KiB
Ruby
429 lines
15 KiB
Ruby
module API
|
|
# Projects API
|
|
class Projects < Grape::API
|
|
before { authenticate! }
|
|
|
|
resource :projects, requirements: { id: /[^\/]+/ } do
|
|
helpers do
|
|
def map_public_to_visibility_level(attrs)
|
|
publik = attrs.delete(:public)
|
|
if publik.present? && !attrs[:visibility_level].present?
|
|
publik = parse_boolean(publik)
|
|
# Since setting the public attribute to private could mean either
|
|
# private or internal, use the more conservative option, private.
|
|
attrs[:visibility_level] = (publik == true) ? Gitlab::VisibilityLevel::PUBLIC : Gitlab::VisibilityLevel::PRIVATE
|
|
end
|
|
attrs
|
|
end
|
|
end
|
|
|
|
# Get a projects list for authenticated user
|
|
#
|
|
# Example Request:
|
|
# GET /projects
|
|
get do
|
|
@projects = current_user.authorized_projects
|
|
@projects = filter_projects(@projects)
|
|
@projects = paginate @projects
|
|
present @projects, with: Entities::ProjectWithAccess, user: current_user
|
|
end
|
|
|
|
# Get an owned projects list for authenticated user
|
|
#
|
|
# Example Request:
|
|
# GET /projects/owned
|
|
get '/owned' do
|
|
@projects = current_user.owned_projects
|
|
@projects = filter_projects(@projects)
|
|
@projects = paginate @projects
|
|
present @projects, with: Entities::ProjectWithAccess, user: current_user
|
|
end
|
|
|
|
# Gets starred project for the authenticated user
|
|
#
|
|
# Example Request:
|
|
# GET /projects/starred
|
|
get '/starred' do
|
|
@projects = current_user.viewable_starred_projects
|
|
@projects = filter_projects(@projects)
|
|
@projects = paginate @projects
|
|
present @projects, with: Entities::Project
|
|
end
|
|
|
|
# Get all projects for admin user
|
|
#
|
|
# Example Request:
|
|
# GET /projects/all
|
|
get '/all' do
|
|
authenticated_as_admin!
|
|
@projects = Project.all
|
|
@projects = filter_projects(@projects)
|
|
@projects = paginate @projects
|
|
present @projects, with: Entities::ProjectWithAccess, user: current_user
|
|
end
|
|
|
|
# Get a single project
|
|
#
|
|
# Parameters:
|
|
# id (required) - The ID of a project
|
|
# Example Request:
|
|
# GET /projects/:id
|
|
get ":id" do
|
|
present user_project, with: Entities::ProjectWithAccess, user: current_user,
|
|
user_can_admin_project: can?(current_user, :admin_project, user_project)
|
|
end
|
|
|
|
# Get events for a single project
|
|
#
|
|
# Parameters:
|
|
# id (required) - The ID of a project
|
|
# Example Request:
|
|
# GET /projects/:id/events
|
|
get ":id/events" do
|
|
events = paginate user_project.events.recent
|
|
present events, with: Entities::Event
|
|
end
|
|
|
|
# Create new project
|
|
#
|
|
# Parameters:
|
|
# name (required) - name for new project
|
|
# description (optional) - short project description
|
|
# issues_enabled (optional)
|
|
# merge_requests_enabled (optional)
|
|
# builds_enabled (optional)
|
|
# wiki_enabled (optional)
|
|
# snippets_enabled (optional)
|
|
# shared_runners_enabled (optional)
|
|
# namespace_id (optional) - defaults to user namespace
|
|
# public (optional) - if true same as setting visibility_level = 20
|
|
# visibility_level (optional) - 0 by default
|
|
# import_url (optional)
|
|
# public_builds (optional)
|
|
# Example Request
|
|
# POST /projects
|
|
post do
|
|
required_attributes! [:name]
|
|
attrs = attributes_for_keys [:name,
|
|
:path,
|
|
:description,
|
|
:issues_enabled,
|
|
:merge_requests_enabled,
|
|
:builds_enabled,
|
|
:wiki_enabled,
|
|
:snippets_enabled,
|
|
:shared_runners_enabled,
|
|
:namespace_id,
|
|
:public,
|
|
:visibility_level,
|
|
:import_url,
|
|
:public_builds]
|
|
attrs = map_public_to_visibility_level(attrs)
|
|
@project = ::Projects::CreateService.new(current_user, attrs).execute
|
|
if @project.saved?
|
|
present @project, with: Entities::Project,
|
|
user_can_admin_project: can?(current_user, :admin_project, @project)
|
|
else
|
|
if @project.errors[:limit_reached].present?
|
|
error!(@project.errors[:limit_reached], 403)
|
|
end
|
|
render_validation_error!(@project)
|
|
end
|
|
end
|
|
|
|
# Create new project for a specified user. Only available to admin users.
|
|
#
|
|
# Parameters:
|
|
# user_id (required) - The ID of a user
|
|
# name (required) - name for new project
|
|
# description (optional) - short project description
|
|
# default_branch (optional) - 'master' by default
|
|
# issues_enabled (optional)
|
|
# merge_requests_enabled (optional)
|
|
# builds_enabled (optional)
|
|
# wiki_enabled (optional)
|
|
# snippets_enabled (optional)
|
|
# shared_runners_enabled (optional)
|
|
# public (optional) - if true same as setting visibility_level = 20
|
|
# visibility_level (optional)
|
|
# import_url (optional)
|
|
# public_builds (optional)
|
|
# Example Request
|
|
# POST /projects/user/:user_id
|
|
post "user/:user_id" do
|
|
authenticated_as_admin!
|
|
user = User.find(params[:user_id])
|
|
attrs = attributes_for_keys [:name,
|
|
:description,
|
|
:default_branch,
|
|
:issues_enabled,
|
|
:merge_requests_enabled,
|
|
:builds_enabled,
|
|
:wiki_enabled,
|
|
:snippets_enabled,
|
|
:shared_runners_enabled,
|
|
:public,
|
|
:visibility_level,
|
|
:import_url,
|
|
:public_builds]
|
|
attrs = map_public_to_visibility_level(attrs)
|
|
@project = ::Projects::CreateService.new(user, attrs).execute
|
|
if @project.saved?
|
|
present @project, with: Entities::Project,
|
|
user_can_admin_project: can?(current_user, :admin_project, @project)
|
|
else
|
|
render_validation_error!(@project)
|
|
end
|
|
end
|
|
|
|
# Fork new project for the current user.
|
|
#
|
|
# Parameters:
|
|
# id (required) - The ID of a project
|
|
# Example Request
|
|
# POST /projects/fork/:id
|
|
post 'fork/:id' do
|
|
@forked_project =
|
|
::Projects::ForkService.new(user_project,
|
|
current_user).execute
|
|
if @forked_project.errors.any?
|
|
conflict!(@forked_project.errors.messages)
|
|
else
|
|
present @forked_project, with: Entities::Project,
|
|
user_can_admin_project: can?(current_user, :admin_project, @forked_project)
|
|
end
|
|
end
|
|
|
|
# Update an existing project
|
|
#
|
|
# Parameters:
|
|
# id (required) - the id of a project
|
|
# name (optional) - name of a project
|
|
# path (optional) - path of a project
|
|
# description (optional) - short project description
|
|
# issues_enabled (optional)
|
|
# merge_requests_enabled (optional)
|
|
# builds_enabled (optional)
|
|
# wiki_enabled (optional)
|
|
# snippets_enabled (optional)
|
|
# shared_runners_enabled (optional)
|
|
# public (optional) - if true same as setting visibility_level = 20
|
|
# visibility_level (optional) - visibility level of a project
|
|
# public_builds (optional)
|
|
# Example Request
|
|
# PUT /projects/:id
|
|
put ':id' do
|
|
attrs = attributes_for_keys [:name,
|
|
:path,
|
|
:description,
|
|
:default_branch,
|
|
:issues_enabled,
|
|
:merge_requests_enabled,
|
|
:builds_enabled,
|
|
:wiki_enabled,
|
|
:snippets_enabled,
|
|
:shared_runners_enabled,
|
|
:public,
|
|
:visibility_level,
|
|
:public_builds]
|
|
attrs = map_public_to_visibility_level(attrs)
|
|
authorize_admin_project
|
|
authorize! :rename_project, user_project if attrs[:name].present?
|
|
if attrs[:visibility_level].present?
|
|
authorize! :change_visibility_level, user_project
|
|
end
|
|
|
|
::Projects::UpdateService.new(user_project,
|
|
current_user, attrs).execute
|
|
|
|
if user_project.errors.any?
|
|
render_validation_error!(user_project)
|
|
else
|
|
present user_project, with: Entities::Project,
|
|
user_can_admin_project: can?(current_user, :admin_project, user_project)
|
|
end
|
|
end
|
|
|
|
# Archive project
|
|
#
|
|
# Parameters:
|
|
# id (required) - The ID of a project
|
|
# Example Request:
|
|
# PUT /projects/:id/archive
|
|
post ':id/archive' do
|
|
authorize!(:archive_project, user_project)
|
|
|
|
user_project.archive!
|
|
|
|
present user_project, with: Entities::Project
|
|
end
|
|
|
|
# Unarchive project
|
|
#
|
|
# Parameters:
|
|
# id (required) - The ID of a project
|
|
# Example Request:
|
|
# PUT /projects/:id/unarchive
|
|
post ':id/unarchive' do
|
|
authorize!(:archive_project, user_project)
|
|
|
|
user_project.unarchive!
|
|
|
|
present user_project, with: Entities::Project
|
|
end
|
|
|
|
# Star project
|
|
#
|
|
# Parameters:
|
|
# id (required) - The ID of a project
|
|
# Example Request:
|
|
# POST /projects/:id/star
|
|
post ':id/star' do
|
|
if current_user.starred?(user_project)
|
|
not_modified!
|
|
else
|
|
current_user.toggle_star(user_project)
|
|
user_project.reload
|
|
|
|
present user_project, with: Entities::Project
|
|
end
|
|
end
|
|
|
|
# Unstar project
|
|
#
|
|
# Parameters:
|
|
# id (required) - The ID of a project
|
|
# Example Request:
|
|
# DELETE /projects/:id/star
|
|
delete ':id/star' do
|
|
if current_user.starred?(user_project)
|
|
current_user.toggle_star(user_project)
|
|
user_project.reload
|
|
|
|
present user_project, with: Entities::Project
|
|
else
|
|
not_modified!
|
|
end
|
|
end
|
|
|
|
# Remove project
|
|
#
|
|
# Parameters:
|
|
# id (required) - The ID of a project
|
|
# Example Request:
|
|
# DELETE /projects/:id
|
|
delete ":id" do
|
|
authorize! :remove_project, user_project
|
|
::Projects::DestroyService.new(user_project, current_user, {}).pending_delete!
|
|
end
|
|
|
|
# Mark this project as forked from another
|
|
#
|
|
# Parameters:
|
|
# id: (required) - The ID of the project being marked as a fork
|
|
# forked_from_id: (required) - The ID of the project it was forked from
|
|
# Example Request:
|
|
# POST /projects/:id/fork/:forked_from_id
|
|
post ":id/fork/:forked_from_id" do
|
|
authenticated_as_admin!
|
|
forked_from_project = find_project(params[:forked_from_id])
|
|
unless forked_from_project.nil?
|
|
if user_project.forked_from_project.nil?
|
|
user_project.create_forked_project_link(forked_to_project_id: user_project.id, forked_from_project_id: forked_from_project.id)
|
|
else
|
|
render_api_error!("Project already forked", 409)
|
|
end
|
|
else
|
|
not_found!("Source Project")
|
|
end
|
|
|
|
end
|
|
|
|
# Remove a forked_from relationship
|
|
#
|
|
# Parameters:
|
|
# id: (required) - The ID of the project being marked as a fork
|
|
# Example Request:
|
|
# DELETE /projects/:id/fork
|
|
delete ":id/fork" do
|
|
authorize! :remove_fork_project, user_project
|
|
if user_project.forked?
|
|
user_project.forked_project_link.destroy
|
|
end
|
|
end
|
|
|
|
# Share project with group
|
|
#
|
|
# Parameters:
|
|
# id (required) - The ID of a project
|
|
# group_id (required) - The ID of a group
|
|
# group_access (required) - Level of permissions for sharing
|
|
#
|
|
# Example Request:
|
|
# POST /projects/:id/share
|
|
post ":id/share" do
|
|
authorize! :admin_project, user_project
|
|
required_attributes! [:group_id, :group_access]
|
|
|
|
unless user_project.allowed_to_share_with_group?
|
|
return render_api_error!("The project sharing with group is disabled", 400)
|
|
end
|
|
|
|
link = user_project.project_group_links.new
|
|
link.group_id = params[:group_id]
|
|
link.group_access = params[:group_access]
|
|
if link.save
|
|
present link, with: Entities::ProjectGroupLink
|
|
else
|
|
render_api_error!(link.errors.full_messages.first, 409)
|
|
end
|
|
end
|
|
|
|
# Upload a file
|
|
#
|
|
# Parameters:
|
|
# id: (required) - The ID of the project
|
|
# file: (required) - The file to be uploaded
|
|
post ":id/uploads" do
|
|
::Projects::UploadService.new(user_project, params[:file]).execute
|
|
end
|
|
|
|
# search for projects current_user has access to
|
|
#
|
|
# Parameters:
|
|
# query (required) - A string contained in the project name
|
|
# per_page (optional) - number of projects to return per page
|
|
# page (optional) - the page to retrieve
|
|
# Example Request:
|
|
# GET /projects/search/:query
|
|
get "/search/:query" do
|
|
ids = current_user.authorized_projects.map(&:id)
|
|
visibility_levels = [ Gitlab::VisibilityLevel::INTERNAL, Gitlab::VisibilityLevel::PUBLIC ]
|
|
projects = Project.where("(id in (?) OR visibility_level in (?)) AND (name LIKE (?))", ids, visibility_levels, "%#{params[:query]}%")
|
|
sort = params[:sort] == 'desc' ? 'desc' : 'asc'
|
|
|
|
projects = case params["order_by"]
|
|
when 'id' then projects.order("id #{sort}")
|
|
when 'name' then projects.order("name #{sort}")
|
|
when 'created_at' then projects.order("created_at #{sort}")
|
|
when 'last_activity_at' then projects.order("last_activity_at #{sort}")
|
|
else projects
|
|
end
|
|
|
|
present paginate(projects), with: Entities::Project
|
|
end
|
|
|
|
|
|
# Get a users list
|
|
#
|
|
# Example Request:
|
|
# GET /users
|
|
get ':id/users' do
|
|
@users = User.where(id: user_project.team.users.map(&:id))
|
|
@users = @users.search(params[:search]) if params[:search].present?
|
|
@users = paginate @users
|
|
present @users, with: Entities::UserBasic
|
|
end
|
|
end
|
|
end
|
|
end
|