Source code for fcust.fcust
"""
Main module.
"""
from pathlib import PosixPath, Path
from shutil import chown
from pwd import getpwuid
from os import getuid
import logging
[docs]def create_logger(cgroup: str, lpath: str = "/tmp/fcust/"):
"""
Function that creates a modified logger to cater fcust functionality.
:param cgroup: group ownership of the main directory
"""
# create logger
logger = logging.getLogger("fcust")
logger.setLevel(logging.DEBUG)
# Create logging path in /tmp
# TODO: make filename depend on day/time? and tempfile pkg?
logpath = Path(lpath)
logpath.mkdir(exist_ok=True)
# getting user name https://stackoverflow.com/a/2899055
cuser = getpwuid(getuid()).pw_name
# Make sure folder is accessible by other common users:
if logpath.group() != cgroup and logpath.owner() == cuser:
chown(logpath, group=cgroup)
perms: str = oct(logpath.stat().st_mode)[-4:]
if perms != "2775" and logpath.owner() == cuser:
logpath.chmod(0o42775)
logpath = logpath.joinpath(cuser + ".log")
# Create handlers
sh = logging.StreamHandler()
fh = logging.FileHandler(str(logpath), mode="w")
sh.setLevel(logging.DEBUG)
fh.setLevel(logging.DEBUG)
# Create formatters and add it to handlers
format1 = logging.Formatter("%(name)s - %(levelname)s - %(message)s")
format2 = logging.Formatter("%(asctime)s - %(name)s - %(levelname)s - %(message)s")
sh.setFormatter(format1)
fh.setFormatter(format2)
# Add handlers to the logger
logger.addHandler(sh)
logger.addHandler(fh)
return logger
[docs]class CommonFolder:
"""
Main class regarding management of a folder that is commonly used across many users.
- The class creates a logfile at /tmp/fcust/$USER.log
- By default the class assumes that the root of the common folder is configured
correctly and then tries to enforce appropriate permissions.
:param folder_path: Path where the common folder is located.
:param common_group: Group name regarding the common folder.
If not passed the existing group of the folder will be assumed to be the proper folder.
"""
def __init__(self, folder_path: PosixPath, common_group: str = ""):
"""
Initialize CommonFolder class.
"""
if not isinstance(folder_path, PosixPath):
raise TypeError(f"Expected PosixPath object instead of {type(folder_path)}")
if not folder_path.exists():
raise FileNotFoundError(
"Folder is expected to be present when the class is initialized."
)
self.path: PosixPath = folder_path
issue_group_warning: bool = False
if common_group == "":
self.group: str = self.path.group()
issue_group_warning = True
else:
self.group = common_group
# getting user name https://stackoverflow.com/a/2899055
self.user: str = getpwuid(getuid()).pw_name
# add logger object to class
self.logger: logging.Logger = create_logger(cgroup=self.group)
self.logger.info(f"CommonFolder class instantiated for: {str(folder_path)}")
if issue_group_warning:
self.logger.warning(
"Common Folder group ownership infered from existing ownership!"
)
[docs] def enforce_permissions(self):
"""
We read the contents of a specified directory and enforce unix permissions.
Files should have 664 permissions
Folders should have 2775 permisions (ie also setguid bit)
Group should be common golder's group.
The function only changes permissions if the user is owner of the relevant resource.
This is done to avoid the need for root access, but requires the function to be
run by all the users sharing the common folder.
"""
try:
self.logger.info("Starting permissions enforcement")
# Let's iterate over folders we find first - glob returns a generator!
list1 = self.path.glob("**/*")
for il in list1:
pil = Path(il)
# Fix group membership if needed
if pil.group() != self.group and pil.owner() == self.user:
chown(pil, group=self.group)
self.logger.debug(f"Fixing group for {str(pil)}")
# Check folder permissions and fix if needed
if pil.is_dir():
perms: str = oct(pil.stat().st_mode)[-4:]
if perms != "2775" and pil.owner() == self.user:
pil.chmod(0o42775)
self.logger.debug(f"Fixing permissions for {str(pil)}")
# Check folder permissions and fix if needed
if pil.is_file():
perms: str = oct(pil.stat().st_mode)[-4:]
if perms != "0664" and pil.owner() == self.user:
pil.chmod(0o40664)
self.logger.debug(f"Fixing permissions for {str(pil)}")
except Exception as exc:
self.logger.critical(exc, exc_info=True)
self.logger.info("Finished permissions enforcement")