[Pkg-puppet-devel] [SCM] Puppet packaging for Debian branch, master, updated. debian/0.24.6-1-356-g5718585

Andrew Shafer andrew at reductivelabs.com
Fri Jan 23 14:21:09 UTC 2009


The following commit has been merged in the master branch:
commit d1abb86565b63f98d8fc1395a78c840c45e47238
Author: Andrew Shafer <andrew at reductivelabs.com>
Date:   Wed Oct 1 18:58:09 2008 -0600

    Add role support to user type and an implemention
    
    modify user type:
        add ensure = role logic
        add roles property
        add manages_solaris_rbac feature
        refactored 'list' property to reuse logic for groups in roles

diff --git a/lib/puppet/property/list.rb b/lib/puppet/property/list.rb
new file mode 100644
index 0000000..4e7f6ec
--- /dev/null
+++ b/lib/puppet/property/list.rb
@@ -0,0 +1,78 @@
+require 'puppet/property'
+
+module Puppet
+    class Property
+        class List < Property
+
+            def should_to_s(should_value)
+                #just return the should value
+                should_value
+            end
+
+            def is_to_s(currentvalue)
+                currentvalue.join(delimiter)
+            end
+
+            def membership
+                :membership
+            end
+
+            def add_should_with_current(should, current)
+                if current.is_a?(Array)
+                    should += current
+                end
+                should.uniq
+            end
+
+            def inclusive?
+                @resource[membership] == :inclusive
+            end
+
+            def should
+                unless defined? @should and @should
+                    return nil
+                end
+
+                members = @should
+                #inclusive means we are managing everything so if it isn't in should, its gone
+                if ! inclusive?
+                    members = add_should_with_current(members, retrieve)
+                end
+
+                members.sort.join(delimiter)
+            end
+
+            def delimiter
+                ","
+            end
+
+            def retrieve
+                #ok, some 'convention' if the list property is named groups, provider should implement a groups method
+                if tmp = provider.send(name) and tmp != :absent
+                    return tmp.split(delimiter)
+                else
+                    return :absent
+                end
+            end
+
+            def prepare_is_for_comparison(is)
+                if is.is_a? Array
+                    is = is.sort.join(delimiter)
+                end
+                is
+            end
+
+            def insync?(is)
+                unless defined? @should and @should
+                    return true
+                end
+
+                unless is
+                    return true
+                end
+
+                return (prepare_is_for_comparison(is) == self.should)
+            end
+        end
+    end
+end
diff --git a/lib/puppet/provider/user/user_role_add.rb b/lib/puppet/provider/user/user_role_add.rb
new file mode 100644
index 0000000..23581ab
--- /dev/null
+++ b/lib/puppet/provider/user/user_role_add.rb
@@ -0,0 +1,89 @@
+require 'puppet/util/user_attr'
+
+Puppet::Type.type(:user).provide :user_role_add, :parent => Puppet::Type::User::ProviderUseradd do
+
+    desc "User management inherits ``useradd`` and adds logic to manage roles on Solaris using roleadd."
+
+    defaultfor :operatingsystem => :solaris
+
+    commands :add => "useradd", :delete => "userdel", :modify => "usermod", :role_add => "roleadd", :role_delete => "roledel", :role_modify => "rolemod"
+    options :home, :flag => "-d", :method => :dir
+    options :comment, :method => :gecos
+    options :groups, :flag => "-G"
+    options :roles, :flag => "-R"
+
+    verify :gid, "GID must be an integer" do |value|
+        value.is_a? Integer
+    end
+
+    verify :groups, "Groups must be comma-separated" do |value|
+        value !~ /\s/
+    end
+
+    has_features :manages_homedir, :allows_duplicates, :manages_solaris_rbac
+
+    if Puppet.features.libshadow?
+        has_feature :manages_passwords
+    end
+
+    def user_attributes
+        @user_attributes ||= UserAttr.get_attributes_by_name(@resource[:name])
+    end
+
+    def flush
+        @user_attributes = nil
+    end
+
+    def command(cmd)
+        if is_role? or (!exists? and @resource[:ensure] == :role)
+            cmd = ("role_" + cmd.to_s).intern
+        end
+        super(cmd)
+    end
+
+    def is_role?
+        user_attributes and user_attributes[:type] == "role"
+    end
+
+    def run(cmd, msg)
+        begin
+            execute(cmd)
+        rescue Puppet::ExecutionFailure => detail
+            raise Puppet::Error, "Could not %s %s %s: %s" %
+                [msg, @resource.class.name, @resource.name, detail]
+        end
+    end
+
+    def transition(type)
+        cmd = [command(:modify)]
+        cmd << "-K" << "type=#{type}"
+        cmd << @resource[:name]
+    end
+
+    def create
+        if is_role?
+            run(transition("normal"), "transition role to")
+        else
+            run(addcmd, "create")
+        end
+    end
+
+    def destroy
+        run(deletecmd, "delete "+ (is_role? ? "role" : "user"))
+    end
+
+    def create_role
+        if exists? and !is_role?
+            run(transition("role"), "transition user to")
+        else
+            run(addcmd, "create role")
+        end
+    end
+
+    def roles
+        if user_attributes
+            user_attributes[:roles]
+        end
+    end
+end
+
diff --git a/lib/puppet/type/user.rb b/lib/puppet/type/user.rb
index bb0a86f..74c4c6a 100755
--- a/lib/puppet/type/user.rb
+++ b/lib/puppet/type/user.rb
@@ -1,5 +1,6 @@
 require 'etc'
 require 'facter'
