diff --git a/ee/lib/remote_development/devfile_operations/restrictions_enforcer.rb b/ee/lib/remote_development/devfile_operations/restrictions_enforcer.rb index 2f18de4cc80642e4a3dc5473a5705af1645dccb1..25334bd186dfe12f930fc961c903e70b5d42daab 100644 --- a/ee/lib/remote_development/devfile_operations/restrictions_enforcer.rb +++ b/ee/lib/remote_development/devfile_operations/restrictions_enforcer.rb @@ -283,6 +283,8 @@ def self.validate_containers(context) # rubocop:enable Metrics/CyclomaticComplexity + # If we support endpoints in `components` other than `container`, make changes accordingly in + # ee/lib/remote_development/workspaces_server_operations/authorize_user_access/authorizer.rb # @param [Hash] context # @return [Hash] def self.validate_endpoints(context) diff --git a/ee/lib/remote_development/workspaces_server_operations/authorize_user_access/authorizer.rb b/ee/lib/remote_development/workspaces_server_operations/authorize_user_access/authorizer.rb index e9540e6870add795e7dfcc1e88fd2bdf97114c11..a197a3cdd580f72a15b4fe20a85d17e53d85cceb 100644 --- a/ee/lib/remote_development/workspaces_server_operations/authorize_user_access/authorizer.rb +++ b/ee/lib/remote_development/workspaces_server_operations/authorize_user_access/authorizer.rb @@ -16,14 +16,18 @@ def self.authorize(context) port: String => port } - # TODO: check if port is available in processed_devfile - unless workspace.user_id == user_id return Gitlab::Fp::Result.err( WorkspaceAuthorizeUserAccessFailed.new({ status: Status::NOT_AUTHORIZED }) ) end + unless port_exposed_for_workspace?(workspace, port) + return Gitlab::Fp::Result.err( + WorkspaceAuthorizeUserAccessFailed.new({ status: Status::PORT_NOT_FOUND }) + ) + end + Gitlab::Fp::Result.ok( context.merge( response_payload: { @@ -36,6 +40,26 @@ def self.authorize(context) ) ) end + + # @param [RemoteDevelopment::Workspace] workspace + # @param [String] port + # @return [Boolean] + def self.port_exposed_for_workspace?(workspace, port) + components = YAML.safe_load(workspace.processed_devfile).to_h.deep_symbolize_keys.fetch(:components, []) + components.each do |component| + # The endpoints can be available in either container, kubernetes and openshift components of the devfile. + # We only support container component so far. + container = component.fetch(:container, {}) + endpoints = container.fetch(:endpoints, []) + endpoints.each do |endpoint| + return true if endpoint.fetch(:targetPort, -1).to_s == port + end + end + + false + end + + private_class_method :port_exposed_for_workspace? end end end diff --git a/ee/spec/lib/remote_development/workspaces_server_operations/authorize_user_access/authorizer_spec.rb b/ee/spec/lib/remote_development/workspaces_server_operations/authorize_user_access/authorizer_spec.rb index 17cda4dd312c032425a6be8113a13b24b9114712..d3e84306aa468934ac5a935b9b9a1a28bc406d13 100644 --- a/ee/spec/lib/remote_development/workspaces_server_operations/authorize_user_access/authorizer_spec.rb +++ b/ee/spec/lib/remote_development/workspaces_server_operations/authorize_user_access/authorizer_spec.rb @@ -3,13 +3,25 @@ require "fast_spec_helper" RSpec.describe RemoteDevelopment::WorkspacesServerOperations::AuthorizeUserAccess::Authorizer, feature_category: :workspaces do + include_context 'with remote development shared fixtures' include ResultMatchers let(:user_id) { 123 } let(:workspace_id) { 456 } - let(:port) { "60001" } + let(:editor_port) { RemoteDevelopment::WorkspaceOperations::Create::CreateConstants::WORKSPACE_EDITOR_PORT.to_s } + let(:ssh_port) { RemoteDevelopment::WorkspaceOperations::Create::CreateConstants::WORKSPACE_SSH_PORT.to_s } + let(:port) { editor_port } let(:workspace_name) { "workspace-abc123" } - let(:workspace) { instance_double("RemoteDevelopment::Workspace", id: workspace_id, name: workspace_name, user_id: workspace_owner_id) } # rubocop:disable RSpec/VerifiedDoubleReference -- We're using the quoted version so we can use fast_spec_helper + let(:processed_devfile) { example_processed_devfile_yaml } + let(:workspace) do + instance_double( + "RemoteDevelopment::Workspace", # rubocop:disable RSpec/VerifiedDoubleReference -- We're using the quoted version so we can use fast_spec_helper + id: workspace_id, + name: workspace_name, + user_id: workspace_owner_id, + processed_devfile: processed_devfile + ) + end let(:context) do { @@ -44,6 +56,17 @@ ) end end + + context "when port is not exposed for the workspace" do + let(:port) { "10" } + + it "returns an err Result with PORT_NOT_FOUND status" do + expect(result).to be_err_result do |message| + expect(message).to be_a RemoteDevelopment::Messages::WorkspaceAuthorizeUserAccessFailed + expect(message.content).to eq({ status: "PORT_NOT_FOUND" }) + end + end + end end context "when user is not authorized (does not own the workspace)" do @@ -76,12 +99,12 @@ end context "with different port values" do - let(:port) { "8080" } + let(:port) { ssh_port } let(:workspace_owner_id) { user_id } it "includes the correct port in the response" do expect(result).to be_ok_result do |returned_context| - expect(returned_context[:response_payload][:info][:port]).to eq("8080") + expect(returned_context[:response_payload][:info][:port]).to eq(ssh_port) end end end