namevar validation only if managed.
[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, :namevar => true) do
79     desc "Either the name of the alias, default, or arbitrary unique string for user specifications"
80     munge do |value|
81       #puts "params \n#{resource.original_parameters.to_yaml}\n"
82       value
83     end
84     # this fails for existing resources, just dont use fake_namevar stuff!
85     validate do |name| 
86       # please forgive this dirty hack, but only managed lines can 
87       # have lines
88       if (name =~ /^fake_namevar_\d+/ and resource.line)
89         raise Puppet::Error, "cannot use reserved namevar #{name}"
90       end
91     end
92   end
93
94
95   #
96   # I changed this to be required. this will allow me to 
97   # do more param checking based on type.
98   #
99   newproperty(:type) do
100     desc "optional parameter used to determine what the record type is"
101     # why isnt this working?
102     validate do |my_type|
103       unless my_type =~ /(default|alias|user_spec)/
104         raise Puppet::Error, "unexpected sudoers type #{my_type}" 
105       end
106     end
107     isrequired
108   end
109
110   newproperty(:sudo_alias) do
111     desc "Type of alias. Options are Cmnd, Host, User, and Runas"
112     newvalue(/^(Cmnd|Host|User|Runas)(_Alias)?$/)
113     # add _Alias if it was ommitted
114     munge do |value|
115       if(value =~ /^(Cmnd|Host|User|Runas)$/) 
116         value << '_Alias'
117       end
118       value
119     end
120     # this is now an alias type
121   end
122
123   newproperty(:items, :array_matching => :all) do
124     desc "list of items applied to an alias"
125   end
126
127   newproperty(:target) do
128     desc "Location of the shells file"
129     defaultto do
130       if
131         @resource.class.defaultprovider.ancestors.include?(Puppet::Provider::ParsedFile)
132         @resource.class.defaultprovider.default_target
133       else
134         nil
135       end
136     end
137   end
138
139 # single user is namevar
140   newproperty(:users, :array_matching => :all) do
141     desc "list of users for user spec"
142     validate do |value|
143       if value =~ /^\s*Defaults/
144         raise Puppet::Error, 'Cannot specify user named Defaults in sudoers'
145       end
146     end
147   end
148
149   newproperty(:hosts, :array_matching => :all) do
150     desc "list of hosts for user spec"
151   end
152
153   newproperty(:commands, :array_matching => :all) do
154     desc "commands to run"
155   end
156
157   newproperty(:parameters, :array_matching => :all) do
158     desc "default parameters"
159   end
160
161   # I should check that this is not /PUPPET NAMEVAR/ 
162   newproperty(:comment) do
163     defaultto ''
164   end
165
166
167
168   # make sure that we only have attributes for either default, alias, or user_spec
169   # I need to think about this... This prevents users from being able 
170   # to set resource defaults...
171   #
172   SUDOERS_DEFAULT = [:parameters]
173   SUDOERS_ALIAS = [:sudo_alias, :items]
174   SUDOERS_SPEC = [:users, :hosts, :commands]
175 #
176 # this does not work both ways for some reason
177 #
178 #
179   validate do
180     # this if ensure if a little hackish - 
181     # balically, when initialize is called from self.instances
182     # none of the attributes are actually set (including type)
183     # the best way to tell if I was called by self.instances
184     # is to check if ensure has a value?
185     if self[:ensure]
186       if self.value(:type) == 'default'
187         checkprops(SUDOERS_DEFAULT)      
188       elsif self.value(:type) == 'alias'
189         checkprops(SUDOERS_ALIAS)      
190       elsif self.value(:type) == 'user_spec'
191         checkprops(SUDOERS_SPEC)      
192       elsif ! self[:type]
193         # this is only during purging (self.instances)
194         raise Puppet::Error, 'attribute type must be set for sudoers type'
195       end
196     else
197       # this occurs with self.instances
198       # is there a better way?
199     end
200   end
201
202   private
203
204     def checkprops(props)
205       props.each do |prop|
206         unless self[prop.to_s]
207           raise Puppet::Error, "missing attribute #{prop} for type #{self[:type]}"
208         end
209       end
210     end
211 #    if self[:sudo_alias] 
212 #      self[:type] = 'alias'
213 #      checkprops(SUDOERS_DEFAULT, SUDOERS_SPEC)
214 #    elsif self[:parameters]
215 #      self[:type] = 'default'
216 #      checkprops(SUDOERS_ALIAS, SUDOERS_SPEC)
217 #    elsif self[:users]
218 #      self[:type] = 'user_spec'
219 #      checkprops(SUDOERS_ALIAS, SUDOERS_DEFAULT)
220 #    else
221 #      # these are parsed records, do nothing 
222 #    end
223     #puts self.should('sudo_alias')
224     #puts self.to_yaml  
225     #puts self.eachproperty do |x| puts x end
226 #  end
227
228 #  private
229
230   # check that we dont have any conflicting attributes
231 #  def checkprops(array_one, array_two)
232 #    combined = Array.new.concat(array_one).concat(array_two)
233 #    combined.each do |item|
234 #      if self[item.to_sym]
235 #        raise Puppet::Error, "Unexpected attribute #{item} for sudo record type #{self[:type]}"
236 #      end
237 #    end 
238 #  end
239 end
240