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
This commit is contained in:
Родитель
2fba85af73
Коммит
d1abb86565
|
@ -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
|
|
@ -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
|
||||
|
|
@ -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,72 +137,40 @@ 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(",")
|
||||
end
|
||||
end
|
||||
|
||||
def retrieve
|
||||
if tmp = provider.groups and tmp != :absent
|
||||
return tmp.split(",")
|
||||
else
|
||||
return :absent
|
||||
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
|
||||
|
||||
return tmp == self.should
|
||||
end
|
||||
|
||||
validate do |value|
|
||||
if value =~ /^\d+$/
|
||||
raise ArgumentError, "Group names must be provided, not numbers"
|
||||
end
|
||||
if value.include?(",")
|
||||
puts value
|
||||
raise ArgumentError, "Group names must be provided as an array, not a comma-separated list"
|
||||
end
|
||||
end
|
||||
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."
|
||||
|
||||
def membership
|
||||
:role_membership
|
||||
end
|
||||
|
||||
validate do |value|
|
||||
if value =~ /^\d+$/
|
||||
raise ArgumentError, "Role names must be provided, not numbers"
|
||||
end
|
||||
if value.include?(",")
|
||||
raise ArgumentError, "Role names must be provided as an array, not a comma-separated list"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
newparam(:name) do
|
||||
desc "User name. While limitations are determined for
|
||||
each operating system, it is generally a good idea to keep to
|
||||
|
@ -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
|
||||
|
|
|
@ -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
|
|
@ -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
|
Загрузка…
Ссылка в новой задаче