Commit c135ea52 authored by Tiger's avatar Tiger

Add licensed feature for internal/kubernetes/project_info endpoint

Although this endpoint previously existed in core, it was
only ever used by EE functionality (all Agent features were
licensed).

Now that some agent features are being made available in core,
we must add license checks to any functionality that will
continue to be a paid feature.
parent 52133b3b
......@@ -68,6 +68,7 @@ class License < ApplicationRecord
ci_cd_projects
ci_secrets_management
cluster_agents
cluster_agents_gitops
cluster_deployments
code_owner_approval_required
commit_committer_check
......
......@@ -9,6 +9,30 @@ module EE
namespace 'kubernetes' do
before { check_agent_token }
helpers do
def agent_has_access_to_project?(project)
project&.licensed_feature_available?(:cluster_agents_gitops) &&
(Guest.can?(:download_code, project) || agent.has_access_to?(project))
end
end
desc 'Gets project info' do
detail 'Retrieves project info (if authorized)'
end
route_setting :authentication, cluster_agent_token_allowed: true
get '/project_info' do
project = find_project(params[:id])
not_found! unless agent_has_access_to_project?(project)
status 200
{
project_id: project.id,
gitaly_info: gitaly_info(project),
gitaly_repository: gitaly_repository(project)
}
end
namespace 'modules/cilium_alert' do
desc 'POST network alerts' do
detail 'Creates network alert'
......
......@@ -64,6 +64,112 @@ RSpec.describe API::Internal::Kubernetes do
end
end
describe 'GET /internal/kubernetes/project_info' do
def send_request(headers: {}, params: {})
get api('/internal/kubernetes/project_info'), params: params, headers: headers.reverse_merge(jwt_auth_headers)
end
before do
stub_licensed_features(cluster_agents_gitops: true)
end
include_examples 'authorization'
include_examples 'agent authentication'
context 'an agent is found' do
let_it_be(:agent_token) { create(:cluster_agent_token) }
shared_examples 'agent token tracking'
context 'project is public' do
let(:project) { create(:project, :public) }
it 'returns expected data', :aggregate_failures do
send_request(params: { id: project.id }, headers: { 'Authorization' => "Bearer #{agent_token.token}" })
expect(response).to have_gitlab_http_status(:success)
expect(json_response).to match(
a_hash_including(
'project_id' => project.id,
'gitaly_info' => a_hash_including(
'address' => match(/\.socket$/),
'token' => 'secret',
'features' => {}
),
'gitaly_repository' => a_hash_including(
'storage_name' => project.repository_storage,
'relative_path' => project.disk_path + '.git',
'gl_repository' => "project-#{project.id}",
'gl_project_path' => project.full_path
)
)
)
end
context 'repository is for project members only' do
let(:project) { create(:project, :public, :repository_private) }
it 'returns 404' do
send_request(params: { id: project.id }, headers: { 'Authorization' => "Bearer #{agent_token.token}" })
expect(response).to have_gitlab_http_status(:not_found)
end
end
end
context 'project is private' do
let(:project) { create(:project, :private) }
it 'returns 404' do
send_request(params: { id: project.id }, headers: { 'Authorization' => "Bearer #{agent_token.token}" })
expect(response).to have_gitlab_http_status(:not_found)
end
context 'and agent belongs to project' do
let(:agent_token) { create(:cluster_agent_token, agent: create(:cluster_agent, project: project)) }
it 'returns 200' do
send_request(params: { id: project.id }, headers: { 'Authorization' => "Bearer #{agent_token.token}" })
expect(response).to have_gitlab_http_status(:success)
end
end
end
context 'project is internal' do
let(:project) { create(:project, :internal) }
it 'returns 404' do
send_request(params: { id: project.id }, headers: { 'Authorization' => "Bearer #{agent_token.token}" })
expect(response).to have_gitlab_http_status(:not_found)
end
end
context 'project does not exist' do
it 'returns 404' do
send_request(params: { id: non_existing_record_id }, headers: { 'Authorization' => "Bearer #{agent_token.token}" })
expect(response).to have_gitlab_http_status(:not_found)
end
end
context 'project does not have access to the feature' do
before do
stub_licensed_features(cluster_agents_gitops: false)
end
it 'returns 404' do
send_request(params: { id: non_existing_record_id }, headers: { 'Authorization' => "Bearer #{agent_token.token}" })
expect(response).to have_gitlab_http_status(:not_found)
end
end
end
end
describe 'POST /internal/kubernetes/modules/cilium_alert' do
let(:method) { :post }
let(:api_url) { '/internal/kubernetes/modules/cilium_alert' }
......
......@@ -79,25 +79,6 @@ module API
gitaly_repository: gitaly_repository(project)
}
end
desc 'Gets project info' do
detail 'Retrieves project info (if authorized)'
end
route_setting :authentication, cluster_agent_token_allowed: true
get '/project_info' do
project = find_project(params[:id])
unless Guest.can?(:download_code, project) || agent.has_access_to?(project)
not_found!
end
status 200
{
project_id: project.id,
gitaly_info: gitaly_info(project),
gitaly_repository: gitaly_repository(project)
}
end
end
namespace 'kubernetes/agent_configuration' do
......
......@@ -177,94 +177,4 @@ RSpec.describe API::Internal::Kubernetes do
end
end
end
describe 'GET /internal/kubernetes/project_info' do
def send_request(headers: {}, params: {})
get api('/internal/kubernetes/project_info'), params: params, headers: headers.reverse_merge(jwt_auth_headers)
end
include_examples 'authorization'
include_examples 'agent authentication'
context 'an agent is found' do
let_it_be(:agent_token) { create(:cluster_agent_token) }
shared_examples 'agent token tracking'
context 'project is public' do
let(:project) { create(:project, :public) }
it 'returns expected data', :aggregate_failures do
send_request(params: { id: project.id }, headers: { 'Authorization' => "Bearer #{agent_token.token}" })
expect(response).to have_gitlab_http_status(:success)
expect(json_response).to match(
a_hash_including(
'project_id' => project.id,
'gitaly_info' => a_hash_including(
'address' => match(/\.socket$/),
'token' => 'secret',
'features' => {}
),
'gitaly_repository' => a_hash_including(
'storage_name' => project.repository_storage,
'relative_path' => project.disk_path + '.git',
'gl_repository' => "project-#{project.id}",
'gl_project_path' => project.full_path
)
)
)
end
context 'repository is for project members only' do
let(:project) { create(:project, :public, :repository_private) }
it 'returns 404' do
send_request(params: { id: project.id }, headers: { 'Authorization' => "Bearer #{agent_token.token}" })
expect(response).to have_gitlab_http_status(:not_found)
end
end
end
context 'project is private' do
let(:project) { create(:project, :private) }
it 'returns 404' do
send_request(params: { id: project.id }, headers: { 'Authorization' => "Bearer #{agent_token.token}" })
expect(response).to have_gitlab_http_status(:not_found)
end
context 'and agent belongs to project' do
let(:agent_token) { create(:cluster_agent_token, agent: create(:cluster_agent, project: project)) }
it 'returns 200' do
send_request(params: { id: project.id }, headers: { 'Authorization' => "Bearer #{agent_token.token}" })
expect(response).to have_gitlab_http_status(:success)
end
end
end
context 'project is internal' do
let(:project) { create(:project, :internal) }
it 'returns 404' do
send_request(params: { id: project.id }, headers: { 'Authorization' => "Bearer #{agent_token.token}" })
expect(response).to have_gitlab_http_status(:not_found)
end
end
context 'project does not exist' do
it 'returns 404' do
send_request(params: { id: non_existing_record_id }, headers: { 'Authorization' => "Bearer #{agent_token.token}" })
expect(response).to have_gitlab_http_status(:not_found)
end
end
end
end
end
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment