"""Abstract permissions container.
"""
from __future__ import print_function
from __future__ import unicode_literals
import typing
from typing import Iterable
import six
from ._typing import Text
if typing.TYPE_CHECKING:
from typing import Iterator, List, Optional, Tuple, Type, Union
[docs]def make_mode(init):
# type: (Union[int, Iterable[Text], None]) -> int
"""Make a mode integer from an initial value.
"""
return Permissions.get_mode(init)
class _PermProperty(object):
"""Creates simple properties to get/set permissions.
"""
def __init__(self, name):
# type: (Text) -> None
self._name = name
self.__doc__ = "Boolean for '{}' permission.".format(name)
def __get__(self, obj, obj_type=None):
# type: (Permissions, Optional[Type[Permissions]]) -> bool
return self._name in obj
def __set__(self, obj, value):
# type: (Permissions, bool) -> None
if value:
obj.add(self._name)
else:
obj.remove(self._name)
[docs]@six.python_2_unicode_compatible
class Permissions(object):
"""An abstraction for file system permissions.
Permissions objects store information regarding the permissions
on a resource. It supports Linux permissions, but is generic enough
to manage permission information from almost any filesystem.
Arguments:
names (list, optional): A list of permissions.
mode (int, optional): A mode integer.
user (str, optional): A triplet of *user* permissions, e.g.
``"rwx"`` or ``"r--"``
group (str, optional): A triplet of *group* permissions, e.g.
``"rwx"`` or ``"r--"``
other (str, optional): A triplet of *other* permissions, e.g.
``"rwx"`` or ``"r--"``
sticky (bool, optional): A boolean for the *sticky* bit.
setuid (bool, optional): A boolean for the *setuid* bit.
setguid (bool, optional): A boolean for the *setguid* bit.
Example:
>>> from fs.permissions import Permissions
>>> p = Permissions(user='rwx', group='rw-', other='r--')
>>> print(p)
rwxrw-r--
>>> p.mode
500
>>> oct(p.mode)
'0764'
"""
_LINUX_PERMS = [
("setuid", 2048),
("setguid", 1024),
("sticky", 512),
("u_r", 256),
("u_w", 128),
("u_x", 64),
("g_r", 32),
("g_w", 16),
("g_x", 8),
("o_r", 4),
("o_w", 2),
("o_x", 1),
] # type: List[Tuple[Text, int]]
_LINUX_PERMS_NAMES = [_name for _name, _mask in _LINUX_PERMS] # type: List[Text]
def __init__(
self,
names=None, # type: Optional[Iterable[Text]]
mode=None, # type: Optional[int]
user=None, # type: Optional[Text]
group=None, # type: Optional[Text]
other=None, # type: Optional[Text]
sticky=None, # type: Optional[bool]
setuid=None, # type: Optional[bool]
setguid=None, # type: Optional[bool]
):
# type: (...) -> None
if names is not None:
self._perms = set(names)
elif mode is not None:
self._perms = {name for name, mask in self._LINUX_PERMS if mode & mask}
else:
perms = self._perms = set()
perms.update("u_" + p for p in user or "" if p != "-")
perms.update("g_" + p for p in group or "" if p != "-")
perms.update("o_" + p for p in other or "" if p != "-")
if sticky:
self._perms.add("sticky")
if setuid:
self._perms.add("setuid")
if setguid:
self._perms.add("setguid")
def __repr__(self):
# type: () -> Text
if not self._perms.issubset(self._LINUX_PERMS_NAMES):
_perms_str = ", ".join("'{}'".format(p) for p in sorted(self._perms))
return "Permissions(names=[{}])".format(_perms_str)
def _check(perm, name):
# type: (Text, Text) -> Text
return name if perm in self._perms else ""
user = "".join((_check("u_r", "r"), _check("u_w", "w"), _check("u_x", "x")))
group = "".join((_check("g_r", "r"), _check("g_w", "w"), _check("g_x", "x")))
other = "".join((_check("o_r", "r"), _check("o_w", "w"), _check("o_x", "x")))
args = []
_fmt = "user='{}', group='{}', other='{}'"
basic = _fmt.format(user, group, other)
args.append(basic)
if self.sticky:
args.append("sticky=True")
if self.setuid:
args.append("setuid=True")
if self.setuid:
args.append("setguid=True")
return "Permissions({})".format(", ".join(args))
def __str__(self):
# type: () -> Text
return self.as_str()
def __iter__(self):
# type: () -> Iterator[Text]
return iter(self._perms)
def __contains__(self, permission):
# type: (object) -> bool
return permission in self._perms
def __eq__(self, other):
# type: (object) -> bool
if isinstance(other, Permissions):
names = other.dump() # type: object
else:
names = other
return self.dump() == names
def __ne__(self, other):
# type: (object) -> bool
return not self.__eq__(other)
[docs] @classmethod
def parse(cls, ls):
# type: (Text) -> Permissions
"""Parse permissions in Linux notation.
"""
user = ls[:3]
group = ls[3:6]
other = ls[6:9]
return cls(user=user, group=group, other=other)
[docs] @classmethod
def load(cls, permissions):
# type: (List[Text]) -> Permissions
"""Load a serialized permissions object.
"""
return cls(names=permissions)
[docs] @classmethod
def create(cls, init=None):
# type: (Union[int, Iterable[Text], None]) -> Permissions
"""Create a permissions object from an initial value.
Arguments:
init (int or list, optional): May be None to use `0o777`
permissions, a mode integer, or a list of permission names.
Returns:
int: mode integer that may be used for instance by `os.makedir`.
Example:
>>> Permissions.create(None)
Permissions(user='rwx', group='rwx', other='rwx')
>>> Permissions.create(0o700)
Permissions(user='rwx', group='', other='')
>>> Permissions.create(['u_r', 'u_w', 'u_x'])
Permissions(user='rwx', group='', other='')
"""
if init is None:
return cls(mode=0o777)
if isinstance(init, cls):
return init
if isinstance(init, int):
return cls(mode=init)
if isinstance(init, list):
return cls(names=init)
raise ValueError("permissions is invalid")
[docs] @classmethod
def get_mode(cls, init):
# type: (Union[int, Iterable[Text], None]) -> int
"""Convert an initial value to a mode integer.
"""
return cls.create(init).mode
[docs] def copy(self):
# type: () -> Permissions
"""Make a copy of this permissions object.
"""
return Permissions(names=list(self._perms))
[docs] def dump(self):
# type: () -> List[Text]
"""Get a list suitable for serialization.
"""
return sorted(self._perms)
[docs] def as_str(self):
# type: () -> Text
"""Get a Linux-style string representation of permissions.
"""
perms = [
c if name in self._perms else "-"
for name, c in zip(self._LINUX_PERMS_NAMES[-9:], "rwxrwxrwx")
]
if "setuid" in self._perms:
perms[2] = "s" if "u_x" in self._perms else "S"
if "setguid" in self._perms:
perms[5] = "s" if "g_x" in self._perms else "S"
if "sticky" in self._perms:
perms[8] = "t" if "o_x" in self._perms else "T"
perm_str = "".join(perms)
return perm_str
@property
def mode(self):
# type: () -> int
"""`int`: mode integer.
"""
mode = 0
for name, mask in self._LINUX_PERMS:
if name in self._perms:
mode |= mask
return mode
@mode.setter
def mode(self, mode):
# type: (int) -> None
self._perms = {name for name, mask in self._LINUX_PERMS if mode & mask}
u_r = _PermProperty("u_r")
u_w = _PermProperty("u_w")
u_x = _PermProperty("u_x")
g_r = _PermProperty("g_r")
g_w = _PermProperty("g_w")
g_x = _PermProperty("g_x")
o_r = _PermProperty("o_r")
o_w = _PermProperty("o_w")
o_x = _PermProperty("o_x")
sticky = _PermProperty("sticky")
setuid = _PermProperty("setuid")
setguid = _PermProperty("setguid")
[docs] def add(self, *permissions):
# type: (*Text) -> None
"""Add permission(s).
Arguments:
*permissions (str): Permission name(s), such as ``'u_w'``
or ``'u_x'``.
"""
self._perms.update(permissions)
[docs] def remove(self, *permissions):
# type: (*Text) -> None
"""Remove permission(s).
Arguments:
*permissions (str): Permission name(s), such as ``'u_w'``
or ``'u_x'``.s
"""
self._perms.difference_update(permissions)
[docs] def check(self, *permissions):
# type: (*Text) -> bool
"""Check if one or more permissions are enabled.
Arguments:
*permissions (str): Permission name(s), such as ``'u_w'``
or ``'u_x'``.
Returns:
bool: `True` if all given permissions are set.
"""
return self._perms.issuperset(permissions)