1
from pwd import struct_passwd
2
from grp import struct_group
8
class UserManagementError(Exception):
9
"""Catch all error for problems with User Management."""
12
class UserNotFoundError(Exception):
13
"""Raised when a user couldn't be found by uid/username."""
16
class GroupNotFoundError(Exception):
17
"""Raised when a group couldn't be found by gid/groupname."""
20
class UserProviderBase(object):
21
"""This is a base class for user Providers."""
23
def __init__(self, locked_users=None):
24
self.locked_users = locked_users or []
30
"""Returns a list of all local users on the computer.
32
Each user is represented as a dict with the keys: C{username},
33
C{name}, C{uid}, C{enabled}, C{location}, C{work-phone} and
37
found_usernames = set()
38
for user in self.get_user_data():
39
if not isinstance(user, struct_passwd):
40
user = struct_passwd(user)
41
if user.pw_name in found_usernames:
43
gecos_data = [x.decode("utf-8", "replace") or None
44
for x in user.pw_gecos.split(",")[:4]]
45
while len(gecos_data) < 4:
46
gecos_data.append(None)
47
name, location, work_phone, home_phone = tuple(gecos_data)
48
enabled = user.pw_name not in self.locked_users
49
users.append({"username": user.pw_name, "name": name,
50
"uid": user.pw_uid, "enabled": enabled,
51
"location": location, "work-phone": work_phone,
52
"home-phone": home_phone,
53
"primary-gid": user.pw_gid})
54
found_usernames.add(user.pw_name)
58
"""Returns a list of groups on the computer.
60
Each group is represented as a dict with the keys: C{name},
61
C{gid} and C{members}.
63
user_names = set([x["username"] for x in self.get_users()])
65
found_groupnames = set()
66
for group in self.get_group_data():
67
if not isinstance(group, struct_group):
68
group = struct_group(group)
69
if group.gr_name in found_groupnames:
71
member_names = user_names.intersection(group.gr_mem)
72
groups.append({"name": group.gr_name, "gid": group.gr_gid,
73
"members": list(member_names)})
74
found_groupnames.add(group.gr_name)
77
def get_uid(self, username):
78
"""Returns the UID for C{username}.
80
@raises UserNotFoundError: Raised if C{username} doesn't match a
83
for data in self.get_users():
84
if data["username"] == username:
86
raise UserNotFoundError("UID not found for user %s." % username)
88
def get_gid(self, groupname):
89
"""Returns the GID for C{groupname}.
91
@raises UserManagementError: Raised if C{groupname} doesn't
92
match a group on the computer.
94
for data in self.get_groups():
95
if data["name"] == groupname:
97
raise GroupNotFoundError("Group not found for group %s." % groupname)
99
class UserProvider(UserProviderBase):
101
popen = subprocess.Popen
103
passwd_fields = ["username", "passwd", "uid", "primary-gid", "gecos",
105
group_fields = ["name", "passwd", "gid", "members"]
107
def __init__(self, locked_users=[], passwd_file="/etc/passwd",
108
group_file="/etc/group"):
109
super(UserProvider, self).__init__(locked_users)
110
self._passwd_file = passwd_file
111
self._group_file = group_file
113
def get_user_data(self):
115
Parse passwd(5) formatted files and return tuples of user data in the
116
form (username, password, uid, primary-group-id, gecos data, home
117
directory, path to the user's shell)
120
passwd_file = open(self._passwd_file, "r")
121
reader = csv.DictReader(passwd_file, fieldnames=self.passwd_fields,
126
# This skips the NIS user marker in the passwd file.
127
if (row["username"].startswith("+") or
128
row["username"].startswith("-")):
131
user_data.append((row["username"], row["passwd"],
132
int(row["uid"]), int(row["primary-gid"]),
133
row["gecos"], row["home"], row["shell"]))
134
except (ValueError, TypeError):
136
logging.warn("passwd file %s is incorrectly formatted: "
137
"line %d." % (self._passwd_file, current_line))
142
def get_group_data(self):
144
Parse group(5) formatted files and return tuples of group data in the
145
form (groupname, group password, group id and a list of member
149
group_file = open(self._group_file, "r")
150
reader = csv.DictReader(group_file, fieldnames=self.group_fields,
155
# Skip if we find the NIS marker
156
if (row["name"].startswith("+") or
157
row["name"].startswith("-")):
160
group_data.append((row["name"], row["passwd"], int(row["gid"]),
161
row["members"].split(",")))
162
except (AttributeError, ValueError):
163
logging.warn("group file %s is incorrectly formatted: "
164
"line %d." % (self._group_file, current_line))
168
csv.register_dialect("unixpwd", delimiter=":", quoting=csv.QUOTE_NONE)