summaryrefslogtreecommitdiff
path: root/lib/hiera/backend/trocla_backend.rb
blob: 7689e022d7ecabb052e155cc046ecead465ce74a (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
# Custom hiera backend for trocla
#
# Only reacts to key namespace trocla::password::<trocla_key>. Looks up
# additional parameters via hiera itself as
# trocla::options::<trocla_key>::format (string) and
# trocla::options::<trocla_key>::options (hash). Looks for <trocla_key> in
# trocla as hiera/<source>/<trocla> with <source> iterating over the configured
# hiera hierarchy. If not found, creates and returns a new password with trocla
# key <trocla_key>.
#
# example entry in hiera.yaml:
# backends:
#   - ...
#   - trocla
# trocla:
#   - configfile: /etc/puppet/troclarc.yaml
#   - format: plain
#   - options:
#     length: 16
#
# example usage in hiera yaml file:
# kerberos::kdc_database_password: "%{hiera('trocla::password::kdc_database_password')}"
# trocla::options::kdc_database_password::format: 'plain'
# trocla::options::kdc_database_password::options:
#   length: 71
class Hiera
  module Backend
    class Trocla_backend

      def initialize
        @trocla = nil

        Hiera.debug("Hiera Trocla backend starting")
        require 'trocla'

        default_configfile = "/etc/puppet/troclarc.yaml"
        default_default_format = "plain"
        default_default_options = {}

        begin
          configfile = Config[:trocla][:configfile] || default_configfile
        rescue
          configfile = default_configfile
        end

        if not File.exist?(configfile)
          Hiera.warn("Trocla config file #{configfile} is not readable")
          return
        end

        begin
          @default_format = Config[:trocla][:format] || default_default_format
        rescue
          @default_format = default_default_format
        end

        begin
          @default_options = Config[:trocla][:options] || default_default_options
        rescue
          @default_options = default_default_options
        end

        @trocla = Trocla.new(configfile)
      end

      def lookup(key, scope, order_override, resolution_type)
        return nil unless @trocla

        Hiera.debug("Looking up #{key} in trocla backend")

        password_namespace = 'trocla::password::'
        options_namespace = 'trocla::options::'

        # we only accept trocla::password:: lookups because we do hiera lookups
        # ourselves and could otherwise cause loops
        return nil unless key.start_with?(password_namespace)

        # cut off trocla hiera namespace: trocla::password::root -> root
        trocla_key = key[password_namespace.length,
                         key.length - password_namespace.length]
        Hiera.debug("Looking for key #{trocla_key} in trocla")

        # HERE BE DRAGONS: hiera lookups from backend to determine additional
        # trocla options for this password
        format = Backend.lookup(options_namespace + trocla_key + '::format',
                                @default_format, scope, nil, :priority)

        answer = nil
        # Go looking for existing password as hiera/<source>/<trocla_key>.
        # Would need to be initialised externally, e.g by calling
        # trocla('hiera/osfamily/Debian/jessie/root' in site.pp.  Alternatively
        # we could use hiera's concept of datafiles to look into different
        # trocla password stores. But this would need somehow providing
        # different troclarcs as well.
        sources = Backend.datasources(scope, order_override) do |source|
          Hiera.debug("Looking for data source #{source}")
          break if answer = @trocla.send(:get_password,
                                         'hiera/' + source + '/' + trocla_key,
                                         format)
        end

        if not answer
          # create a new password
          options = Backend.lookup(options_namespace + trocla_key + '::options',
                                   @default_options, scope, nil, :hash)
          answer = @trocla.send(:password, trocla_key, format, options)
        end

        return answer
      end

    end
  end
end