diff --git a/products/resourcemanager/inspec.yaml b/products/resourcemanager/inspec.yaml index 3802c767ff5d..d3840a16ba5a 100644 --- a/products/resourcemanager/inspec.yaml +++ b/products/resourcemanager/inspec.yaml @@ -33,6 +33,7 @@ overrides: !ruby/object:Overrides::ResourceOverrides exclude: false method_name_separator: ':' fetch_iam_policy_verb: :POST + iam_conditions_request_type: :REQUEST_BODY Organization: !ruby/object:Overrides::Inspec::ResourceOverride privileged: true # Name should be organizations/123456 diff --git a/provider/inspec/common~copy.yaml b/provider/inspec/common~copy.yaml index 20f7872f22ff..bac95edd4840 100644 --- a/provider/inspec/common~copy.yaml +++ b/provider/inspec/common~copy.yaml @@ -14,4 +14,5 @@ 'libraries/google/iam/property/iam_policy_audit_configs.rb': 'templates/inspec/iam_policy/properties/iam_policy_audit_configs.rb' 'libraries/google/iam/property/iam_policy_audit_configs_audit_log_configs.rb': 'templates/inspec/iam_policy/properties/iam_policy_audit_configs_audit_log_configs.rb' 'libraries/google/iam/property/iam_policy_bindings.rb': 'templates/inspec/iam_policy/properties/iam_policy_bindings.rb' +'libraries/google/iam/property/iam_binding_condition.rb': 'templates/inspec/iam_policy/properties/iam_binding_condition.rb' 'Gemfile': 'provider/inspec/Gemfile' diff --git a/templates/inspec/iam_binding/iam_binding.erb b/templates/inspec/iam_binding/iam_binding.erb index 5e21aea4ed53..2766e98991ef 100644 --- a/templates/inspec/iam_binding/iam_binding.erb +++ b/templates/inspec/iam_binding/iam_binding.erb @@ -12,11 +12,13 @@ class <%= object.name -%>IamBinding < GcpResourceBase attr_reader :params + attr_reader :condition + def initialize(params) super(params.merge({ use_http_transport: true })) raise "Expected 'role' to be defined for iam_binding resource" unless params.key?(:role) @params = params - @fetched = @connection.fetch(product_url, resource_base_url, params, '<%= object.iam_policy.fetch_iam_policy_verb.capitalize -%>') + @fetched = @connection.fetch(product_url, resource_base_url, params, '<%= object.iam_policy.fetch_iam_policy_verb.capitalize -%>'<% if object.iam_policy.iam_conditions_request_type == :REQUEST_BODY -%>, {'options' => {'requestedPolicyVersion' => 3 }}.to_json <% end -%>) parse unless @fetched.nil? end @@ -24,7 +26,24 @@ class <%= object.name -%>IamBinding < GcpResourceBase @bindings = GoogleInSpec::Iam::Property::IamPolicyBindingsArray.parse(@fetched['bindings'], to_s) @bindings.each do |binding| next if binding.role != params[:role] + if params[:condition] + # Control defines a condition, match via this condition + condition = params[:condition] + if condition[:title] && condition[:title] != binding&.condition&.title + next + end + if condition[:description] && condition[:description] != binding&.condition&.description + next + end + if condition[:expression] && condition[:expression] != binding&.condition&.expression + next + end + else + # No condition defined in control, skip any binding with a condition + next unless (binding.condition.title.nil? && binding.condition.description.nil? && binding.condition.expression.nil?) + end @members_list = binding.members + @condition = binding.condition @iam_binding_exists = true end end diff --git a/templates/inspec/iam_binding/iam_binding.md.erb b/templates/inspec/iam_binding/iam_binding.md.erb index 9ee93f2901fb..6edac6cfa49c 100644 --- a/templates/inspec/iam_binding/iam_binding.md.erb +++ b/templates/inspec/iam_binding/iam_binding.md.erb @@ -20,6 +20,21 @@ describe <%= iam_resource_name -%>(<%= identifiers_out -%>, role: "roles/editor" end ``` +<% if object.iam_policy.iam_conditions_request_type == :REQUEST_BODY -%> + +This resource supports [IAM conditions](https://cloud.google.com/iam/docs/conditions-overview). Specifying a `condition` in the constructor matches only bindings with that condition. `condition` has three possible fields, `title`, `expression` and `description`. If any of these fields are unspecified they will not be matched. + +``` +describe <%= iam_resource_name -%>(<%= identifiers_out -%>, role: "roles/browser", condition: { title: "my title" }) do + it { should exist } + its('members.count'){ should cmp 1 } + its('members') { should include 'user:testuser@example.com' } + its('condition.title') {should cmp 'my title' } + its('condition.expression') { should cmp "request.time < timestamp('2020-10-01T00:00:00.000Z')" } +end +``` +<% end -%> + ## Properties Properties that can be accessed from the `<%= iam_resource_name -%>` resource: @@ -27,6 +42,16 @@ Properties that can be accessed from the `<%= iam_resource_name -%>` resource: * `members`: Specifies the identities requesting access for a Cloud Platform resource. +<% if object.iam_policy.iam_conditions_request_type == :REQUEST_BODY -%> + * `condition`: Contains information about when this binding is to be applied. + + * `expression`: Textual representation of an expression in Common Expression Language syntax. + + * `title`: An optional title for the expression, i.e. a short string describing its purpose. + + * `description`: An optional description of the expression. This is a longer text which describes the expression. + +<% end -%> <% unless @api.apis_required.empty? -%> ## GCP Permissions diff --git a/templates/inspec/iam_policy/iam_policy.md.erb b/templates/inspec/iam_policy/iam_policy.md.erb index 8e42c7a1a209..d98bc4b68290 100644 --- a/templates/inspec/iam_policy/iam_policy.md.erb +++ b/templates/inspec/iam_policy/iam_policy.md.erb @@ -25,6 +25,10 @@ end end end ``` +<% if object.iam_policy.iam_conditions_request_type == :REQUEST_BODY -%> + +This resource supports [IAM conditions](https://cloud.google.com/iam/docs/conditions-overview). +<% end -%> ## Properties Properties that can be accessed from the `<%= iam_resource_name -%>` resource: @@ -37,6 +41,16 @@ Properties that can be accessed from the `<%= iam_resource_name -%>` resource: * `members`: Specifies the identities requesting access for a Cloud Platform resource. +<% if object.iam_policy.iam_conditions_request_type == :REQUEST_BODY -%> + * `condition`: Contains information about when this binding is to be applied. + + * `expression`: Textual representation of an expression in Common Expression Language syntax. + + * `title`: An optional title for the expression, i.e. a short string describing its purpose. + + * `description`: An optional description of the expression. This is a longer text which describes the expression. + +<% end -%> * `audit_configs`: Specifies cloud audit logging configuration for this policy. * `service`: Specifies a service that will be enabled for audit logging. For example, `storage.googleapis.com`, `cloudsql.googleapis.com`. `allServices` is a special value that covers all services. diff --git a/templates/inspec/iam_policy/properties/iam_binding_condition.rb b/templates/inspec/iam_policy/properties/iam_binding_condition.rb new file mode 100644 index 000000000000..42f6e6c27cd4 --- /dev/null +++ b/templates/inspec/iam_policy/properties/iam_binding_condition.rb @@ -0,0 +1,40 @@ +# frozen_string_literal: false + +# Copyright 2017 Google Inc. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +module GoogleInSpec + module Iam + module Property + class IamBindingCondition + attr_reader :title + + attr_reader :description + + attr_reader :expression + + def initialize(args = nil, parent_identifier = nil) + return if args.nil? + @parent_identifier = parent_identifier + @title = args['title'] + @description = args['description'] + @expression = args['expression'] + end + + def to_s + "#{@parent_identifier} IamBindingCondition" + end + end + end + end +end \ No newline at end of file diff --git a/templates/inspec/iam_policy/properties/iam_policy_bindings.rb b/templates/inspec/iam_policy/properties/iam_policy_bindings.rb index c2dc42449436..7277ae4cbe95 100644 --- a/templates/inspec/iam_policy/properties/iam_policy_bindings.rb +++ b/templates/inspec/iam_policy/properties/iam_policy_bindings.rb @@ -13,6 +13,7 @@ # See the License for the specific language governing permissions and # limitations under the License. +require 'google/iam/property/iam_binding_condition' module GoogleInSpec module Iam module Property @@ -21,11 +22,14 @@ class IamPolicyBindings attr_reader :members + attr_reader :condition + def initialize(args = nil, parent_identifier = nil) return if args.nil? @parent_identifier = parent_identifier @role = args['role'] @members = args['members'] + @condition = GoogleInSpec::Iam::Property::IamBindingCondition.new(args['condition'], to_s) end def to_s