+require 'puppet/property/list'
 
 module Puppet
     newtype(:user) do
@@ -21,6 +22,9 @@ module Puppet
             "The provider can modify user passwords, by accepting a password
             hash."
 
+        feature :manages_solaris_rbac,
+            "The provider can manage roles and normal users"
+
         newproperty(:ensure, :parent => Puppet::Property::Ensure) do
             newvalue(:present, :event => :user_created) do
                 provider.create
@@ -30,6 +34,10 @@ module Puppet
                 provider.delete
             end
 
+            newvalue(:role, :event => :role_created, :required_features => :manages_solaris_rbac) do
+                provider.create_role
+            end
+
             desc "The basic state that the object should be in."
 
             # If they're talking about the thing at all, they generally want to
@@ -44,7 +52,11 @@ module Puppet
 
             def retrieve
                 if provider.exists?
-                    return :present
+                    if provider.respond_to?(:is_role?) and provider.is_role?
+                        return :role
+                    else
+                        return :present
+                    end
                 else
                     return :absent
                 end
@@ -125,68 +137,36 @@ module Puppet
             end
         end
 
-        newproperty(:groups) do
+        newproperty(:groups, :parent => Puppet::Property::List) do
             desc "The groups of which the user is a member.  The primary
                 group should not be listed.  Multiple groups should be
                 specified as an array."
 
-            def should_to_s(newvalue)
-                self.should
-            end
-
-            def is_to_s(currentvalue)
-                currentvalue.join(",")
-            end
-
-            # We need to override this because the groups need to
-            # be joined with commas
-            def should
-                current_value = retrieve
-
-                unless defined? @should and @should
-                    return nil
-                end
-
-                if @resource[:membership] == :inclusive
-                    return @should.sort.join(",")
-                else
-                    members = @should
-                    if current_value.is_a?(Array)
-                        members += current_value
-                    end
-                    return members.uniq.sort.join(",")
+            validate do |value|
+                if value =~ /^\d+$/
+                    raise ArgumentError, "Group names must be provided, not numbers"
                 end
-            end
-
-            def retrieve
-                if tmp = provider.groups and tmp != :absent
-                    return tmp.split(",")
-                else
-                    return :absent
+                if value.include?(",")
+                    puts value
+                    raise ArgumentError, "Group names must be provided as an array, not a comma-separated list"
                 end
             end
+        end
 
-            def insync?(is)
-                unless defined? @should and @should
-                    return true
-                end
-                unless defined? is and is
-                    return true
-                end
-                tmp = is
-                if is.is_a? Array
-                    tmp = is.sort.join(",")
-                end
+        newproperty(:roles, :parent => Puppet::Property::List, :required_features => :manages_solaris_rbac) do
+            desc "The roles of which the user the user has.  The roles should be
+                specified as an array."
 
-                return tmp == self.should
+            def membership
+                :role_membership
             end
 
             validate do |value|
                 if value =~ /^\d+$/
-                    raise ArgumentError, "Group names must be provided, not numbers"
+                    raise ArgumentError, "Role names must be provided, not numbers"
                 end
                 if value.include?(",")
-                    raise ArgumentError, "Group names must be provided as an array, not a comma-separated list"
+                    raise ArgumentError, "Role names must be provided as an array, not a comma-separated list"
                 end
             end
         end
@@ -202,7 +182,17 @@ module Puppet
             desc "Whether specified groups should be treated as the only groups
                 of which the user is a member or whether they should merely
                 be treated as the minimum membership list."
-                
+
+            newvalues(:inclusive, :minimum)
+
+            defaultto :minimum
+        end
+
+        newparam(:role_membership) do
+            desc "Whether specified roles should be treated as the only roles
+                of which the user is a member or whether they should merely
+                be treated as the minimum membership list."
+
             newvalues(:inclusive, :minimum)
 
             defaultto :minimum
