diff --git a/radicale/auth/PAM.py b/radicale/auth/PAM.py index 25e66b196..7efc1b117 100644 --- a/radicale/auth/PAM.py +++ b/radicale/auth/PAM.py @@ -91,3 +91,44 @@ def is_authenticated(user, password): GROUP_MEMBERSHIP) return False + + +def is_in_group(user, group): + """Check if the user is in the aforementioned group.""" + + # Check whether the user exists in the PAM system + try: + pwd.getpwnam(user).pw_uid + except KeyError: + log.LOGGER.debug("User %s not found" % user) + return False + else: + log.LOGGER.debug("User %s found" % user) + + # Check whether the group exists + try: + # Obtain supplementary groups + members = grp.getgrnam(group).gr_mem + except KeyError: + log.LOGGER.debug( + "Group %s doesn't exist" %group) + return False + + # Check whether the user exists + try: + # Get user primary group + primary_group = grp.getgrgid(pwd.getpwnam(user).pw_gid).gr_name + except KeyError: + log.LOGGER.debug("The PAM user (%s) doesn't exist" % user) + return False + + # Check whether the user belongs to the required group + # (primary or supplementary) + if primary_group == group or user in members: + log.LOGGER.debug( + "The PAM user belongs to the required group (%s)" %group) + return True + else: + log.LOGGER.debug( + "The PAM user doesn't belong to the required group (%s)" %group) + return False diff --git a/radicale/auth/__init__.py b/radicale/auth/__init__.py index 5dbdde47a..6aff3e0a1 100644 --- a/radicale/auth/__init__.py +++ b/radicale/auth/__init__.py @@ -44,6 +44,9 @@ def load(): module = getattr(root_module, auth_type) # Override auth.is_authenticated sys.modules[__name__].is_authenticated = module.is_authenticated + # Override auth.is_in_group if available + if hasattr(module, 'is_in_group'): + sys.modules[__name__].is_in_group = module.is_in_group return module @@ -54,3 +57,13 @@ def is_authenticated(user, password): """ return True # Default is always True: no authentication + + +def is_in_group(user, group): + """Check if the user is in the aforementioned group. + + This method is overriden if an auth module providing group support is + loaded. + + """ + return False # Default is always False: user in no group diff --git a/radicale/rights/regex.py b/radicale/rights/regex.py index 35e5893c6..d47b7a2d1 100644 --- a/radicale/rights/regex.py +++ b/radicale/rights/regex.py @@ -42,14 +42,14 @@ import sys import os.path -from .. import config, log +from .. import config, log, auth # Manage Python2/3 different modules if sys.version_info[0] == 2: - from ConfigParser import ConfigParser + from ConfigParser import ConfigParser, NoOptionError from StringIO import StringIO else: - from configparser import ConfigParser + from configparser import ConfigParser, NoOptionError from io import StringIO @@ -81,6 +81,13 @@ def _read_from_sections(user, collection_url, permission): for section in regex.sections(): re_user = regex.get(section, "user") re_collection = regex.get(section, "collection") + group = [] + try: + group = regex.get(section, "group", []) + except NoOptionError: + pass + if group: + group = [ x.strip() for x in group.split(',') ] log.LOGGER.debug( "Test if '%s:%s' matches against '%s:%s' from section '%s'" % ( user, collection_url, re_user, re_collection, section)) @@ -88,9 +95,10 @@ def _read_from_sections(user, collection_url, permission): if user_match: re_collection = re_collection.format(*user_match.groups()) if re.match(re_collection, collection_url): - log.LOGGER.debug("Section '%s' matches" % section) - if permission in regex.get(section, "permission"): - return True + if any(map(lambda g: auth.is_in_group(user, g), group)): + log.LOGGER.debug("Section '%s' matches" % section) + if permission in regex.get(section, "permission"): + return True else: log.LOGGER.debug("Section '%s' does not match" % section) return False diff --git a/rights b/rights index db3e51cf8..96f69f141 100644 --- a/rights +++ b/rights @@ -36,3 +36,11 @@ permission: r user: .+ collection: ^%(login)s/.*$ permission: w + +# Give access to all user in groups admins and wheel to collections starting by +# admins/. +[admins] +user: .+ +group: admins, wheel +collection: ^admins/.+$ +permission: rw