golden hour
/opt/saltstack/salt/lib/python3.10/site-packages/salt/states
⬆️ Go Up
Upload
File/Folder
Size
Actions
__init__.py
25 B
Del
OK
__pycache__
-
Del
OK
acme.py
5.08 KB
Del
OK
alias.py
2.49 KB
Del
OK
alternatives.py
6.75 KB
Del
OK
ansiblegate.py
7.93 KB
Del
OK
apache.py
3.95 KB
Del
OK
apache_conf.py
2.72 KB
Del
OK
apache_module.py
2.73 KB
Del
OK
apache_site.py
2.66 KB
Del
OK
aptpkg.py
1.42 KB
Del
OK
archive.py
68.24 KB
Del
OK
artifactory.py
6.84 KB
Del
OK
at.py
7.48 KB
Del
OK
augeas.py
10.57 KB
Del
OK
aws_sqs.py
2.59 KB
Del
OK
azurearm_compute.py
11.78 KB
Del
OK
azurearm_dns.py
26.05 KB
Del
OK
azurearm_network.py
89.12 KB
Del
OK
azurearm_resource.py
28.23 KB
Del
OK
beacon.py
7.58 KB
Del
OK
bigip.py
96.63 KB
Del
OK
blockdev.py
5.13 KB
Del
OK
boto3_elasticache.py
48.01 KB
Del
OK
boto3_elasticsearch.py
32.58 KB
Del
OK
boto3_route53.py
37.54 KB
Del
OK
boto3_sns.py
12.69 KB
Del
OK
boto_apigateway.py
82.83 KB
Del
OK
boto_asg.py
31.93 KB
Del
OK
boto_cfn.py
11.53 KB
Del
OK
boto_cloudfront.py
6.01 KB
Del
OK
boto_cloudtrail.py
13.18 KB
Del
OK
boto_cloudwatch_alarm.py
6.4 KB
Del
OK
boto_cloudwatch_event.py
12.33 KB
Del
OK
boto_cognitoidentity.py
13.69 KB
Del
OK
boto_datapipeline.py
18.5 KB
Del
OK
boto_dynamodb.py
29.32 KB
Del
OK
boto_ec2.py
71.98 KB
Del
OK
boto_elasticache.py
16.75 KB
Del
OK
boto_elasticsearch_domain.py
12.27 KB
Del
OK
boto_elb.py
55.1 KB
Del
OK
boto_elbv2.py
12.19 KB
Del
OK
boto_iam.py
69.16 KB
Del
OK
boto_iam_role.py
27.12 KB
Del
OK
boto_iot.py
25.33 KB
Del
OK
boto_kinesis.py
16.69 KB
Del
OK
boto_kms.py
12.11 KB
Del
OK
boto_lambda.py
35.52 KB
Del
OK
boto_lc.py
11.04 KB
Del
OK
boto_rds.py
26 KB
Del
OK
boto_route53.py
19.49 KB
Del
OK
boto_s3.py
9.32 KB
Del
OK
boto_s3_bucket.py
24.67 KB
Del
OK
boto_secgroup.py
32.62 KB
Del
OK
boto_sns.py
8.92 KB
Del
OK
boto_sqs.py
7.97 KB
Del
OK
boto_vpc.py
62.23 KB
Del
OK
bower.py
8.26 KB
Del
OK
btrfs.py
10.34 KB
Del
OK
cabal.py
5.73 KB
Del
OK
ceph.py
1.9 KB
Del
OK
chef.py
3.76 KB
Del
OK
chocolatey.py
16.15 KB
Del
OK
chronos_job.py
4.6 KB
Del
OK
cimc.py
14.32 KB
Del
OK
cisconso.py
3.14 KB
Del
OK
cloud.py
14.4 KB
Del
OK
cmd.py
40.92 KB
Del
OK
composer.py
8.38 KB
Del
OK
consul.py
5.4 KB
Del
OK
cron.py
23.39 KB
Del
OK
cryptdev.py
6.17 KB
Del
OK
csf.py
9.98 KB
Del
OK
cyg.py
7.05 KB
Del
OK
ddns.py
4.2 KB
Del
OK
debconfmod.py
6.33 KB
Del
OK
dellchassis.py
24.49 KB
Del
OK
disk.py
6.49 KB
Del
OK
docker_container.py
85.27 KB
Del
OK
docker_image.py
16.7 KB
Del
OK
docker_network.py
36.78 KB
Del
OK
docker_volume.py
6.72 KB
Del
OK
drac.py
4.17 KB
Del
OK
dvs.py
26.29 KB
Del
OK
elasticsearch.py
20.38 KB
Del
OK
elasticsearch_index.py
3.25 KB
Del
OK
elasticsearch_index_template.py
3.67 KB
Del
OK
environ.py
5.81 KB
Del
OK
eselect.py
2.27 KB
Del
OK
esxcluster.py
22.4 KB
Del
OK
esxdatacenter.py
4.44 KB
Del
OK
esxi.py
63.07 KB
Del
OK
esxvm.py
20.11 KB
Del
OK
etcd_mod.py
11 KB
Del
OK
ethtool.py
9.88 KB
Del
OK
event.py
2.48 KB
Del
OK
file.py
316.7 KB
Del
OK
firewall.py
1.33 KB
Del
OK
firewalld.py
26.08 KB
Del
OK
gem.py
7.13 KB
Del
OK
git.py
123.85 KB
Del
OK
github.py
27.25 KB
Del
OK
glance_image.py
2.26 KB
Del
OK
glassfish.py
21.47 KB
Del
OK
glusterfs.py
12.21 KB
Del
OK
gnomedesktop.py
7.47 KB
Del
OK
gpg.py
5.28 KB
Del
OK
grafana.py
12.11 KB
Del
OK
grafana4_dashboard.py
17.31 KB
Del
OK
grafana4_datasource.py
6.15 KB
Del
OK
grafana4_org.py
7.73 KB
Del
OK
grafana4_user.py
5.52 KB
Del
OK
grafana_dashboard.py
17.74 KB
Del
OK
grafana_datasource.py
5.31 KB
Del
OK
grains.py
15.57 KB
Del
OK
group.py
9.84 KB
Del
OK
heat.py
9.69 KB
Del
OK
helm.py
10.39 KB
Del
OK
hg.py
6.33 KB
Del
OK
highstate_doc.py
1.41 KB
Del
OK
host.py
8.64 KB
Del
OK
http.py
7.46 KB
Del
OK
icinga2.py
9.07 KB
Del
OK
idem.py
3.91 KB
Del
OK
ifttt.py
2.12 KB
Del
OK
incron.py
5.71 KB
Del
OK
influxdb08_database.py
2.85 KB
Del
OK
influxdb08_user.py
3.39 KB
Del
OK
influxdb_continuous_query.py
2.83 KB
Del
OK
influxdb_database.py
2.11 KB
Del
OK
influxdb_retention_policy.py
4.82 KB
Del
OK
influxdb_user.py
4.84 KB
Del
OK
infoblox_a.py
4.24 KB
Del
OK
infoblox_cname.py
4.19 KB
Del
OK
infoblox_host_record.py
6.59 KB
Del
OK
infoblox_range.py
6.85 KB
Del
OK
ini_manage.py
12.67 KB
Del
OK
ipmi.py
8.42 KB
Del
OK
ipset.py
9.66 KB
Del
OK
iptables.py
27.65 KB
Del
OK
jboss7.py
23.95 KB
Del
OK
jenkins.py
3.36 KB
Del
OK
junos.py
17.78 KB
Del
OK
kapacitor.py
6.46 KB
Del
OK
kernelpkg.py
6.42 KB
Del
OK
keyboard.py
2.01 KB
Del
OK
keystone.py
27.12 KB
Del
OK
keystone_domain.py
2.81 KB
Del
OK
keystone_endpoint.py
4.69 KB
Del
OK
keystone_group.py
3.25 KB
Del
OK
keystone_project.py
3.36 KB
Del
OK
keystone_role.py
2.33 KB
Del
OK
keystone_role_grant.py
4.08 KB
Del
OK
keystone_service.py
2.89 KB
Del
OK
keystone_user.py
3.47 KB
Del
OK
keystore.py
5.67 KB
Del
OK
kmod.py
8.59 KB
Del
OK
kubernetes.py
24.87 KB
Del
OK
layman.py
2.44 KB
Del
OK
ldap.py
19.78 KB
Del
OK
libcloud_dns.py
5.7 KB
Del
OK
libcloud_loadbalancer.py
5.66 KB
Del
OK
libcloud_storage.py
5.13 KB
Del
OK
linux_acl.py
24.42 KB
Del
OK
locale.py
2.52 KB
Del
OK
logadm.py
4.67 KB
Del
OK
logrotate.py
3.86 KB
Del
OK
loop.py
7.74 KB
Del
OK
lvm.py
13.33 KB
Del
OK
lvs_server.py
6.28 KB
Del
OK
lvs_service.py
4.38 KB
Del
OK
lxc.py
22.17 KB
Del
OK
lxd.py
7.88 KB
Del
OK
lxd_container.py
22.25 KB
Del
OK
lxd_image.py
10.59 KB
Del
OK
lxd_profile.py
7.11 KB
Del
OK
mac_assistive.py
1.55 KB
Del
OK
mac_keychain.py
5.59 KB
Del
OK
mac_xattr.py
3.15 KB
Del
OK
macdefaults.py
2.65 KB
Del
OK
macpackage.py
6.76 KB
Del
OK
makeconf.py
6.87 KB
Del
OK
marathon_app.py
4.45 KB
Del
OK
mdadm_raid.py
6.41 KB
Del
OK
memcached.py
3.95 KB
Del
OK
modjk.py
2.84 KB
Del
OK
modjk_worker.py
6.49 KB
Del
OK
module.py
18.64 KB
Del
OK
mongodb_database.py
1.65 KB
Del
OK
mongodb_user.py
6.26 KB
Del
OK
monit.py
2.68 KB
Del
OK
mount.py
50.32 KB
Del
OK
mssql_database.py
3 KB
Del
OK
mssql_login.py
3.64 KB
Del
OK
mssql_role.py
2.37 KB
Del
OK
mssql_user.py
3.51 KB
Del
OK
msteams.py
2.53 KB
Del
OK
mysql_database.py
6.05 KB
Del
OK
mysql_grants.py
8.49 KB
Del
OK
mysql_query.py
13.07 KB
Del
OK
mysql_user.py
9.51 KB
Del
OK
net_napalm_yang.py
9.15 KB
Del
OK
netacl.py
31.92 KB
Del
OK
netconfig.py
33.42 KB
Del
OK
netntp.py
12.51 KB
Del
OK
netsnmp.py
11.33 KB
Del
OK
netusers.py
16.1 KB
Del
OK
network.py
23.97 KB
Del
OK
neutron_network.py
3.96 KB
Del
OK
neutron_secgroup.py
4 KB
Del
OK
neutron_secgroup_rule.py
4.75 KB
Del
OK
neutron_subnet.py
4.29 KB
Del
OK
nexus.py
4.97 KB
Del
OK
nfs_export.py
4.92 KB
Del
OK
nftables.py
19.5 KB
Del
OK
npm.py
11.21 KB
Del
OK
ntp.py
2.12 KB
Del
OK
nxos.py
10.37 KB
Del
OK
nxos_upgrade.py
3.5 KB
Del
OK
openstack_config.py
3.26 KB
Del
OK
openvswitch_bridge.py
4.36 KB
Del
OK
openvswitch_db.py
2.24 KB
Del
OK
openvswitch_port.py
17.24 KB
Del
OK
opsgenie.py
4.07 KB
Del
OK
pagerduty.py
1.89 KB
Del
OK
pagerduty_escalation_policy.py
5.42 KB
Del
OK
pagerduty_schedule.py
6.09 KB
Del
OK
pagerduty_service.py
3.93 KB
Del
OK
pagerduty_user.py
1.18 KB
Del
OK
panos.py
48.13 KB
Del
OK
pbm.py
20.46 KB
Del
OK
pcs.py
36.46 KB
Del
OK
pdbedit.py
3.43 KB
Del
OK
pecl.py
3.65 KB
Del
OK
pip_state.py
38.55 KB
Del
OK
pkg.py
138.08 KB
Del
OK
pkgbuild.py
11.37 KB
Del
OK
pkgng.py
685 B
Del
OK
pkgrepo.py
27.53 KB
Del
OK
portage_config.py
5.01 KB
Del
OK
ports.py
5.65 KB
Del
OK
postgres_cluster.py
4.19 KB
Del
OK
postgres_database.py
6.08 KB
Del
OK
postgres_extension.py
5.68 KB
Del
OK
postgres_group.py
8.52 KB
Del
OK
postgres_initdb.py
2.84 KB
Del
OK
postgres_language.py
3.94 KB
Del
OK
postgres_privileges.py
7.86 KB
Del
OK
postgres_schema.py
4.34 KB
Del
OK
postgres_tablespace.py
6.62 KB
Del
OK
postgres_user.py
9.49 KB
Del
OK
powerpath.py
2.34 KB
Del
OK
probes.py
15.06 KB
Del
OK
process.py
1.32 KB
Del
OK
proxy.py
4.94 KB
Del
OK
pushover.py
3.13 KB
Del
OK
pyenv.py
6.07 KB
Del
OK
pyrax_queues.py
2.97 KB
Del
OK
quota.py
1.4 KB
Del
OK
rabbitmq_cluster.py
1.84 KB
Del
OK
rabbitmq_plugin.py
2.77 KB
Del
OK
rabbitmq_policy.py
4.59 KB
Del
OK
rabbitmq_upstream.py
7.9 KB
Del
OK
rabbitmq_user.py
8.89 KB
Del
OK
rabbitmq_vhost.py
3.04 KB
Del
OK
rbac_solaris.py
6.67 KB
Del
OK
rbenv.py
7.36 KB
Del
OK
rdp.py
1.28 KB
Del
OK
redismod.py
4.76 KB
Del
OK
reg.py
19.22 KB
Del
OK
restconf.py
6.41 KB
Del
OK
rsync.py
4.45 KB
Del
OK
rvm.py
6.56 KB
Del
OK
salt_proxy.py
1.34 KB
Del
OK
saltmod.py
33.12 KB
Del
OK
saltutil.py
8.91 KB
Del
OK
schedule.py
12.47 KB
Del
OK
selinux.py
18.61 KB
Del
OK
serverdensity_device.py
6.41 KB
Del
OK
service.py
37.89 KB
Del
OK
slack.py
4.98 KB
Del
OK
smartos.py
44.83 KB
Del
OK
smtp.py
2.3 KB
Del
OK
snapper.py
7.24 KB
Del
OK
solrcloud.py
4.48 KB
Del
OK
splunk.py
4.32 KB
Del
OK
splunk_search.py
3.17 KB
Del
OK
sqlite3.py
14.7 KB
Del
OK
ssh_auth.py
19.57 KB
Del
OK
ssh_known_hosts.py
7.92 KB
Del
OK
stateconf.py
494 B
Del
OK
status.py
2.21 KB
Del
OK
statuspage.py
17.29 KB
Del
OK
supervisord.py
10.48 KB
Del
OK
svn.py
8.14 KB
Del
OK
sysctl.py
4.11 KB
Del
OK
sysfs.py
2.13 KB
Del
OK
syslog_ng.py
2.97 KB
Del
OK
sysrc.py
2.82 KB
Del
OK
telemetry_alert.py
7.04 KB
Del
OK
test.py
13.09 KB
Del
OK
testinframod.py
1.35 KB
Del
OK
timezone.py
3.42 KB
Del
OK
tls.py
1.81 KB
Del
OK
tomcat.py
9.72 KB
Del
OK
trafficserver.py
8.82 KB
Del
OK
tuned.py
3.32 KB
Del
OK
uptime.py
1.87 KB
Del
OK
user.py
38.63 KB
Del
OK
vagrant.py
11.4 KB
Del
OK
vault.py
3.28 KB
Del
OK
vbox_guest.py
4.05 KB
Del
OK
victorops.py
3.32 KB
Del
OK
virt.py
80.41 KB
Del
OK
virtualenv_mod.py
11.21 KB
Del
OK
webutil.py
3.89 KB
Del
OK
win_certutil.py
4.8 KB
Del
OK
win_dacl.py
7.96 KB
Del
OK
win_dism.py
14.97 KB
Del
OK
win_dns_client.py
8.32 KB
Del
OK
win_firewall.py
6.87 KB
Del
OK
win_iis.py
31.56 KB
Del
OK
win_lgpo.py
24.99 KB
Del
OK
win_lgpo_reg.py
10.96 KB
Del
OK
win_license.py
1.6 KB
Del
OK
win_network.py
14.18 KB
Del
OK
win_path.py
6.39 KB
Del
OK
win_pki.py
5.56 KB
Del
OK
win_powercfg.py
3.79 KB
Del
OK
win_servermanager.py
10.4 KB
Del
OK
win_shortcut.py
7.81 KB
Del
OK
win_smtp_server.py
10.01 KB
Del
OK
win_snmp.py
6.64 KB
Del
OK
win_system.py
13.78 KB
Del
OK
win_wua.py
16.27 KB
Del
OK
win_wusa.py
3.53 KB
Del
OK
winrepo.py
2.74 KB
Del
OK
wordpress.py
4.82 KB
Del
OK
x509.py
27.86 KB
Del
OK
x509_v2.py
64.78 KB
Del
OK
xml.py
1.75 KB
Del
OK
xmpp.py
2.61 KB
Del
OK
zabbix_action.py
9.35 KB
Del
OK
zabbix_host.py
27.25 KB
Del
OK
zabbix_hostgroup.py
5.64 KB
Del
OK
zabbix_mediatype.py
16.89 KB
Del
OK
zabbix_template.py
35.14 KB
Del
OK
zabbix_user.py
17.6 KB
Del
OK
zabbix_usergroup.py
9.64 KB
Del
OK
zabbix_usermacro.py
9.69 KB
Del
OK
zabbix_valuemap.py
8.11 KB
Del
OK
zcbuildout.py
5.16 KB
Del
OK
zenoss.py
2.89 KB
Del
OK
zfs.py
34.48 KB
Del
OK
zk_concurrency.py
5.81 KB
Del
OK
zone.py
46.48 KB
Del
OK
zookeeper.py
11.55 KB
Del
OK
zpool.py
13.4 KB
Del
OK
Edit: user.py
""" Management of user accounts. ============================ The user module is used to create and manage user settings, users can be set as either absent or present .. code-block:: yaml fred: user.present: - fullname: Fred Jones - shell: /bin/zsh - home: /home/fred - uid: 4000 - gid: 4000 - groups: - wheel - storage - games testuser: user.absent """ import logging import os import salt.utils.data import salt.utils.dateutils import salt.utils.platform import salt.utils.user import salt.utils.versions from salt.exceptions import CommandExecutionError log = logging.getLogger(__name__) def _group_changes(cur, wanted, remove=False): """ Determine if the groups need to be changed """ cur = set(cur) wanted = set(wanted) if cur == wanted or (not remove and wanted.issubset(cur)): return False all_grps = {name: __salt__["group.info"](name) for name in cur.union(wanted)} if remove: diff = wanted.symmetric_difference(cur) else: diff = wanted.difference(cur) remain = list(diff) for diff_grp in diff: for grp, info in all_grps.items(): if grp == diff_grp: continue if all_grps[diff_grp]["gid"] == info["gid"]: # dupe detected remain.remove(diff_grp) return bool(remain) def _changes( name, uid=None, gid=None, groups=None, optional_groups=None, remove_groups=True, home=None, createhome=True, password=None, enforce_password=True, empty_password=False, shell=None, fullname="", roomnumber="", workphone="", homephone="", other="", loginclass=None, date=None, mindays=0, maxdays=999999, inactdays=0, warndays=7, expire=None, win_homedrive=None, win_profile=None, win_logonscript=None, win_description=None, allow_uid_change=False, allow_gid_change=False, password_lock=None, ): """ Return a dict of the changes required for a user if the user is present, otherwise return False. Updated in 2015.8.0 to include support for windows homedrive, profile, logonscript, and description fields. Updated in 2014.7.0 to include support for shadow attributes, all attributes supported as integers only. """ if "shadow.info" in __salt__: lshad = __salt__["shadow.info"](name) lusr = __salt__["user.info"](name) if not lusr: return False change = {} wanted_groups = sorted(set((groups or []) + (optional_groups or []))) lusr_groups_gids = [ __salt__["file.group_to_gid"](gname) for gname in lusr["groups"] ] dupe_groups = {} for idx, _gid in enumerate(lusr_groups_gids): if lusr_groups_gids.count(_gid) > 1: if _gid not in dupe_groups: dupe_groups[_gid] = [] dupe_groups[_gid].append(lusr["groups"][idx]) if not remove_groups or groups is None and not optional_groups: wanted_groups = sorted(set(wanted_groups + lusr["groups"])) if uid and lusr["uid"] != uid: change["uid"] = uid if gid is not None and lusr["gid"] not in (gid, __salt__["file.group_to_gid"](gid)): change["gid"] = gid default_grp = __salt__["file.gid_to_group"](gid if gid is not None else lusr["gid"]) old_default_grp = __salt__["file.gid_to_group"](lusr["gid"]) # Remove the default group from the list for comparison purposes. # Remove default group from wanted_groups, as this requirement is # already met if default_grp in lusr["groups"] or default_grp in wanted_groups: if default_grp in salt.utils.data.flatten(dupe_groups.values()): dupe_gid = __salt__["file.group_to_gid"](default_grp) for gname in dupe_groups[dupe_gid]: if gname in lusr["groups"]: lusr["groups"].remove(gname) if gname in wanted_groups: wanted_groups.remove(gname) else: if default_grp in lusr["groups"]: lusr["groups"].remove(default_grp) if default_grp in wanted_groups: wanted_groups.remove(default_grp) # If the group is being changed, make sure that the old primary group is # also removed from the list. Otherwise, if a user's gid is being changed # and their old primary group is reassigned as an additional group, Salt # will not properly detect the need for the change. if old_default_grp != default_grp and old_default_grp in lusr["groups"]: if old_default_grp in salt.utils.data.flatten(dupe_groups.values()): dupe_gid = __salt__["file.group_to_gid"](old_default_grp) for gname in dupe_groups[dupe_gid]: lusr["groups"].remove(gname) else: lusr["groups"].remove(old_default_grp) # If there's a group by the same name as the user, remove it from the list # for comparison purposes. if name in lusr["groups"] and name not in wanted_groups: if name in salt.utils.data.flatten(dupe_groups.values()): dupe_gid = __salt__["file.group_to_gid"](name) for gname in dupe_groups[dupe_gid]: lusr["groups"].remove(gname) else: lusr["groups"].remove(name) if _group_changes(lusr["groups"], wanted_groups, remove_groups): if wanted_groups or remove_groups: change["groups"] = wanted_groups if home and lusr["home"] != home: change["home"] = home if createhome: newhome = home if home else lusr["home"] if newhome is not None and not os.path.isdir(newhome): change["homeDoesNotExist"] = newhome if shell and lusr["shell"] != shell: change["shell"] = shell if "shadow.info" in __salt__ and "shadow.default_hash" in __salt__: if password and not empty_password: default_hash = __salt__["shadow.default_hash"]() if ( lshad["passwd"] == default_hash or lshad["passwd"] != default_hash and enforce_password ): if lshad["passwd"] != password: change["passwd"] = password if empty_password and lshad["passwd"] != "": change["empty_password"] = True if date is not None and lshad["lstchg"] != date: change["date"] = date if mindays is not None and lshad["min"] != mindays: change["mindays"] = mindays if maxdays is not None and lshad["max"] != maxdays: change["maxdays"] = maxdays if inactdays is not None and lshad["inact"] != inactdays: change["inactdays"] = inactdays if warndays is not None and lshad["warn"] != warndays: change["warndays"] = warndays if expire and lshad["expire"] != expire: change["expire"] = expire if (password_lock and not lshad["passwd"].startswith("!")) or ( password_lock is False and lshad["passwd"].startswith("!") ): change["password_lock"] = password_lock elif "shadow.info" in __salt__ and salt.utils.platform.is_windows(): if ( expire and expire != -1 and salt.utils.dateutils.strftime(lshad["expire"]) != salt.utils.dateutils.strftime(expire) ): change["expire"] = expire if password_lock is False and lusr["account_locked"]: change["password_lock"] = password_lock # GECOS fields fullname = salt.utils.data.decode(fullname) lusr["fullname"] = salt.utils.data.decode(lusr["fullname"]) if fullname is not None and lusr["fullname"] != fullname: change["fullname"] = fullname if win_homedrive and lusr["homedrive"] != win_homedrive: change["win_homedrive"] = win_homedrive if win_profile and lusr["profile"] != win_profile: change["win_profile"] = win_profile if win_logonscript and lusr["logonscript"] != win_logonscript: change["win_logonscript"] = win_logonscript if win_description and lusr["description"] != win_description: change["win_description"] = win_description # MacOS doesn't have full GECOS support, so check for the "ch" functions # and ignore these parameters if these functions do not exist. if "user.chroomnumber" in __salt__ and roomnumber is not None: roomnumber = salt.utils.data.decode(roomnumber) lusr["roomnumber"] = salt.utils.data.decode(lusr["roomnumber"]) if lusr["roomnumber"] != roomnumber: change["roomnumber"] = roomnumber if "user.chworkphone" in __salt__ and workphone is not None: workphone = salt.utils.data.decode(workphone) lusr["workphone"] = salt.utils.data.decode(lusr["workphone"]) if lusr["workphone"] != workphone: change["workphone"] = workphone if "user.chhomephone" in __salt__ and homephone is not None: homephone = salt.utils.data.decode(homephone) lusr["homephone"] = salt.utils.data.decode(lusr["homephone"]) if lusr["homephone"] != homephone: change["homephone"] = homephone if "user.chother" in __salt__ and other is not None: other = salt.utils.data.decode(other) lusr["other"] = salt.utils.data.decode(lusr["other"]) if lusr["other"] != other: change["other"] = other # OpenBSD/FreeBSD login class if __grains__["kernel"] in ("OpenBSD", "FreeBSD"): if loginclass: if __salt__["user.get_loginclass"](name) != loginclass: change["loginclass"] = loginclass errors = [] if not allow_uid_change and "uid" in change: errors.append( "Changing uid ({} -> {}) not permitted, set allow_uid_change to " "True to force this change. Note that this will not change file " "ownership.".format(lusr["uid"], uid) ) if not allow_gid_change and "gid" in change: errors.append( "Changing gid ({} -> {}) not permitted, set allow_gid_change to " "True to force this change. Note that this will not change file " "ownership.".format(lusr["gid"], gid) ) if errors: raise CommandExecutionError( "Encountered error checking for needed changes", info=errors ) return change def present( name, uid=None, gid=None, usergroup=None, groups=None, optional_groups=None, remove_groups=True, home=None, createhome=True, password=None, hash_password=False, enforce_password=True, empty_password=False, shell=None, unique=True, system=False, fullname=None, roomnumber=None, workphone=None, homephone=None, other=None, loginclass=None, date=None, mindays=None, maxdays=None, inactdays=None, warndays=None, expire=None, win_homedrive=None, win_profile=None, win_logonscript=None, win_description=None, nologinit=False, allow_uid_change=False, allow_gid_change=False, password_lock=None, ): """ Ensure that the named user is present with the specified properties name The name of the user to manage uid The user id to assign. If not specified, and the user does not exist, then the next available uid will be assigned. gid The id of the default group to assign to the user. Either a group name or gid can be used. If not specified, and the user does not exist, then the next available gid will be assigned. allow_uid_change : False Set to ``True`` to allow the state to update the uid. .. versionadded:: 2018.3.1 allow_gid_change : False Set to ``True`` to allow the state to update the gid. .. versionadded:: 2018.3.1 usergroup If True, a group with the same name as the user will be created. If False, a group with the same name as the user will not be created. The default is distribution-specific. See the USERGROUPS_ENAB section of the login.defs(5) man page. .. note:: Only supported on GNU/Linux distributions .. versionadded:: 3001 groups A list of groups to assign the user to, pass a list object. If a group specified here does not exist on the minion, the state will fail. If set to the empty list, the user will be removed from all groups except the default group. If unset, salt will assume current groups are still wanted, and will make no changes to them. optional_groups A list of groups to assign the user to, pass a list object. If a group specified here does not exist on the minion, the state will silently ignore it. NOTE: If the same group is specified in both "groups" and "optional_groups", then it will be assumed to be required and not optional. remove_groups Remove groups that the user is a member of that weren't specified in the state, Default is ``True``. home The custom login directory of user. Uses default value of underlying system if not set. Notice that this directory does not have to exist. This also the location of the home directory to create if createhome is set to True. createhome : True If set to ``False``, the home directory will not be created if it doesn't already exist. .. warning:: Not supported on Windows or Mac OS. Additionally, parent directories will *not* be created. The parent directory for ``home`` must already exist. nologinit : False If set to ``True``, it will not add the user to lastlog and faillog databases. .. note:: Not supported on Windows. password A password hash to set for the user. This field is only supported on Linux, FreeBSD, NetBSD, OpenBSD, and Solaris. If the ``empty_password`` argument is set to ``True`` then ``password`` is ignored. For Windows this is the plain text password. For Linux, the hash can be generated with ``mkpasswd -m sha-256``. .. versionchanged:: 0.16.0 BSD support added. hash_password Set to True to hash the clear text password. Default is ``False``. enforce_password Set to False to keep the password from being changed if it has already been set and the password hash differs from what is specified in the "password" field. This option will be ignored if "password" is not specified, Default is ``True``. empty_password Set to True to enable password-less login for user, Default is ``False``. password_lock Set to ``False`` to unlock a user's password (or Windows account). On non-Windows systems ONLY, this parameter can be set to ``True`` to lock a user's password. Default is ``None``, which does not take action on the password (or Windows account). .. versionadded:: 3006.0 shell The login shell, defaults to the system default shell unique Require a unique UID, Default is ``True``. system Choose UID in the range of FIRST_SYSTEM_UID and LAST_SYSTEM_UID, Default is ``False``. loginclass The login class, defaults to empty (BSD only) User comment field (GECOS) support (currently Linux, BSD, and MacOS only): The below values should be specified as strings to avoid ambiguities when the values are loaded. (Especially the phone and room number fields which are likely to contain numeric data) fullname The user's full name roomnumber The user's room number (not supported in MacOS) workphone The user's work phone number (not supported in MacOS) homephone The user's home phone number (not supported in MacOS) other The user's other attribute (not supported in MacOS) If GECOS field contains more than 4 commas, this field will have the rest of 'em .. versionchanged:: 2014.7.0 Shadow attribute support added. Shadow attributes support (currently Linux only): The below values should be specified as integers. date Date of last change of password, represented in days since epoch (January 1, 1970). mindays The minimum number of days between password changes. maxdays The maximum number of days between password changes. inactdays The number of days after a password expires before an account is locked. warndays Number of days prior to maxdays to warn users. expire Date that account expires, represented in days since epoch (January 1, 1970). The below parameters apply to windows only: win_homedrive (Windows Only) The drive letter to use for the home directory. If not specified the home directory will be a unc path. Otherwise the home directory will be mapped to the specified drive. Must be a letter followed by a colon. Because of the colon, the value must be surrounded by single quotes. ie: ``- win_homedrive: 'U:'`` .. versionchanged:: 2015.8.0 win_profile (Windows Only) The custom profile directory of the user. Uses default value of underlying system if not set. .. versionchanged:: 2015.8.0 win_logonscript (Windows Only) The full path to the logon script to run when the user logs in. .. versionchanged:: 2015.8.0 win_description (Windows Only) A brief description of the purpose of the users account. .. versionchanged:: 2015.8.0 """ # First check if a password is set. If password is set, check if # hash_password is True, then hash it. if password and hash_password: log.debug("Hashing a clear text password") # in case a password is already set, it will contain a Salt # which should be re-used to generate the new hash, other- # wise the Salt will be generated randomly, causing the # hash to change each time and thereby making the # user.present state non-idempotent. algorithms = { "1": "md5", "2a": "blowfish", "5": "sha256", "6": "sha512", } try: _, algo, shadow_salt, shadow_hash = __salt__["shadow.info"](name)[ "passwd" ].split("$", 4) if algo == "1": log.warning("Using MD5 for hashing passwords is considered insecure!") log.debug( "Re-using existing shadow salt for hashing password using %s", algorithms.get(algo), ) password = __salt__["shadow.gen_password"]( password, crypt_salt=shadow_salt, algorithm=algorithms.get(algo) ) except ValueError: log.info( "No existing shadow salt found, defaulting to a randomly generated" " new one" ) password = __salt__["shadow.gen_password"](password) if fullname is not None: fullname = salt.utils.data.decode(str(fullname)) if roomnumber is not None: roomnumber = salt.utils.data.decode(str(roomnumber)) if workphone is not None: workphone = salt.utils.data.decode(str(workphone)) if homephone is not None: homephone = salt.utils.data.decode(str(homephone)) if other is not None: other = salt.utils.data.decode(str(other)) # createhome not supported on Windows if __grains__["kernel"] == "Windows": createhome = False ret = { "name": name, "changes": {}, "result": True, "comment": "User {} is present and up to date".format(name), } # the comma is used to separate field in GECOS, thus resulting into # salt adding the end of fullname each time this function is called for gecos_field in [fullname, roomnumber, workphone]: if isinstance(gecos_field, str) and "," in gecos_field: ret["comment"] = "Unsupported char ',' in {}".format(gecos_field) ret["result"] = False return ret if groups: missing_groups = [x for x in groups if not __salt__["group.info"](x)] if missing_groups: ret["comment"] = "The following group(s) are not present: {}".format( ",".join(missing_groups) ) ret["result"] = False return ret if optional_groups: present_optgroups = [x for x in optional_groups if __salt__["group.info"](x)] for missing_optgroup in [ x for x in optional_groups if x not in present_optgroups ]: log.debug( 'Optional group "%s" for user "%s" is not present', missing_optgroup, name, ) else: present_optgroups = None # Log a warning for all groups specified in both "groups" and # "optional_groups" lists. if groups and optional_groups: for isected in set(groups).intersection(optional_groups): log.warning( 'Group "%s" specified in both groups and optional_groups for user %s', isected, name, ) # If usergroup was specified, we'll also be creating a new # group. We should report this change without setting the gid # variable. if usergroup and __salt__["file.group_to_gid"](name) != "": changes_gid = name else: changes_gid = gid try: changes = _changes( name, uid, changes_gid, groups, present_optgroups, remove_groups, home, createhome, password, enforce_password, empty_password, shell, fullname, roomnumber, workphone, homephone, other, loginclass, date, mindays, maxdays, inactdays, warndays, expire, win_homedrive, win_profile, win_logonscript, win_description, allow_uid_change, allow_gid_change, password_lock=password_lock, ) except CommandExecutionError as exc: ret["result"] = False ret["comment"] = exc.strerror return ret if changes: if __opts__["test"]: ret["result"] = None ret["comment"] = "The following user attributes are set to be changed:\n" for key, val in changes.items(): if key == "passwd": val = "XXX-REDACTED-XXX" elif key == "group" and not remove_groups: key = "ensure groups" ret["comment"] += "{}: {}\n".format(key, val) return ret # The user is present if "shadow.info" in __salt__: lshad = __salt__["shadow.info"](name) if __grains__["kernel"] in ("OpenBSD", "FreeBSD"): lcpre = __salt__["user.get_loginclass"](name) pre = __salt__["user.info"](name) # Make changes if "passwd" in changes: del changes["passwd"] if not empty_password: __salt__["shadow.set_password"](name, password) else: log.warning("No password will be set when empty_password=True") if changes.pop("empty_password", False) is True: __salt__["shadow.del_password"](name) if "password_lock" in changes: passlock = changes.pop("password_lock") if not passlock and salt.utils.platform.is_windows(): __salt__["shadow.unlock_account"](name) elif not passlock: __salt__["shadow.unlock_password"](name) elif passlock and not salt.utils.platform.is_windows(): __salt__["shadow.lock_password"](name) else: log.warning("Account locking is not available on Windows.") if "date" in changes: del changes["date"] __salt__["shadow.set_date"](name, date) def _change_homedir(name, val): if __grains__["kernel"] in ("Darwin", "Windows"): __salt__["user.chhome"](name, val) else: __salt__["user.chhome"](name, val, persist=False) _homedir_changed = False if "home" in changes: val = changes.pop("home") if "homeDoesNotExist" not in changes: _change_homedir(name, val) _homedir_changed = True if "homeDoesNotExist" in changes: val = changes.pop("homeDoesNotExist") if not _homedir_changed: _change_homedir(name, val) if not os.path.isdir(val): __salt__["file.mkdir"](val, pre["uid"], pre["gid"], 0o755) if "mindays" in changes: del changes["mindays"] __salt__["shadow.set_mindays"](name, mindays) if "maxdays" in changes: del changes["maxdays"] __salt__["shadow.set_maxdays"](name, maxdays) if "inactdays" in changes: del changes["inactdays"] __salt__["shadow.set_inactdays"](name, inactdays) if "warndays" in changes: del changes["warndays"] __salt__["shadow.set_warndays"](name, warndays) if "expire" in changes: del changes["expire"] __salt__["shadow.set_expire"](name, expire) if "win_homedrive" in changes: __salt__["user.update"](name=name, homedrive=changes.pop("win_homedrive")) if "win_profile" in changes: __salt__["user.update"](name=name, profile=changes.pop("win_profile")) if "win_logonscript" in changes: __salt__["user.update"]( name=name, logonscript=changes.pop("win_logonscript") ) if "win_description" in changes: __salt__["user.update"]( name=name, description=changes.pop("win_description") ) # Do the changes that have "ch" functions for them, but skip changing # groups for now. Changing groups before changing the chgid could cause # unpredictable results, including failure to set the proper groups. # NOTE: list(changes) required here to avoid modifying dictionary # during iteration. for key in [ x for x in list(changes) if x != "groups" and "user.ch{}".format(x) in __salt__ ]: __salt__["user.ch{}".format(key)](name, changes.pop(key)) # Do group changes last if "groups" in changes: __salt__["user.chgroups"](name, changes.pop("groups"), not remove_groups) if changes: ret.get("warnings", []).append( "Unhandled changes: {}".format(", ".join(changes)) ) post = __salt__["user.info"](name) spost = {} if "shadow.info" in __salt__ and lshad["passwd"] != password: spost = __salt__["shadow.info"](name) if __grains__["kernel"] in ("OpenBSD", "FreeBSD"): lcpost = __salt__["user.get_loginclass"](name) # See if anything changed for key in post: if post[key] != pre[key]: ret["changes"][key] = post[key] if "shadow.info" in __salt__: for key in spost: if lshad[key] != spost[key]: if key == "passwd": ret["changes"][key] = "XXX-REDACTED-XXX" else: ret["changes"][key] = spost[key] if __grains__["kernel"] in ("OpenBSD", "FreeBSD") and lcpost != lcpre: ret["changes"]["loginclass"] = lcpost if ret["changes"]: ret["comment"] = "Updated user {}".format(name) changes = _changes( name, uid, gid, groups, present_optgroups, remove_groups, home, createhome, password, enforce_password, empty_password, shell, fullname, roomnumber, workphone, homephone, other, loginclass, date, mindays, maxdays, inactdays, warndays, expire, win_homedrive, win_profile, win_logonscript, win_description, allow_uid_change=True, allow_gid_change=True, password_lock=password_lock, ) # allow_uid_change and allow_gid_change passed as True to avoid race # conditions where a uid/gid is modified outside of Salt. If an # unauthorized change was requested, it would have been caught the # first time we ran _changes(). if changes: ret["comment"] = "These values could not be changed: {}".format(changes) ret["result"] = False return ret if changes is False: # The user is not present, make it! if __opts__["test"]: ret["result"] = None ret["comment"] = "User {} set to be added".format(name) return ret if groups and present_optgroups: groups.extend(present_optgroups) elif present_optgroups: groups = present_optgroups[:] # Setup params specific to Linux and Windows to be passed to the # add.user function if not salt.utils.platform.is_windows(): params = { "name": name, "uid": uid, "gid": gid, "groups": groups, "home": home, "shell": shell, "unique": unique, "system": system, "fullname": fullname, "roomnumber": roomnumber, "workphone": workphone, "homephone": homephone, "other": other, "createhome": createhome, "nologinit": nologinit, "loginclass": loginclass, "usergroup": usergroup, } else: params = { "name": name, "password": password, "fullname": fullname, "description": win_description, "groups": groups, "home": home, "homedrive": win_homedrive, "profile": win_profile, "logonscript": win_logonscript, } result = __salt__["user.add"](**params) if result is True: ret["comment"] = "New user {} created".format(name) ret["changes"] = __salt__["user.info"](name) if not createhome: # pwd incorrectly reports presence of home ret["changes"]["home"] = "" if ( "shadow.info" in __salt__ and not salt.utils.platform.is_windows() and not salt.utils.platform.is_darwin() ): if password and not empty_password: __salt__["shadow.set_password"](name, password) spost = __salt__["shadow.info"](name) if spost["passwd"] != password: ret[ "comment" ] = "User {} created but failed to set password to {}".format( name, "XXX-REDACTED-XXX" ) ret["result"] = False ret["changes"]["password"] = "XXX-REDACTED-XXX" if empty_password and not password: __salt__["shadow.del_password"](name) spost = __salt__["shadow.info"](name) if spost["passwd"] != "": ret[ "comment" ] = "User {} created but failed to empty password".format(name) ret["result"] = False ret["changes"]["password"] = "" if date is not None: __salt__["shadow.set_date"](name, date) spost = __salt__["shadow.info"](name) if spost["lstchg"] != date: ret["comment"] = ( "User {} created but failed to set" " last change date to" " {}".format(name, date) ) ret["result"] = False ret["changes"]["date"] = date if mindays: __salt__["shadow.set_mindays"](name, mindays) spost = __salt__["shadow.info"](name) if spost["min"] != mindays: ret["comment"] = ( "User {} created but failed to set" " minimum days to" " {}".format(name, mindays) ) ret["result"] = False ret["changes"]["mindays"] = mindays if maxdays: __salt__["shadow.set_maxdays"](name, maxdays) spost = __salt__["shadow.info"](name) if spost["max"] != maxdays: ret["comment"] = ( "User {} created but failed to set" " maximum days to" " {}".format(name, maxdays) ) ret["result"] = False ret["changes"]["maxdays"] = maxdays if inactdays: __salt__["shadow.set_inactdays"](name, inactdays) spost = __salt__["shadow.info"](name) if spost["inact"] != inactdays: ret["comment"] = ( "User {} created but failed to set" " inactive days to" " {}".format(name, inactdays) ) ret["result"] = False ret["changes"]["inactdays"] = inactdays if warndays: __salt__["shadow.set_warndays"](name, warndays) spost = __salt__["shadow.info"](name) if spost["warn"] != warndays: ret[ "comment" ] = "User {} created but failed to set warn days to {}".format( name, warndays ) ret["result"] = False ret["changes"]["warndays"] = warndays if expire: __salt__["shadow.set_expire"](name, expire) spost = __salt__["shadow.info"](name) if spost["expire"] != expire: ret["comment"] = ( "User {} created but failed to set" " expire days to" " {}".format(name, expire) ) ret["result"] = False ret["changes"]["expire"] = expire elif salt.utils.platform.is_windows(): if password and not empty_password: if not __salt__["user.setpassword"](name, password): ret[ "comment" ] = "User {} created but failed to set password to {}".format( name, "XXX-REDACTED-XXX" ) ret["result"] = False ret["changes"]["passwd"] = "XXX-REDACTED-XXX" if expire: __salt__["shadow.set_expire"](name, expire) spost = __salt__["shadow.info"](name) if salt.utils.dateutils.strftime( spost["expire"] ) != salt.utils.dateutils.strftime(expire): ret["comment"] = ( "User {} created but failed to set" " expire days to" " {}".format(name, expire) ) ret["result"] = False ret["changes"]["expiration_date"] = spost["expire"] elif salt.utils.platform.is_darwin() and password and not empty_password: if not __salt__["shadow.set_password"](name, password): ret[ "comment" ] = "User {} created but failed to set password to {}".format( name, "XXX-REDACTED-XXX" ) ret["result"] = False ret["changes"]["passwd"] = "XXX-REDACTED-XXX" else: # if we failed to create a user, result is either false or # str in the case of windows so handle both cases here if isinstance(result, str): ret["comment"] = result else: ret["comment"] = "Failed to create new user {}".format(name) ret["result"] = False return ret def absent(name, purge=False, force=False): """ Ensure that the named user is absent name The name of the user to remove purge Set purge to True to delete all of the user's files as well as the user, Default is ``False``. force If the user is logged in, the absent state will fail. Set the force option to True to remove the user even if they are logged in. Not supported in FreeBSD and Solaris, Default is ``False``. """ ret = {"name": name, "changes": {}, "result": True, "comment": ""} lusr = __salt__["user.info"](name) if lusr: # The user is present, make it not present if __opts__["test"]: ret["result"] = None ret["comment"] = "User {} set for removal".format(name) return ret beforegroups = set(salt.utils.user.get_group_list(name)) ret["result"] = __salt__["user.delete"](name, purge, force) aftergroups = {g for g in beforegroups if __salt__["group.info"](g)} if ret["result"]: ret["changes"] = {} for g in beforegroups - aftergroups: ret["changes"]["{} group".format(g)] = "removed" ret["changes"][name] = "removed" ret["comment"] = "Removed user {}".format(name) else: ret["result"] = False ret["comment"] = "Failed to remove user {}".format(name) return ret ret["comment"] = "User {} is not present".format(name) return ret
Save