diff --git a/.github/dependabot.yml b/.github/dependabot.yml index e3a9de6ea..40bb46093 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -15,18 +15,3 @@ updates: - dependency-name: "googleauth" versions: - ">=0.12" - - dependency-name: "azure_mgmt_resources" - versions: - - ">=0.16" - - dependency-name: "azure_mgmt_security" - versions: - - ">=0.19" - - dependency-name: "azure_mgmt_storage" - versions: - - ">=0.19" - - dependency-name: "azure_mgmt_key_vault" - versions: - - ">=0.18" - - dependency-name: "azure_graph_rbac" - versions: - - ">=0.17" diff --git a/lib/train/transports/azure.rb b/lib/train/transports/azure.rb index 322c9b4c7..4a0057fca 100644 --- a/lib/train/transports/azure.rb +++ b/lib/train/transports/azure.rb @@ -1,13 +1,7 @@ require "train/plugins" -require "ms_rest_azure" -require "azure_mgmt_resources" -require "azure_graph_rbac" -require "azure_mgmt_key_vault" require "socket" unless defined?(Socket) require "timeout" unless defined?(Timeout) require "train/transports/helpers/azure/file_credentials" -require "train/transports/clients/azure/graph_rbac" -require "train/transports/clients/azure/vault" module Train::Transports class Azure < Train.plugin(1) @@ -16,7 +10,6 @@ class Azure < Train.plugin(1) option :client_id, default: ENV["AZURE_CLIENT_ID"] option :client_secret, default: ENV["AZURE_CLIENT_SECRET"] option :subscription_id, default: ENV["AZURE_SUBSCRIPTION_ID"] - option :msi_port, default: ENV["AZURE_MSI_PORT"] || "50342" # This can provide the client id and secret option :credentials_file, default: ENV["AZURE_CRED_FILE"] @@ -25,6 +18,7 @@ def connection(_ = nil) @connection ||= Connection.new(@options) end + # @note all logic in this class is kept only to be moved later into inspec-azure class Connection < BaseConnection attr_reader :options @@ -45,132 +39,32 @@ def initialize(options) options[:credentials_file] = DEFAULT_FILE if options[:credentials_file].nil? @options.merge!(Helpers::Azure::FileCredentials.parse(**@options)) end - - @options[:msi_port] = @options[:msi_port].to_i unless @options[:msi_port].nil? - - # additional platform details - release = Gem.loaded_specs["azure_mgmt_resources"].version - @platform_details = { release: "azure_mgmt_resources-v#{release}" } - - connect end def platform force_platform!("azure", @platform_details) end - def azure_client(klass = ::Azure::Resources::Profiles::Latest::Mgmt::Client, opts = {}) + # @note klass needs to be overriden to accept resource management class + def azure_client(klass = nil, opts = {}) if cache_enabled?(:api_call) return @cache[:api_call][klass.to_s.to_sym] unless @cache[:api_call][klass.to_s.to_sym].nil? end - if klass == ::Azure::Resources::Profiles::Latest::Mgmt::Client - @credentials[:base_url] = MsRestAzure::AzureEnvironments::AzureCloud.resource_manager_endpoint_url - elsif klass == ::Azure::GraphRbac::Profiles::Latest::Client - client = GraphRbac.client(@credentials) - elsif klass == ::Azure::KeyVault::Profiles::Latest::Mgmt::Client - client = Vault.client(opts[:vault_name], @credentials) - end - client ||= klass.new(@credentials) - # Cache if enabled @cache[:api_call][klass.to_s.to_sym] ||= client if cache_enabled?(:api_call) client end - def connect - if msi_auth? - # this needs set for azure cloud to authenticate - ENV["MSI_VM"] = "true" - provider = ::MsRestAzure::MSITokenProvider.new(@options[:msi_port]) - else - provider = ::MsRestAzure::ApplicationTokenProvider.new( - @options[:tenant_id], - @options[:client_id], - @options[:client_secret] - ) - end - - @credentials = { - credentials: ::MsRest::TokenCredentials.new(provider), - subscription_id: @options[:subscription_id], - tenant_id: @options[:tenant_id], - } - @credentials[:client_id] = @options[:client_id] unless @options[:client_id].nil? - @credentials[:client_secret] = @options[:client_secret] unless @options[:client_secret].nil? - end - def uri "azure://#{@options[:subscription_id]}" end - # Returns the api version for the specified resource type - # - # If an api version has been specified in the options then the apis version table is updated - # with that value and it is returned - # - # However if it is not specified, or multiple types are being interrogated then this method - # will interrogate Azure for each of the types versions and pick the latest one. This is added - # to the apis table so that it can be retrieved quickly again of another one of those resources - # is encountered again in the resource collection. - # - # @param string resource_type The resource type for which the API is required - # @param hash options Options have that have been passed to the resource during the test. - # @option opts [String] :group_name Resource group name - # @option opts [String] :type Azure resource type - # @option opts [String] :name Name of specific resource to look for - # @option opts [String] :apiversion If looking for a specific item or type specify the api version to use - # - # @return string API Version of the specified resource type - def get_api_version(resource_type, options) - # if an api version has been set in the options, add to the apis hashtable with - # the resource type - if options[:apiversion] - @apis[resource_type] = options[:apiversion] - else - # only attempt to get the api version from Azure if the resource type - # is not present in the apis hashtable - unless @apis.key?(resource_type) - - # determine the namespace for the resource type - namespace, type = resource_type.split(%r{/}) - - client = azure_client(::Azure::Resources::Profiles::Latest::Mgmt::Client) - provider = client.providers.get(namespace) - - # get the latest API version for the type - # assuming that this is the first one in the list - api_versions = (provider.resource_types.find { |v| v.resource_type == type }).api_versions - @apis[resource_type] = api_versions[0] - end - end - - # return the api version for the type - @apis[resource_type] - end - def unique_identifier options[:subscription_id] || options[:tenant_id] end - - def msi_auth? - @options[:client_id].nil? && @options[:client_secret].nil? && port_open?(@options[:msi_port]) - end - - private - - def port_open?(port, seconds = 3) - Timeout.timeout(seconds) do - TCPSocket.new("localhost", port).close - true - rescue SystemCallError - false - end - rescue Timeout::Error - false - end end end end diff --git a/lib/train/transports/clients/azure/graph_rbac.rb b/lib/train/transports/clients/azure/graph_rbac.rb deleted file mode 100644 index 74d78a5f9..000000000 --- a/lib/train/transports/clients/azure/graph_rbac.rb +++ /dev/null @@ -1,33 +0,0 @@ -require "azure_graph_rbac" - -# Wrapper class for ::Azure::GraphRbac::Profiles::Latest::Client allowing custom configuration, -# for example, defining additional settings for the ::MsRestAzure::ApplicationTokenProvider. -class GraphRbac - AUTH_ENDPOINT = MsRestAzure::AzureEnvironments::AzureCloud.active_directory_endpoint_url - API_ENDPOINT = MsRestAzure::AzureEnvironments::AzureCloud.active_directory_graph_resource_id - - def self.client(credentials) - credentials[:credentials] = ::MsRest::TokenCredentials.new(provider(credentials)) - credentials[:base_url] = API_ENDPOINT - - ::Azure::GraphRbac::Profiles::Latest::Client.new(credentials) - end - - def self.provider(credentials) - ::MsRestAzure::ApplicationTokenProvider.new( - credentials[:tenant_id], - credentials[:client_id], - credentials[:client_secret], - settings - ) - end - - def self.settings - client_settings = MsRestAzure::ActiveDirectoryServiceSettings.get_azure_settings - client_settings.authentication_endpoint = AUTH_ENDPOINT - client_settings.token_audience = API_ENDPOINT - client_settings - end - - private_class_method :provider, :settings -end diff --git a/lib/train/transports/clients/azure/vault.rb b/lib/train/transports/clients/azure/vault.rb deleted file mode 100644 index 5696a6d3d..000000000 --- a/lib/train/transports/clients/azure/vault.rb +++ /dev/null @@ -1,39 +0,0 @@ -require "azure_mgmt_key_vault" - -# Wrapper class for ::Azure::KeyVault::Profiles::Latest::Mgmt::Client allowing custom configuration, -# for example, defining additional settings for the ::MsRestAzure::ApplicationTokenProvider. -class Vault - AUTH_ENDPOINT = MsRestAzure::AzureEnvironments::AzureCloud.active_directory_endpoint_url - RESOURCE_ENDPOINT = "https://vault.azure.net".freeze - - def self.client(vault_name, credentials) - raise ::Train::UserError, "Vault Name cannot be nil" if vault_name.nil? - - credentials[:credentials] = ::MsRest::TokenCredentials.new(provider(credentials)) - credentials[:base_url] = api_endpoint(vault_name) - - ::Azure::KeyVault::Profiles::Latest::Mgmt::Client.new(credentials) - end - - def self.provider(credentials) - ::MsRestAzure::ApplicationTokenProvider.new( - credentials[:tenant_id], - credentials[:client_id], - credentials[:client_secret], - settings - ) - end - - def self.api_endpoint(vault_name) - "https://#{vault_name}#{MsRestAzure::AzureEnvironments::AzureCloud.key_vault_dns_suffix}" - end - - def self.settings - client_settings = MsRestAzure::ActiveDirectoryServiceSettings.get_azure_settings - client_settings.authentication_endpoint = AUTH_ENDPOINT - client_settings.token_audience = RESOURCE_ENDPOINT - client_settings - end - - private_class_method :provider, :api_endpoint, :settings -end diff --git a/test/unit/transports/azure_test.rb b/test/unit/transports/azure_test.rb index 7615ab278..8cf6c95fb 100644 --- a/test/unit/transports/azure_test.rb +++ b/test/unit/transports/azure_test.rb @@ -1,10 +1,5 @@ require "helper" -# Required because this test file acesses classes under Azure:: -require "azure_mgmt_resources" -require "azure_graph_rbac" -require "azure_mgmt_key_vault" - describe "azure transport" do def transport(options = nil) ENV["AZURE_TENANT_ID"] = "test_tenant_id" @@ -76,62 +71,6 @@ def initialize(hash) _(client.is_a?(AzureResource)).must_equal true _(cache[:api_call].count).must_equal 0 end - - it "can use azure_client default client" do - management_api_client = Azure::Resources::Profiles::Latest::Mgmt::Client - client = connection.azure_client - _(client.class).must_equal management_api_client - end - - it "can use azure_client graph client" do - graph_api_client = Azure::GraphRbac::Profiles::Latest::Client - client = connection.azure_client(graph_api_client) - _(client.class).must_equal graph_api_client - end - - it "can use azure_client vault client" do - vault_api_client = ::Azure::KeyVault::Profiles::Latest::Mgmt::Client - client = connection.azure_client(vault_api_client, vault_name: "Test Vault") - _(client.class).must_equal vault_api_client - end - - it "cannot instantiate azure_client vault client without a vault name" do - vault_api_client = ::Azure::KeyVault::Profiles::Latest::Mgmt::Client - assert_raises(Train::UserError) do - connection.azure_client(vault_api_client) - end - end - end - - describe "connect" do - it "validate credentials" do - connection.connect - token = credentials[:credentials].instance_variable_get(:@token_provider) - _(token.class).must_equal MsRestAzure::ApplicationTokenProvider - - _(credentials[:credentials].class).must_equal MsRest::TokenCredentials - _(credentials[:tenant_id]).must_equal "test_tenant_id" - _(credentials[:client_id]).must_equal "test_client_id" - _(credentials[:client_secret]).must_equal "test_client_secret" - _(credentials[:subscription_id]).must_equal "test_subscription_id" - end - - it "validate msi credentials" do - options[:client_id] = nil - options[:client_secret] = nil - Train::Transports::Azure::Connection.any_instance.stubs(:port_open?).returns(true) - - connection.connect - token = credentials[:credentials].instance_variable_get(:@token_provider) - _(token.class).must_equal MsRestAzure::MSITokenProvider - - _(credentials[:credentials].class).must_equal MsRest::TokenCredentials - _(credentials[:tenant_id]).must_equal "test_tenant_id" - _(credentials[:subscription_id]).must_equal "test_subscription_id" - _(credentials[:client_id]).must_be_nil - _(credentials[:client_secret]).must_be_nil - _(options[:msi_port]).must_equal 50342 - end end describe "unique_identifier" do diff --git a/train.gemspec b/train.gemspec index 774d43eb5..57427b3eb 100644 --- a/train.gemspec +++ b/train.gemspec @@ -33,11 +33,6 @@ Gem::Specification.new do |spec| # azure, docker, gcp dependencies spec.add_dependency "activesupport", ">= 6.0.3.1" spec.add_dependency "inifile", "~> 3.0" - spec.add_dependency "azure_graph_rbac", "~> 0.16" - spec.add_dependency "azure_mgmt_key_vault", "~> 0.17" - spec.add_dependency "azure_mgmt_resources", "~> 0.15" - spec.add_dependency "azure_mgmt_security", "~> 0.18" - spec.add_dependency "azure_mgmt_storage", "~> 0.18" spec.add_dependency "docker-api", ">= 1.26", "< 3.0" spec.add_dependency "google-api-client", ">= 0.23.9", "<= 0.52.0" spec.add_dependency "googleauth", ">= 0.6.6", "<= 0.14.0"