cookbooks/fb_users/resources/default.rb (156 lines of code) (raw):

# # Copyright (c) 2019-present, Vicarious, Inc. # Copyright (c) 2020-present, Facebook, Inc. # All rights reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # default_action [:manage] action_class do def bootstrap_pgroups # You can't add users if their primary group doesn't exist. So, first # we find all primary groups, and make sure they exist, or create them if node['fb_users']['user_defaults']['gid'] pgroups = [node['fb_users']['user_defaults']['gid']] end pgroups += node['fb_users']['users'].map { |_, info| info['gid'] } pgroups = pgroups.compact.sort.uniq Chef::Log.debug( 'fb_users: the following groups are GIDs and may need bootstrapping: ' + "#{pgroups.join(', ')}.", ) pgroups.each do |grp| if node['etc']['group'][grp] && node['etc']['group'][grp]['gid'] == ::FB::Users::GID_MAP[grp]['gid'] Chef::Log.debug( "fb_users: Will not bootstrap group #{grp} since it exists, and has" + ' the right GID', ) next end info = node['fb_users']['groups'][grp] # We may not have this group if it's a remote one, so check we do and # that it's set to create if info && info['action'] && info['action'] != :delete group "bootstrap #{grp}" do # ~FB015 group_name grp gid ::FB::Users::GID_MAP[grp]['gid'] action :create # we'll likely modify the group below, but if it has no members and no # comment, then we won't, so lets hook up the notifies in both places # just in case info['notifies']&.each_value do |notif| timing = notif['timing'] || 'delayed' notifies notif['action'].to_sym, notif['resource'], timing.to_sym end end else Chef::Log.debug( "fb_users: Will not bootstrap group #{grp} since it is marked for " + 'deletion', ) next end end end end action :manage do # The idea of primary groups doesn't exist on windows, so none of this # is necessary unless node.windows? bootstrap_pgroups end begin data_bag_passwords = data_bag('fb_users_auth') rescue Net::HTTPServerException data_bag_passwords = {} end set_passwords = !node.windows? || node['fb_users']['set_passwords_on_windows'] # Now we can add all the users node['fb_users']['users'].each do |username, info| # helper variables mapinfo = ::FB::Users::UID_MAP[username] pgroup = info['gid'] || node['fb_users']['user_defaults']['gid'] homedir = info['home'] || "/home/#{username}" homedir_group = info['homedir_group'] || pgroup # If `manage_home` isn't set, we'll use a user-specified default. # If *that* isn't set, use the filesystem type to determine manage_homedir = info['manage_home'] if manage_homedir.nil? if node['fb_users']['user_defaults']['manage_home'].nil? manage_homedir = true unless node.windows? homebase = ::File.dirname(homedir) if node['filesystem']['by_mountpoint'][homebase] homebase_type = node['filesystem']['by_mountpoint'][homebase]['fs_type'] if homebase_type.start_with?('nfs', 'autofs') manage_homedir = false end end end else manage_homedir = node['fb_users']['user_defaults']['manage_home'] end end # delete any users and optionally clean up home dirs if `manage_home true` if info['action'] == :delete # keep property list in sync with FB::Users._validate user username do # ~FB014 manage_home manage_homedir action :remove info['notifies']&.each_value do |notif| timing = notif['timing'] || 'delayed' notifies notif['action'].to_sym, notif['resource'], timing.to_sym end end next end pass = info['password'] if !pass && data_bag_passwords.include?(username) Chef::Log.debug("fb_users[#{username}]: Using password from data_bag") pass = data_bag_item('fb_users_auth', username)['password'] end # disabling fc009 because it triggers on 'secure_token' below which # is already guarded by a version 'if' user username do # ~FC009 ~FB014 uid mapinfo['uid'].to_i # the .to_i here is important - if the usermap accidentally # quotes the gid, then it will try to look up a group named "142" # or whatever. # # We explicityly pass in a GID here instead of a name to ensure that # as GIDs are moving, we get the intended outcome. gid ::FB::Users::GID_MAP[pgroup]['gid'].to_i system mapinfo['system'] unless mapinfo['system'].nil? shell info['shell'] || node['fb_users']['user_defaults']['shell'] manage_home manage_homedir home homedir comment mapinfo['comment'] if mapinfo['comment'] if pass && set_passwords password pass end if FB::Version.new(Chef::VERSION) >= FB::Version.new('15') && !info['secure_token'].nil? secure_token info['secure_token'] end info['notifies']&.each_value do |notif| timing = notif['timing'] || 'delayed' notifies notif['action'].to_sym, notif['resource'], timing.to_sym end action :create end if manage_homedir directory homedir do owner mapinfo['uid'].to_i group ::FB::Users::GID_MAP[homedir_group]['gid'].to_i mode info['homedir_mode'] if info['homedir_mode'] action :create end end end # and then converge all groups node['fb_users']['groups'].each do |groupname, info| if info['action'] == :delete group groupname do # ~FB015 action :remove info['notifies']&.each_value do |notif| timing = notif['timing'] || 'delayed' notifies notif['action'].to_sym, notif['resource'], timing.to_sym end end next end mapinfo = ::FB::Users::GID_MAP[groupname] # disabling fc009 becasue it triggers on 'comment' below which # is already guarded by a version 'if' group groupname do # ~FC009 ~FB015 gid mapinfo['gid'].to_i system mapinfo['system'] unless mapinfo['system'].nil? if info['members'] if info['members'].instance_of?(Proc) members lazy { info['members'].call } else members info['members'] end end if FB::Version.new(Chef::VERSION) >= FB::Version.new('14.9') && mapinfo['comment'] comment mapinfo['comment'] end info['notifies']&.each_value do |notif| timing = notif['timing'] || 'delayed' notifies notif['action'].to_sym, notif['resource'], timing.to_sym end append false action :create end end end