e7f6cbec15871ced7f2dbf90548b898b0c25b6c8
[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 puts hash[:line]
62         # should name already be set when get get here?
63         #hash = Puppet::Provider::Sudoers::Parsed.parse_user_spec($1, $2)
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             
86             hash[currentsymbol] << element.gsub(/\s/, '')
87           end
88         end
89 puts hash.to_yaml
90       else 
91         raise ArgumentError, "unexpected line #{hash[:line]}" 
92       end
93     }
94
95 #  def self.parse_user_spec(lhs, rhs) 
96 #    hash[:type] = 'spec'
97 #    hash[:commands] = rhs.gsub(/\s/, '').split(',')
98 #    lhs_array = lhs.split(',')  
99 #    # every element will be a user until the hit the delim
100 #    currentsymbol = :users
101 #    lhs_array do |element|
102 #      # all elements will be a single string, except the one that splits users and hosts
103 #      if element =~ /(\S+)\s*(\S+)/
104 #        # sweet we found the delim between user and host
105 #        hash[currentsymbol]=element
106 #        # now everything else will be a host
107 #        currentsymbol=:hosts
108 #        hash[currentsymbol]=element
109 #      else
110 #        hash[currentsymbol]=element
111 #      end
112 #    end
113 #  end 
114
115   def self.to_line(hash) 
116     puts "\nEntering self.to_line for #{hash[:name]}"
117     #puts hash.to_yaml
118     # dynamically call a function based on the value of hash[:type]
119     if(hash[:type] == 'alias')
120       self.alias_to_line(hash) 
121     elsif(hash[:type] == 'spec')
122       self.spec_to_line(hash)
123     elsif(hash[:type] == 'default')
124       default_to_line(hash)
125     end
126   end
127
128   def self.spec_to_line(hash)
129 puts hash.to_yaml
130     #required
131     users=self.array_convert(hash[:users])
132     # required
133     hosts=self.array_convert(hash[:hosts])
134     # required
135     commands=self.array_convert(hash[:commands])
136     puts "adding line:  #{users} #{hosts}=#{commands}"
137     "#{users} #{hosts}=#{commands}"
138   end
139
140   def self.alias_to_line(hash) 
141     # do I need to ensure that the required elements are here?
142     # shouldnt the type do that? check file, its similar
143     # since different attributes make sense based on ensure value (dir/file/symlink)
144     items=self.array_convert(hash[:items])
145     #puts "adding line: #{hash[:sudo_alias]} #{hash[:name]}=#{items}"
146     "#{hash[:sudo_alias]} #{hash[:name]}=#{items}"
147   end
148
149   def self.default_to_line(hash)
150     users=hash[:users]
151     # optional?
152     hosts=hash[:hosts]
153   end
154
155   def self.array_convert(list)
156     if list.class == Array
157       list.join(',')
158     else
159       list
160     end
161   end
162
163   def self.flush(record)
164 #  a little pre-flush host visudo action
165 #
166     super(record)
167   end
168
169 # lets assume that runas is always there and lets not deal with options yet
170 #  record_line :spec, :fields => %w{users hosts runas specs type},
171 #    :match => %r{(\S+)\s+(\S+)=(\(\S+\))\s+(.+)},
172 #    :post_parse => proc { |hash|
173 #      puts 'spec'
174 #    } 
175
176 # I dont know if I can properly support multiple commands
177 # because they are composite, one command, one runas, multiple tags
178 #  record_line :spec, :fields => %w{user host runas tags commands name},
179 #    :match => %r{^\s*(\S+)\s+(\S+)\s*=\s*(\(\S+\))?(.+)$},
180 #    :optional => %w{runas tags}
181
182 # I need to override flush to validate sudoers
183 #
184 #
185 #
186 end