import gws
import gws.config
import gws.types as t
#:export
[docs]class UserProps(t.Data):
displayName: str
#:export IRole
[docs]class Role(t.IRole):
def __init__(self, name):
self.name = name
[docs] def can_use(self, obj, parent=None):
if obj == self:
return True
return _can_use([self.name], obj, parent)
[docs]def make_fid(user):
return f'{user.provider.uid}::{user.uid}'
[docs]def parse_fid(fid):
s = fid.split('::', 1)
if len(s) == 2:
return s
raise ValueError(f'invalid fid: {fid!r}')
#:export IUser
[docs]class User(t.IUser):
attributes = {}
provider: t.IAuthProvider
roles: t.List[str] = []
uid = ''
@property
def props(self) -> t.UserProps:
return t.UserProps()
@property
def display_name(self) -> str:
return self.attributes.get('displayName')
@property
def is_guest(self) -> bool:
return False
@property
def fid(self) -> str:
return make_fid(self)
[docs] def has_role(self, role: str) -> bool:
return role in self.roles
[docs] def init_from_source(self, provider, uid, roles=None, attributes=None) -> t.IUser:
attributes = dict(attributes or {})
for a, b in _aliases:
if a in attributes:
attributes[b] = attributes[a]
elif b in attributes:
attributes[a] = attributes[b]
if 'displayName' not in attributes:
attributes['displayName'] = attributes.get('login', '')
attributes['uid'] = uid
attributes['provider_uid'] = provider.uid
attributes['guest'] = self.is_guest
roles = list(roles) if roles else []
roles.append(_ROLE_GUEST if self.is_guest else _ROLE_USER)
roles.append(_ROLE_ALL)
return self.init_from_data(provider, uid, roles, attributes)
[docs] def init_from_data(self, provider, uid, roles, attributes) -> t.IUser:
self.attributes = attributes
self.provider = provider
self.roles = sorted(set(roles))
self.uid = uid
gws.log.debug(f'inited user: prov={provider.uid!r} uid={uid!r} roles={roles!r}')
return self
[docs] def attribute(self, key: str, default: str = '') -> str:
return self.attributes.get(key, default)
[docs] def can_use(self, obj, parent=None) -> bool:
if obj == self:
return True
return _can_use(self.roles, obj, parent)
[docs]class Guest(User):
@property
def is_guest(self):
return True
[docs]class System(User):
[docs] def can_use(self, obj, parent=None):
gws.log.debug(f'PERMS: sys_allow : obj={_repr(obj)}')
return True
[docs]class Nobody(User):
[docs] def can_use(self, obj, parent=None):
gws.log.debug(f'PERMS: sys_deny : obj={_repr(obj)}')
return False
[docs]class ValidUser(User):
@property
def props(self):
return UserProps({
'displayName': self.display_name
})
# https://tools.ietf.org/html/rfc4519
_aliases = [
('c', 'countryName'),
('cn', 'commonName'),
('dc', 'domainComponent'),
('l', 'localityName'),
('o', 'organizationName'),
('ou', 'organizationalUnitName'),
('sn', 'surname'),
('st', 'stateOrProvinceName'),
('street', 'streetAddress'),
# non-standard
('login', 'userPrincipalName'),
]
_ROLE_ADMIN = 'admin'
_ROLE_USER = 'user'
_ROLE_GUEST = 'guest'
_ROLE_ALL = 'all'
def _can_use(roles, target, parent):
if not target:
gws.log.debug(f'PERMS: query: t={_repr(target)} roles={roles!r}: empty')
return False
if _ROLE_ADMIN in roles:
gws.log.debug(f'PERMS: query: t={_repr(target)} roles={roles!r} found: _ROLE_ADMIN')
return True
c = _check_access(roles, target, target)
if c is not None:
return c
current = parent or gws.get(target, 'parent')
while current:
c = _check_access(roles, target, current)
if c is not None:
return c
current = gws.get(current, 'parent')
gws.log.debug(f'PERMS: query: obj={_repr(target)} roles={roles!r}: not found')
return False
def _check_access(roles, target, current):
access = gws.get(current, 'access')
if not access:
return
for a in access:
if a.role in roles:
gws.log.debug(f'PERMS: query: t={_repr(target)} roles={roles!r} found: {a.role}:{a.type} in {_repr(current)}')
return a.type == 'allow'
def _repr(obj):
if not obj:
return repr(obj)
return repr(gws.get(obj, 'uid') or obj)