summaryrefslogtreecommitdiffstats
path: root/lib/puppet/type/user.rb
blob: 0b668395da41c7654e7142efcf70b6c8bdd04f44 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
require 'etc'
require 'facter'

module Puppet
    newtype(:user) do
        @doc = "Manage users.  This type is mostly built to manage system
            users, so it is lacking some features useful for managing normal
            users.
            
            This resource type uses the prescribed native tools for creating
            groups and generally uses POSIX APIs for retrieving information
            about them.  It does not directly modify /etc/passwd or anything."

        feature :allows_duplicates,
            "The provider supports duplicate users with the same UID."

        feature :manages_homedir,
            "The provider can create and remove home directories."

        feature :manages_passwords,
            "The provider can modify user passwords, by accepting a password
            hash."

        newproperty(:ensure, :parent => Puppet::Property::Ensure) do
            newvalue(:present, :event => :user_created) do
                provider.create
            end

            newvalue(:absent, :event => :user_removed) do
                provider.delete
            end

            desc "The basic state that the object should be in."

            # If they're talking about the thing at all, they generally want to
            # say it should exist.
            defaultto do
                if @resource.managed?
                    :present
                else
                    nil
                end
            end

            def retrieve
                if provider.exists?
                    return :present
                else
                    return :absent
                end
            end

            # The default 'sync' method only selects among a list of registered
            # values.
            def sync
#                if self.insync?
#                    self.info "already in sync"
#                    return nil
                #else
                    #self.info "%s vs %s" % [self.is.inspect, self.should.inspect]
#               end
                unless self.class.values
                    self.devfail "No values defined for %s" %
                        self.class.name
                end

                # Set ourselves to whatever our should value is.
                self.set(self.should)
            end

        end

        newproperty(:uid) do
            desc "The user ID.  Must be specified numerically.  For new users
                being created, if no user ID is specified then one will be
                chosen automatically, which will likely result in the same user
                having different IDs on different systems, which is not
                recommended.  This is especially noteworthy if you use Puppet
                to manage the same user on both Darwin and other platforms,
                since Puppet does the ID generation for you on Darwin, but the
                tools do so on other platforms."

            munge do |value|
                case value
                when String
                    if value =~ /^[-0-9]+$/
                        value = Integer(value)
                    end
                end

                return value
            end
        end

        newproperty(:gid) do
            desc "The user's primary group.  Can be specified numerically or
                by name."
            
            def found?
                defined? @found and @found
            end

            munge do |gid|
                method = :getgrgid
                case gid
                when String
                    if gid =~ /^[-0-9]+$/
                        gid = Integer(gid)
                    else
                        method = :getgrnam
                    end
                when Symbol
                    unless gid == :auto or gid == :absent
                        self.devfail "Invalid GID %s" % gid
                    end
                    # these are treated specially by sync()
                    return gid
                end

                if group = Puppet::Util.gid(gid)
                    @found = true
                    return group
                else
                    @found = false
                    return gid
                end
            end

            # *shudder*  Make sure that we've looked up the group and gotten
            # an ID for it.  Yuck-o.
            def should
                unless defined? @should
                    return super
                end
                unless found?
                    @should = @should.each { |val|
                        next unless val
                        Puppet::Util.gid(val)
                    }
                end
                super
            end
        end

        newproperty(:comment) do
            desc "A description of the user.  Generally is a user's full name."
        end

        newproperty(:home) do
            desc "The home directory of the user.  The directory must be created
                separately and is not currently checked for existence."
        end

        newproperty(:shell) do
            desc "The user's login shell.  The shell must exist and be
                executable."
        end

        newproperty(:password, :required_features => :manages_passwords) do
            desc "The user's password, in whatever encrypted format the local machine requires. Be sure to enclose any value that includes a dollar sign ($) in single quotes (\')."
        end

        newproperty(:groups) 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?(",")
                    raise ArgumentError, "Group names must be provided as an array, not a comma-separated list"
                end
            end
        end

        # these three properties are all implemented differently on each platform,
        # so i'm disabling them for now

        # FIXME Puppet::Property::UserLocked is currently non-functional
        #newproperty(:locked) do
        #    desc "The expected return code.  An error will be returned if the
        #        executed command returns something else."
        #end

        # FIXME Puppet::Property::UserExpire is currently non-functional
        #newproperty(:expire) do
        #    desc "The expected return code.  An error will be returned if the
        #        executed command returns something else."
        #    @objectaddflag = "-e"
        #end

        # FIXME Puppet::Property::UserInactive is currently non-functional
        #newproperty(:inactive) do
        #    desc "The expected return code.  An error will be returned if the
        #        executed command returns something else."
        #    @objectaddflag = "-f"
        #end

        newparam(:name) do
            desc "User name.  While limitations are determined for
                each operating system, it is generally a good idea to keep to
                the degenerate 8 characters, beginning with a letter."
            isnamevar
        end

        newparam(:membership) do
            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(:allowdupe, :boolean => true) do
            desc "Whether to allow duplicate UIDs."
                
            newvalues(:true, :false)

            defaultto false
        end

        newparam(:managehome, :boolean => true) do
            desc "Whether to manage the home directory when managing the user."

            newvalues(:true, :false)

            defaultto false

            validate do |val|
                if val.to_s == "true"
                    unless provider.class.manages_homedir?
                        raise ArgumentError, "User provider %s can not manage home directories" % provider.class.name
                    end
                end
            end
        end

        # Autorequire the group, if it's around
        autorequire(:group) do
            autos = []

            if obj = @parameters[:gid] and groups = obj.shouldorig
                groups = groups.collect { |group|
                    if group =~ /^\d+$/
                        Integer(group)
                    else
                        group
                    end
                }
                autos = groups.reject { |g| g.is_a?(Integer) }
            end

            if obj = @parameters[:groups] and groups = obj.should
                autos += groups.split(",")
            end

            autos
        end

        def retrieve
            absent = false
            properties().inject({}) { |prophash, property|
                current_value = :absent

                if absent
                   prophash[property] = :absent
                else
                    current_value = property.retrieve
                    prophash[property] = current_value
                end

                if property.name == :ensure and current_value == :absent
                    absent = true
#                    next
                end
                prophash
            }
        end
    end
end