[master 2/5] Allow the kickstart --groups list to specify GIDs.
dashea
installerbot-noreply at redhat.com
Wed Sep 30 20:20:11 UTC 2015
From: David Shea <dshea at redhat.com>
This accomplishes several things:
- All of the information that can be set in the user scope can now be
encoded in the kickstart.
- We can tell the difference between groups created by the kickstart
group command, where existing groups should be rejected, and groups
requested by the user --groups list, where existing groups should be
accepted.
- This removes the need for a lot of more terrible code in the user
spoke, since the spoke can communicate with the advanced dialog with
strings instead of specially crafted user objects. This effectively
fixes #1226564.
---
pyanaconda/ui/gui/spokes/user.glade | 1 +
pyanaconda/ui/gui/spokes/user.py | 86 +++++++------------------------------
pyanaconda/users.py | 41 +++++++++++++-----
3 files changed, 47 insertions(+), 81 deletions(-)
diff --git a/pyanaconda/ui/gui/spokes/user.glade b/pyanaconda/ui/gui/spokes/user.glade
index 335c3c7..e15c966 100644
--- a/pyanaconda/ui/gui/spokes/user.glade
+++ b/pyanaconda/ui/gui/spokes/user.glade
@@ -274,6 +274,7 @@
<property name="use_underline">True</property>
<property name="xalign">0</property>
<property name="draw_indicator">True</property>
+ <signal name="toggled" handler="on_admin_toggled" swapped="no"/>
</object>
<packing>
<property name="left_attach">1</property>
diff --git a/pyanaconda/ui/gui/spokes/user.py b/pyanaconda/ui/gui/spokes/user.py
index 4777da9..e21cc81 100644
--- a/pyanaconda/ui/gui/spokes/user.py
+++ b/pyanaconda/ui/gui/spokes/user.py
@@ -66,14 +66,13 @@ def _validateGroups(self, inputcheck):
return InputCheck.CHECK_OK
- def __init__(self, user, groupDict, data):
+ def __init__(self, user, data):
GUIObject.__init__(self, data)
self._saveButton = self.builder.get_object("save_button")
GUIDialogInputCheckHandler.__init__(self, self._saveButton)
self._user = user
- self._groupDict = groupDict
# Track whether the user has requested a home directory other
# than the default.
@@ -110,23 +109,6 @@ def _apply_checkboxes(self, _editable=None, data=None):
self._spinUid.set_sensitive(c_uid)
self._spinGid.set_sensitive(c_gid)
- def _parse_groups(self):
- group_strings = self._tGroups.get_text().split(",")
- group_objects = []
-
- for group in group_strings:
- # Skip empty strings
- if not group:
- continue
-
- (group_name, group_id) = GROUPLIST_FANCY_PARSE.match(group).groups()
- if group_id:
- group_id = int(group_id)
-
- group_objects.append(self.data.GroupData(name=group_name, gid=group_id))
-
- return group_objects
-
def refresh(self):
if self._user.homedir:
homedir = self._user.homedir
@@ -143,18 +125,7 @@ def refresh(self):
self._spinUid.update()
self._spinGid.update()
- groups = []
- for group_name in self._user.groups:
- group = self._groupDict[group_name]
-
- if group.name and group.gid is not None:
- groups.append("%s (%d)" % (group.name, group.gid))
- elif group.name:
- groups.append(group.name)
- elif group.gid is not None:
- groups.append("(%d)" % (group.gid,))
-
- self._tGroups.set_text(", ".join(groups))
+ self._tGroups.set_text(", ".join(self._user.groups))
def run(self):
self.window.show()
@@ -185,12 +156,7 @@ def run(self):
else:
self._user.gid = None
- groups = self._parse_groups()
- self._user.groups = []
- self._groupDict.clear()
- for group in groups:
- self._groupDict[group.name] = group
- self._user.groups.append(group.name)
+ self._user.groups = [g.strip() for g in self._tGroups.get_text().split(",")]
break
# Input checks fail, try again
else:
@@ -268,8 +234,6 @@ def initialize(self):
self._user = self.data.user.userList[0]
else:
self._user = self.data.UserData()
- self._wheel = self.data.GroupData(name="wheel")
- self._groupDict = {"wheel": self._wheel}
# placeholders for the text boxes
self.fullname = self.builder.get_object("t_fullname")
@@ -351,8 +315,7 @@ def initialize(self):
self.add_re_check(self.fullname, GECOS_VALID, _("Full name cannot contain colon characters"))
- self._advanced = AdvancedUserDialog(self._user, self._groupDict,
- self.data)
+ self._advanced = AdvancedUserDialog(self._user, self.data)
self._advanced.initialize()
def refresh(self):
@@ -362,7 +325,7 @@ def refresh(self):
self.username.set_text(self._user.name)
self.fullname.set_text(self._user.gecos)
- self.admin.set_active(self._wheel.name in self._user.groups)
+ self.admin.set_active("wheel" in self._user.groups)
self.pw.emit("changed")
self.confirm.emit("changed")
@@ -381,7 +344,7 @@ def refresh(self):
def status(self):
if len(self.data.user.userList) == 0:
return _("No user will be created")
- elif self._wheel.name in self.data.user.userList[0].groups:
+ elif "wheel" in self.data.user.userList[0].groups:
return _("Administrator %s will be created") % self.data.user.userList[0].name
else:
return _("User %s will be created") % self.data.user.userList[0].name
@@ -415,28 +378,8 @@ def apply(self):
self._user.name = self.username.get_text()
self._user.gecos = self.fullname.get_text()
- # Remove any groups that were created in a previous visit to this spoke
- self.data.group.groupList = [g for g in self.data.group.groupList \
- if not hasattr(g, 'anaconda_group')]
-
# the user will be created only if the username is set
if self._user.name:
- if self.admin.get_active() and \
- self._wheel.name not in self._user.groups:
- self._user.groups.append(self._wheel.name)
- elif not self.admin.get_active() and \
- self._wheel.name in self._user.groups:
- self._user.groups.remove(self._wheel.name)
-
- anaconda_groups = [self._groupDict[g] for g in self._user.groups
- if g != self._wheel.name]
-
- self.data.group.groupList += anaconda_groups
-
- # Flag the groups as being created in this spoke
- for g in anaconda_groups:
- g.anaconda_group = True
-
if self._user not in self.data.user.userList:
self.data.user.userList.append(self._user)
@@ -530,6 +473,14 @@ def full_name_changed(self, editable=None, data=None):
self.username.set_text(username)
self.guesser[self.username] = True
+ def on_admin_toggled(self, togglebutton, data=None):
+ # Add or remove "wheel" from the grouplist on changes to the admin checkbox
+ if togglebutton.get_active():
+ if "wheel" not in self._user.groups:
+ self._user.groups.append("wheel")
+ elif "wheel" in self._user.groups:
+ self._user.groups.delete("wheel")
+
def _checkPasswordEmpty(self, inputcheck):
"""Check whether a password has been specified at all.
@@ -637,18 +588,11 @@ def on_advanced_clicked(self, _button, data=None):
self._user.name = self.username.get_text()
- if self.admin.get_active() and \
- self._wheel.name not in self._user.groups:
- self._user.groups.append(self._wheel.name)
- elif not self.admin.get_active() and \
- self._wheel.name in self._user.groups:
- self._user.groups.remove(self._wheel.name)
-
self._advanced.refresh()
with self.main_window.enlightbox(self._advanced.window):
self._advanced.run()
- self.admin.set_active(self._wheel.name in self._user.groups)
+ self.admin.set_active("wheel" in self._user.groups)
def on_back_clicked(self, button):
# If the failed check is for non-ASCII characters,
diff --git a/pyanaconda/users.py b/pyanaconda/users.py
index b7e9273..49820e20 100644
--- a/pyanaconda/users.py
+++ b/pyanaconda/users.py
@@ -33,6 +33,7 @@
from pyanaconda.iutil import open # pylint: disable=redefined-builtin
from pyanaconda.constants import PASSWORD_MIN_LEN
from pyanaconda.errors import errorHandler, PasswordCryptError, ERROR_RAISE
+from pyanaconda.regexes import GROUPLIST_FANCY_PARSE
import logging
log = logging.getLogger("anaconda")
@@ -167,14 +168,18 @@ def _getpwnam(self, user_name, root):
return None
- def _groupExists(self, group_name, root):
- """Returns whether a group with the given name already exists."""
+ def _getgrnam(self, group_name, root):
+ """Like grp.getgrnam, but able to use a different root.
+
+ Just returns the grp structure as a list, same reason as above.
+ """
with open(root + "/etc/group", "r") as f:
for line in f:
- if line.split(":")[0] == group_name:
- return True
+ fields = line.split(":")
+ if fields[0] == group_name:
+ return fields
- return False
+ return None
@contextmanager
def _ensureLoginDefs(self, root):
@@ -207,7 +212,7 @@ def createGroup(self, group_name, **kwargs):
"""
root = kwargs.get("root", iutil.getSysroot())
- if self._groupExists(group_name, root):
+ if self._getgrnam(group_name, root):
raise ValueError("Group %s already exists" % group_name)
args = ["-R", root]
@@ -232,8 +237,9 @@ def createUser(self, user_name, *args, **kwargs):
If none is given, the cryptPassword default is used.
:keyword str gecos: The GECOS information (full name, office, phone, etc.).
Defaults to "".
- :keyword groups: A list of existing group names the user should be
- added to. Defaults to [].
+ :keyword groups: A list of group names the user should be added to.
+ Each group name can contain an optional GID in parenthesis,
+ such as "groupName(5000)". Defaults to [].
:type groups: list of str
:keyword str homedir: The home directory for the new user. Defaults to
/home/<name>.
@@ -271,8 +277,23 @@ def createUser(self, user_name, *args, **kwargs):
else:
args.append('-U')
- if kwargs.get("groups"):
- args.extend(['-G', ",".join(kwargs["groups"])])
+ # If any requested groups do not exist, create them.
+ group_list = []
+ for group in kwargs.get("groups", []):
+ group_name, gid = GROUPLIST_FANCY_PARSE.match(group).groups()
+ existing_group = self._getgrnam(group_name, root)
+
+ # Check for a bad GID request
+ if gid and existing_group and gid != existing_group[2]:
+ raise ValueError("Group %s already exists with GID %s" % (group_name, gid))
+
+ # Otherwise, create the group if it does not already exist
+ if not existing_group:
+ self.createGroup(group_name, gid=gid, root=root)
+ group_list.append(group_name)
+
+ if group_list:
+ args.extend(['-G', ",".join(group_list)])
if kwargs.get("homedir"):
homedir = kwargs["homedir"]
--
To view this commit on github, visit https://github.com/rhinstaller/anaconda/commit/31770d7dd369a4c553024d2c6f7b7747b219e047
More information about the anaconda-patches
mailing list