1# -*- test-case-name: twisted.python.test.test_fakepwd -*- 2# Copyright (c) Twisted Matrix Laboratories. 3# See LICENSE for details. 4 5""" 6L{twisted.python.fakepwd} provides a fake implementation of the L{pwd} API. 7""" 8 9from typing import List 10 11__all__ = ["UserDatabase", "ShadowDatabase"] 12 13 14class _UserRecord: 15 """ 16 L{_UserRecord} holds the user data for a single user in L{UserDatabase}. 17 It corresponds to the C{passwd} structure from the L{pwd} module. 18 See that module for attribute documentation. 19 """ 20 21 def __init__( 22 self, 23 name: str, 24 password: str, 25 uid: int, 26 gid: int, 27 gecos: str, 28 home: str, 29 shell: str, 30 ) -> None: 31 self.pw_name = name 32 self.pw_passwd = password 33 self.pw_uid = uid 34 self.pw_gid = gid 35 self.pw_gecos = gecos 36 self.pw_dir = home 37 self.pw_shell = shell 38 39 def __len__(self) -> int: 40 return 7 41 42 def __getitem__(self, index): 43 return ( 44 self.pw_name, 45 self.pw_passwd, 46 self.pw_uid, 47 self.pw_gid, 48 self.pw_gecos, 49 self.pw_dir, 50 self.pw_shell, 51 )[index] 52 53 54class UserDatabase: 55 """ 56 L{UserDatabase} holds a traditional POSIX user data in memory and makes it 57 available via the same API as L{pwd}. 58 59 @ivar _users: A C{list} of L{_UserRecord} instances holding all user data 60 added to this database. 61 """ 62 63 _users: List[_UserRecord] 64 65 def __init__(self) -> None: 66 self._users = [] 67 68 def addUser( 69 self, 70 username: str, 71 password: str, 72 uid: int, 73 gid: int, 74 gecos: str, 75 home: str, 76 shell: str, 77 ) -> None: 78 """ 79 Add a new user record to this database. 80 81 @param username: The value for the C{pw_name} field of the user 82 record to add. 83 84 @param password: The value for the C{pw_passwd} field of the user 85 record to add. 86 87 @param uid: The value for the C{pw_uid} field of the user record to 88 add. 89 90 @param gid: The value for the C{pw_gid} field of the user record to 91 add. 92 93 @param gecos: The value for the C{pw_gecos} field of the user record 94 to add. 95 96 @param home: The value for the C{pw_dir} field of the user record to 97 add. 98 99 @param shell: The value for the C{pw_shell} field of the user record to 100 add. 101 """ 102 self._users.append( 103 _UserRecord(username, password, uid, gid, gecos, home, shell) 104 ) 105 106 def getpwuid(self, uid: int) -> _UserRecord: 107 """ 108 Return the user record corresponding to the given uid. 109 """ 110 for entry in self._users: 111 if entry.pw_uid == uid: 112 return entry 113 raise KeyError() 114 115 def getpwnam(self, name: str) -> _UserRecord: 116 """ 117 Return the user record corresponding to the given username. 118 """ 119 if not isinstance(name, str): 120 raise TypeError(f"getpwuam() argument must be str, not {type(name)}") 121 for entry in self._users: 122 if entry.pw_name == name: 123 return entry 124 raise KeyError() 125 126 def getpwall(self) -> List[_UserRecord]: 127 """ 128 Return a list of all user records. 129 """ 130 return self._users 131 132 133class _ShadowRecord: 134 """ 135 L{_ShadowRecord} holds the shadow user data for a single user in 136 L{ShadowDatabase}. It corresponds to C{spwd.struct_spwd}. See that class 137 for attribute documentation. 138 """ 139 140 def __init__( 141 self, 142 username: str, 143 password: str, 144 lastChange: int, 145 min: int, 146 max: int, 147 warn: int, 148 inact: int, 149 expire: int, 150 flag: int, 151 ) -> None: 152 self.sp_nam = username 153 self.sp_pwd = password 154 self.sp_lstchg = lastChange 155 self.sp_min = min 156 self.sp_max = max 157 self.sp_warn = warn 158 self.sp_inact = inact 159 self.sp_expire = expire 160 self.sp_flag = flag 161 162 def __len__(self) -> int: 163 return 9 164 165 def __getitem__(self, index): 166 return ( 167 self.sp_nam, 168 self.sp_pwd, 169 self.sp_lstchg, 170 self.sp_min, 171 self.sp_max, 172 self.sp_warn, 173 self.sp_inact, 174 self.sp_expire, 175 self.sp_flag, 176 )[index] 177 178 179class ShadowDatabase: 180 """ 181 L{ShadowDatabase} holds a shadow user database in memory and makes it 182 available via the same API as C{spwd}. 183 184 @ivar _users: A C{list} of L{_ShadowRecord} instances holding all user data 185 added to this database. 186 187 @since: 12.0 188 """ 189 190 _users: List[_ShadowRecord] 191 192 def __init__(self) -> None: 193 self._users = [] 194 195 def addUser( 196 self, 197 username: str, 198 password: str, 199 lastChange: int, 200 min: int, 201 max: int, 202 warn: int, 203 inact: int, 204 expire: int, 205 flag: int, 206 ) -> None: 207 """ 208 Add a new user record to this database. 209 210 @param username: The value for the C{sp_nam} field of the user record to 211 add. 212 213 @param password: The value for the C{sp_pwd} field of the user record to 214 add. 215 216 @param lastChange: The value for the C{sp_lstchg} field of the user 217 record to add. 218 219 @param min: The value for the C{sp_min} field of the user record to add. 220 221 @param max: The value for the C{sp_max} field of the user record to add. 222 223 @param warn: The value for the C{sp_warn} field of the user record to 224 add. 225 226 @param inact: The value for the C{sp_inact} field of the user record to 227 add. 228 229 @param expire: The value for the C{sp_expire} field of the user record 230 to add. 231 232 @param flag: The value for the C{sp_flag} field of the user record to 233 add. 234 """ 235 self._users.append( 236 _ShadowRecord( 237 username, password, lastChange, min, max, warn, inact, expire, flag 238 ) 239 ) 240 241 def getspnam(self, username: str) -> _ShadowRecord: 242 """ 243 Return the shadow user record corresponding to the given username. 244 """ 245 if not isinstance(username, str): 246 raise TypeError(f"getspnam() argument must be str, not {type(username)}") 247 for entry in self._users: 248 if entry.sp_nam == username: 249 return entry 250 raise KeyError(username) 251 252 def getspall(self): 253 """ 254 Return a list of all shadow user records. 255 """ 256 return self._users 257