From 4e222deaae92292ee8c8954e0639081212b492aa Mon Sep 17 00:00:00 2001 From: Sam Levenick Date: Fri, 1 Mar 2019 18:45:27 +0000 Subject: [PATCH] Inspec resource folder Signed-off-by: Modular Magician --- .../google_resourcemanager_folder.md | 38 ++++++++ .../google_resourcemanager_folders.md | 36 ++++++++ libraries/google_resourcemanager_folder.rb | 62 +++++++++++++ libraries/google_resourcemanager_folders.rb | 92 +++++++++++++++++++ test/integration/build/gcp-mm.tf | 15 +++ .../configuration/mm-attributes.yml | 5 +- .../controls/google_resourcemanager_folder.rb | 33 +++++++ .../google_resourcemanager_folders.rb | 32 +++++++ 8 files changed, 312 insertions(+), 1 deletion(-) create mode 100644 docs/resources/google_resourcemanager_folder.md create mode 100644 docs/resources/google_resourcemanager_folders.md create mode 100644 libraries/google_resourcemanager_folder.rb create mode 100644 libraries/google_resourcemanager_folders.rb create mode 100644 test/integration/verify/controls/google_resourcemanager_folder.rb create mode 100644 test/integration/verify/controls/google_resourcemanager_folders.rb diff --git a/docs/resources/google_resourcemanager_folder.md b/docs/resources/google_resourcemanager_folder.md new file mode 100644 index 000000000..203595877 --- /dev/null +++ b/docs/resources/google_resourcemanager_folder.md @@ -0,0 +1,38 @@ +--- +title: About the google_resourcemanager_folder resource +platform: gcp +--- + +## Syntax +A `google_resourcemanager_folder` is used to test a Google Folder resource + +## Examples +``` +describe.one do + google_resourcemanager_folders(parent: 'organizations/12345').names.each do |name| + describe google_resourcemanager_folder(name: name) do + it { should exist } + its('display_name') { should eq 'inspec-gcp-folder' } + end + end +end +``` + +## Properties +Properties that can be accessed from the `google_resourcemanager_folder` resource: + + * `name`: The resource name of the Folder. Its format is folders/{folder_id}, for example: "folders/1234". + + * `lifecycle_state`: The lifecycle state of the folder. Updates to the lifecycleState must be performed via folders.delete and folders.undelete. + + * `create_time`: Time of creation + + * `parent`: The Folder’s parent's resource name. Updates to the folder's parent must be performed via folders.move. + + * `display_name`: The folder’s display name. A folder’s display name must be unique amongst its siblings, e.g. no two folders with the same parent can share the same display name. The display name must start and end with a letter or digit, may contain letters, digits, spaces, hyphens and underscores and can be no longer than 30 characters. This is captured by the regular expression: `[\p{L}\p{N}]([\p{L}\p{N}_- ]{0,28}[\p{L}\p{N}])?`. + + + +## GCP Permissions + +Ensure the [Cloud Resource Manager API](https://console.cloud.google.com/apis/library/cloudresourcemanager.googleapis.com/) is enabled for the current project. diff --git a/docs/resources/google_resourcemanager_folders.md b/docs/resources/google_resourcemanager_folders.md new file mode 100644 index 000000000..af1d42018 --- /dev/null +++ b/docs/resources/google_resourcemanager_folders.md @@ -0,0 +1,36 @@ +--- +title: About the google_resourcemanager_folders resource +platform: gcp +--- + +## Syntax +A `google_resourcemanager_folders` is used to test a Google Folder resource + +## Examples +``` +describe.one do + google_resourcemanager_folders(parent: 'organizations/12345').display_names.each do |display_name| + describe display_name do + it { should eq 'inspec-gcp-folder' } + end + end +end +``` + +## Properties +Properties that can be accessed from the `google_resourcemanager_folders` resource: + +See [google_resourcemanager_folder.md](google_resourcemanager_folder.md) for more detailed information + * `names`: an array of `google_resourcemanager_folder` name + * `lifecycle_states`: an array of `google_resourcemanager_folder` lifecycle_state + * `create_times`: an array of `google_resourcemanager_folder` create_time + * `parents`: an array of `google_resourcemanager_folder` parent + * `display_names`: an array of `google_resourcemanager_folder` display_name + +## Filter Criteria +This resource supports all of the above properties as filter criteria, which can be used +with `where` as a block or a method. + +## GCP Permissions + +Ensure the [Cloud Resource Manager API](https://console.cloud.google.com/apis/library/cloudresourcemanager.googleapis.com/) is enabled for the current project. diff --git a/libraries/google_resourcemanager_folder.rb b/libraries/google_resourcemanager_folder.rb new file mode 100644 index 000000000..c85af34b2 --- /dev/null +++ b/libraries/google_resourcemanager_folder.rb @@ -0,0 +1,62 @@ +# frozen_string_literal: false + +# ---------------------------------------------------------------------------- +# +# *** AUTO GENERATED CODE *** AUTO GENERATED CODE *** +# +# ---------------------------------------------------------------------------- +# +# This file is automatically generated by Magic Modules and manual +# changes will be clobbered when the file is regenerated. +# +# Please read more about how to change this file in README.md and +# CONTRIBUTING.md located at the root of this package. +# +# ---------------------------------------------------------------------------- +require 'gcp_backend' + +# A provider to manage Resource Manager resources. +class Folder < GcpResourceBase + name 'google_resourcemanager_folder' + desc 'Folder' + supports platform: 'gcp' + + attr_reader :name + attr_reader :lifecycle_state + attr_reader :create_time + attr_reader :parent + attr_reader :display_name + + def initialize(params) + super(params.merge({ use_http_transport: true })) + @fetched = @connection.fetch(product_url, resource_base_url, params) + parse unless @fetched.nil? + end + + def parse + @name = @fetched['name'] + @lifecycle_state = @fetched['lifecycleState'] + @create_time = parse_time_string(@fetched['createTime']) + @parent = @fetched['parent'] + @display_name = @fetched['displayName'] + end + + # Handles parsing RFC3339 time string + def parse_time_string(time_string) + time_string ? Time.parse(time_string) : nil + end + + def exists? + !@fetched.nil? + end + + private + + def product_url + 'https://cloudresourcemanager.googleapis.com/v2/' + end + + def resource_base_url + '{{name}}' + end +end diff --git a/libraries/google_resourcemanager_folders.rb b/libraries/google_resourcemanager_folders.rb new file mode 100644 index 000000000..9e46d9bcb --- /dev/null +++ b/libraries/google_resourcemanager_folders.rb @@ -0,0 +1,92 @@ +# frozen_string_literal: false + +# ---------------------------------------------------------------------------- +# +# *** AUTO GENERATED CODE *** AUTO GENERATED CODE *** +# +# ---------------------------------------------------------------------------- +# +# This file is automatically generated by Magic Modules and manual +# changes will be clobbered when the file is regenerated. +# +# Please read more about how to change this file in README.md and +# CONTRIBUTING.md located at the root of this package. +# +# ---------------------------------------------------------------------------- +require 'gcp_backend' +class Folders < GcpResourceBase + name 'google_resourcemanager_folders' + desc 'Folder plural resource' + supports platform: 'gcp' + + attr_reader :table + + filter_table_config = FilterTable.create + + filter_table_config.add(:names, field: :name) + filter_table_config.add(:lifecycle_states, field: :lifecycle_state) + filter_table_config.add(:create_times, field: :create_time) + filter_table_config.add(:parents, field: :parent) + filter_table_config.add(:display_names, field: :display_name) + + filter_table_config.connect(self, :table) + + def initialize(params = {}) + super(params.merge({ use_http_transport: true })) + @params = params + @table = fetch_wrapped_resource('folders') + end + + def fetch_wrapped_resource(wrap_path) + # fetch_resource returns an array of responses (to handle pagination) + result = @connection.fetch_all(product_url, resource_base_url, @params) + return if result.nil? + + # Conversion of string -> object hash to symbol -> object hash that InSpec needs + converted = [] + result.each do |response| + next if response.nil? || !response.key?(wrap_path) + response[wrap_path].each do |hash| + hash_with_symbols = {} + hash.each_key do |key| + name, value = transform(key, hash) + hash_with_symbols[name] = value + end + converted.push(hash_with_symbols) + end + end + + converted + end + + def transform(key, value) + return transformers[key].call(value) if transformers.key?(key) + + [key.to_sym, value] + end + + def transformers + { + 'name' => ->(obj) { return :name, obj['name'] }, + 'lifecycleState' => ->(obj) { return :lifecycle_state, obj['lifecycleState'] }, + 'createTime' => ->(obj) { return :create_time, parse_time_string(obj['createTime']) }, + 'parent' => ->(obj) { return :parent, obj['parent'] }, + 'displayName' => ->(obj) { return :display_name, obj['displayName'] }, + } + end + + # Handles parsing RFC3339 time string + def parse_time_string(time_string) + time_string ? Time.parse(time_string) : nil + end + + private + + def product_url + 'https://cloudresourcemanager.googleapis.com/v2/' + end + + def resource_base_url + 'folders?parent={{parent}}' + end +end diff --git a/test/integration/build/gcp-mm.tf b/test/integration/build/gcp-mm.tf index b940273d5..798c6cd22 100644 --- a/test/integration/build/gcp-mm.tf +++ b/test/integration/build/gcp-mm.tf @@ -110,6 +110,15 @@ variable "repository" { type = "map" } +variable "folder" { + type = "map" +} + +variable "gcp_organization_id" { + type = "string" + default = "none" +} + resource "google_compute_ssl_policy" "custom-ssl-policy" { name = "${var.ssl_policy["name"]}" min_tls_version = "${var.ssl_policy["min_tls_version"]}" @@ -442,4 +451,10 @@ resource "google_bigquery_table" "gcp-inspec-bigquery-table" { resource "google_sourcerepo_repository" "gcp-inspec-sourcerepo-repository" { project = "${var.gcp_project_id}" name = "${var.repository["name"]}" +} + +resource "google_folder" "inspec-gcp-folder" { + count = "${var.gcp_organization_id == "none" ? 0 : var.gcp_enable_privileged_resources}" + display_name = "${var.folder["display_name"]}" + parent = "${var.gcp_organization_id}" } \ No newline at end of file diff --git a/test/integration/configuration/mm-attributes.yml b/test/integration/configuration/mm-attributes.yml index 3c3dde1ce..03eb34035 100644 --- a/test/integration/configuration/mm-attributes.yml +++ b/test/integration/configuration/mm-attributes.yml @@ -186,4 +186,7 @@ bigquery_table: time_partitioning_type: DAY repository: - name: inspec-gcp-repository \ No newline at end of file + name: inspec-gcp-repository + +folder: + display_name: inspec-gcp-folder \ No newline at end of file diff --git a/test/integration/verify/controls/google_resourcemanager_folder.rb b/test/integration/verify/controls/google_resourcemanager_folder.rb new file mode 100644 index 000000000..dd653f52d --- /dev/null +++ b/test/integration/verify/controls/google_resourcemanager_folder.rb @@ -0,0 +1,33 @@ +# ---------------------------------------------------------------------------- +# +# *** AUTO GENERATED CODE *** AUTO GENERATED CODE *** +# +# ---------------------------------------------------------------------------- +# +# This file is automatically generated by Magic Modules and manual +# changes will be clobbered when the file is regenerated. +# +# Please read more about how to change this file in README.md and +# CONTRIBUTING.md located at the root of this package. +# +# ---------------------------------------------------------------------------- + +title 'Test GCP google_resourcemanager_folder resource.' + +folder = attribute('folder', default: {"display_name"=>"inspec-gcp-folder"}) +gcp_organization_id = attribute(:gcp_organization_id, default: gcp_organization_id, description: 'The identifier of the organization that is the parent of this folder') +gcp_enable_privileged_resources = attribute(:gcp_enable_privileged_resources, default:0, description:'Flag to enable privileged resources requiring elevated privileges in GCP.') +control 'google_resourcemanager_folder-1.0' do + impact 1.0 + title 'google_resourcemanager_folder resource test' + + only_if { gcp_enable_privileged_resources.to_i == 1 && gcp_organization_id != ''} + describe.one do + google_resourcemanager_folders(parent: "organizations/#{gcp_organization_id}").names.each do |name| + describe google_resourcemanager_folder(name: name) do + it { should exist } + its('display_name') { should eq folder['display_name'] } + end + end + end +end diff --git a/test/integration/verify/controls/google_resourcemanager_folders.rb b/test/integration/verify/controls/google_resourcemanager_folders.rb new file mode 100644 index 000000000..f9baed1b2 --- /dev/null +++ b/test/integration/verify/controls/google_resourcemanager_folders.rb @@ -0,0 +1,32 @@ +# ---------------------------------------------------------------------------- +# +# *** AUTO GENERATED CODE *** AUTO GENERATED CODE *** +# +# ---------------------------------------------------------------------------- +# +# This file is automatically generated by Magic Modules and manual +# changes will be clobbered when the file is regenerated. +# +# Please read more about how to change this file in README.md and +# CONTRIBUTING.md located at the root of this package. +# +# ---------------------------------------------------------------------------- + +title 'Test GCP google_resourcemanager_folders resource.' + +folder = attribute('folder', default: {"display_name"=>"inspec-gcp-folder"}) +gcp_organization_id = attribute(:gcp_organization_id, default: gcp_organization_id, description: 'The identifier of the organization that is the parent of this folder') +gcp_enable_privileged_resources = attribute(:gcp_enable_privileged_resources, default:0, description:'Flag to enable privileged resources requiring elevated privileges in GCP.') +control 'google_resourcemanager_folders-1.0' do + impact 1.0 + title 'google_resourcemanager_folders resource test' + + only_if { gcp_enable_privileged_resources.to_i == 1 && gcp_organization_id != ''} + describe.one do + google_resourcemanager_folders(parent: "organizations/#{gcp_organization_id}").display_names.each do |display_name| + describe display_name do + it { should eq folder['display_name'] } + end + end + end +end