diff --git a/docs/resources/aws_apigateway_client_certificate.md b/docs/resources/aws_apigateway_client_certificate.md new file mode 100644 index 000000000..303dec30b --- /dev/null +++ b/docs/resources/aws_apigateway_client_certificate.md @@ -0,0 +1,83 @@ +--- +title: About the aws_apigateway_client_certificate Resource +platform: aws +--- + +# aws_apigateway_client_certificate + +Use the `aws_apigateway_client_certificate` InSpec audit resource to test properties of a single specific AWS API Gateway client certificate. + +The `AWS::ApiGateway::ClientCertificate` resource creates a client certificate that API Gateway uses to configure client-side SSL authentication for sending requests to the integration endpoint. + +## Syntax + +Ensure that the client certificate exists. + + describe aws_apigateway_client_certificate(client_certificate_id: 'CLIENT_CERTIFICATE_ID') do + it { should exist } + end + +## Parameters + +`client_certificate_id` _(required)_ + +The identifier of the client certificate. + +For additional information, see the [AWS documentation on AWS APIGateway ClientCertificate.](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-apigateway-clientcertificate.html). + +## Properties + +| Property | Description | +| --- | --- | +| client_certificate_id | The identifier of the client certificate. | +| description | The description of the client certificate. | +| pem_encoded_certificate | The PEM-encoded public key of the client certificate, which can be used to configure certificate authentication in the integration endpoint .| +| created_date | The timestamp when the client certificate was created.| +| expiration_date | The timestamp when the client certificate will expire.| +| tags | The collection of tags. Each tag element is associated with a given resource. | + +## Examples + +### Ensure a client certificate id is available. + + describe aws_apigateway_client_certificate(client_certificate_id: 'CLIENT_CERTIFICATE_ID') do + its('client_certificate_id') { should eq 'CLIENT_CERTIFICATE_ID' } + end + +### Ensure a pem encoded certificate is available. + + describe aws_apigateway_client_certificate(client_certificate_id: 'CLIENT_CERTIFICATE_ID') do + its('pem_encoded_certificate') { should eq 'PEM_ENCODED_CERTIFICATE' } + end + +## Matchers + +This InSpec audit resource has the following special matchers. For a full list of available matchers, please visit our [Universal Matchers page](https://www.inspec.io/docs/reference/matchers/). + +The controls will pass if the `get` method returns at least one result. + +### exist + +Use `should` to test that the entity exists. + + describe aws_apigateway_client_certificate(client_certificate_id: 'CLIENT_CERTIFICATE_ID') do + it { should exist } + end + +Use `should_not` to test the entity does not exist. + + describe aws_apigateway_client_certificate(client_certificate_id: 'CLIENT_CERTIFICATE_ID') do + it { should_not exist } + end + +### be_available + +Use `should` to check if the entity is available. + + describe aws_apigateway_client_certificate(client_certificate_id: 'CLIENT_CERTIFICATE_ID') do + it { should be_available } + end + +## AWS Permissions + +Your [Principal](https://docs.aws.amazon.com/IAM/latest/UserGuide/intro-structure.html#intro-structure-principal) will need the `APIGateway:Client:ClientCertificate` action with `Effect` set to `Allow`. diff --git a/docs/resources/aws_apigateway_client_certificates.md b/docs/resources/aws_apigateway_client_certificates.md new file mode 100644 index 000000000..4ee90240c --- /dev/null +++ b/docs/resources/aws_apigateway_client_certificates.md @@ -0,0 +1,73 @@ +--- +title: About the aws_apigateway_client_certificates Resource +platform: aws +--- + +# aws_apigateway_client_certificates + +Use the `aws_apigateway_client_certificates` InSpec audit resource to test properties of multiple AWS API Gateway client certificates. + +The `AWS::ApiGateway::ClientCertificate` resource creates a client certificate that API Gateway uses to configure client-side SSL authentication for sending requests to the integration endpoint. + +## Syntax + +Ensure that the client certificate exists. + + describe aws_apigateway_client_certificates do + it { should exist } + end + +## Parameters + +This resource does not require any parameters. + +For additional information, see the [AWS documentation on AWS API Gateway client certificate.](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-apigateway-clientcertificate.html). + +## Properties + +| Property | Description | Field | +| --- | --- | --- | +| client_certificate_ids | The identifier of the client certificate. | client_certificate_id | +| descriptions | The description of the client certificate. | description | +| pem_encoded_certificates | The PEM-encoded public key of the client certificate, which can be used to configure certificate authentication in the integration endpoint .| pem_encoded_certificate | +| created_dates | The timestamp when the client certificate was created.| created_date | +| expiration_dates | The timestamp when the client certificate will expire.| expiration_date | +| tags | The collection of tags. Each tag element is associated with a given resource. | tags | + +## Examples + +### Ensure a client certificate ID is available. + + describe aws_apigateway_client_certificates do + its('client_certificate_ids') { should include 'CLIENT_CERTIFICATE_ID' } + end + +### Ensure a PEM encoded certificate is available. + + describe aws_apigateway_client_certificates do + its('pem_encoded_certificates') { should include 'PEM_ENCODED_CERTIFICATE' } + end + +## Matchers + +This InSpec audit resource has the following special matchers. For a full list of available matchers, please visit our [Universal Matchers page](https://www.inspec.io/docs/reference/matchers/). + +The controls will pass if the `get` method returns at least one result. + +### exist + +Use `should` to test that the entity exists. + + describe aws_apigateway_client_certificates do + it { should exist } + end + +Use `should_not` to test the entity does not exist. + + describe aws_apigateway_api_keys do + it { should_not exist } + end + +## AWS Permissions + +Your [Principal](https://docs.aws.amazon.com/IAM/latest/UserGuide/intro-structure.html#intro-structure-principal) will need the `APIGateway:Client:ClientCertificate` action with `Effect` set to `Allow`. diff --git a/libraries/aws_apigateway_client_certificate.rb b/libraries/aws_apigateway_client_certificate.rb new file mode 100644 index 000000000..46f8084df --- /dev/null +++ b/libraries/aws_apigateway_client_certificate.rb @@ -0,0 +1,40 @@ +# frozen_string_literal: true + +require 'aws_backend' + +class AWSApiGatewayClientCertificate < AwsResourceBase + name 'aws_apigateway_client_certificate' + desc 'Gets information about the current ClientCertificate resource.' + + example " + describe aws_apigateway_client_certificate(client_certificate_id: 'ClientCertificateID') do + it { should exist } + end + " + + def initialize(opts = {}) + opts = { client_certificate_id: opts } if opts.is_a?(String) + super(opts) + validate_parameters(required: %i(client_certificate_id)) + raise ArgumentError, "#{@__resource_name__}: client_certificate_id must be provided" unless opts[:client_certificate_id] && !opts[:client_certificate_id].empty? + @display_name = opts[:client_certificate_id] + catch_aws_errors do + resp = @aws.apigateway_client.get_client_certificate({ client_certificate_id: opts[:client_certificate_id] }) + @res = resp.to_h + create_resource_methods(@res) + end + end + + def client_certificate_id + return nil unless exists? + @res[:client_certificate_id] + end + + def exists? + !@res.nil? && !@res.empty? + end + + def to_s + "Client Certificate ID: #{@display_name}" + end +end diff --git a/libraries/aws_apigateway_client_certificates.rb b/libraries/aws_apigateway_client_certificates.rb new file mode 100644 index 000000000..9a56ec49f --- /dev/null +++ b/libraries/aws_apigateway_client_certificates.rb @@ -0,0 +1,47 @@ +# frozen_string_literal: true + +require 'aws_backend' + +class AWSApiGatewayClientCertificates < AwsResourceBase + name 'aws_apigateway_client_certificates' + desc 'Gets a collection of ClientCertificate resources.' + + example " + describe aws_apigateway_client_certificates do + it { should exist } + end + " + + attr_reader :table + + FilterTable.create + .register_column(:client_certificate_ids, field: :client_certificate_id) + .register_column(:descriptions, field: :description) + .register_column(:pem_encoded_certificates, field: :pem_encoded_certificate) + .register_column(:created_dates, field: :created_date) + .register_column(:expiration_dates, field: :expiration_date) + .register_column(:tags, field: :tags) + .install_filter_methods_on_resource(self, :table) + + def initialize(opts = {}) + super(opts) + validate_parameters + @table = fetch_data + end + + def fetch_data + catch_aws_errors do + @table = @aws.apigateway_client.get_client_certificates.map do |table| + table.items.map { |table_name| { + client_certificate_id: table_name.client_certificate_id, + description: table_name.description, + pem_encoded_certificate: table_name.pem_encoded_certificate, + created_date: table_name.created_date, + expiration_date: table_name.expiration_date, + tags: table_name.tags, + } + } + end.flatten + end + end +end diff --git a/test/integration/verify/controls/aws_apigateway_client_certificate.rb b/test/integration/verify/controls/aws_apigateway_client_certificate.rb new file mode 100644 index 000000000..08c1b5efd --- /dev/null +++ b/test/integration/verify/controls/aws_apigateway_client_certificate.rb @@ -0,0 +1,23 @@ +aws_api_gateway_client_certificate_id = attribute(:aws_api_gateway_client_certificate_id, value: '', description: '') +aws_api_gateway_client_certificate_created_date = attribute(:aws_api_gateway_client_certificate_created_date, value: '', description: '') +aws_api_gateway_client_certificate_expiration_date = attribute(:aws_api_gateway_client_certificate_expiration_date, value: '', description: '') +aws_api_gateway_client_certificate_pem_encoded_certificate = attribute(:aws_api_gateway_client_certificate_pem_encoded_certificate, value: '', description: '') + +control 'aws-apigateway-client-certificate' do + + impact 1.0 + title 'Ensure API Gateway Client Certificate resource has the correct properties.' + + describe aws_apigateway_client_certificate(client_certificate_id: aws_api_gateway_client_certificate_id) do + it { should exist } + end + + describe aws_apigateway_client_certificate(client_certificate_id: aws_api_gateway_client_certificate_id) do + its('client_certificate_id') { should eq aws_api_gateway_client_certificate_id } + its('description') { should eq 'My client certificate.' } + its('pem_encoded_certificate') { should eq aws_api_gateway_client_certificate_pem_encoded_certificate } + its('created_date') { should eq Time.parse(aws_api_gateway_client_certificate_created_date) } + its('expiration_date') { should eq Time.parse(aws_api_gateway_client_certificate_expiration_date) } + its('tags') { should be_empty } + end +end \ No newline at end of file diff --git a/test/integration/verify/controls/aws_apigateway_client_certificates.rb b/test/integration/verify/controls/aws_apigateway_client_certificates.rb new file mode 100644 index 000000000..1c4ba47d2 --- /dev/null +++ b/test/integration/verify/controls/aws_apigateway_client_certificates.rb @@ -0,0 +1,19 @@ +aws_api_gateway_client_certificate_id = attribute(:aws_api_gateway_client_certificate_id, value: '', description: '') +aws_api_gateway_client_certificate_created_date = attribute(:aws_api_gateway_client_certificate_created_date, value: '', description: '') +aws_api_gateway_client_certificate_expiration_date = attribute(:aws_api_gateway_client_certificate_expiration_date, value: '', description: '') +aws_api_gateway_client_certificate_pem_encoded_certificate = attribute(:aws_api_gateway_client_certificate_pem_encoded_certificate, value: '', description: '') + +control 'aws-apigateway-client-certificates' do + + impact 1.0 + title 'Ensure API Gateway Client Certificate resource has the correct properties.' + + describe aws_apigateway_client_certificates do + its('client_certificate_ids') { should include aws_api_gateway_client_certificate_id } + its('descriptions') { should include 'My client certificate.' } + its('pem_encoded_certificates') { should include aws_api_gateway_client_certificate_pem_encoded_certificate } + its('created_dates') { should include Time.parse(aws_api_gateway_client_certificate_created_date) } + its('expiration_dates') { should include Time.parse(aws_api_gateway_client_certificate_expiration_date) } + its('tags') { should_not be_empty } + end +end \ No newline at end of file diff --git a/test/unit/resources/aws_apigateway_client_certificate_test.rb b/test/unit/resources/aws_apigateway_client_certificate_test.rb new file mode 100644 index 000000000..0448fcd06 --- /dev/null +++ b/test/unit/resources/aws_apigateway_client_certificate_test.rb @@ -0,0 +1,64 @@ +require 'helper' +require 'aws_apigateway_client_certificate' +require 'aws-sdk-core' + +class AWSApiGatewayClientCertificateConstructorTest < Minitest::Test + + def test_empty_params_not_ok + assert_raises(ArgumentError) { AWSApiGatewayClientCertificate.new(client_args: { stub_responses: true }) } + end + + def test_empty_param_arg_not_ok + assert_raises(ArgumentError) { AWSApiGatewayClientCertificate.new(client_certificate_id: '', client_args: { stub_responses: true }) } + end + + def test_rejects_unrecognized_params + assert_raises(ArgumentError) { AWSApiGatewayClientCertificate.new(unexpected: 9) } + end +end + +class AWSApiGatewayClientCertificateSuccessPathTest < Minitest::Test + + def setup + data = {} + data[:method] = :get_client_certificate + mock_data = {} + mock_data[:client_certificate_id] = 'test1' + mock_data[:description] = 'test1' + mock_data[:pem_encoded_certificate] = 'test1' + mock_data[:created_date] = Time.parse("2013-06-11T23:52:02Z2020-06-05T11:30:39.730000+01:00") + mock_data[:expiration_date] = Time.parse("2013-06-11T23:52:02Z2020-06-05T11:30:39.730000+01:00") + mock_data[:tags] = {} + data[:data] = [mock_data] + data[:client] = Aws::APIGateway::Client + @resp = AWSApiGatewayClientCertificate.new(client_certificate_id: 'test1', client_args: { stub_responses: true }, stub_data: [data]) + end + + def test_client_certificate_exists + assert @resp.exists? + end + + def test_client_certificate_id + assert_equal(@resp.client_certificate_id, 'test1') + end + + def test_description + assert_equal(@resp.description, 'test1') + end + + def test_pem_encoded_certificate + assert_equal(@resp.pem_encoded_certificate, 'test1') + end + + def test_created_date + assert_equal(@resp.created_date, Time.parse("2013-06-11T23:52:02Z2020-06-05T11:30:39.730000+01:00")) + end + + def test_expiration_date + assert_equal(@resp.expiration_date, Time.parse("2013-06-11T23:52:02Z2020-06-05T11:30:39.730000+01:00")) + end + + def test_tags + assert_equal(@resp.tags, {}) + end +end diff --git a/test/unit/resources/aws_apigateway_client_certificates_test.rb b/test/unit/resources/aws_apigateway_client_certificates_test.rb new file mode 100644 index 000000000..9ae2c586e --- /dev/null +++ b/test/unit/resources/aws_apigateway_client_certificates_test.rb @@ -0,0 +1,60 @@ +require 'helper' +require 'aws_apigateway_client_certificates' +require 'aws-sdk-core' + +class AWSApiGatewayClientCertificatesConstructorTest < Minitest::Test + + def test_empty_params_ok + AWSApiGatewayClientCertificates.new(client_args: { stub_responses: true }) + end + + def test_rejects_other_args + assert_raises(ArgumentError) { AWSApiGatewayClientCertificates.new('rubbish') } + end + + def test_api_mapping_non_existing_for_empty_response + refute AWSApiGatewayClientCertificates.new(client_args: { stub_responses: true }).exist? + end +end + +class AWSApiGatewayClientCertificatesHappyPathTest < Minitest::Test + + def setup + data = {} + data[:method] = :get_client_certificates + mock_data = {} + mock_data[:client_certificate_id] = 'test1' + mock_data[:description] = 'test1' + mock_data[:pem_encoded_certificate] = 'test1' + mock_data[:created_date] = Time.parse("2013-06-11T23:52:02Z2020-06-05T11:30:39.730000+01:00") + mock_data[:expiration_date] = Time.parse("2013-06-11T23:52:02Z2020-06-05T11:30:39.730000+01:00") + mock_data[:tags] = {} + data[:data] = { :items => [mock_data] } + data[:client] = Aws::APIGateway::Client + @resp = AWSApiGatewayClientCertificates.new(client_args: { stub_responses: true }, stub_data: [data]) + end + + def test_client_certificate_ids + assert_equal(@resp.client_certificate_ids, ['test1']) + end + + def test_descriptions + assert_equal(@resp.descriptions, ['test1']) + end + + def test_pem_encoded_certificates + assert_equal(@resp.pem_encoded_certificates, ['test1']) + end + + def test_created_dates + assert_equal(@resp.created_dates, [Time.parse("2013-06-11T23:52:02Z2020-06-05T11:30:39.730000+01:00")]) + end + + def test_expiration_dates + assert_equal(@resp.expiration_dates, [Time.parse("2013-06-11T23:52:02Z2020-06-05T11:30:39.730000+01:00")]) + end + + def test_tags + assert_equal(@resp.tags, [{}]) + end +end