still working
[puppet_sudo.git] / lib / puppet / provider / sudoers / parsed.rb
1 require 'puppet/provider/parsedfile'
2 sudoers = "/etc/sudoers"
3
4 #
5 # crontab does the same thing, it uses a comment to specify uniqueness
6 #
7  
8 Puppet::Type.type(:sudoers).provide(
9   :parsed, 
10   :parent => Puppet::Provider::ParsedFile, 
11   :default_target => '/etc/sudoers', 
12   # what the heck does this mean?
13   :filetype => :flat
14 ) do
15  
16   desc "The sudoers provider that uses the ParsedFile class"
17
18   commands :visudo => 'visudo'
19
20   # this is just copied from hosts
21   text_line :comment, :match => %r{^#}, :post_parse => proc { |record|
22     # we determine the name from the comment above user spec lines
23     if record[:line] =~ /Puppet Name: (.+)\s*$/
24 ## ok, we set record name, but how is this applied to the next line?
25       record[:name] = $1
26     end
27   } 
28
29   text_line :blank, :match => /^\s*$/;
30
31   # ignore for now, I will support this line later
32 #  text_line :defaults, :match => /^Defaults/
33
34 # I need to parse the , delim list into an array
35 # I am pretty sure that I would do this with a block statement
36
37 #
38 # it seems like I have to put type here for it to be accessible to 
39 # to_line
40 #
41
42
43 # not bothering to specify match or fields, I will determine all of this
44 # in post_parse
45
46 # parse everything else?
47   record_line :parsed, :fields => %w{line},
48     :match => /(.*)/,
49     :post_parse => proc { |hash|
50       puts "\npost_parse"
51       puts hash[:line]
52       # create records for aliases
53       if (hash[:line] =~ /^\s*(User_Alias|Runas_Alias|Host_Alias|Cmnd_Alias)\s+(\S+)\s*=\s*(.+)$/)
54         hash[:type] = 'alias'
55         hash[:sudo_alias] = $1
56         hash[:name] = $2
57         hash[:items] = $3.gsub(/\s/, '').split(',')
58         #puts hash.to_yaml
59       # create records for user specs
60       elsif (hash[:line] =~ /^(.*)?=(.*)$/)
61         #hash = Puppet::Provider::Sudoers::Parsed.parse_user_spec($1, $2)
62 puts hash[:line]
63         # should name already be set when get get here?
64         lhs = $1
65         rhs = $2
66         hash[:type] = 'spec'
67         hash[:commands] = rhs.gsub(/\s/, '').split(',')
68         lhs_array = lhs.split(',')  
69         # every element will be a user until the hit the delim
70         currentsymbol = :users
71         hash[:users] = Array.new
72         hash[:hosts] = Array.new
73         lhs_array.each do |element|
74 puts "!! #{element}"
75         # all elements will be a single string, except the one that splits users and hosts
76           if element =~ /^\s*(\S+)\s+(\S+)\s*$/
77             user, host  = $1, $2
78             raise Exception, 'found more than one whitespace delin when parsing left hand side of user spec' if currentsymbol==:hosts
79             # sweet we found the delim between user and host
80             hash[currentsymbol] << user.gsub(/\s/, '')
81             # now everything else will be a host
82             currentsymbol=:hosts
83             hash[currentsymbol] << host.gsub(/\s/, '')
84           elsif element =~ /\s*\S+\s*/
85             hash[currentsymbol] << element.gsub(/\s/, '')
86           else 
87             raise ArgumentError, "unexpected line #{hash[:line]}" 
88           end
89         end
90       end
91       puts hash.to_yaml
92     }
93
94 #  def self.parse_user_spec(lhs, rhs) 
95 #puts hash[:line]
96 #    # should name already be set when get get here?
97 #    #lhs = $1
98 #    #rhs = $2
99 #    hash[:type] = 'spec'
100 #    hash[:commands] = rhs.gsub(/\s/, '').split(',')
101 #    lhs_array = lhs.split(',')  
102 #    # every element will be a user until the hit the delim
103 #    currentsymbol = :users
104 #    hash[:users] = Array.new
105 #    hash[:hosts] = Array.new
106 #    lhs_array.each do |element|
107 #puts "!! #{element}"
108 #    # all elements will be a single string, except the one that splits users and hosts
109 #    if element =~ /^\s*(\S+)\s+(\S+)\s*$/
110 #      user, host  = $1, $2
111 #      raise Exception, 'found more than one whitespace delin when parsing left hand side of user spec' if currentsymbol==:hosts
112 #      # sweet we found the delim between user and host
113 #      hash[currentsymbol] << user.gsub(/\s/, '')
114 #      # now everything else will be a host
115 #      currentsymbol=:hosts
116 #      hash[currentsymbol] << host.gsub(/\s/, '')
117 #    elsif element =~ /\s*\S+\s*/
118 #      hash[currentsymbol] << element.gsub(/\s/, '')
119 #    end
120 #    puts hash.to_yaml
121 #  end
122 #    hash[:type] = 'spec'
123 #    hash[:commands] = rhs.gsub(/\s/, '').split(',')
124 #    lhs_array = lhs.split(',')  
125 #    # every element will be a user until the hit the delim
126 #    currentsymbol = :users
127 #    lhs_array do |element|
128 #      # all elements will be a single string, except the one that splits users and hosts
129 #      if element =~ /(\S+)\s*(\S+)/
130 #        # sweet we found the delim between user and host
131 #        hash[currentsymbol]=element
132 #        # now everything else will be a host
133 #        currentsymbol=:hosts
134 #        hash[currentsymbol]=element
135 #      else
136 #        hash[currentsymbol]=element
137 #      end
138 #    end
139 #  end 
140
141   def self.prefetch_hook(records)
142 puts "HERE!!!"
143     records
144   end
145
146   def self.to_line(hash) 
147     puts "\nEntering self.to_line for #{hash[:name]}"
148     #puts hash.to_yaml
149     # dynamically call a function based on the value of hash[:type]
150     if(hash[:type] == 'alias')
151       self.alias_to_line(hash) 
152     elsif(hash[:type] == 'spec')
153       self.spec_to_line(hash)
154     elsif(hash[:type] == 'default')
155       default_to_line(hash)
156     end
157   end
158
159   def self.spec_to_line(hash)
160 puts hash.to_yaml
161     #required
162     users=self.array_convert(hash[:users])
163     # required
164     hosts=self.array_convert(hash[:hosts])
165     # required
166     commands=self.array_convert(hash[:commands])
167     puts "adding line:  #{users} #{hosts}=#{commands}"
168     "#{users} #{hosts}=#{commands}"
169   end
170
171   def self.alias_to_line(hash) 
172     # do I need to ensure that the required elements are here?
173     # shouldnt the type do that? check file, its similar
174     # since different attributes make sense based on ensure value (dir/file/symlink)
175     items=self.array_convert(hash[:items])
176     #puts "adding line: #{hash[:sudo_alias]} #{hash[:name]}=#{items}"
177     "#{hash[:sudo_alias]} #{hash[:name]}=#{items}"
178   end
179
180   def self.default_to_line(hash)
181     users=hash[:users]
182     # optional?
183     hosts=hash[:hosts]
184   end
185
186   def self.array_convert(list)
187     if list.class == Array
188       list.join(',')
189     else
190       list
191     end
192   end
193
194   def self.flush(record)
195 #  a little pre-flush host visudo action
196 #
197     super(record)
198   end
199
200 # lets assume that runas is always there and lets not deal with options yet
201 #  record_line :spec, :fields => %w{users hosts runas specs type},
202 #    :match => %r{(\S+)\s+(\S+)=(\(\S+\))\s+(.+)},
203 #    :post_parse => proc { |hash|
204 #      puts 'spec'
205 #    } 
206
207 # I dont know if I can properly support multiple commands
208 # because they are composite, one command, one runas, multiple tags
209 #  record_line :spec, :fields => %w{user host runas tags commands name},
210 #    :match => %r{^\s*(\S+)\s+(\S+)\s*=\s*(\(\S+\))?(.+)$},
211 #    :optional => %w{runas tags}
212
213 # I need to override flush to validate sudoers
214 #
215 #
216 #
217 end