summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--NOTES5
-rw-r--r--README5
-rw-r--r--files/sudoers27
-rw-r--r--lib/puppet/provider/sudoers/parsed.rb123
-rw-r--r--lib/puppet/type/sudoers.rb94
-rw-r--r--manifests/init.pp10
-rw-r--r--manifests/tests/sudo.pp1
-rw-r--r--tests/sudo.pp1
-rw-r--r--tests/sudoers-delete.pp21
-rw-r--r--tests/sudoers.pp21
10 files changed, 308 insertions, 0 deletions
diff --git a/NOTES b/NOTES
new file mode 100644
index 0000000..77a33c7
--- /dev/null
+++ b/NOTES
@@ -0,0 +1,5 @@
+only properties can be accessed by the parsedfile provider, this sucks.
+
+what is the difference between self.function and function? why must I use self for everything?
+
+I will not support multiline
diff --git a/README b/README
new file mode 100644
index 0000000..955a020
--- /dev/null
+++ b/README
@@ -0,0 +1,5 @@
+This is a type and a provider for managing sudoers file.
+
+It doesnt work yet, but it will. still trying to learn how parsedfile works and how much I can push its boundaries.
+
+It will update this README when its ready for consumption.
diff --git a/files/sudoers b/files/sudoers
new file mode 100644
index 0000000..bb03635
--- /dev/null
+++ b/files/sudoers
@@ -0,0 +1,27 @@
+#
+# DO NOT EDIT! This file is managed by Puppet so any changes you make locally will be lost.
+#
+## Command Aliases
+## These are groups of related commands...
+
+## Installation and management of software
+Cmnd_Alias SOFTWARE = /bin/rpm, /usr/bin/up2date, /usr/bin/yum, /sbin/service httpd *, /sbin/service interchange *, /sbin/service puppet *, /sbin/service mvc_cached *
+
+#
+# This flag needs to be off for ControlTier to work:
+#
+# Defaults requiretty
+Defaults env_reset
+Defaults env_keep = "COLORS DISPLAY HOSTNAME HISTSIZE INPUTRC KDEDIR \
+ LS_COLORS MAIL PS1 PS2 QTDIR USERNAME \
+ LANG LC_ADDRESS LC_CTYPE LC_COLLATE LC_IDENTIFICATION \
+ LC_MEASUREMENT LC_MESSAGES LC_MONETARY LC_NAME LC_NUMERIC \
+ LC_PAPER LC_TELEPHONE LC_TIME LC_ALL LANGUAGE LINGUAS \
+ _XKB_CHARSET XAUTHORITY"
+#
+# User and group based sudo permissions
+#
+
+root ALL=(ALL) ALL
+%wheel ALL=(ALL) ALL
+%interch ALL=NOPASSWD: SOFTWARE
diff --git a/lib/puppet/provider/sudoers/parsed.rb b/lib/puppet/provider/sudoers/parsed.rb
new file mode 100644
index 0000000..520eef5
--- /dev/null
+++ b/lib/puppet/provider/sudoers/parsed.rb
@@ -0,0 +1,123 @@
+require 'puppet/provider/parsedfile'
+sudoers = "/etc/sudoers"
+
+#
+# crontab does the same thing, it uses a comment to specify uniqueness
+#
+
+Puppet::Type.type(:sudoers).provide(
+ :parsed,
+ :parent => Puppet::Provider::ParsedFile,
+ :default_target => '/etc/sudoers',
+ # what the heck does this mean?
+ :filetype => :flat
+) do
+
+ desc "The sudoers provider that uses the ParsedFile class"
+
+ commands :visudo => 'visudo'
+
+ # this is just copied from hosts
+ text_line :comment, :match => %r{^#}, :post_parse => proc { |record|
+ # we determine the name from the comment above user spec lines
+ if record[:line] =~ /Puppet Name: (.+)\s*$/
+# ok, we set record name, but how is this applied to the next line?
+ record[:name] = $1
+ end
+ }
+
+ text_line :blank, :match => /^\s*$/;
+
+ # ignore for now, I will support this line later
+ text_line :defaults, :match => /^Defaults/
+
+# I need to parse the , delim list into an array
+# I am pretty sure that I would do this with a block statement
+
+#
+# it seems like I have to put type here for it to be accessible to
+# to_line
+#
+
+
+# not bothering to specify match or fields, I will determine all of this
+# in post_parse
+
+# parse everything else?
+ record_line :parsed, :fields => %w{line},
+ :match => /(.*)/,
+ :post_parse => proc { |hash|
+ puts "\npost_parse"
+ puts hash[:line]
+ if (hash[:line] =~ /^\s*(User_Alias|Runas_Alias|Host_Alias|Cmnd_Alias)\s+(\S+)\s*=\s*(.+)$/)
+ hash[:sudo_alias] = $1
+ hash[:name] = $2
+ hash[:items] = $3
+ hash[:items]=hash[:items].gsub(/\s/, '').split(',')
+ #puts hash.to_yaml
+ elsif (hash[:line] =~ /^(.*)?=(.*)$/)
+ # should name already be set when get get here?
+ hash = parse_user_spec($1, $2)
+ else
+ raise ArgumentError, "unexpected line #{hash[:line]}"
+ end
+ }
+
+ def self.parse_user_spec(lhs, rhs)
+ lhs_array = lhs.split(',')
+ end
+
+ def self.to_line(hash)
+ puts "\nEntering self.to_line for #{hash[:name]}"
+ puts hash.to_yaml
+ # dynamically call a function based on the value of hash[:type]
+ if(hash[:type] == 'alias')
+ self.alias_to_line(hash)
+ elsif(hash[:type] == 'spec')
+ spec_to_line(hash)
+ elsif(hash[:type] == 'default')
+ default_to_line(hash)
+ end
+ end
+
+ def self.spec_to_line(hash)
+ "spec"
+ end
+
+ def self.alias_to_line(hash)
+ # do I need to ensure that the required elements are here?
+ # shouldnt the type do that? check file, its similar
+ # since different attributes make sense based on ensure value (dir/file/symlink)
+ items=hash[:items]
+ items=items.join(',') if items.class == Array
+ "#{hash[:sudo_alias]} #{hash[:name]}=#{items}"
+ end
+
+ def default_to_line(hash)
+ "default"
+ end
+
+ def self.flush(record)
+# a little pre-flush host visudo action
+#
+ super(record)
+ end
+
+# lets assume that runas is always there and lets not deal with options yet
+# record_line :spec, :fields => %w{users hosts runas specs type},
+# :match => %r{(\S+)\s+(\S+)=(\(\S+\))\s+(.+)},
+# :post_parse => proc { |hash|
+# puts 'spec'
+# }
+
+# I dont know if I can properly support multiple commands
+# because they are composite, one command, one runas, multiple tags
+# record_line :spec, :fields => %w{user host runas tags commands name},
+# :match => %r{^\s*(\S+)\s+(\S+)\s*=\s*(\(\S+\))?(.+)$},
+# :optional => %w{runas tags}
+
+# I need to override flush to validate sudoers
+#
+#
+#
+end
diff --git a/lib/puppet/type/sudoers.rb b/lib/puppet/type/sudoers.rb
new file mode 100644
index 0000000..6c94269
--- /dev/null
+++ b/lib/puppet/type/sudoers.rb
@@ -0,0 +1,94 @@
+Puppet::Type.newtype(:sudoers) do
+ @doc = "Manage the contents of /etc/sudoers
+
+there are two types of things here:
+
+ sudoer{'NAME':
+ ensure => (absent|present)
+ type => (alias|spec) # required??
+ alias => (User_alias|Runas_alias|Host_alias|Cmnd_alias),
+ items => [] # this is only for aliases
+ user_list => []
+ host_list => []
+ operator_list => []
+ # NOPASSWD, PASSWD, NOEXEC, EXEC, SETENV and NOSETENV
+ tag_list => []
+ command_list => []
+ }
+
+ alias NAME - starts with CAP ([A-Z]([A-Z][0-9]_)*)
+
+aliases, user specifications
+ User_alias
+ Runas_alias
+ Host_alias
+ Cmnd_alias
+
+alias spec:
+
+ Alias_Type NAME = item1, item2, item3 : NAME = item4, item5
+
+
+order matters!!
+
+
+ "
+ # we can either remove or add lines
+ # they should also be purgable?(whats the namesvar for specs?)
+ ensurable
+
+ newparam(:name) do
+ desc "Either the name of the alias to create
+ or for user specification, a random string in a comment that serves as a place holder (kind of ugly, but its true)
+ "
+
+ isnamevar
+ end
+
+#
+# this has to be a property to be found by parsedfile, but
+# its really a parameter
+
+ newproperty(:type) do
+ desc "Either determines which type of sudo configuration line is
+ is being managed. Either user_spec or alias"
+ end
+
+ newproperty(:sudo_alias) do
+ desc "Types of alias."
+ 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
+
+ newproperty(:users, :array_matching => :all) do
+ desc "list of users for user spec"
+ end
+
+ newproperty(:hosts, :array_matching => :all) do
+ desc "list of hosts for user spec"
+ end
+
+ newproperty(:runas, :array_matching => :all) do
+ desc "user to run commands as"
+ end
+ newproperty(:commands, :array_matching => :all) do
+ desc "commands to run"
+ end
+
+end
+
diff --git a/manifests/init.pp b/manifests/init.pp
new file mode 100644
index 0000000..ba052c2
--- /dev/null
+++ b/manifests/init.pp
@@ -0,0 +1,10 @@
+# sudo class
+class sudo {
+ package { "sudo": ensure => present }
+ file{"/etc/sudoers":
+ owner => "root",
+ group => "root",
+ mode => "400",
+ source => ["puppet:///modules/site-files/sudoers", "puppet:///modules/sudo/sudoers" ]
+ }
+}
diff --git a/manifests/tests/sudo.pp b/manifests/tests/sudo.pp
new file mode 100644
index 0000000..a27b70b
--- /dev/null
+++ b/manifests/tests/sudo.pp
@@ -0,0 +1 @@
+include sudo
diff --git a/tests/sudo.pp b/tests/sudo.pp
new file mode 100644
index 0000000..a27b70b
--- /dev/null
+++ b/tests/sudo.pp
@@ -0,0 +1 @@
+include sudo
diff --git a/tests/sudoers-delete.pp b/tests/sudoers-delete.pp
new file mode 100644
index 0000000..3085a63
--- /dev/null
+++ b/tests/sudoers-delete.pp
@@ -0,0 +1,21 @@
+sudoers{'blah1':
+ target => '/tmp/sudoers',
+ ensure => absent,
+ alias => 'Cmnd_Alias',
+ items => ['blah4', 'blah2'],
+ linetype => 'alias',
+}
+#sudoers{'blah2':
+# target => '/tmp/sudoers',
+## ensure => present,
+# alias => 'Host_Alias',
+# items => ['blah2', 'blah3'],
+# type => 'alias',
+#}
+##sudoers{'blah3':
+# target => '/tmp/sudoers',
+## ensure => present,
+# users => 'dan',
+# hosts => 'localhost',
+# type => 'spec',
+#}
diff --git a/tests/sudoers.pp b/tests/sudoers.pp
new file mode 100644
index 0000000..ee829d1
--- /dev/null
+++ b/tests/sudoers.pp
@@ -0,0 +1,21 @@
+sudoers{'blah1':
+ #target => '/tmp/sudoers',
+ ensure => present,
+ sudo_alias => 'Cmnd_Alias',
+ items => ['blah4', 'blah2'],
+ type => 'alias',
+}
+sudoers{'blah2':
+ #target => '/tmp/sudoers',
+ ensure => present,
+ sudo_alias => 'Host_Alias',
+ items => ['blah2', 'blah3'],
+ type => 'alias',
+}
+#sudoers{'blah3':
+# target => '/tmp/sudoers',
+# ensure => present,
+# users => 'dan',
+# hosts => 'localhost',
+# type => 'spec',
+#}