[BACKEND] Validate write permissions on working directory and output directory
This commit is contained in:
parent
8d5d2be6a7
commit
a56e1249ec
5 changed files with 56 additions and 0 deletions
|
|
@ -12,6 +12,8 @@ from ytdl_sub.config.defaults import DEFAULT_FFPROBE_PATH
|
||||||
from ytdl_sub.config.defaults import DEFAULT_LOCK_DIRECTORY
|
from ytdl_sub.config.defaults import DEFAULT_LOCK_DIRECTORY
|
||||||
from ytdl_sub.config.defaults import MAX_FILE_NAME_BYTES
|
from ytdl_sub.config.defaults import MAX_FILE_NAME_BYTES
|
||||||
from ytdl_sub.prebuilt_presets import PREBUILT_PRESETS
|
from ytdl_sub.prebuilt_presets import PREBUILT_PRESETS
|
||||||
|
from ytdl_sub.utils.exceptions import SubscriptionPermissionError
|
||||||
|
from ytdl_sub.utils.file_handler import FileHandler
|
||||||
from ytdl_sub.validators.file_path_validators import FFmpegFileValidator
|
from ytdl_sub.validators.file_path_validators import FFmpegFileValidator
|
||||||
from ytdl_sub.validators.file_path_validators import FFprobeFileValidator
|
from ytdl_sub.validators.file_path_validators import FFprobeFileValidator
|
||||||
from ytdl_sub.validators.strict_dict_validator import StrictDictValidator
|
from ytdl_sub.validators.strict_dict_validator import StrictDictValidator
|
||||||
|
|
@ -147,6 +149,12 @@ class ConfigOptions(StrictDictValidator):
|
||||||
key="file_name_max_bytes", validator=IntValidator, default=MAX_FILE_NAME_BYTES
|
key="file_name_max_bytes", validator=IntValidator, default=MAX_FILE_NAME_BYTES
|
||||||
)
|
)
|
||||||
|
|
||||||
|
if not FileHandler.is_path_writable(self.working_directory):
|
||||||
|
raise SubscriptionPermissionError(
|
||||||
|
"ytdl-sub does not have permissions to the working directory: "
|
||||||
|
f"{self.working_directory}"
|
||||||
|
)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def working_directory(self) -> str:
|
def working_directory(self) -> str:
|
||||||
"""
|
"""
|
||||||
|
|
|
||||||
|
|
@ -10,6 +10,8 @@ from ytdl_sub.config.preset_options import OutputOptions
|
||||||
from ytdl_sub.config.preset_options import YTDLOptions
|
from ytdl_sub.config.preset_options import YTDLOptions
|
||||||
from ytdl_sub.downloaders.url.validators import MultiUrlValidator
|
from ytdl_sub.downloaders.url.validators import MultiUrlValidator
|
||||||
from ytdl_sub.entries.variables.override_variables import SubscriptionVariables
|
from ytdl_sub.entries.variables.override_variables import SubscriptionVariables
|
||||||
|
from ytdl_sub.utils.exceptions import SubscriptionPermissionError
|
||||||
|
from ytdl_sub.utils.file_handler import FileHandler
|
||||||
from ytdl_sub.utils.file_handler import FileHandlerTransactionLog
|
from ytdl_sub.utils.file_handler import FileHandlerTransactionLog
|
||||||
from ytdl_sub.utils.logger import Logger
|
from ytdl_sub.utils.logger import Logger
|
||||||
from ytdl_sub.ytdl_additions.enhanced_download_archive import EnhancedDownloadArchive
|
from ytdl_sub.ytdl_additions.enhanced_download_archive import EnhancedDownloadArchive
|
||||||
|
|
@ -94,6 +96,12 @@ class BaseSubscription(ABC):
|
||||||
|
|
||||||
self._exception: Optional[Exception] = None
|
self._exception: Optional[Exception] = None
|
||||||
|
|
||||||
|
if not FileHandler.is_path_writable(self.output_directory):
|
||||||
|
raise SubscriptionPermissionError(
|
||||||
|
"ytdl-sub does not have write permissions to the output directory: "
|
||||||
|
f"{self.output_directory}"
|
||||||
|
)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def download_archive(self) -> EnhancedDownloadArchive:
|
def download_archive(self) -> EnhancedDownloadArchive:
|
||||||
"""
|
"""
|
||||||
|
|
|
||||||
|
|
@ -40,3 +40,7 @@ class FileNotDownloadedException(ValueError):
|
||||||
|
|
||||||
class ExperimentalFeatureNotEnabled(ValidationException):
|
class ExperimentalFeatureNotEnabled(ValidationException):
|
||||||
"""Feature is not enabled for usage"""
|
"""Feature is not enabled for usage"""
|
||||||
|
|
||||||
|
|
||||||
|
class SubscriptionPermissionError(ValidationException):
|
||||||
|
"""Early-caught permission error"""
|
||||||
|
|
|
||||||
|
|
@ -379,6 +379,22 @@ class FileHandler:
|
||||||
"""
|
"""
|
||||||
return self._file_handler_transaction_log
|
return self._file_handler_transaction_log
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def is_path_writable(cls, src_file_path: Union[str, Path]) -> bool:
|
||||||
|
""""
|
||||||
|
Check whether a path is writable. If it does not exist, try to find the base directory
|
||||||
|
and check permissions on that.
|
||||||
|
"""
|
||||||
|
path = os.path.abspath(src_file_path)
|
||||||
|
|
||||||
|
while not os.path.exists(path):
|
||||||
|
new_path = os.path.dirname(path)
|
||||||
|
if new_path == path: # reached root
|
||||||
|
break
|
||||||
|
path = new_path
|
||||||
|
|
||||||
|
return os.access(path, os.W_OK)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def copy(cls, src_file_path: Union[str, Path], dst_file_path: Union[str, Path]):
|
def copy(cls, src_file_path: Union[str, Path], dst_file_path: Union[str, Path]):
|
||||||
"""
|
"""
|
||||||
|
|
|
||||||
20
tests/unit/utils/test_file_handler.py
Normal file
20
tests/unit/utils/test_file_handler.py
Normal file
|
|
@ -0,0 +1,20 @@
|
||||||
|
import pytest
|
||||||
|
|
||||||
|
from ytdl_sub.utils.file_handler import FileHandler
|
||||||
|
from ytdl_sub.utils.system import IS_WINDOWS
|
||||||
|
|
||||||
|
|
||||||
|
class TestFileHandler:
|
||||||
|
|
||||||
|
def test_directory_exists(self):
|
||||||
|
if IS_WINDOWS:
|
||||||
|
return
|
||||||
|
|
||||||
|
assert FileHandler.is_path_writable("/tmp")
|
||||||
|
assert FileHandler.is_path_writable("/tmp/")
|
||||||
|
assert FileHandler.is_path_writable("/tmp/non-existent")
|
||||||
|
assert FileHandler.is_path_writable("/tmp/non-existent/")
|
||||||
|
assert FileHandler.is_path_writable("/tmp/non-existent/nested")
|
||||||
|
|
||||||
|
assert not FileHandler.is_path_writable("/lol-in-root")
|
||||||
|
assert not FileHandler.is_path_writable("/")
|
||||||
Loading…
Reference in a new issue