~ack/landscape-client/sources.list-preserve-old-permissions

« back to all changes in this revision

Viewing changes to landscape/user/provider.py

  • Committer: Christopher Armstrong
  • Date: 2008-06-10 10:56:01 UTC
  • Revision ID: radix@twistedmatrix.com-20080610105601-l9qfvqjf88e7j8b6
Import landscape-client into public branch

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
from pwd import struct_passwd
 
2
from grp import struct_group
 
3
import csv
 
4
import subprocess
 
5
import logging
 
6
 
 
7
 
 
8
class UserManagementError(Exception):
 
9
    """Catch all error for problems with User Management."""
 
10
 
 
11
 
 
12
class UserNotFoundError(Exception):
 
13
    """Raised when a user couldn't be found by uid/username."""
 
14
 
 
15
 
 
16
class GroupNotFoundError(Exception):
 
17
    """Raised when a group couldn't be found by gid/groupname."""
 
18
 
 
19
 
 
20
class UserProviderBase(object):
 
21
    """This is a base class for user Providers."""
 
22
 
 
23
    def __init__(self, locked_users=None):
 
24
        self.locked_users = locked_users or []
 
25
 
 
26
        self._min_uid = 1000
 
27
        self._max_uid = 60000
 
28
 
 
29
    def get_users(self):
 
30
        """Returns a list of all local users on the computer.
 
31
 
 
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
 
34
        C{home-phone}.  
 
35
        """
 
36
        users = []
 
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:
 
42
                continue
 
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)
 
55
        return users
 
56
 
 
57
    def get_groups(self):
 
58
        """Returns a list of groups on the computer.
 
59
 
 
60
        Each group is represented as a dict with the keys: C{name},
 
61
        C{gid} and C{members}.
 
62
        """
 
63
        user_names = set([x["username"] for x in self.get_users()])
 
64
        groups = []
 
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:
 
70
                continue
 
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)
 
75
        return groups
 
76
 
 
77
    def get_uid(self, username):
 
78
        """Returns the UID for C{username}.
 
79
 
 
80
        @raises UserNotFoundError: Raised if C{username} doesn't match a
 
81
            user on the computer.
 
82
        """
 
83
        for data in self.get_users():
 
84
            if data["username"] == username:
 
85
                return data["uid"]
 
86
        raise UserNotFoundError("UID not found for user %s." % username)
 
87
 
 
88
    def get_gid(self, groupname):
 
89
        """Returns the GID for C{groupname}.
 
90
 
 
91
        @raises UserManagementError: Raised if C{groupname} doesn't
 
92
            match a group on the computer.
 
93
        """
 
94
        for data in self.get_groups():
 
95
            if data["name"] == groupname:
 
96
                return data["gid"]
 
97
        raise GroupNotFoundError("Group not found for group %s." % groupname)
 
98
 
 
99
class UserProvider(UserProviderBase):
 
100
 
 
101
    popen = subprocess.Popen
 
102
 
 
103
    passwd_fields = ["username", "passwd", "uid", "primary-gid", "gecos",
 
104
                     "home", "shell"]
 
105
    group_fields = ["name", "passwd", "gid", "members"]
 
106
 
 
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
 
112
 
 
113
    def get_user_data(self):
 
114
        """
 
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)
 
118
        """
 
119
        user_data = []
 
120
        passwd_file = open(self._passwd_file, "r")
 
121
        reader = csv.DictReader(passwd_file, fieldnames=self.passwd_fields,
 
122
                                dialect="unixpwd")
 
123
        current_line = 0
 
124
        for row in reader:
 
125
            current_line += 1
 
126
            # This skips the NIS user marker in the passwd file.
 
127
            if (row["username"].startswith("+") or
 
128
                row["username"].startswith("-")):
 
129
                continue
 
130
            try:
 
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):
 
135
 
 
136
                logging.warn("passwd file %s is incorrectly formatted: "
 
137
                             "line %d." % (self._passwd_file, current_line))
 
138
 
 
139
        passwd_file.close()
 
140
        return user_data
 
141
 
 
142
    def get_group_data(self):
 
143
        """
 
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
 
146
        usernames).
 
147
        """
 
148
        group_data = []
 
149
        group_file = open(self._group_file, "r")
 
150
        reader = csv.DictReader(group_file, fieldnames=self.group_fields,
 
151
                                dialect="unixpwd")
 
152
        current_line = 0
 
153
        for row in reader:
 
154
            current_line += 1
 
155
            # Skip if we find the NIS marker
 
156
            if (row["name"].startswith("+") or
 
157
                row["name"].startswith("-")):
 
158
                continue
 
159
            try:
 
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))
 
165
        group_file.close()
 
166
        return group_data
 
167
 
 
168
csv.register_dialect("unixpwd", delimiter=":", quoting=csv.QUOTE_NONE)