From a31ba582b476f111d9c5a05c2df89b2112838b83 Mon Sep 17 00:00:00 2001 From: trbs Date: Fri, 31 May 2013 20:53:37 -0400 Subject: [PATCH] Add support for Open and NetBSD platforms for user and group modules, including a new login_class parameter for FreeBSD, OpenBSD and NetBSD. --- system/group | 108 +++++++++++++++-- system/user | 320 ++++++++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 415 insertions(+), 13 deletions(-) diff --git a/system/group b/system/group index eb93d93c2d6..034baa514d3 100644 --- a/system/group +++ b/system/group @@ -61,8 +61,8 @@ import platform class Group(object): """ This is a generic Group manipulation class that is subclassed - based on platform. - + based on platform. + A subclass may wish to override the following action methods:- - group_del() - group_add() @@ -96,7 +96,7 @@ class Group(object): def group_del(self): cmd = [self.module.get_bin_path('groupdel', True), self.name] return self.execute_command(cmd) - + def group_add(self, **kwargs): cmd = [self.module.get_bin_path('groupadd', True)] for key in kwargs: @@ -107,7 +107,7 @@ class Group(object): cmd.append('-r') cmd.append(self.name) return self.execute_command(cmd) - + def group_mod(self, **kwargs): cmd = [self.module.get_bin_path('groupmod', True)] info = self.group_info() @@ -122,14 +122,14 @@ class Group(object): return (True, '', '') cmd.append(self.name) return self.execute_command(cmd) - + def group_exists(self): try: if grp.getgrnam(self.name): return True except KeyError: return False - + def group_info(self): if not self.group_exists(): return False @@ -162,7 +162,7 @@ class SunOS(Group): cmd.append(kwargs[key]) cmd.append(self.name) return self.execute_command(cmd) - + # =========================================== @@ -228,25 +228,111 @@ class FreeBsdGroup(Group): cmd = [self.module.get_bin_path('pw', True), 'groupdel', self.name] return self.execute_command(cmd) - def group_add(self,**kwargs): + def group_add(self, **kwargs): cmd = [self.module.get_bin_path('pw', True), 'groupadd', self.name] if self.gid is not None: - cmd.append('-g %d' % int(self.gid)) + cmd.append('-g %d' % int(self.gid)) return self.execute_command(cmd) - def group_mod(self,**kwargs): + def group_mod(self, **kwargs): cmd = [self.module.get_bin_path('pw', True), 'groupmod', self.name] info = self.group_info() cmd_len = len(cmd) if self.gid is not None and int(self.gid) != info[2]: - cmd.append('-g %d' % int(self.gid)) + cmd.append('-g %d' % int(self.gid)) # modify the group if cmd will do anything if cmd_len != len(cmd): + if self.module.check_mode: + return (True, '', '') return self.execute_command(cmd) return (None, '', '') # =========================================== +class OpenBsdGroup(Group): + """ + This is a OpenBSD Group manipulation class. + + This overrides the following methods from the generic class:- + - group_del() + - group_add() + - group_mod() + """ + + platform = 'OpenBSD' + distribution = None + GROUPFILE = '/etc/group' + + def group_del(self): + cmd = [self.module.get_bin_path('groupdel', True), self.name] + return self.execute_command(cmd) + + def group_add(self, **kwargs): + cmd = [self.module.get_bin_path('groupadd', True)] + if self.gid is not None: + cmd.append('-g') + cmd.append('%d' % int(self.gid)) + cmd.append(self.name) + return self.execute_command(cmd) + + def group_mod(self, **kwargs): + cmd = [self.module.get_bin_path('groupmod', True)] + info = self.group_info() + cmd_len = len(cmd) + if self.gid is not None and int(self.gid) != info[2]: + cmd.append('-g') + cmd.append('%d' % int(self.gid)) + if len(cmd) == 1: + return (None, '', '') + if self.module.check_mode: + return (True, '', '') + cmd.append(self.name) + return self.execute_command(cmd) + +# =========================================== + +class NetBsdGroup(Group): + """ + This is a NetBSD Group manipulation class. + + This overrides the following methods from the generic class:- + - group_del() + - group_add() + - group_mod() + """ + + platform = 'NetBSD' + distribution = None + GROUPFILE = '/etc/group' + + def group_del(self): + cmd = [self.module.get_bin_path('groupdel', True), self.name] + return self.execute_command(cmd) + + def group_add(self, **kwargs): + cmd = [self.module.get_bin_path('groupadd', True)] + if self.gid is not None: + cmd.append('-g') + cmd.append('%d' % int(self.gid)) + cmd.append(self.name) + return self.execute_command(cmd) + + def group_mod(self, **kwargs): + cmd = [self.module.get_bin_path('groupmod', True)] + info = self.group_info() + cmd_len = len(cmd) + if self.gid is not None and int(self.gid) != info[2]: + cmd.append('-g') + cmd.append('%d' % int(self.gid)) + if len(cmd) == 1: + return (None, '', '') + if self.module.check_mode: + return (True, '', '') + cmd.append(self.name) + return self.execute_command(cmd) + +# =========================================== + def main(): module = AnsibleModule( argument_spec = dict( diff --git a/system/user b/system/user index 2f9924b4868..88f79aab246 100644 --- a/system/user +++ b/system/user @@ -106,6 +106,10 @@ options: description: - When used with C(state=absent), behavior is as with C(userdel --force). + login_class: + required: false + description: + - Optionally sets the user's login class for FreeBSD, OpenBSD and NetBSD systems. remove: required: false default: "no" @@ -215,6 +219,7 @@ class User(object): self.remove = module.params['remove'] self.createhome = module.params['createhome'] self.system = module.params['system'] + self.login_class = module.params['login_class'] self.append = module.params['append'] self.sshkeygen = module.params['generate_ssh_key'] self.ssh_bits = module.params['ssh_key_bits'] @@ -526,7 +531,7 @@ class FreeBsdUser(User): This is a FreeBSD User manipulation class - it uses the pw command to manipulate the user database, followed by the chpass command to change the password. - + This overrides the following methods from the generic class:- - create_user() - remove_user() @@ -554,7 +559,7 @@ class FreeBsdUser(User): self.module.get_bin_path('pw', True), 'useradd', '-n', - self.name + self.name, ] if self.uid is not None: @@ -590,6 +595,10 @@ class FreeBsdUser(User): cmd.append('-s') cmd.append(self.shell) + if self.login_class is not None: + cmd.append('-L') + cmd.append(self.login_class) + # system cannot be handled currently - should we error if its requested? # create the user (rc, out, err) = self.execute_command(cmd) @@ -645,6 +654,10 @@ class FreeBsdUser(User): cmd.append('-s') cmd.append(self.shell) + if self.login_class is not None: + cmd.append('-L') + cmd.append(self.login_class) + if self.groups is not None: current_groups = self.user_group_membership() groups = self.get_groups_set() @@ -690,6 +703,308 @@ class FreeBsdUser(User): # =========================================== +class OpenBSDUser(User): + """ + This is a OpenBSD User manipulation class. + Main differences are that OpenBSD:- + - has no concept of "system" account. + - has no force delete user + + This overrides the following methods from the generic class:- + - create_user() + - remove_user() + - modify_user() + """ + + platform = 'OpenBSD' + distribution = None + SHADOWFILE = '/etc/master.passwd' + + def create_user(self): + cmd = [self.module.get_bin_path('useradd', True)] + + if self.uid is not None: + cmd.append('-u') + cmd.append(self.uid) + + if self.non_unique: + cmd.append('-o') + + if self.group is not None: + if not self.group_exists(self.group): + self.module.fail_json(msg="Group %s does not exist" % self.group) + cmd.append('-g') + cmd.append(self.group) + + if self.groups is not None: + groups = self.get_groups_set() + cmd.append('-G') + cmd.append(','.join(groups)) + + if self.comment is not None: + cmd.append('-c') + cmd.append(self.comment) + + if self.home is not None: + cmd.append('-d') + cmd.append(self.home) + + if self.shell is not None: + cmd.append('-s') + cmd.append(self.shell) + + if self.login_class is not None: + cmd.append('-L') + cmd.append(self.login_class) + + if self.password is not None: + cmd.append('-p') + cmd.append(self.password) + + if self.createhome: + cmd.append('-m') + + cmd.append(self.name) + return self.execute_command(cmd) + + def remove_user_userdel(self): + cmd = [self.module.get_bin_path('userdel', True)] + if self.remove: + cmd.append('-r') + cmd.append(self.name) + return self.execute_command(cmd) + + def modify_user(self): + cmd = [self.module.get_bin_path('usermod', True)] + info = self.user_info() + + if self.uid is not None and info[2] != int(self.uid): + cmd.append('-u') + cmd.append(self.uid) + + if self.non_unique: + cmd.append('-o') + + if self.group is not None: + if not self.group_exists(self.group): + self.module.fail_json(msg="Group %s does not exist" % self.group) + ginfo = self.group_info(self.group) + if info[3] != ginfo[2]: + cmd.append('-g') + cmd.append(self.group) + + if self.groups is not None: + current_groups = self.user_group_membership() + groups_need_mod = False + groups_option = '-G' + groups = [] + + if self.groups == '': + if current_groups and not self.append: + groups_need_mod = True + else: + groups = self.get_groups_set() + group_diff = set(current_groups).symmetric_difference(groups) + + if group_diff: + if self.append: + for g in groups: + if g in group_diff: + groups_option = '-S' + groups_need_mod = True + break + else: + groups_need_mod = True + + if groups_need_mod: + cmd.append(groups_option) + cmd.append(','.join(groups)) + + if self.comment is not None and info[4] != self.comment: + cmd.append('-c') + cmd.append(self.comment) + + if self.home is not None and info[5] != self.home: + cmd.append('-d') + cmd.append(self.home) + + if self.shell is not None and info[6] != self.shell: + cmd.append('-s') + cmd.append(self.shell) + + if self.login_class is not None: + cmd.append('-L') + cmd.append(self.login_class) + + if self.password is not None and info[1] != self.password: + cmd.append('-p') + cmd.append(self.password) + + # skip if no changes to be made + if len(cmd) == 1: + return (None, '', '') + elif self.module.check_mode: + return (0, '', '') + + cmd.append(self.name) + return self.execute_command(cmd) + + +# =========================================== + +class NetBSDUser(User): + """ + This is a NetBSD User manipulation class. + Main differences are that NetBSD:- + - has no concept of "system" account. + - has no force delete user + + + This overrides the following methods from the generic class:- + - create_user() + - remove_user() + - modify_user() + """ + + platform = 'NetBSD' + distribution = None + SHADOWFILE = '/etc/master.passwd' + + def create_user(self): + cmd = [self.module.get_bin_path('useradd', True)] + + if self.uid is not None: + cmd.append('-u') + cmd.append(self.uid) + + if self.non_unique: + cmd.append('-o') + + if self.group is not None: + if not self.group_exists(self.group): + self.module.fail_json(msg="Group %s does not exist" % self.group) + cmd.append('-g') + cmd.append(self.group) + + if self.groups is not None: + groups = self.get_groups_set() + if len(groups) > 16: + self.module.fail_json(msg="Too many groups (%d) NetBSD allows for 16 max." % len(groups)) + cmd.append('-G') + cmd.append(','.join(groups)) + + if self.comment is not None: + cmd.append('-c') + cmd.append(self.comment) + + if self.home is not None: + cmd.append('-d') + cmd.append(self.home) + + if self.shell is not None: + cmd.append('-s') + cmd.append(self.shell) + + if self.login_class is not None: + cmd.append('-L') + cmd.append(self.login_class) + + if self.password is not None: + cmd.append('-p') + cmd.append(self.password) + + if self.createhome: + cmd.append('-m') + + cmd.append(self.name) + return self.execute_command(cmd) + + def remove_user_userdel(self): + cmd = [self.module.get_bin_path('userdel', True)] + if self.remove: + cmd.append('-r') + cmd.append(self.name) + return self.execute_command(cmd) + + def modify_user(self): + cmd = [self.module.get_bin_path('usermod', True)] + info = self.user_info() + + if self.uid is not None and info[2] != int(self.uid): + cmd.append('-u') + cmd.append(self.uid) + + if self.non_unique: + cmd.append('-o') + + if self.group is not None: + if not self.group_exists(self.group): + self.module.fail_json(msg="Group %s does not exist" % self.group) + ginfo = self.group_info(self.group) + if info[3] != ginfo[2]: + cmd.append('-g') + cmd.append(self.group) + + if self.groups is not None: + current_groups = self.user_group_membership() + groups_need_mod = False + groups = [] + + if self.groups == '': + if current_groups and not self.append: + groups_need_mod = True + else: + groups = self.get_groups_set() + group_diff = set(current_groups).symmetric_difference(groups) + + if group_diff: + if self.append: + for g in groups: + if g in group_diff: + groups = set(current_groups).union(groups) + groups_need_mod = True + break + else: + groups_need_mod = True + + if groups_need_mod: + if len(groups) > 16: + self.module.fail_json(msg="Too many groups (%d) NetBSD allows for 16 max." % len(groups)) + cmd.append('-G') + cmd.append(','.join(groups)) + + if self.comment is not None and info[4] != self.comment: + cmd.append('-c') + cmd.append(self.comment) + + if self.home is not None and info[5] != self.home: + cmd.append('-d') + cmd.append(self.home) + + if self.shell is not None and info[6] != self.shell: + cmd.append('-s') + cmd.append(self.shell) + + if self.login_class is not None: + cmd.append('-L') + cmd.append(self.login_class) + + if self.password is not None and info[1] != self.password: + cmd.append('-p') + cmd.append(self.password) + + # skip if no changes to be made + if len(cmd) == 1: + return (None, '', '') + elif self.module.check_mode: + return (0, '', '') + + cmd.append(self.name) + return self.execute_command(cmd) + + +# =========================================== + class SunOS(User): """ This is a SunOS User manipulation class - The main difference between @@ -1024,6 +1339,7 @@ def main(): home=dict(default=None, type='str'), shell=dict(default=None, type='str'), password=dict(default=None, type='str'), + login_class=dict(default=None, type='str'), # following options are specific to userdel force=dict(default='no', type='bool'), remove=dict(default='no', type='bool'),