88 lines
3.8 KiB
Python
88 lines
3.8 KiB
Python
from pathlib import Path
|
|
import shutil
|
|
from typing import List, Optional
|
|
|
|
from pydantic import Field, field_validator
|
|
from pydantic_settings import BaseSettings, SettingsConfigDict
|
|
|
|
|
|
class Settings(BaseSettings):
|
|
model_config = SettingsConfigDict(env_file=".env", extra="ignore")
|
|
|
|
token: str = Field(alias="TOKEN")
|
|
server_name: str = Field(default="user-manage-api", alias="SERVER_NAME")
|
|
home_base_dir: str = Field(default="/home", alias="HOME_BASE_DIR")
|
|
link_home_dir: str = Field(default="", alias="LINK_HOME_DIR")
|
|
whitelist_users: str = Field(default="", alias="WHITELIST_USERS")
|
|
whitelist_groups: str = Field(default="", alias="WHITELIST_GROUPS")
|
|
locked_users: str = Field(default="", alias="LOCKED_USERS")
|
|
hidden_users: str = Field(default="", alias="HIDDEN_USERS")
|
|
hidden_groups: str = Field(default="", alias="HIDDEN_GROUPS")
|
|
user_uid_min: Optional[int] = Field(default=None, alias="USER_UID_MIN")
|
|
user_uid_max: Optional[int] = Field(default=None, alias="USER_UID_MAX")
|
|
group_gid_min: Optional[int] = Field(default=None, alias="GROUP_GID_MIN")
|
|
group_gid_max: Optional[int] = Field(default=None, alias="GROUP_GID_MAX")
|
|
use_libuser: bool = Field(default=False, alias="USE_LIBUSER")
|
|
log_level: str = Field(default="INFO", alias="LOG_LEVEL")
|
|
log_path: str = Field(default="./logs/user_manage_api.log", alias="LOG_PATH")
|
|
sudo_path: str = Field(default="/usr/bin/sudo", alias="SUDO_PATH")
|
|
command_timeout_seconds: int = 10
|
|
|
|
@field_validator("user_uid_min", "user_uid_max", "group_gid_min", "group_gid_max", mode="before")
|
|
@classmethod
|
|
def empty_string_to_none(cls, value: object) -> object:
|
|
if value == "":
|
|
return None
|
|
|
|
return value
|
|
|
|
@property
|
|
def whitelist_user_list(self) -> List[str]:
|
|
return self._parse_comma_separated_list(self.whitelist_users)
|
|
|
|
@property
|
|
def whitelist_group_list(self) -> List[str]:
|
|
return self._parse_comma_separated_list(self.whitelist_groups)
|
|
|
|
@property
|
|
def locked_user_list(self) -> List[str]:
|
|
return self._parse_comma_separated_list(self.locked_users)
|
|
|
|
@property
|
|
def hidden_user_list(self) -> List[str]:
|
|
return self._parse_comma_separated_list(self.hidden_users)
|
|
|
|
@property
|
|
def hidden_group_list(self) -> List[str]:
|
|
return self._parse_comma_separated_list(self.hidden_groups)
|
|
|
|
def _parse_comma_separated_list(self, value: str) -> List[str]:
|
|
return [item.strip() for item in value.split(",") if item.strip()]
|
|
|
|
|
|
def validate_settings(settings: Settings) -> None:
|
|
if not settings.token.strip():
|
|
raise ValueError("TOKEN is required and cannot be empty.")
|
|
|
|
log_parent = Path(settings.log_path).parent
|
|
log_parent.mkdir(parents=True, exist_ok=True)
|
|
if not log_parent.exists() or not log_parent.is_dir():
|
|
raise ValueError(f"LOG_PATH parent is invalid: {log_parent}")
|
|
|
|
if shutil.which(settings.sudo_path) is None and not Path(settings.sudo_path).exists():
|
|
raise ValueError(f"SUDO_PATH is not executable: {settings.sudo_path}")
|
|
|
|
if settings.user_uid_min is not None and settings.user_uid_max is not None and settings.user_uid_min > settings.user_uid_max:
|
|
raise ValueError("USER_UID_MIN cannot be greater than USER_UID_MAX.")
|
|
|
|
if settings.group_gid_min is not None and settings.group_gid_max is not None and settings.group_gid_min > settings.group_gid_max:
|
|
raise ValueError("GROUP_GID_MIN cannot be greater than GROUP_GID_MAX.")
|
|
|
|
required_commands = ["useradd", "usermod", "userdel", "groupadd", "groupdel", "chpasswd", "id", "getent"]
|
|
if settings.link_home_dir.strip():
|
|
required_commands.extend(["mkdir", "ln", "chown", "unlink"])
|
|
|
|
for command in required_commands:
|
|
if shutil.which(command) is None:
|
|
raise ValueError(f"Required command not found in PATH: {command}")
|