Commit 186a28fb authored by Douglas Barbosa Alexandre's avatar Douglas Barbosa Alexandre

Merge branch 'incubation-5mp-google-cloud-service-account-protected-status' into 'master'

GCP service account CI vars protected if environment is protected

See merge request gitlab-org/gitlab!77590
parents 2f425df3 5e6c6be4
......@@ -24,24 +24,15 @@ class Projects::GoogleCloud::ServiceAccountsController < Projects::GoogleCloud::
end
def create
google_api_client = GoogleApi::CloudPlatform::Client.new(token_in_session, nil)
service_accounts_service = GoogleCloud::ServiceAccountsService.new(project)
gcp_project = params[:gcp_project]
environment = params[:environment]
generated_name = "GitLab :: #{@project.name} :: #{environment}"
generated_desc = "GitLab generated service account for project '#{@project.name}' and environment '#{environment}'"
service_account = google_api_client.create_service_account(gcp_project, generated_name, generated_desc)
service_account_key = google_api_client.create_service_account_key(gcp_project, service_account.unique_id)
service_accounts_service.add_for_project(
environment,
service_account.project_id,
service_account.to_json,
service_account_key.to_json
)
response = GoogleCloud::CreateServiceAccountsService.new(
project,
current_user,
google_oauth2_token: token_in_session,
gcp_project_id: params[:gcp_project],
environment_name: params[:environment]
).execute
redirect_to project_google_cloud_index_path(project), notice: _('Service account generated successfully')
redirect_to project_google_cloud_index_path(project), notice: response.message
rescue Google::Apis::ClientError, Google::Apis::ServerError, Google::Apis::AuthorizationError => error
handle_gcp_error(error, project)
end
......
# frozen_string_literal: true
module GoogleCloud
class CreateServiceAccountsService < :: BaseService
def execute
service_account = google_api_client.create_service_account(gcp_project_id, service_account_name, service_account_desc)
service_account_key = google_api_client.create_service_account_key(gcp_project_id, service_account.unique_id)
service_accounts_service.add_for_project(
environment_name,
service_account.project_id,
service_account.to_json,
service_account_key.to_json,
environment_protected?
)
ServiceResponse.success(message: _('Service account generated successfully'), payload: {
service_account: service_account,
service_account_key: service_account_key
})
end
private
def google_oauth2_token
@params[:google_oauth2_token]
end
def gcp_project_id
@params[:gcp_project_id]
end
def environment_name
@params[:environment_name]
end
def google_api_client
GoogleApi::CloudPlatform::Client.new(google_oauth2_token, nil)
end
def service_accounts_service
GoogleCloud::ServiceAccountsService.new(project)
end
def service_account_name
"GitLab :: #{project.name} :: #{environment_name}"
end
def service_account_desc
"GitLab generated service account for project '#{project.name}' and environment '#{environment_name}'"
end
# Overriden in EE
def environment_protected?
false
end
end
end
GoogleCloud::CreateServiceAccountsService.prepend_mod
......@@ -27,39 +27,42 @@ module GoogleCloud
end
end
def add_for_project(environment, gcp_project_id, service_account, service_account_key)
def add_for_project(environment, gcp_project_id, service_account, service_account_key, is_protected)
project_var_create_or_replace(
environment,
'GCP_PROJECT_ID',
gcp_project_id
gcp_project_id,
is_protected
)
project_var_create_or_replace(
environment,
'GCP_SERVICE_ACCOUNT',
service_account
service_account,
is_protected
)
project_var_create_or_replace(
environment,
'GCP_SERVICE_ACCOUNT_KEY',
service_account_key
service_account_key,
is_protected
)
end
private
def group_vars_by_environment
filtered_vars = @project.variables.filter { |variable| GCP_KEYS.include? variable.key }
filtered_vars = project.variables.filter { |variable| GCP_KEYS.include? variable.key }
filtered_vars.each_with_object({}) do |variable, grouped|
grouped[variable.environment_scope] ||= {}
grouped[variable.environment_scope][variable.key] = variable.value
end
end
def project_var_create_or_replace(environment_scope, key, value)
def project_var_create_or_replace(environment_scope, key, value, is_protected)
params = { key: key, filter: { environment_scope: environment_scope } }
existing_variable = ::Ci::VariablesFinder.new(@project, params).execute.first
existing_variable = ::Ci::VariablesFinder.new(project, params).execute.first
existing_variable.destroy if existing_variable
@project.variables.create!(key: key, value: value, environment_scope: environment_scope, protected: true)
project.variables.create!(key: key, value: value, environment_scope: environment_scope, protected: is_protected)
end
end
end
# frozen_string_literal: true
module EE
module GoogleCloud
module CreateServiceAccountsService
extend ::Gitlab::Utils::Override
include ::Gitlab::Utils::StrongMemoize
private
def existing_environments
strong_memoize(:environment) do
Environments::EnvironmentsFinder.new(project, current_user, name: environment_name).execute
end
end
def environment
strong_memoize(:environment) do
existing_environments.first
end
end
override :environment_protected?
def environment_protected?
environment ? environment.protected? : false
end
end
end
end
# frozen_string_literal: true
require 'spec_helper'
# Mock Types
MockGoogleOAuth2Credentials = Struct.new(:app_id, :app_secret)
MockServiceAccount = Struct.new(:project_id, :unique_id)
RSpec.describe GoogleCloud::CreateServiceAccountsService do
let(:google_oauth2_token) { 'mock-token' }
let(:gcp_project_id) { 'mock-gcp-project-id' }
describe '#execute' do
before do
stub_licensed_features(protected_environments: true)
allow(Gitlab::Auth::OAuth::Provider)
.to receive(:config_for)
.with('google_oauth2')
.and_return(MockGoogleOAuth2Credentials.new('mock-app-id', 'mock-app-secret'))
allow_next_instance_of(GoogleApi::CloudPlatform::Client) do |client|
allow(client)
.to receive(:create_service_account)
.and_return(MockServiceAccount.new('mock-project-id', 'mock-unique-id'))
allow(client)
.to receive(:create_service_account_key)
.and_return('mock-key')
end
end
context 'when environment is new' do
it 'creates unprotected vars' do
project = create(:project)
response = described_class.new(
project,
nil,
google_oauth2_token: google_oauth2_token,
gcp_project_id: gcp_project_id,
environment_name: '*'
).execute
expect(response.status).to eq(:success)
expect(response.message).to eq('Service account generated successfully')
expect(project.variables.count).to eq(3)
expect(project.variables.first.protected).to eq(false)
expect(project.variables.second.protected).to eq(false)
expect(project.variables.third.protected).to eq(false)
end
end
context 'when environment is not protected' do
it 'creates unprotected vars' do
project = create(:project)
environment = create(:environment, project: project, name: 'staging')
response = described_class.new(
project,
nil,
google_oauth2_token: google_oauth2_token,
gcp_project_id: gcp_project_id,
environment_name: environment.name
).execute
expect(response.status).to eq(:success)
expect(response.message).to eq('Service account generated successfully')
expect(project.variables.count).to eq(3)
expect(project.variables.first.protected).to eq(false)
expect(project.variables.second.protected).to eq(false)
expect(project.variables.third.protected).to eq(false)
end
end
context 'when environment is protected', :aggregate_failures do
it 'creates protected vars' do
project = create(:project)
environment = create(:environment, project: project, name: 'production')
create(:protected_environment, project: project, name: environment.name)
response = described_class.new(
project,
nil,
google_oauth2_token: google_oauth2_token,
gcp_project_id: gcp_project_id,
environment_name: environment.name
).execute
expect(response.status).to eq(:success)
expect(response.message).to eq('Service account generated successfully')
expect(project.variables.count).to eq(3)
expect(project.variables.first.protected).to eq(true)
expect(project.variables.second.protected).to eq(true)
expect(project.variables.third.protected).to eq(true)
end
end
end
end
# frozen_string_literal: true
require 'spec_helper'
# Mock Types
MockGoogleOAuth2Credentials = Struct.new(:app_id, :app_secret)
MockServiceAccount = Struct.new(:project_id, :unique_id)
RSpec.describe GoogleCloud::CreateServiceAccountsService do
describe '#execute' do
before do
allow(Gitlab::Auth::OAuth::Provider).to receive(:config_for)
.with('google_oauth2')
.and_return(MockGoogleOAuth2Credentials.new('mock-app-id', 'mock-app-secret'))
allow_next_instance_of(GoogleApi::CloudPlatform::Client) do |client|
allow(client).to receive(:create_service_account)
.and_return(MockServiceAccount.new('mock-project-id', 'mock-unique-id'))
allow(client).to receive(:create_service_account_key)
.and_return('mock-key')
end
end
it 'creates unprotected vars', :aggregate_failures do
project = create(:project)
service = described_class.new(
project,
nil,
google_oauth2_token: 'mock-token',
gcp_project_id: 'mock-gcp-project-id',
environment_name: '*'
)
response = service.execute
expect(response.status).to eq(:success)
expect(response.message).to eq('Service account generated successfully')
expect(project.variables.count).to eq(3)
expect(project.variables.first.protected).to eq(false)
expect(project.variables.second.protected).to eq(false)
expect(project.variables.third.protected).to eq(false)
end
end
end
......@@ -60,8 +60,8 @@ RSpec.describe GoogleCloud::ServiceAccountsService do
let_it_be(:project) { create(:project) }
it 'saves GCP creds as project CI vars' do
service.add_for_project('env_1', 'gcp_prj_id_1', 'srv_acc_1', 'srv_acc_key_1')
service.add_for_project('env_2', 'gcp_prj_id_2', 'srv_acc_2', 'srv_acc_key_2')
service.add_for_project('env_1', 'gcp_prj_id_1', 'srv_acc_1', 'srv_acc_key_1', true)
service.add_for_project('env_2', 'gcp_prj_id_2', 'srv_acc_2', 'srv_acc_key_2', false)
list = service.find_for_project
......@@ -81,7 +81,7 @@ RSpec.describe GoogleCloud::ServiceAccountsService do
end
it 'replaces previously stored CI vars with new CI vars' do
service.add_for_project('env_1', 'new_project', 'srv_acc_1', 'srv_acc_key_1')
service.add_for_project('env_1', 'new_project', 'srv_acc_1', 'srv_acc_key_1', false)
list = service.find_for_project
......@@ -101,9 +101,16 @@ RSpec.describe GoogleCloud::ServiceAccountsService do
end
end
it 'underlying project CI vars must be protected' do
expect(project.variables.first.protected).to eq(true)
expect(project.variables.second.protected).to eq(true)
it 'underlying project CI vars must be protected as per value' do
service.add_for_project('env_1', 'gcp_prj_id_1', 'srv_acc_1', 'srv_acc_key_1', true)
service.add_for_project('env_2', 'gcp_prj_id_2', 'srv_acc_2', 'srv_acc_key_2', false)
expect(project.variables[0].protected).to eq(true)
expect(project.variables[1].protected).to eq(true)
expect(project.variables[2].protected).to eq(true)
expect(project.variables[3].protected).to eq(false)
expect(project.variables[4].protected).to eq(false)
expect(project.variables[5].protected).to eq(false)
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