1
0
Fork 0
mirror of https://github.com/ansible-collections/community.general.git synced 2024-09-14 20:13:21 +02:00

New Module: Homectl module for managing systemd-homed (#4018)

* initial development of homectl module

* botmeta

* fix some linting

* Update .github/BOTMETA.yml

Co-authored-by: Felix Fontein <felix@fontein.de>

* Update plugins/modules/system/homectl.py

Co-authored-by: Felix Fontein <felix@fontein.de>

* Update plugins/modules/system/homectl.py

Co-authored-by: Felix Fontein <felix@fontein.de>

* Update plugins/modules/system/homectl.py

Co-authored-by: Felix Fontein <felix@fontein.de>

* Update plugins/modules/system/homectl.py

Co-authored-by: Felix Fontein <felix@fontein.de>

* use array form of run_command

Co-authored-by: Felix Fontein <felix@fontein.de>

* Update plugins/modules/system/homectl.py

Co-authored-by: Felix Fontein <felix@fontein.de>

* added mofifying user record and cleaned up based on comments

* added updating records/multiple changes regarding options, examples doc, return doc

* add integration tests and more overall improvements

* Update plugins/modules/system/homectl.py

Co-authored-by: Felix Fontein <felix@fontein.de>

* Update plugins/modules/system/homectl.py

Co-authored-by: Felix Fontein <felix@fontein.de>

* Update plugins/modules/system/homectl.py

Co-authored-by: Felix Fontein <felix@fontein.de>

* Update plugins/modules/system/homectl.py

Co-authored-by: Felix Fontein <felix@fontein.de>

* Update plugins/modules/system/homectl.py

Co-authored-by: Felix Fontein <felix@fontein.de>

* Update plugins/modules/system/homectl.py

Co-authored-by: Felix Fontein <felix@fontein.de>

* Update plugins/modules/system/homectl.py

Co-authored-by: Felix Fontein <felix@fontein.de>

* Update plugins/modules/system/homectl.py

Co-authored-by: Felix Fontein <felix@fontein.de>

* Update plugins/modules/system/homectl.py

Co-authored-by: Felix Fontein <felix@fontein.de>

* removed modify handle within present

* adding more options and better checking of user records when updating

* Apply suggestions from code review

Co-authored-by: Felix Fontein <felix@fontein.de>

* Update plugins/modules/system/homectl.py

Co-authored-by: Felix Fontein <felix@fontein.de>

* Add code review changes

- remove unsafe_shell with run_command.
- use dict.pop() in user_metadata dict.
- consistent quoting to single quotes.
- change logic to determine check mode better
- fix integration tests and added check_mode tests

* Fix handling of mount opts

When a user is created without mountopts homed will use nodev and nosuid
by default, however the user record metadata will not contain these
values. This commit takes extra care that correct value is being set to
true or false. So if a user gives mountopts with just nodev we need to
make sure the nosuid and noexec gets set to false, etc. If mountopts are
same as currently in user record make sure nothing would be changed and
outputs correctly.

Also fixed some tests.

* change fmethod modify_user to prepare_modify_user_command

* Code review fixes and add existing user pw checking

- Added methods to check existing users password is correct by comparing
  the hash stored in homed user record and the hash of given password
- Updated integration tests for above case
- Added aliases file so CI can run

* Apply suggestions from code review

Co-authored-by: Felix Fontein <felix@fontein.de>

Co-authored-by: Felix Fontein <felix@fontein.de>
This commit is contained in:
James Livulpi 2022-01-28 01:11:12 -05:00 committed by GitHub
parent 7aab4497ac
commit ab7e4ee578
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 832 additions and 0 deletions

2
.github/BOTMETA.yml vendored
View file

@ -1022,6 +1022,8 @@ files:
$modules/system/gconftool2.py:
maintainers: Akasurde kevensen
labels: gconftool2
$modules/system/homectl.py:
maintainers: jameslivulpi
$modules/system/interfaces_file.py:
maintainers: obourdon hryamzik
labels: interfaces_file

1
plugins/modules/homectl.py Symbolic link
View file

@ -0,0 +1 @@
./system/homectl.py

View file

@ -0,0 +1,650 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
# (c) 2022, James Livulpi
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
from __future__ import absolute_import, division, print_function
__metaclass__ = type
DOCUMENTATION = '''
---
module: homectl
author:
- "James Livulpi (@jameslivulpi)"
short_description: Manage user accounts with systemd-homed
version_added: 4.4.0
description:
- Manages a user's home directory managed by systemd-homed.
options:
name:
description:
- The user name to create, remove, or update.
required: true
aliases: [ 'user', 'username' ]
type: str
password:
description:
- Set the user's password to this.
- Homed requires this value to be in cleartext on user creation and updating a user.
- The module takes the password and generates a password hash in SHA-512 with 10000 rounds of salt generation using crypt.
- See U(https://systemd.io/USER_RECORD/).
- This is required for I(state=present). When an existing user is updated this is checked against the stored hash in homed.
type: str
state:
description:
- The operation to take on the user.
choices: [ 'absent', 'present' ]
default: present
type: str
storage:
description:
- Indicates the storage mechanism for the user's home directory.
- If the storage type is not specified, ``homed.conf(5)`` defines which default storage to use.
- Only used when a user is first created.
choices: [ 'classic', 'luks', 'directory', 'subvolume', 'fscrypt', 'cifs' ]
type: str
disksize:
description:
- The intended home directory disk space.
- Human readable value such as C(10G), C(10M), or C(10B).
type: str
resize:
description:
- When used with I(disksize) this will attempt to resize the home directory immediately.
default: false
type: bool
realname:
description:
- The user's real ('human') name.
- This can also be used to add a comment to maintain compatability with C(useradd).
aliases: [ 'comment' ]
type: str
realm:
description:
- The 'realm' a user is defined in.
type: str
email:
description:
- The email address of the user.
type: str
location:
description:
- A free-form location string describing the location of the user.
type: str
iconname:
description:
- The name of an icon picked by the user, for example for the purpose of an avatar.
- Should follow the semantics defined in the Icon Naming Specification.
- See U(https://specifications.freedesktop.org/icon-naming-spec/icon-naming-spec-latest.html) for specifics.
type: str
homedir:
description:
- Path to use as home directory for the user.
- This is the directory the user's home directory is mounted to while the user is logged in.
- This is not where the user's data is actually stored, see I(imagepath) for that.
- Only used when a user is first created.
type: path
imagepath:
description:
- Path to place the user's home directory.
- See U(https://www.freedesktop.org/software/systemd/man/homectl.html#--image-path=PATH) for more information.
- Only used when a user is first created.
type: path
uid:
description:
- Sets the UID of the user.
- If using I(gid) homed requires the value to be the same.
- Only used when a user is first created.
type: int
gid:
description:
- Sets the gid of the user.
- If using I(uid) homed requires the value to be the same.
- Only used when a user is first created.
type: int
mountopts:
description:
- String separated by comma each indicating mount options for a users home directory.
- Valid options are C(nosuid), C(nodev) or C(noexec).
- Homed by default uses C(nodev) and C(nosuid) while C(noexec) is off.
type: str
umask:
description:
- Sets the umask for the user's login sessions
- Value from C(0000) to C(0777).
type: int
memberof:
description:
- String separated by comma each indicating a UNIX group this user shall be a member of.
- Groups the user should be a member of should be supplied as comma separated list.
aliases: [ 'groups' ]
type: str
skeleton:
description:
- The absolute path to the skeleton directory to populate a new home directory from.
- This is only used when a home directory is first created.
- If not specified homed by default uses C(/etc/skel).
aliases: [ 'skel' ]
type: path
shell:
description:
- Shell binary to use for terminal logins of given user.
- If not specified homed by default uses C(/bin/bash).
type: str
environment:
description:
- String separated by comma each containing an environment variable and its value to
set for the user's login session, in a format compatible with ``putenv()``.
- Any environment variable listed here is automatically set by pam_systemd for all
login sessions of the user.
aliases: [ 'setenv' ]
type: str
timezone:
description:
- Preferred timezone to use for the user.
- Should be a tzdata compatible location string such as C(America/New_York).
type: str
locked:
description:
- Whether the user account should be locked or not.
type: bool
language:
description:
- The preferred language/locale for the user.
- This should be in a format compatible with the C($LANG) environment variable.
type: str
passwordhint:
description:
- Password hint for the given user.
type: str
sshkeys:
description:
- String separated by comma each listing a SSH public key that is authorized to access the account.
- The keys should follow the same format as the lines in a traditional C(~/.ssh/authorized_key) file.
type: str
notbefore:
description:
- A time since the UNIX epoch before which the record should be considered invalid for the purpose of logging in.
type: int
notafter:
description:
- A time since the UNIX epoch after which the record should be considered invalid for the purpose of logging in.
type: int
'''
EXAMPLES = '''
- name: Add the user 'james'
community.general.homectl:
name: johnd
password: myreallysecurepassword1!
state: present
- name: Add the user 'alice' with a zsh shell, uid of 1000, and gid of 2000
community.general.homectl:
name: alice
password: myreallysecurepassword1!
state: present
shell: /bin/zsh
uid: 1000
gid: 1000
- name: Modify an existing user 'frank' to have 10G of diskspace and resize usage now
community.general.homectl:
name: frank
password: myreallysecurepassword1!
state: present
disksize: 10G
resize: yes
- name: Remove an existing user 'janet'
community.general.homectl:
name: janet
state: absent
'''
RETURN = '''
data:
description: A json dictionary returned from C(homectl inspect -j).
returned: success
type: dict
sample: {
"data": {
"binding": {
"e9ed2a5b0033427286b228e97c1e8343": {
"fileSystemType": "btrfs",
"fileSystemUuid": "7bd59491-2812-4642-a492-220c3f0c6c0b",
"gid": 60268,
"imagePath": "/home/james.home",
"luksCipher": "aes",
"luksCipherMode": "xts-plain64",
"luksUuid": "7f05825a-2c38-47b4-90e1-f21540a35a81",
"luksVolumeKeySize": 32,
"partitionUuid": "5a906126-d3c8-4234-b230-8f6e9b427b2f",
"storage": "luks",
"uid": 60268
}
},
"diskSize": 3221225472,
"disposition": "regular",
"lastChangeUSec": 1641941238208691,
"lastPasswordChangeUSec": 1641941238208691,
"privileged": {
"hashedPassword": [
"$6$ov9AKni.trf76inT$tTtfSyHgbPTdUsG0CvSSQZXGqFGdHKQ9Pb6e0BTZhDmlgrL/vA5BxrXduBi8u/PCBiYUffGLIkGhApjKMK3bV."
]
},
"signature": [
{
"data": "o6zVFbymcmk4YTVaY6KPQK23YCp+VkXdGEeniZeV1pzIbFzoaZBvVLPkNKMoPAQbodY5BYfBtuy41prNL78qAg==",
"key": "-----BEGIN PUBLIC KEY-----\nMCowBQYDK2VwAyEAbs7ELeiEYBxkUQhxZ+5NGyu6J7gTtZtZ5vmIw3jowcY=\n-----END PUBLIC KEY-----\n"
}
],
"status": {
"e9ed2a5b0033427286b228e97c1e8343": {
"diskCeiling": 21845405696,
"diskFloor": 268435456,
"diskSize": 3221225472,
"service": "io.systemd.Home",
"signedLocally": true,
"state": "inactive"
}
},
"userName": "james",
}
}
'''
import crypt
import json
from ansible.module_utils.basic import AnsibleModule
from ansible.module_utils.basic import jsonify
from ansible.module_utils.common.text.formatters import human_to_bytes
class Homectl(object):
'''#TODO DOC STRINGS'''
def __init__(self, module):
self.module = module
self.state = module.params['state']
self.name = module.params['name']
self.password = module.params['password']
self.storage = module.params['storage']
self.disksize = module.params['disksize']
self.resize = module.params['resize']
self.realname = module.params['realname']
self.realm = module.params['realm']
self.email = module.params['email']
self.location = module.params['location']
self.iconname = module.params['iconname']
self.homedir = module.params['homedir']
self.imagepath = module.params['imagepath']
self.uid = module.params['uid']
self.gid = module.params['gid']
self.umask = module.params['umask']
self.memberof = module.params['memberof']
self.skeleton = module.params['skeleton']
self.shell = module.params['shell']
self.environment = module.params['environment']
self.timezone = module.params['timezone']
self.locked = module.params['locked']
self.passwordhint = module.params['passwordhint']
self.sshkeys = module.params['sshkeys']
self.language = module.params['language']
self.notbefore = module.params['notbefore']
self.notafter = module.params['notafter']
self.mountopts = module.params['mountopts']
self.result = {}
# Cannot run homectl commands if service is not active
def homed_service_active(self):
is_active = True
cmd = ['systemctl', 'show', 'systemd-homed.service', '-p', 'ActiveState']
rc, show_service_stdout, stderr = self.module.run_command(cmd)
if rc == 0:
state = show_service_stdout.rsplit('=')[1]
if state.strip() != 'active':
is_active = False
return is_active
def user_exists(self):
exists = False
valid_pw = False
# Get user properties if they exist in json
rc, stdout, stderr = self.get_user_metadata()
if rc == 0:
exists = True
# User exists now compare password given with current hashed password stored in the user metadata.
if self.state != 'absent': # Don't need checking on remove user
stored_pwhash = json.loads(stdout)['privileged']['hashedPassword'][0]
if self._check_password(stored_pwhash):
valid_pw = True
return exists, valid_pw
def create_user(self):
record = self.create_json_record(create=True)
cmd = [self.module.get_bin_path('homectl', True)]
cmd.append('create')
cmd.append('--identity=-') # Read the user record from standard input.
return(self.module.run_command(cmd, data=record))
def _hash_password(self, password):
method = crypt.METHOD_SHA512
salt = crypt.mksalt(method, rounds=10000)
pw_hash = crypt.crypt(password, salt)
return pw_hash
def _check_password(self, pwhash):
hash = crypt.crypt(self.password, pwhash)
return pwhash == hash
def remove_user(self):
cmd = [self.module.get_bin_path('homectl', True)]
cmd.append('remove')
cmd.append(self.name)
return self.module.run_command(cmd)
def prepare_modify_user_command(self):
record = self.create_json_record()
cmd = [self.module.get_bin_path('homectl', True)]
cmd.append('update')
cmd.append(self.name)
cmd.append('--identity=-') # Read the user record from standard input.
# Resize disksize now resize = true
# This is not valid in user record (json) and requires it to be passed on command.
if self.disksize and self.resize:
cmd.append('--and-resize')
cmd.append('true')
self.result['changed'] = True
return cmd, record
def get_user_metadata(self):
cmd = [self.module.get_bin_path('homectl', True)]
cmd.append('inspect')
cmd.append(self.name)
cmd.append('-j')
cmd.append('--no-pager')
rc, stdout, stderr = self.module.run_command(cmd)
return rc, stdout, stderr
# Build up dictionary to jsonify for homectl commands.
def create_json_record(self, create=False):
record = {}
user_metadata = {}
self.result['changed'] = False
# Get the current user record if not creating a new user record.
if not create:
rc, user_metadata, stderr = self.get_user_metadata()
user_metadata = json.loads(user_metadata)
# Remove elements that are not meant to be updated from record.
# These are always part of the record when a user exists.
user_metadata.pop('signature', None)
user_metadata.pop('binding', None)
user_metadata.pop('status', None)
# Let last change Usec be updated by homed when command runs.
user_metadata.pop('lastChangeUSec', None)
# Now only change fields that are called on leaving whats currently in the record intact.
record = user_metadata
record['userName'] = self.name
record['secret'] = {'password': [self.password]}
if create:
password_hash = self._hash_password(self.password)
record['privileged'] = {'hashedPassword': [password_hash]}
self.result['changed'] = True
if self.uid and self.gid and create:
record['uid'] = self.uid
record['gid'] = self.gid
self.result['changed'] = True
if self.memberof:
member_list = list(self.memberof.split(','))
if member_list != record.get('memberOf', [None]):
record['memberOf'] = member_list
self.result['changed'] = True
if self.realname:
if self.realname != record.get('realName'):
record['realName'] = self.realname
self.result['changed'] = True
# Cannot update storage unless were creating a new user.
# See 'Fields in the binding section' at https://systemd.io/USER_RECORD/
if self.storage and create:
record['storage'] = self.storage
self.result['changed'] = True
# Cannot update homedir unless were creating a new user.
# See 'Fields in the binding section' at https://systemd.io/USER_RECORD/
if self.homedir and create:
record['homeDirectory'] = self.homedir
self.result['changed'] = True
# Cannot update imagepath unless were creating a new user.
# See 'Fields in the binding section' at https://systemd.io/USER_RECORD/
if self.imagepath and create:
record['imagePath'] = self.imagepath
self.result['changed'] = True
if self.disksize:
# convert humand readble to bytes
if self.disksize != record.get('diskSize'):
record['diskSize'] = human_to_bytes(self.disksize)
self.result['changed'] = True
if self.realm:
if self.realm != record.get('realm'):
record['realm'] = self.realm
self.result['changed'] = True
if self.email:
if self.email != record.get('emailAddress'):
record['emailAddress'] = self.email
self.result['changed'] = True
if self.location:
if self.location != record.get('location'):
record['location'] = self.location
self.result['changed'] = True
if self.iconname:
if self.iconname != record.get('iconName'):
record['iconName'] = self.iconname
self.result['changed'] = True
if self.skeleton:
if self.skeleton != record.get('skeletonDirectory'):
record['skeletonDirectory'] = self.skeleton
self.result['changed'] = True
if self.shell:
if self.shell != record.get('shell'):
record['shell'] = self.shell
self.result['changed'] = True
if self.umask:
if self.umask != record.get('umask'):
record['umask'] = self.umask
self.result['changed'] = True
if self.environment:
if self.environment != record.get('environment', [None]):
record['environment'] = list(self.environment.split(','))
self.result['changed'] = True
if self.timezone:
if self.timezone != record.get('timeZone'):
record['timeZone'] = self.timezone
self.result['changed'] = True
if self.locked:
if self.locked != record.get('locked'):
record['locked'] = self.locked
self.result['changed'] = True
if self.passwordhint:
if self.passwordhint != record.get('privileged', {}).get('passwordHint'):
record['privileged']['passwordHint'] = self.passwordhint
self.result['changed'] = True
if self.sshkeys:
if self.sshkeys != record.get('privileged', {}).get('sshAuthorizedKeys'):
record['privileged']['sshAuthorizedKeys'] = list(self.sshkeys.split(','))
self.result['changed'] = True
if self.language:
if self.locked != record.get('preferredLanguage'):
record['preferredLanguage'] = self.language
self.result['changed'] = True
if self.notbefore:
if self.locked != record.get('notBeforeUSec'):
record['notBeforeUSec'] = self.notbefore
self.result['changed'] = True
if self.notafter:
if self.locked != record.get('notAfterUSec'):
record['notAfterUSec'] = self.notafter
self.result['changed'] = True
if self.mountopts:
opts = list(self.mountopts.split(','))
if 'nosuid' in opts:
if record.get('mountNoSuid') is not True:
record['mountNoSuid'] = True
self.result['changed'] = True
else:
if record.get('mountNoSuid') is not False:
record['mountNoSuid'] = False
self.result['changed'] = True
if 'nodev' in opts:
if record.get('mountNoDevices') is not True:
record['mountNoDevices'] = True
self.result['changed'] = True
else:
if record.get('mountNoDevices') is not False:
record['mountNoDevices'] = False
self.result['changed'] = True
if 'noexec' in opts:
if record.get('mountNoExecute') is not True:
record['mountNoExecute'] = True
self.result['changed'] = True
else:
if record.get('mountNoExecute') is not False:
record['mountNoExecute'] = False
self.result['changed'] = True
return jsonify(record)
def main():
module = AnsibleModule(
argument_spec=dict(
state=dict(type='str', default='present', choices=['absent', 'present']),
name=dict(type='str', required=True, aliases=['user', 'username']),
password=dict(type='str', no_log=True),
storage=dict(type='str', choices=['classic', 'luks', 'directory', 'subvolume', 'fscrypt', 'cifs']),
disksize=dict(type='str'),
resize=dict(type='bool', default=False),
realname=dict(type='str', aliases=['comment']),
realm=dict(type='str'),
email=dict(type='str'),
location=dict(type='str'),
iconname=dict(type='str'),
homedir=dict(type='path'),
imagepath=dict(type='path'),
uid=dict(type='int'),
gid=dict(type='int'),
umask=dict(type='int'),
environment=dict(type='str', aliases=['setenv']),
timezone=dict(type='str'),
memberof=dict(type='str', aliases=['groups']),
skeleton=dict(type='path', aliases=['skel']),
shell=dict(type='str'),
locked=dict(type='bool'),
passwordhint=dict(type='str', no_log=True),
sshkeys=dict(type='str', no_log=True),
language=dict(type='str'),
notbefore=dict(type='int'),
notafter=dict(type='int'),
mountopts=dict(type='str'),
),
supports_check_mode=True,
required_if=[
('state', 'present', ['password']),
('resize', True, ['disksize']),
]
)
homectl = Homectl(module)
homectl.result['state'] = homectl.state
# First we need to make sure homed service is active
if not homectl.homed_service_active():
module.fail_json(msg='systemd-homed.service is not active')
# handle removing user
if homectl.state == 'absent':
user_exists, valid_pwhash = homectl.user_exists()
if user_exists:
if module.check_mode:
module.exit_json(changed=True)
rc, stdout, stderr = homectl.remove_user()
if rc != 0:
module.fail_json(name=homectl.name, msg=stderr, rc=rc)
homectl.result['changed'] = True
homectl.result['rc'] = rc
homectl.result['msg'] = 'User %s removed!' % homectl.name
else:
homectl.result['changed'] = False
homectl.result['msg'] = 'User does not exist!'
# Handle adding a user
if homectl.state == 'present':
user_exists, valid_pwhash = homectl.user_exists()
if not user_exists:
if module.check_mode:
module.exit_json(changed=True)
rc, stdout, stderr = homectl.create_user()
if rc != 0:
module.fail_json(name=homectl.name, msg=stderr, rc=rc)
rc, user_metadata, stderr = homectl.get_user_metadata()
homectl.result['data'] = json.loads(user_metadata)
homectl.result['rc'] = rc
homectl.result['msg'] = 'User %s created!' % homectl.name
else:
if valid_pwhash:
# Run this to see if changed would be True or False which is useful for check_mode
cmd, record = homectl.prepare_modify_user_command()
else:
# User gave wrong password fail with message
homectl.result['changed'] = False
homectl.result['msg'] = 'User exists but password is incorrect!'
module.fail_json(**homectl.result)
if module.check_mode:
module.exit_json(**homectl.result)
# Now actually modify the user if changed was set to true at any point.
if homectl.result['changed']:
rc, stdout, stderr = module.run_command(cmd, data=record)
if rc != 0:
module.fail_json(name=homectl.name, msg=stderr, rc=rc, changed=False)
rc, user_metadata, stderr = homectl.get_user_metadata()
homectl.result['data'] = json.loads(user_metadata)
homectl.result['rc'] = rc
if homectl.result['changed']:
homectl.result['msg'] = 'User %s modified' % homectl.name
module.exit_json(**homectl.result)
if __name__ == '__main__':
main()

View file

@ -0,0 +1,5 @@
shippable/posix/group1
skip/aix
skip/freebsd
skip/osx
skip/macos

View file

@ -0,0 +1,174 @@
# Get systemd version and if it doesn't exist don't run these tests.
- name: check systemd version
command: "systemctl --version"
register: systemd_version
ignore_errors: yes
- name: check homectl version
command: homectl --version
register: homectl_version
ignore_errors: yes
- block:
- name: Check and start systemd-homed service
service:
name: systemd-homed.service
state: started
enabled: yes
- name: Add a user 'james'
community.general.homectl:
name: james
password: myreallysecurepassword1!
state: present
- name: verify user added
command: homectl inspect james
register: james_info
- name: Add the user 'tom' with a zsh shell, uid of 1000, and gid of 1000
community.general.homectl:
name: tom
password: myreallysecurepassword1!
state: present
shell: /bin/zsh
uid: 1000
gid: 1000
disksize: 10G
register: tom_userinfo
- name: Try to add user 'james' that already exists
community.general.homectl:
name: james
password: myreallysecurepassword1!
state: present
shell: /bin/ksh
register: user_exists
- name: Try to use 'resize=yes' option without 'disksize' option (not allowed)
community.general.homectl:
name: foo
password: uq4895738!@#$%dfd
state: present
resize: yes
register: resize_out
ignore_errors: yes
- name: Use option 'disksize=1G' without option resize (allowed)
community.general.homectl:
name: foobar
password: "uq4895738!@#$%dfd"
state: present
disksize: 1G
register: disk_out
ignore_errors: yes
- name: Try to Create user without giving password
community.general.homectl:
name: danielle
register: danielle_out
ignore_errors: yes
- name: remove user 'foobar' without requiring password
community.general.homectl:
name: foobar
state: absent
register: delete_foobar_out
- name: modify user 'james' to have zsh shell and timezone 'America/New_York'
community.general.homectl:
name: james
password: myreallysecurepassword1!
state: present
shell: /bin/zsh
timezone: America/New_York
register: lukuser_modify_out
- name: create user 'jake' with all mount options
community.general.homectl:
name: jake
password: myreallysecurepassword12!
mountopts: noexec,nosuid,nodev
sshkeys: "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQDUSW/q2qFZPr2vS0qrmAs+1iQI1jLIBdJ4CVIhE3KnIwxkkiInS8mJ+t0FBTeK3ks3GZLPVYC1v9o2P+oqyUk1CiBnCsMXGJud+L/t8b5r8MiJMyP7Jzd6yhmcvenjvz+vY06jQ9chWAtThEknuaOMongIpQQzSLGbdMy0yMsz4GEjicwdcj1PDwItPvUt4TL4K7V9NE672idADlRt6qng4UwpziqlYgsyIG46ettDras8hGAPricrhFWUS2rLDsCD0thkPFdR8HL1ZWTZ6LtolhO4MYtgntzXn708TTmFC2oIDluzyxVoUYmsfVotVdXFZcOWffnwbCgU+tn75JXTLozgTbV3VWmkxpJFErCWPerxcZv3+7b0f36/Y0gRNjM9HERLDSE1c8yz29NOLY0qH5306aByjOaerxNq9+ZOU/Fmf5/VfGIUp/FdLxDw+V0AzejFG580VAcstEMsOHSdwTbi3gf6LoGSiRyWKKDod0TZCMC6RzfdsfdsfI9CClGl0s= test@router.home"
register: jake_out
- name: Try to remove user 'janet' that doesn't exist
community.general.homectl:
name: janet
state: absent
register: user_not_exist
ignore_errors: yes
- name: Use check_mode to try and create user 'diana'
community.general.homectl:
name: diana
password: helloworld123!@
state: present
check_mode: yes
register: diana_create_checkmode_out
- name: Verify user 'diana' was not created with check_mode
command: homectl inspect diana
register: user_diana_exists
ignore_errors: yes
- name: Try to modify user 'jake' with only noexec mount option in check_mode
community.general.homectl:
name: jake
password: myreallysecurepassword12!
state: present
mountopts: noexec
check_mode: yes
register: jake_checkmode_out
- name: Verify user 'jake' was not modified and still has all mount options
command: homectl inspect jake
register: user_jake_details_out
- name: Modify user 'jake' with only noexec mount option
community.general.homectl:
name: jake
password: myreallysecurepassword12!
state: present
mountopts: noexec
register: jake_modify_out
- name: modify user 'jake' again with only noexec mount option to make sure changed is false as nothing has changed.
community.general.homectl:
name: jake
password: myreallysecurepassword12!
state: present
mountopts: noexec
register: jake_modify_again_out
- name: Try to modify user 'jake' with an incorrect password
community.general.homectl:
name: jake
password: incorrectPassword!
state: present
mountopts: noexec
locked: yes
ignore_errors: yes
register: jake_incorrect_pass_out
- assert:
that:
- james_info.rc == 0
- tom_userinfo.data['gid'] == 1000 and tom_userinfo.data['uid'] == 1000
- user_exists is changed and user_exists.data['shell'] == '/bin/ksh'
- resize_out is not changed
- disk_out is changed
- delete_foobar_out is changed
- danielle_out is not changed
- lukuser_modify_out.data['timeZone'] == "America/New_York" and lukuser_modify_out.data['shell'] == "/bin/zsh"
- user_not_exist is not changed and user_not_exist.msg == "User does not exist!"
- jake_out is changed and jake_out.data['mountNoDevices'] == True and jake_out.data['mountNoSuid'] == True and jake_out.data['mountNoExecute'] == True
- diana_create_checkmode_out is changed and 'No home for user diana known' in user_diana_exists.stderr
- "jake_checkmode_out is changed and 'Mount Flags: nosuid nodev noexec' in user_jake_details_out.stdout"
- jake_modify_out is changed and jake_modify_out.data['privileged']['sshAuthorizedKeys'] is not none
- jake_modify_out.data['mountNoDevices'] == False and jake_modify_out.data['mountNoExecute'] == True and jake_modify_out.data['mountNoSuid'] == False
- jake_modify_again_out is not changed
- jake_incorrect_pass_out is not changed and jake_incorrect_pass_out is failed and jake_incorrect_pass_out.msg == 'User exists but password is incorrect!'
# homectl was first introduced in systemd 245 so check version >= 245 and make sure system has systemd and homectl command
when: systemd_version.rc == 0 and (systemd_version.stdout | regex_search('[0-9][0-9][0-9]') | int >= 245) and homectl_version.rc == 0