arch-image-builder/builder/lib/shadow.py
BigfootACA 067ee2d341 add more comments
Signed-off-by: BigfootACA <bigfoot@classfun.cn>
2024-05-20 09:56:42 +08:00

271 lines
6.0 KiB
Python

import io
from typing import Self
from builder.lib.serializable import SerializableDict, SerializableList
def zero2empty(num: int) -> str:
"""
Convert number to string, when zero return empty
"""
return str(num) if num != 0 else ""
def none2empty(val: str) -> str:
"""
Return a string, when None return empty
"""
return val if val else ""
class UserEntry(SerializableDict):
name: str = None
def from_line(self, line: str): pass
def to_line(self) -> str: pass
class UserFile(SerializableList):
def load_line(self, line: str): pass
def unload(self): pass
def load_str(self, content: str | list[str]) -> Self:
"""
Load whole file as string
"""
if type(content) is str:
content = content.split("\n")
for line in content:
line = line.strip()
if line.startswith("#"): continue
if len(line) <= 0: continue
self.load_line(line)
return self
def load_fp(self, fp: io.TextIOWrapper) -> Self:
self.load_str(fp.readlines())
return self
def load_file(self, file: str) -> Self:
with open(file, "r") as f:
self.load_fp(f)
return self
def from_str(self, content: str) -> Self:
self.unload()
self.load_str(content)
return self
def from_fp(self, fp: io.TextIOWrapper) -> Self:
self.unload()
self.load_fp(fp)
return self
def from_file(self, file: str) -> Self:
self.unload()
self.load_file(file)
return self
class ShadowEntry(UserEntry):
name: str = None
password: str = None
last_change: int = 0
min_age: int = 0
max_age: int = 0
warning_period: int = 0
inactivity_period: int = 0
expiration: int = 0
def from_line(self, line: str):
values = line.split(":")
if len(values) != 8:
raise ValueError("fields mismatch")
self.name = values[0]
self.password = values[1]
self.last_change = int(values[2]) if len(values[2]) > 0 else 0
self.min_age = int(values[3]) if len(values[3]) > 0 else 0
self.max_age = int(values[4]) if len(values[4]) > 0 else 0
self.warning_period = int(values[5]) if len(values[5]) > 0 else 0
self.inactivity_period = int(values[6]) if len(values[6]) > 0 else 0
self.expiration = int(values[7]) if len(values[7]) > 0 else 0
def to_line(self) -> str:
values = [
none2empty(self.name),
none2empty(self.password),
zero2empty(self.last_change),
zero2empty(self.min_age),
zero2empty(self.max_age),
zero2empty(self.warning_period),
zero2empty(self.inactivity_period),
zero2empty(self.expiration),
]
return (":".join(values)) + "\n"
class GshadowEntry(UserEntry):
name: str = None
password: str = None
admins: list[str] = None
members: list[str] = None
@property
def admin(self):
return ",".join(self.admins)
@admin.setter
def admin(self, val: str):
self.admins = val.split(",")
@property
def member(self):
return ",".join(self.members)
@member.setter
def member(self, val: str):
self.members = val.split(",")
def from_line(self, line: str):
values = line.split(":")
if len(values) != 4:
raise ValueError("fields mismatch")
self.name = values[0]
self.password = values[1]
self.admin = values[2]
self.member = values[3]
def to_line(self) -> str:
values = [
none2empty(self.name),
none2empty(self.password),
none2empty(self.admin),
none2empty(self.member),
]
return (":".join(values)) + "\n"
class PasswdEntry(UserEntry):
name: str = None
password: str = None
uid: int = -1
gid: int = -1
comment: str = None
home: str = None
shell: str = None
def from_line(self, line: str):
values = line.split(":")
if len(values) != 7:
raise ValueError("fields mismatch")
self.name = values[0]
self.password = values[1]
self.uid = int(values[2])
self.gid = int(values[3])
self.comment = values[4]
self.home = values[5]
self.shell = values[6]
def to_line(self) -> str:
values = [
none2empty(self.name),
none2empty(self.password),
str(self.uid),
str(self.gid),
none2empty(self.comment),
none2empty(self.home),
none2empty(self.shell),
]
return (":".join(values)) + "\n"
class GroupEntry(UserEntry):
name: str = None
password: str = None
gid: int = -1
users: list[str] = None
@property
def user(self):
return ",".join(self.users)
@user.setter
def user(self, val: str):
self.users = val.split(",")
def from_line(self, line: str):
values = line.split(":")
if len(values) != 4:
raise ValueError("fields mismatch")
self.name = values[0]
self.password = values[1]
self.gid = int(values[2])
self.user = values[3]
def to_line(self) -> str:
values = [
none2empty(self.name),
none2empty(self.password),
str(self.gid),
none2empty(self.user),
]
return (":".join(values)) + "\n"
class ShadowFile(list[ShadowEntry], UserFile):
def unload(self): self.clear()
def load_line(self, line: str):
ent = ShadowEntry()
ent.from_line(line)
self.append(ent)
def lookup_name(self, name: str) -> ShadowEntry:
return next((e for e in self if e.name == name), None)
class GshadowFile(list[GshadowEntry], UserFile):
def unload(self): self.clear()
def load_line(self, line: str):
ent = GshadowEntry()
ent.from_line(line)
self.append(ent)
def lookup_name(self, name: str) -> GshadowEntry:
return next((e for e in self if e.name == name), None)
class PasswdFile(list[PasswdEntry], UserFile):
def unload(self): self.clear()
def load_line(self, line: str):
ent = PasswdEntry()
ent.from_line(line)
self.append(ent)
def lookup_name(self, name: str) -> PasswdEntry:
return next((e for e in self if e.name == name), None)
def lookup_uid(self, uid: int) -> PasswdEntry:
return next((e for e in self if e.uid == uid), None)
def lookup_gid(self, gid: int) -> PasswdEntry:
return next((e for e in self if e.gid == gid), None)
class GroupFile(list[GroupEntry], UserFile):
def unload(self): self.clear()
def load_line(self, line: str):
ent = GroupEntry()
ent.from_line(line)
self.append(ent)
def lookup_name(self, name: str) -> GroupEntry:
return next((e for e in self if e.name == name), None)
def lookup_gid(self, gid: int) -> GroupEntry:
return next((e for e in self if e.gid == gid), None)