module GClouder::Resources::Project::IAMPolicyBinding

Public Class Methods

clean() click to toggle source
# File lib/gclouder/resources/project/iam_policy_binding.rb, line 16
def self.clean
  return if undefined.empty?

  header :clean

  undefined.each do |region, roles|
    info region, indent: 2, heading: true
    roles.each do |role|
      info role["name"], indent: 3, heading: true
      role["members"].each do |member|
        message = member
        message += " (not defined locally)"
        warning message, indent: 4
      end
    end
  end
end
ensure() click to toggle source
# File lib/gclouder/resources/project/iam_policy_binding.rb, line 179
def self.ensure
  return if Local.list.empty?
  header

  Local.list.each do |region, roles|
    info region, indent: 2, heading: true

    roles.each do |role|
      info role["name"], indent: 3, heading: true

      role["members"].each do |member|
        if member.start_with?("sink:")
          sink_name = member
          member = sink(member)

          if member.empty? && cli_args[:dry_run]
            add "unknown - serviceAccount does not exist [#{sink_name}]", indent: 4
            next
          elsif member.empty?
            fatal "unable to find sink serviceAccount (writer identity) - does sink exist for name: #{sink_name}"
          end
        end

        if policy_member?(project_id, role["name"], member)
          good member, indent: 4
          next
        end

        if project_owner?
          add member, indent: 4
          Binding.ensure(project_id, member, role["name"])
          next
        end

        add "#{member} [skipping] (insufficient permissions to create user)", indent: 4
      end
    end
  end
end
executioner() click to toggle source
# File lib/gclouder/resources/project/iam_policy_binding.rb, line 230
def self.executioner
  GClouder::Project::ID.id
end
executioner_formatted() click to toggle source
# File lib/gclouder/resources/project/iam_policy_binding.rb, line 234
def self.executioner_formatted
  "user:#{executioner.strip}"
end
header(stage = :ensure) click to toggle source
# File lib/gclouder/resources/project/iam_policy_binding.rb, line 12
def self.header(stage = :ensure)
  info "[#{stage}] project / iam-policy-binding", indent: 1, title: true
end
is_service_account?(member) click to toggle source
# File lib/gclouder/resources/project/iam_policy_binding.rb, line 38
def self.is_service_account?(member)
  member.include? "gserviceaccount.com"
end
policy_member?(project, role, member) click to toggle source
# File lib/gclouder/resources/project/iam_policy_binding.rb, line 219
def self.policy_member?(project, role, member)
  bindings = gcloud("--format json projects get-iam-policy #{project} | jq '.bindings[] | select(.role == \"roles/#{role}\")'", force: true)
  return false if bindings.empty?
  fatal "could not get policy bindings for project: #{project}" unless bindings.key?("members")
  bindings["members"].include?(member)
end
project_id() click to toggle source
# File lib/gclouder/resources/project/iam_policy_binding.rb, line 226
def self.project_id
  project["project_id"]
end
project_owner?() click to toggle source
# File lib/gclouder/resources/project/iam_policy_binding.rb, line 238
def self.project_owner?
  return false unless executioner
  policy_member?(project_id, "owner", executioner_formatted)
end
resource?(resources, resource) click to toggle source
# File lib/gclouder/resources/project/iam_policy_binding.rb, line 92
def self.resource?(resources, resource)
  !resources.fetch_with_default("name", resource, {}).empty?
end
resource_array_append(resources, resource_name, resource_key, obj) click to toggle source
# File lib/gclouder/resources/project/iam_policy_binding.rb, line 96
def self.resource_array_append(resources, resource_name, resource_key, obj)
  resource = resources.fetch_with_default("name", resource_name, {})
  resource[resource_key] ||= []
  resource[resource_key] << obj
end
sink(member) click to toggle source
# File lib/gclouder/resources/project/iam_policy_binding.rb, line 173
def self.sink(member)
  gcloud("beta logging sinks describe #{member.gsub('sink:', '')} | jq -r .writerIdentity", force: true).chomp
rescue
  fatal "failed to lookup writer identity for sink: #{member}"
end
undefined() click to toggle source
# File lib/gclouder/resources/project/iam_policy_binding.rb, line 42
def self.undefined
  # each remote role
  Remote.list.each_with_object({}) do |(region, remote_roles), collection|
    # each role, {owner, ...}
    remote_roles.each do |remote_role|
      role_found = false

      next unless remote_role.key?("members")

      # for each remote member
      remote_role["members"].each do |remote_member|

        next unless Local.list.key?("global")

        # see if the members role exists locally
        Local.list["global"].each do |e|
          next unless e["name"] == remote_role["name"]

          role_found = true

          # if it does then check if member is in member list for role

          # member is defined, so skip it
          next if e["members"].include?(remote_member)

          # member is one we don't want to manage, so skip it
          next if unmanaged_service_account?(remote_member)

          # member is undefined so add it to collection

          collection["global"] ||= []

          # add role if it doesn't exist in collection
          if !resource?(collection["global"], remote_role["name"])
            collection["global"] <<  { "name" => remote_role["name"], "members" => [] }
          end

          # add memeber to role
          resource_array_append(collection["global"], remote_role["name"], "members", remote_member)
        end
      end

      # if entire role is missing from local..
      next if role_found
      collection["global"] ||= []
      collection["global"] << remote_role
    end
  end
end
unmanaged_service_account?(member) click to toggle source
# File lib/gclouder/resources/project/iam_policy_binding.rb, line 34
def self.unmanaged_service_account?(member)
  is_service_account?(member) && !member.include?(project["project_id"])
end
validate() click to toggle source
# File lib/gclouder/resources/project/iam_policy_binding.rb, line 102
def self.validate
  return if Local.list.empty?
  header :validate

  failure = false

  Local.list.each do |region, roles|
    info region, indent: 2, heading: true

    next if roles.empty?

    roles.each do |role|
      if !role.is_a?(Hash)
        bad "role type is not a Hash: #{role}"
        failure = true
        next
      end

      if !role.key?("name")
        bad "missing key: name"
        failure = true
        next
      end

      if !role.key?("members")
        bad "missing key: members"
        failure = true
        next
      end

      info role["name"], indent: 3, heading: true

      unless role["members"].is_a?(Array)
        bad "value not an array for key: #{role}", indent: 4
        fatal "failure due to invalid config"
      end

      next unless role.key?("members")

      role["members"].each do |member|

        if !member.is_a?(String)
          bad "member isn't a String: #{member}", indent: 3
          failure = true
          next
        end

        info member, indent: 4, heading: true

        good "member is a String", indent: 5

        case member
        when /^user:/
          good "member is a 'user'", indent: 5
        when /^group:/
          good "member is a 'group'", indent: 5
        when /^serviceAccount:/
          good "member is a 'serviceAccount'", indent: 5
        when /^sink:/
          good "member is a 'sink'", indent: 5
        else
          bad "member is an unknown type", indent: 5
          failure = true
        end
      end
    end
  end

  fatal "config validation failure" if failure
end