Skip to content
Snippets Groups Projects
Verified Commit e0f2c4cb authored by Surabhi Suman's avatar Surabhi Suman :two: Committed by GitLab
Browse files

feat: Disable Duo Workflow access when duo features disabled

This restricts Duo Workflow access when duo_features_enabled
is set to false on project, group or instance level.
Changelog: changed
EE: true
parent aed1e97b
No related branches found
No related tags found
2 merge requests!170053Security patch upgrade alert: Only expose to admins 17-4,!168155Disable Duo Workflow access when duo features disabled
......@@ -974,7 +974,8 @@ module ProjectPolicy
with_scope :subject
condition(:duo_workflow_available) do
::Gitlab::Llm::StageCheck.available?(@subject, :duo_workflow)
@subject.duo_features_enabled &&
::Gitlab::Llm::StageCheck.available?(@subject, :duo_workflow)
end
rule { duo_workflow_enabled & duo_workflow_available & can?(:developer_access) }.policy do
......
......@@ -15,7 +15,10 @@ class Workflows < ::API::Base
helpers do
def find_workflow!(id)
::Ai::DuoWorkflows::Workflow.for_user_with_id!(current_user.id, id)
workflow = ::Ai::DuoWorkflows::Workflow.for_user_with_id!(current_user.id, id)
return workflow if current_user.can?(:read_duo_workflow, workflow)
forbidden!
end
def find_event!(workflow, id)
......@@ -176,6 +179,7 @@ def render_response(response)
end
patch '/:id' do
workflow = find_workflow!(params[:id])
forbidden! unless current_user.can?(:update_duo_workflow, workflow)
service = ::Ai::DuoWorkflows::UpdateWorkflowStatusService.new(
workflow: workflow,
......
......@@ -51,6 +51,15 @@
end
it { is_expected.to be_allowed(:read_duo_workflow_event) }
context "when duo_features_enabled settings is turned off" do
before do
project.project_setting.update!(duo_features_enabled: false)
end
it { is_expected.to be_disallowed(:read_duo_workflow) }
it { is_expected.to be_disallowed(:update_duo_workflow) }
end
end
end
end
......
......@@ -56,6 +56,15 @@
it { is_expected.to be_allowed(:read_duo_workflow) }
it { is_expected.to be_allowed(:update_duo_workflow) }
context "when duo_features_enabled settings is turned off" do
before do
project.project_setting.update!(duo_features_enabled: false)
end
it { is_expected.to be_disallowed(:read_duo_workflow) }
it { is_expected.to be_disallowed(:update_duo_workflow) }
end
end
end
end
......
......@@ -4206,33 +4206,39 @@ def create_member_role(member, abilities = member_role_abilities)
describe 'duo_workflow' do
let(:project) { public_project_in_group }
where(:duo_workflow_feature_flag, :stage_check_available, :current_user, :match_expected_result) do
true | true | ref(:owner) | be_allowed(:duo_workflow)
true | true | ref(:maintainer) | be_allowed(:duo_workflow)
true | true | ref(:developer) | be_allowed(:duo_workflow)
true | true | ref(:guest) | be_disallowed(:duo_workflow)
true | true | ref(:non_member) | be_disallowed(:duo_workflow)
true | false | ref(:owner) | be_disallowed(:duo_workflow)
true | false | ref(:maintainer) | be_disallowed(:duo_workflow)
true | false | ref(:developer) | be_disallowed(:duo_workflow)
true | false | ref(:guest) | be_disallowed(:duo_workflow)
true | false | ref(:non_member) | be_disallowed(:duo_workflow)
false | true | ref(:owner) | be_disallowed(:duo_workflow)
false | true | ref(:maintainer) | be_disallowed(:duo_workflow)
false | true | ref(:developer) | be_disallowed(:duo_workflow)
false | true | ref(:guest) | be_disallowed(:duo_workflow)
false | true | ref(:non_member) | be_disallowed(:duo_workflow)
false | false | ref(:owner) | be_disallowed(:duo_workflow)
false | false | ref(:maintainer) | be_disallowed(:duo_workflow)
false | false | ref(:developer) | be_disallowed(:duo_workflow)
false | false | ref(:guest) | be_disallowed(:duo_workflow)
false | false | ref(:non_member) | be_disallowed(:duo_workflow)
where(:duo_workflow_feature_flag, :stage_check_available, :duo_features_enabled, :current_user, :match_expected_result) do
true | true | true | ref(:owner) | be_allowed(:duo_workflow)
true | true | true | ref(:maintainer) | be_allowed(:duo_workflow)
true | true | true | ref(:developer) | be_allowed(:duo_workflow)
true | true | true | ref(:guest) | be_disallowed(:duo_workflow)
true | true | true | ref(:non_member) | be_disallowed(:duo_workflow)
true | false | true | ref(:owner) | be_disallowed(:duo_workflow)
true | false | true | ref(:maintainer) | be_disallowed(:duo_workflow)
true | false | true | ref(:developer) | be_disallowed(:duo_workflow)
true | false | true | ref(:guest) | be_disallowed(:duo_workflow)
true | false | true | ref(:non_member) | be_disallowed(:duo_workflow)
false | true | true | ref(:owner) | be_disallowed(:duo_workflow)
false | true | true | ref(:maintainer) | be_disallowed(:duo_workflow)
false | true | true | ref(:developer) | be_disallowed(:duo_workflow)
false | true | true | ref(:guest) | be_disallowed(:duo_workflow)
false | true | true | ref(:non_member) | be_disallowed(:duo_workflow)
false | false | true | ref(:owner) | be_disallowed(:duo_workflow)
false | false | true | ref(:maintainer) | be_disallowed(:duo_workflow)
false | false | true | ref(:developer) | be_disallowed(:duo_workflow)
false | false | true | ref(:guest) | be_disallowed(:duo_workflow)
false | false | true | ref(:non_member) | be_disallowed(:duo_workflow)
false | false | false | ref(:owner) | be_disallowed(:duo_workflow)
false | false | false | ref(:maintainer) | be_disallowed(:duo_workflow)
false | false | false | ref(:developer) | be_disallowed(:duo_workflow)
false | false | false | ref(:guest) | be_disallowed(:duo_workflow)
false | false | false | ref(:non_member) | be_disallowed(:duo_workflow)
end
with_them do
before do
stub_feature_flags(duo_workflow: duo_workflow_feature_flag)
allow(::Gitlab::Llm::StageCheck).to receive(:available?).with(project, :duo_workflow).and_return(stage_check_available)
stub_ee_application_setting(duo_features_enabled: duo_features_enabled)
end
it { is_expected.to match_expected_result }
......
......@@ -8,18 +8,18 @@
let_it_be(:group) { create(:group) }
let_it_be(:project) { create(:project, :repository, group: group) }
let_it_be(:user) { create(:user, maintainer_of: project) }
let(:workflow) { create(:duo_workflows_workflow, user: user, project: project) }
let(:duo_workflow_service_url) { 'duo-workflow-service.example.com:50052' }
let(:oauth_token) { create(:oauth_access_token, user: user, scopes: [:ai_workflows]) }
let_it_be(:workflow) { create(:duo_workflows_workflow, user: user, project: project) }
let_it_be(:duo_workflow_service_url) { 'duo-workflow-service.example.com:50052' }
let_it_be(:oauth_token) { create(:oauth_access_token, user: user, scopes: [:ai_workflows]) }
before do
allow(::Gitlab::Llm::StageCheck).to receive(:available?).with(project, :duo_workflow).and_return(true)
end
describe 'POST /ai/duo_workflows/workflows' do
let(:path) { "/ai/duo_workflows/workflows" }
let(:params) { { project_id: project.id } }
before do
allow(::Gitlab::Llm::StageCheck).to receive(:available?).with(project, :duo_workflow).and_return(true)
end
context 'when success' do
it 'creates the Ai::DuoWorkflows::Workflow' do
expect do
......@@ -50,25 +50,36 @@
end
end
context 'with a project where the user is not a developer' do
let(:guest) { create(:user, guest_of: project) }
context 'when failure' do
shared_examples 'workflow access is forbidden' do
it 'workflow access is forbidden' do
post api(path, user), params: params
it 'is forbidden' do
post api(path, guest), params: params
expect(response).to have_gitlab_http_status(:forbidden)
end
end
expect(response).to have_gitlab_http_status(:forbidden)
context 'with a project where the user is not a developer' do
let(:user) { create(:user, guest_of: project) }
it_behaves_like 'workflow access is forbidden'
end
end
context 'when the duo_workflows feature flag is disabled for the user' do
before do
stub_feature_flags(duo_workflow: false)
context 'when the duo_workflows feature flag is disabled for the user' do
before do
stub_feature_flags(duo_workflow: false)
end
it_behaves_like 'workflow access is forbidden'
end
it 'is forbidden' do
post api(path, user), params: params
context 'when duo_features_enabled settings is turned off' do
before do
project.project_setting.update!(duo_features_enabled: false)
project.reload
end
expect(response).to have_gitlab_http_status(:forbidden)
it_behaves_like 'workflow access is forbidden'
end
end
......@@ -139,6 +150,18 @@
end
end
context 'when duo_features_enabled settings is turned off' do
before do
workflow.project.project_setting.update!(duo_features_enabled: false)
workflow.project.reload
end
it 'returns forbidden' do
get api(path, user)
expect(response).to have_gitlab_http_status(:forbidden)
end
end
context 'with a workflow belonging to a different user' do
let(:workflow) { create(:duo_workflows_workflow) }
......@@ -191,8 +214,8 @@
let(:path) { "/ai/duo_workflows/workflows/#{workflow.id}/checkpoints" }
it 'returns the checkpoints in descending order of thread_ts' do
checkpoint1 = create(:duo_workflows_checkpoint, project: project)
checkpoint2 = create(:duo_workflows_checkpoint, project: project)
checkpoint1 = create(:duo_workflows_checkpoint, workflow: workflow)
checkpoint2 = create(:duo_workflows_checkpoint, workflow: workflow)
workflow.checkpoints << checkpoint1
workflow.checkpoints << checkpoint2
......
......@@ -87,5 +87,17 @@
end
end
end
context 'when duo_features_enabled settings is turned off' do
before do
project.project_setting.update!(duo_features_enabled: false)
end
it 'returns an empty array' do
post_graphql(query, current_user: user)
expect(returned_workflows).to be_empty
end
end
end
end
......@@ -47,7 +47,7 @@
allow(::Gitlab::Llm::StageCheck).to receive(:available?).with(project, :duo_workflow).and_return(false)
end
it 'returns an empty array' do
it 'does not receive any data' do
expect(response).to be_nil
end
end
......@@ -78,6 +78,16 @@
expect(updated_workflow['errors']).to eq([])
expect(updated_workflow['workflowStatus']).to eq("CREATED")
end
context 'when duo_features_enabled settings is turned off' do
before do
project.project_setting.update!(duo_features_enabled: false)
end
it 'does not receive any data' do
expect(response).to be_nil
end
end
end
end
......
......@@ -127,6 +127,21 @@
expect(result[:reason]).to eq(:unauthorized)
expect(workflow.reload.human_status_name).to eq("running")
end
context "when duo_features_enabled settings is turned off" do
before do
project.project_setting.update!(duo_features_enabled: false)
end
it "returns not found", :aggregate_failures do
result = described_class.new(workflow: workflow, current_user: user, status_event: "finish").execute
expect(result[:status]).to eq(:error)
expect(result[:message]).to eq("Can not update workflow")
expect(result[:reason]).to eq(:unauthorized)
expect(workflow.reload.human_status_name).to eq("running")
end
end
end
end
end
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment