onddo/chef-encrypted-attributes

View on GitHub
lib/chef/encrypted_attribute/remote_nodes.rb

Summary

Maintainability
A
35 mins
Test Coverage
# encoding: UTF-8
#
# Author:: Xabier de Zuazo (<xabier@zuazo.org>)
# Copyright:: Copyright (c) 2014 Onddo Labs, SL.
# License:: Apache License, Version 2.0
#
# 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.
#

require 'chef/encrypted_attribute/exceptions'
require 'chef/encrypted_attribute/search_helper'
require 'chef/encrypted_attribute/cache_lru'
require 'chef/encrypted_attribute/remote_clients'

class Chef
  class EncryptedAttribute
    # Helpers to search nodes remotely and get it's public keys.
    class RemoteNodes
      extend ::Chef::EncryptedAttribute::SearchHelper

      # Remote nodes search results cache.
      #
      # You can disable it setting it's size to zero:
      #
      # ```ruby
      # Chef::EncryptedAttribute::RemoteNodes.cache.max_size(0)
      # ```
      #
      # @return [CacheLru] Remote nodes LRU cache.
      def self.cache
        @@cache ||= Chef::EncryptedAttribute::CacheLru.new
      end

      # Gets remote node public key.
      #
      # It first tries to read the key from the `node['public_key']` attribute.
      #
      # If the `"public_key"` attribute does not exist, it tries to read the
      # node client key directly using the Chef API (this require **admin**
      # privileges).
      #
      # @param node [Chef::Node] Chef node object.
      # @return [String] Chef client public key as string.
      # @raise [InsufficientPrivileges] if you lack enoght privileges to read
      #   the keys from the Chef Server.
      # @raise [ClientNotFound] if client does not exist.
      # @raise [Net::HTTPServerException] for Chef Server HTTP errors.
      def self.get_public_key(node)
        return node['public_key'] unless node['public_key'].nil?
        RemoteClients.get_public_key(node['name'])
      rescue Net::HTTPServerException => e
        raise e unless e.response.code == '403'
        raise InsufficientPrivileges,
              "You cannot read #{node['name']} client key. Consider including "\
              'the encrypted_attributes::expose_key recipe in the '\
              "#{node['name']} node run list."
      end

      # Searches for node client public keys.
      #
      # It first tries to read the key from the `node['public_key']` attribute.
      #
      # If the `"public_key"` attribute does not exist, it tries to read the
      # node client key directly using the Chef API (this require **admin**
      # privileges).
      #
      # @param search [Array<String>, String] search queries to perform, the
      #   query result will be *OR*-ed.
      # @param rows [Integer] maximum number of rows to return in searches.
      # @param partial_search [Boolean] whether to use partial search.
      # @return [Array<String>] list of public keys.
      # @raise [InsufficientPrivileges] if you lack enough privileges to read
      #   the keys from the Chef Server.
      # @raise [ClientNotFound] if client does not exist.
      # @raise [Net::HTTPServerException] for Chef Server HTTP errors.
      # @raise [SearchFailure] if there is a Chef search error.
      # @raise [SearchFatalError] if the Chef search response is wrong.
      # @raise [InvalidSearchKeys] if search keys structure is wrong.
      def self.search_public_keys(
            search = '*:*', rows = 1000, partial_search = true
      )
        escaped_query = escape_query(search)
        return cache[escaped_query] if cache.key?(escaped_query)
        cache[escaped_query] =
          search(
            :node, search,
            { 'name' => %w(name), 'public_key' => %w(public_key) },
            rows, partial_search
          ).map { |node| get_public_key(node) }.compact
      end
    end
  end
end