summaryrefslogtreecommitdiff
path: root/lib/puppet/type/sudoers.rb
blob: e9805b9d80ff06fbda857d7f9e99f1d22090a760 (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
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
Puppet::Type.newtype(:sudoers) do
  @doc = "Manage the contents of /etc/sudoers

Author:: Dan Bode (dan@reductivelabs.com)
Copyright:: BLAH!!
License:: GPL3

= Summary

The sudoers type supports managing individual lines from the sudoers file.

Supports present/absent.

supports purging.

= Record Types

There are 3 types of records that are supported:

== Aliases:
 
Manages an alias line of a sudoers file.

Example:
 
sudoers{'ALIAS_NAME':
  ensure => present,
  sudo_alias => 'Cmnd',
  items => ['/bin/true', '/usr/bin/su - bob'],
}

creates the following line:

Cmnd_Alias ALIAS_NAME=/bin/true,/usr/bin/su - bob

== User Specification

sudoers line that specifies how users can run commands.

This there is no way to clearly determine uniqueness, a comment line is added above user spec lines that contains the namevar.

Example:

sudoers{'NAME':
  ensure => present,
  users => ['dan1', 'dan2'],
  hosts => 'ALL',
  commands => [
    '(root) /usr/bin/su - easapp',
    '(easapp)/usr/local/eas-ts/bin/appctl',
  ],
}

creates the following line:  

#Puppet NAMEVAR NAME
dan1,dan2 ALL=(root) /usr/bin/su - easapp,(easapp)/usr/local/eas-ts/bin/appctl

Defaults:

the default name is used to determine uniqueness.

sudoers{'Defaults@host':
  parameters => ['x=y', 'one=1', 'two=2'],
}

Defaults@host x=y,one=1,two=2

== Notes:

- parsing of multi-line sudoers records is not currently supported.
- ordering only applies when records are created.

            "
  # support absent and present (also purge -> true)
  ensurable

  newparam(:name) do
    desc "Either the name of the alias, default, or arbitrary unique string for user specifications"
    isnamevar
    munge do |value|
      #puts "params \n#{resource.original_parameters.to_yaml}\n"
      value
    end
  end

  newproperty(:type) do
    desc "optional parameter used to determine what the record type is"
  end

  newproperty(:sudo_alias) do
    desc "Type of alias. Options are Cmnd, Host, User, and Runas"
    newvalue(/^(Cmnd|Host|User|Runas)(_Alias)?$/)
    # add _Alias if it was ommitted
    munge do |value|
      if(value =~ /^(Cmnd|Host|User|Runas)$/) 
        value << '_Alias'
      end
      value
    end
    # this is now an alias type
  end

  newproperty(:items, :array_matching => :all) do
    desc "list of items applied to an alias"
  end

  newproperty(:target) do
    desc "Location of the shells file"

    defaultto do
      if
        @resource.class.defaultprovider.ancestors.include?(Puppet::Provider::ParsedFile)
        @resource.class.defaultprovider.default_target
      else
        nil
      end
    end
  end

# single user is namevar
  newproperty(:users, :array_matching => :all) do
    desc "list of users for user spec"
    validate do |value|
      if value =~ /^\s*Defaults/
        raise Puppet::Error, 'Cannot specify user named Defaults in sudoers'
      end
    end
  end

  newproperty(:hosts, :array_matching => :all) do
    desc "list of hosts for user spec"
  end

  newproperty(:commands, :array_matching => :all) do
    desc "commands to run"
  end

  newproperty(:parameters, :array_matching => :all) do
    desc "default parameters"
  end

  # make sure that we only have attributes for either default, alias, or user_spec
  SUDOERS_DEFAULT = [:parameters]
  SUDOERS_ALIAS = [:sudo_alias, :items]
  SUDOERS_SPEC = [:users, :hosts, :commands]
  validate do
    if self[:sudo_alias] 
      self[:type] = 'alias'
      checkprops(SUDOERS_DEFAULT, SUDOERS_SPEC)
    elsif self[:parameters]
      self[:type] = 'default'
      checkprops(SUDOERS_ALIAS, SUDOERS_SPEC)
    elsif self[:users]
      self[:type] = 'user_spec'
      checkprops(SUDOERS_ALIAS, SUDOERS_DEFAULT)
    else
      # these are parsed records, do nothing 
    end
    #puts self.should('sudo_alias')
    #puts self.to_yaml  
    #puts self.eachproperty do |x| puts x end
  end

  private

  # check that we dont have any conflicting attributes
  def checkprops(array_one, array_two)
    combined = Array.new.concat(array_one).concat(array_two)
    combined.each do |item|
      if self[item.to_sym]
        raise Puppet::Error, "Unexpected attribute #{item} for sudo record type #{self[:type]}"
      end
    end 
  end
end