diff --git a/spec/unit/property/list.rb b/spec/unit/property/list.rb
new file mode 100644
index 0000000..9c832c0
--- /dev/null
+++ b/spec/unit/property/list.rb
@@ -0,0 +1,147 @@
+#!/usr/bin/env ruby
+
+Dir.chdir(File.dirname(__FILE__)) { (s = lambda { |f| File.exist?(f) ? require(f) : Dir.chdir("..") { s.call(f) } }).call("spec/spec_helper.rb") }
+
+require 'puppet/property/list'
+
+list_class = Puppet::Property::List
+
+describe list_class do
+
+    it "should be a subclass of Property" do
+        list_class.superclass.must == Puppet::Property
+    end
+
+    describe "as an instance" do
+        before do
+            # Wow that's a messy interface to the resource.
+            list_class.initvars
+            @resource = stub 'resource', :[]= => nil, :property => nil
+            @property = list_class.new(:resource => @resource)
+        end
+
+        it "should have a , as default delimiter" do
+            @property.delimiter.should == ","
+        end
+
+        it "should have a :membership as default membership" do
+            @property.membership.should == :membership
+        end
+
+        it "should return the same value passed into should_to_s" do
+            @property.should_to_s("foo") == "foo"
+        end
+
+        it "should return the passed in array values joined with the delimiter from is_to_s" do
+            @property.is_to_s(["foo","bar"]).should == "foo,bar"
+        end
+
+        describe "when adding should to current" do
+            it "should add the arrays when current is an array" do
+                @property.add_should_with_current(["foo"], ["bar"]).should == ["foo", "bar"]
+            end
+
+            it "should return should if current is not a array" do
+                @property.add_should_with_current(["foo"], :absent).should == ["foo"]
+            end
+
+            it "should return only the uniq elements" do
+                @property.add_should_with_current(["foo", "bar"], ["foo", "baz"]).should == ["foo", "bar", "baz"]
+            end
+        end
+
+        describe "when calling inclusive?" do
+            it "should use the membership method to look up on the @resource" do
+                @property.expects(:membership).returns(:membership)
+                @resource.expects(:[]).with(:membership)
+                @property.inclusive?
+            end
+
+            it "should return true when @resource[membership] == inclusive" do
+                @property.stubs(:membership).returns(:membership)
+                @resource.stubs(:[]).with(:membership).returns(:inclusive)
+                @property.inclusive?.must == true
+            end
+
+            it "should return false when @resource[membership] != inclusive" do
+                @property.stubs(:membership).returns(:membership)
+                @resource.stubs(:[]).with(:membership).returns(:minimum)
+                @property.inclusive?.must == false
+            end
+        end
+
+        describe "when calling should" do
+            it "should return nil if @should is nil" do
+                @property.should.must == nil
+            end
+
+            it "should return the sorted values of @should as a string if inclusive" do
+                @property.should = ["foo", "bar"]
+                @property.expects(:inclusive?).returns(true)
+                @property.should.must == "bar,foo"
+            end
+
+            it "should return the uniq sorted values of @should + retrieve as a string if !inclusive" do
+                @property.should = ["foo", "bar"]
+                @property.expects(:inclusive?).returns(false)
+                @property.expects(:retrieve).returns(["foo","baz"])
+                @property.should.must == "bar,baz,foo"
+            end
+        end
+
+        describe "when calling retrieve" do
+            before do
+                @provider = mock("provider")
+                @property.stubs(:provider).returns(@provider)
+            end
+
+            it "should send 'name' to the provider" do
+                @provider.expects(:send).with(:group)
+                @property.expects(:name).returns(:group)
+                @property.retrieve
+            end
+
+            it "should return an array with the provider returned info" do
+                @provider.stubs(:send).with(:group).returns("foo,bar,baz")
+                @property.stubs(:name).returns(:group)
+                @property.retrieve == ["foo", "bar", "baz"]
+            end
+
+            it "should return :absent when the provider returns :absent" do
+                @provider.stubs(:send).with(:group).returns(:absent)
+                @property.stubs(:name).returns(:group)
+                @property.retrieve == :absent
+            end
+        end
+
+        describe "when calling insync?" do
+            it "should return true unless @should is defined and not nil" do
+                @property.insync?("foo") == true
+            end
+
+            it "should return true unless the passed in values is not nil" do
+                @property.should = "foo"
+                @property.insync?(nil) == true
+            end
+
+            it "should call prepare_is_for_comparison with value passed in and should" do
+                @property.should = "foo"
+                @property.expects(:prepare_is_for_comparison).with("bar")
+                @property.expects(:should)
+                @property.insync?("bar")
+            end
+
+            it "should return true if prepared value == should value" do
+                @property.should = "bar,foo"
+                @property.expects(:inclusive?).returns(true)
+                @property.insync?(["bar","foo"]).must == true
+            end
+
+            it "should return false if prepared value != should value" do
+                @property.should = "bar,baz,foo"
+                @property.expects(:inclusive?).returns(true)
+                @property.insync?(["bar","foo"]).must == false
+            end
+        end
+    end
+end
diff --git a/spec/unit/provider/user/user_role_add.rb b/spec/unit/provider/user/user_role_add.rb
new file mode 100644
index 0000000..87ad920
--- /dev/null
+++ b/spec/unit/provider/user/user_role_add.rb
@@ -0,0 +1,131 @@
+#!/usr/bin/env ruby
+
+require File.dirname(__FILE__) + '/../../../spec_helper'
+
+provider_class = Puppet::Type.type(:user).provider(:user_role_add)
+
+describe provider_class do
+    before do
+        @resource = stub("resource", :name => "myuser", :managehome? => nil)
+        @resource.stubs(:should).returns "fakeval"
+        @resource.stubs(:[]).returns "fakeval"
+        @resource.stubs(:allowdupe?).returns false
+        @provider = provider_class.new(@resource)
+    end
+
+    describe "when calling command" do
+        before do
+            klass = stub("provider")
+            klass.stubs(:command).with(:foo).returns("userfoo")
+            klass.stubs(:command).with(:role_foo).returns("rolefoo")
+            @provider.stubs(:class).returns(klass)
+        end
+
+        it "should use the command if not a role and ensure!=role" do
+            @provider.stubs(:is_role?).returns(false)
+            @provider.stubs(:exists?).returns(false)
+            @resource.stubs(:[]).with(:ensure).returns(:present)
+            @provider.command(:foo).should == "userfoo"
+        end
+
+        it "should use the role command when a role" do
+            @provider.stubs(:is_role?).returns(true)
+            @provider.command(:foo).should == "rolefoo"
+        end
+
+        it "should use the role command when !exists and ensure=role" do
+            @provider.stubs(:is_role?).returns(false)
+            @provider.stubs(:exists?).returns(false)
+            @resource.stubs(:[]).with(:ensure).returns(:role)
+            @provider.command(:foo).should == "rolefoo"
+        end
+    end
+
+    describe "when calling transition" do
+        it "should return foomod setting the type to bar" do
+            @provider.expects(:command).with(:modify).returns("foomod")
+            @provider.transition("bar").should == ["foomod", "-K", "type=bar", "fakeval"]
+        end
+    end
+
+    describe "when calling create" do
+        it "should use the add command when the user doesn't exist" do
+            @provider.stubs(:exists?).returns(false)
+            @provider.expects(:addcmd).returns("useradd")
+            @provider.expects(:run)
+            @provider.create
+        end
+
+        it "should use transition(normal) when the user is a role" do
+            @provider.stubs(:exists?).returns(true)
+            @provider.stubs(:is_role?).returns(true)
+            @provider.expects(:transition).with("normal")
+            @provider.expects(:run)
+            @provider.create
+        end
+    end
+
+   describe "when calling destroy" do
+       it "should use the delete command if the user exists and is not a role" do
+            @provider.stubs(:exists?).returns(true)
+            @provider.stubs(:is_role?).returns(false)
+            @provider.expects(:deletecmd)
+            @provider.expects(:run)
+            @provider.destroy
+       end
+
+       it "should use the delete command if the user is a role" do
+            @provider.stubs(:exists?).returns(true)
+            @provider.stubs(:is_role?).returns(true)
+            @provider.expects(:deletecmd)
+            @provider.expects(:run)
+            @provider.destroy
+       end
+   end
+
+   describe "when calling create_role" do
+       it "should use the transition(role) if the user exists" do
+            @provider.stubs(:exists?).returns(true)
+            @provider.stubs(:is_role?).returns(false)
+            @provider.expects(:transition).with("role")
+            @provider.expects(:run)
+            @provider.create_role
+       end
+
+       it "should use the add command when role doesn't exists" do
+            @provider.stubs(:exists?).returns(false)
+            @provider.expects(:addcmd)
+            @provider.expects(:run)
+            @provider.create_role
+       end
+   end
+
+    describe "when allow duplicate is enabled" do
+        before do
+            @resource.expects(:allowdupe?).returns true
+            @provider.expects(:execute).with { |args| args.include?("-o") }
+        end
+
+        it "should add -o when the user is being created" do
+            @provider.create
+        end
+
+        it "should add -o when the uid is being modified" do
+            @provider.uid = 150
+        end
+    end
+
+    describe "when getting roles" do
+        it "should get the user_attributes" do
+            @provider.expects(:user_attributes)
+            @provider.roles
+        end
+
+        it "should get the :roles attribute" do
+            attributes = mock("attributes")
+            attributes.expects(:[]).with(:roles)
+            @provider.stubs(:user_attributes).returns(attributes)
+            @provider.roles
+        end
+    end
+end

-- 
Puppet packaging for Debian



More information about the Pkg-puppet-devel mailing list