8aa01dbeb90e4bbdb15acc65041ae8ad67daf41c
[puppet_sudo.git] / lib / puppet / type / sudoers.rb
1 Puppet::Type.newtype(:sudoers) do
2   @doc = "Manage the contents of /etc/sudoers
3
4 Author:: Dan Bode (dan@reductivelabs.com)
5 Copyright:: BLAH!!
6 License:: GPL3
7
8 = Summary
9
10 The sudoers type supports managing individual lines from the sudoers file.
11
12 Supports present/absent.
13
14 supports purging.
15
16 = Record Types
17
18 There are 3 types of records that are supported:
19
20 == Aliases:
21  
22 Manages an alias line of a sudoers file.
23
24 Example:
25  
26 sudoers{'ALIAS_NAME':
27   ensure => present,
28   sudo_alias => 'Cmnd',
29   items => ['/bin/true', '/usr/bin/su - bob'],
30 }
31
32 creates the following line:
33
34 Cmnd_Alias ALIAS_NAME=/bin/true,/usr/bin/su - bob
35
36 == User Specification
37
38 sudoers line that specifies how users can run commands.
39
40 This there is no way to clearly determine uniqueness, a comment line is added above user spec lines that contains the namevar.
41
42 Example:
43
44 sudoers{'NAME':
45   ensure => present,
46   users => ['dan1', 'dan2'],
47   hosts => 'ALL',
48   commands => [
49     '(root) /usr/bin/su - easapp',
50     '(easapp)/usr/local/eas-ts/bin/appctl',
51   ],
52 }
53
54 creates the following line:  
55
56 #Puppet NAMEVAR NAME
57 dan1,dan2 ALL=(root) /usr/bin/su - easapp,(easapp)/usr/local/eas-ts/bin/appctl
58
59 Defaults:
60
61 the default name is used to determine uniqueness.
62
63 sudoers{'Defaults@host':
64   parameters => ['x=y', 'one=1', 'two=2'],
65 }
66
67 Defaults@host x=y,one=1,two=2
68
69 == Notes:
70
71 - parsing of multi-line sudoers records is not currently supported.
72 - ordering only applies when records are created.
73
74             "
75   # support absent and present (also purge -> true)
76   ensurable
77
78   newparam(:name) do
79     desc "Either the name of the alias, default, or arbitrary unique string for user specifications"
80     isnamevar
81     munge do |value|
82       #puts "params \n#{resource.original_parameters.to_yaml}\n"
83       value
84     end
85     # this fails for existing resources, just dont use fake_namevar stuff!
86     validate do |name| 
87       if name =~ /^fake_namevar_\d+/
88         unless resource.original_parameters[:provider].get('record_type') == :parsed
89           raise Puppet::Error, "cannot use reserved namevar #{name}"
90         end
91       end
92     end
93   end
94
95
96   #
97   # I changed this to be required. this will allow me to 
98   # do more param checking based on type.
99   #
100   newproperty(:type) do
101     desc "optional parameter used to determine what the record type is"
102     # why isnt this working?
103     validate do |my_type|
104       unless my_type =~ /(default|alias|user_spec)/
105         raise Puppet::Error, "unexpected sudoers type #{my_type}" 
106       end
107     end
108     isrequired
109   end
110
111   newproperty(:sudo_alias) do
112     desc "Type of alias. Options are Cmnd, Host, User, and Runas"
113     newvalue(/^(Cmnd|Host|User|Runas)(_Alias)?$/)
114     # add _Alias if it was ommitted
115     munge do |value|
116       if(value =~ /^(Cmnd|Host|User|Runas)$/) 
117         value << '_Alias'
118       end
119       value
120     end
121     # this is now an alias type
122   end
123
124   newproperty(:items, :array_matching => :all) do
125     desc "list of items applied to an alias"
126   end
127
128   newproperty(:target) do
129     desc "Location of the shells file"
130     defaultto do
131       if
132         @resource.class.defaultprovider.ancestors.include?(Puppet::Provider::ParsedFile)
133         @resource.class.defaultprovider.default_target
134       else
135         nil
136       end
137     end
138   end
139
140 # single user is namevar
141   newproperty(:users, :array_matching => :all) do
142     desc "list of users for user spec"
143     validate do |value|
144       if value =~ /^\s*Defaults/
145         raise Puppet::Error, 'Cannot specify user named Defaults in sudoers'
146       end
147     end
148   end
149
150   newproperty(:hosts, :array_matching => :all) do
151     desc "list of hosts for user spec"
152   end
153
154   newproperty(:commands, :array_matching => :all) do
155     desc "commands to run"
156   end
157
158   newproperty(:parameters, :array_matching => :all) do
159     desc "default parameters"
160   end
161
162   # I should check that this is not /PUPPET NAMEVAR/ 
163   newproperty(:comment) do
164     defaultto ''
165   end
166
167
168
169   # make sure that we only have attributes for either default, alias, or user_spec
170   # I need to think about this... This prevents users from being able 
171   # to set resource defaults...
172   #
173   SUDOERS_DEFAULT = [:parameters]
174   SUDOERS_ALIAS = [:sudo_alias, :items]
175   SUDOERS_SPEC = [:users, :hosts, :commands]
176 #
177 # this does not work both ways for some reason
178 #
179 #
180   validate do
181     # this if ensure if a little hackish - 
182     # balically, when initialize is called from self.instances
183     # none of the attributes are actually set (including type)
184     # the best way to tell if I was called by self.instances
185     # is to check if ensure has a value?
186     if self[:ensure]
187       if self.value(:type) == 'default'
188         checkprops(SUDOERS_DEFAULT)      
189       elsif self.value(:type) == 'alias'
190         checkprops(SUDOERS_ALIAS)      
191       elsif self.value(:type) == 'user_spec'
192         checkprops(SUDOERS_SPEC)      
193       elsif ! self[:type]
194         # this is only during purging (self.instances)
195         raise Puppet::Error, 'attribute type must be set for sudoers type'
196       end
197     else
198       # this occurs with self.instances
199       # is there a better way?
200     end
201   end
202
203   private
204
205     def checkprops(props)
206       props.each do |prop|
207         unless self[prop.to_s]
208           raise Puppet::Error, "missing attribute #{prop} for type #{self[:type]}"
209         end
210       end
211     end
212 #    if self[:sudo_alias] 
213 #      self[:type] = 'alias'
214 #      checkprops(SUDOERS_DEFAULT, SUDOERS_SPEC)
215 #    elsif self[:parameters]
216 #      self[:type] = 'default'
217 #      checkprops(SUDOERS_ALIAS, SUDOERS_SPEC)
218 #    elsif self[:users]
219 #      self[:type] = 'user_spec'
220 #      checkprops(SUDOERS_ALIAS, SUDOERS_DEFAULT)
221 #    else
222 #      # these are parsed records, do nothing 
223 #    end
224     #puts self.should('sudo_alias')
225     #puts self.to_yaml  
226     #puts self.eachproperty do |x| puts x end
227 #  end
228
229 #  private
230
231   # check that we dont have any conflicting attributes
232 #  def checkprops(array_one, array_two)
233 #    combined = Array.new.concat(array_one).concat(array_two)
234 #    combined.each do |item|
235 #      if self[item.to_sym]
236 #        raise Puppet::Error, "Unexpected attribute #{item} for sudo record type #{self[:type]}"
237 #      end
238 #    end 
239 #  end
240 end
241