Example #1
0
    def test_backend_acl_not_existing_group(self):
        assert u'NotExistingGroup' not in flaskg.groups

        acl_rights = ["NotExistingGroup:read,write,admin All:read"]
        acl = AccessControlList(acl_rights, valid=app.cfg.acl_rights_contents)

        assert not acl.may(u"Someone", "write")
Example #2
0
    def testApplyACLByGroup(self):
        """ security: applying acl by group name"""
        # This acl string...
        acl_rights = [
            "PGroup,AllGroup:read,write,admin "
            "AGroup:read "
        ]
        acl = AccessControlList(acl_rights, valid=app.cfg.acl_rights_contents)

        # Should apply these rights:
        users = (
            # user, rights
            ('Antony', ('read', 'write', 'admin', )),  # in PGroup
            ('Beatrice', ('read', 'write', 'admin', )),  # in PGroup
            ('Charles', ('read', )),  # virtually in AGroup
        )

        # Check rights
        for user, may in users:
            mayNot = [right for right in app.cfg.acl_rights_contents
                      if right not in may]
            # User should have these rights...
            for right in may:
                assert acl.may(user, right)
            # But NOT these:
            for right in mayNot:
                assert not acl.may(user, right)
Example #3
0
    def testApplyACLByGroup(self):
        """ security: applying acl by group name"""
        # This acl string...
        acl_rights = [
            "PGroup,AllGroup:read,write,admin "
            "AGroup:read "
        ]
        acl = AccessControlList(acl_rights, valid=app.cfg.acl_rights_contents)

        # Should apply these rights:
        users = (
            # user, rights
            ('Antony', ('read', 'write', 'admin', )),  # in PGroup
            ('Beatrice', ('read', 'write', 'admin', )),  # in PGroup
            ('Charles', ('read', )),  # virtually in AGroup
        )

        # Check rights
        for user, may in users:
            mayNot = [right for right in app.cfg.acl_rights_contents
                      if right not in may]
            # User should have these rights...
            for right in may:
                assert acl.may(user, right)
            # But NOT these:
            for right in mayNot:
                assert not acl.may(user, right)
Example #4
0
    def test_backend_acl_allow(self):
        """
        Test if the wiki group backend works with acl code.
        Check user which has rights.
        """
        acl_rights = ["AdminGroup:admin,read,write"]
        acl = AccessControlList(acl_rights, valid=app.cfg.acl_rights_contents)

        for user in self.expanded_groups['AdminGroup']:
            for permission in ["read", "write", "admin"]:
                assert acl.may(u"Admin1", permission), '{0} must have {1} permission because he is member of the AdminGroup'.format(user, permission)
Example #5
0
    def test_backend_acl_with_all(self):
        acl_rights = ["EditorGroup:read,write,admin All:read"]
        acl = AccessControlList(acl_rights, valid=app.cfg.acl_rights_contents)

        for member in self.expanded_groups[u'EditorGroup']:
            for permission in ["read", "write", "admin"]:
                assert acl.may(member, permission)

        assert acl.may(u"Someone", "read")
        for permission in ["write", "admin"]:
            assert not acl.may(u"Someone", permission)
Example #6
0
    def testApplyACLByUser(self):
        """ security: applying acl by user name"""
        # This acl string...
        acl_rights = [
            "-MinusGuy:read "
            "+MinusGuy:read "
            "+PlusGuy:read "
            "-PlusGuy:read "
            "Admin1,Admin2:read,write,admin  "
            "Admin3:read,write,admin  "
            "JoeDoe:read,write  "
            "name with spaces,another one:read,write  "
            "CamelCase,extended name:read,write  "
            "BadGuy:  "
            "All:read  "
        ]
        acl = AccessControlList(acl_rights, valid=app.cfg.acl_rights_contents)

        # Should apply these rights:
        users = (
            # user,                 rights
            # CamelCase names
            ('Admin1', ('read', 'write', 'admin')),
            ('Admin2', ('read', 'write', 'admin')),
            ('Admin3', ('read', 'write', 'admin')),
            ('JoeDoe', ('read', 'write')),
            ('SomeGuy', ('read', )),
            # Extended names or mix of extended and CamelCase
            ('name with spaces', ('read', 'write', )),
            ('another one', ('read', 'write', )),
            ('CamelCase', ('read', 'write', )),
            ('extended name', ('read', 'write', )),
            # Blocking bad guys
            ('BadGuy', ()),
            # All other users - every one not mentioned in the acl lines
            ('All', ('read', )),
            ('Anonymous', ('read', )),
            # we check whether ACL processing stops for a user/right match
            # with ACL modifiers
            ('MinusGuy', ()),
            ('PlusGuy', ('read', )),
        )

        # Check rights
        for user, may in users:
            mayNot = [right for right in app.cfg.acl_rights_contents
                      if right not in may]
            # User should have these rights...
            for right in may:
                assert acl.may(user, right)
            # But NOT these:
            for right in mayNot:
                assert not acl.may(user, right)
Example #7
0
    def testApplyACLByUser(self):
        """ security: applying acl by user name"""
        # This acl string...
        acl_rights = [
            "-MinusGuy:read "
            "+MinusGuy:read "
            "+PlusGuy:read "
            "-PlusGuy:read "
            "Admin1,Admin2:read,write,admin  "
            "Admin3:read,write,admin  "
            "JoeDoe:read,write  "
            "name with spaces,another one:read,write  "
            "CamelCase,extended name:read,write  "
            "BadGuy:  "
            "All:read  "
        ]
        acl = AccessControlList(acl_rights, valid=app.cfg.acl_rights_contents)

        # Should apply these rights:
        users = (
            # user,                 rights
            # CamelCase names
            ('Admin1', ('read', 'write', 'admin')),
            ('Admin2', ('read', 'write', 'admin')),
            ('Admin3', ('read', 'write', 'admin')),
            ('JoeDoe', ('read', 'write')),
            ('SomeGuy', ('read', )),
            # Extended names or mix of extended and CamelCase
            ('name with spaces', ('read', 'write', )),
            ('another one', ('read', 'write', )),
            ('CamelCase', ('read', 'write', )),
            ('extended name', ('read', 'write', )),
            # Blocking bad guys
            ('BadGuy', ()),
            # All other users - every one not mentioned in the acl lines
            ('All', ('read', )),
            ('Anonymous', ('read', )),
            # we check whether ACL processing stops for a user/right match
            # with ACL modifiers
            ('MinusGuy', ()),
            ('PlusGuy', ('read', )),
        )

        # Check rights
        for user, may in users:
            mayNot = [right for right in app.cfg.acl_rights_contents
                      if right not in may]
            # User should have these rights...
            for right in may:
                assert acl.may(user, right)
            # But NOT these:
            for right in mayNot:
                assert not acl.may(user, right)
Example #8
0
    def test_backend_acl_deny(self):
        """
        Test if the wiki group backend works with acl code.
        Check user which does not have rights.
        """
        acl_rights = ["AdminGroup:read,write"]
        acl = AccessControlList(acl_rights, valid=app.cfg.acl_rights_contents)

        assert u"SomeUser" not in flaskg.groups['AdminGroup']
        for permission in ["read", "write"]:
            assert not acl.may(u"SomeUser", permission), 'SomeUser must not have {0} permission because he is not listed in the AdminGroup'.format(permission)

        assert u'Admin1' in flaskg.groups['AdminGroup']
        assert not acl.may(u"Admin1", "admin")
Example #9
0
    def test_wiki_backend_item_acl_usergroupmember_item(self):
        """
        Test if the wiki group backend works with acl code.
        First check acl rights of a user that is not a member of group
        then add user member to an item group and check acl rights
        """
        become_trusted()
        update_item(u'NewGroup', {USERGROUP: ["ExampleUser"]}, DATA)

        acl_rights = ["NewGroup:read,write"]
        acl = AccessControlList(acl_rights, valid=app.cfg.acl_rights_contents)

        has_rights_before = acl.may(u"AnotherUser", "read")

        # update item - add AnotherUser to a item group NewGroup
        update_item(u'NewGroup', {USERGROUP: ["AnotherUser"]}, '')

        has_rights_after = acl.may(u"AnotherUser", "read")

        assert not has_rights_before, 'AnotherUser has no read rights because in the beginning he is not a member of a group item NewGroup'
        assert has_rights_after, 'AnotherUser must have read rights because after appenditem he is member of NewGroup'
    def test_wiki_backend_item_acl_usergroupmember_item(self):
        """
        Test if the wiki group backend works with acl code.
        First check acl rights of a user that is not a member of group
        then add user member to an item group and check acl rights
        """
        become_trusted()
        update_item(u'NewGroup', {USERGROUP: ["ExampleUser"]}, DATA)

        acl_rights = ["NewGroup:read,write"]
        acl = AccessControlList(acl_rights, valid=app.cfg.acl_rights_contents)

        has_rights_before = acl.may(u"AnotherUser", "read")

        # update item - add AnotherUser to a item group NewGroup
        update_item(u'NewGroup', {USERGROUP: ["AnotherUser"]}, '')

        has_rights_after = acl.may(u"AnotherUser", "read")

        assert not has_rights_before, 'AnotherUser has no read rights because in the beginning he is not a member of a group item NewGroup'
        assert has_rights_after, 'AnotherUser must have read rights because after appenditem he is member of NewGroup'
Example #11
0
    def __call__(self,
                 value,
                 start_pos=0,
                 positions=False,
                 mode=u'',
                 **kwargs):
        """
        Calls AccessControlList for tokenization

        Analyzer behaviour:

        In index mode:
            Input: u"JoeDoe,JaneDoe:admin,read,write,destroy +EditorGroup:write All:read"

            Output: "u'JoeDoe:+read', u'JoeDoe:+write', u'JoeDoe:-create', u'JoeDoe:+admin',
                     u'JoeDoe:+destroy', u'JaneDoe:+read', u'JaneDoe:+write', u'JaneDoe:-create',
                     u'JaneDoe:+admin', u'JaneDoe:+destroy', u'EditorGroup:+write', u'All:+read',
                     u'All:-write', u'All:-create', u'All:-admin', u'All:-destroy'

        In query mode:
            Input: u"JoeDoe:+write"

            Output: u"JoeDoe:+write"

        :param value: unicode string
        :param positions: Whether to record token positions in the token.
        :param start_pos: The position number of the first token. For example,
            if you set start_pos=2, the tokens will be numbered 2,3,4,...
            instead of 0,1,2,...
        """
        assert isinstance(value, unicode)
        pos = start_pos
        tk = Token()
        tk.mode = mode
        if mode == "query":
            tk.text = value
            if positions:
                tk.pos = pos
            yield tk
        else:
            acl = AccessControlList([value], valid=self._acl_rights_contents)
            for name, permissions in acl.acl:
                for permission in permissions:
                    sign = "+" if permissions[permission] else "-"
                    tk.text = u"{0}:{1}{2}".format(name, sign, permission)
                    if positions:
                        tk.pos = pos
                        pos += 1
                    yield tk
Example #12
0
 def _get_acl(self, itemname):
     """
     Get ACL strings from the last revision's metadata and return ACL object.
     """
     try:
         item = self.backend.get_item(itemname)
         # we always use the ACLs set on the latest revision:
         current_rev = item.get_revision(-1)
         acls = current_rev[ACL]
     except (NoSuchItemError, NoSuchRevisionError, KeyError):
         # do not use default acl here
         acls = []
     if not isinstance(acls, (tuple, list)):
         acls = (acls, )
     default = self.default.default
     return AccessControlList(self.cfg,
                              acls,
                              default=default,
                              valid=self.valid)
Example #13
0
 def __init__(self,
              cfg,
              backend,
              hierarchic=False,
              before=u"",
              default=u"",
              after=u"",
              valid=None):
     """
     @type backend: Some object that implements the storage API.
     @param backend: The unprotected backend that we want to protect.
     @type hierarchic: bool
     @param hierarchic: Indicate whether we want to process ACLs in hierarchic mode.
     @type before: unicode
     @param before: ACL to be applied before all the other ACLs.
     @type default: unicode
     @param default: If no ACL information is given on the item in question, use this default.
     @type after: unicode
     @param after: ACL to be applied after all the other ACLs.
     @type valid: list of strings or None
     @param valid: If a list is given, only strings in the list are treated as valid acl privilege descriptors.
                   If None is give, the global wiki default is used.
     """
     self.cfg = cfg
     self.backend = backend
     self.hierarchic = hierarchic
     self.valid = valid
     self.before = AccessControlList(cfg, [before],
                                     default=default,
                                     valid=valid)
     self.default = AccessControlList(cfg, [default],
                                      default=default,
                                      valid=valid)
     self.after = AccessControlList(cfg, [after],
                                    default=default,
                                    valid=valid)
Example #14
0
    def __init__(self):
        """ Init Config instance """
        self.cache = CacheClass()

        if self.config_check_enabled:
            self._config_check()

        # define directories
        data_dir = os.path.normpath(self.data_dir)
        self.data_dir = data_dir

        # Try to decode certain names which allow unicode
        self._decode()

        # After that, pre-compile some regexes
        self.cache.item_dict_regex = re.compile(self.item_dict_regex, re.UNICODE)
        self.cache.item_group_regex = re.compile(self.item_group_regex, re.UNICODE)

        # the ..._regexact versions only match if nothing is left (exact match)
        self.cache.item_dict_regexact = re.compile(u'^{0}$'.format(self.item_dict_regex, re.UNICODE))
        self.cache.item_group_regexact = re.compile(u'^{0}$'.format(self.item_group_regex, re.UNICODE))

        # compiled functions ACL
        self.cache.acl_functions = AccessControlList([self.acl_functions], valid=self.acl_rights_functions)

        plugins._loadPluginModule(self)

        if self.user_defaults[TIMEZONE] is None:
            self.user_defaults[TIMEZONE] = self.timezone_default
        if self.user_defaults[THEME_NAME] is None:
            self.user_defaults[THEME_NAME] = self.theme_default
        # Note: do not assign user_defaults['locale'] = locale_default
        # to give browser language detection a chance.
        try:
            self.language_default = parse_locale(self.locale_default)[0]
        except ValueError:
            raise error.ConfigurationError("Invalid locale_default value (give something like 'en_US').")

        # post process
        self.auth_can_logout = []
        self.auth_login_inputs = []
        found_names = []
        for auth in self.auth:
            if not auth.name:
                raise error.ConfigurationError("Auth methods must have a name.")
            if auth.name in found_names:
                raise error.ConfigurationError("Auth method names must be unique.")
            found_names.append(auth.name)
            if auth.logout_possible and auth.name:
                self.auth_can_logout.append(auth.name)
            for input in auth.login_inputs:
                if input not in self.auth_login_inputs:
                    self.auth_login_inputs.append(input)
        self.auth_have_login = len(self.auth_login_inputs) > 0
        self.auth_methods = found_names

        # internal dict for plugin `modules' lists
        self._site_plugin_lists = {}

        # check if mail is possible and set flag:
        self.mail_enabled = (self.mail_smarthost is not None or self.mail_sendmail is not None) and self.mail_from
        self.mail_enabled = self.mail_enabled and True or False

        if self.namespace_mapping is None:
            raise error.ConfigurationError(
                "No storage configuration specified! You need to define a namespace_mapping. "
                "For further reference, please see HelpOnStorageConfiguration.")

        if self.backend_mapping is None:
            raise error.ConfigurationError(
                "No storage configuration specified! You need to define a backend_mapping. " +
                "For further reference, please see HelpOnStorageConfiguration.")

        if self.acl_mapping is None:
            raise error.ConfigurationError(
                "No acl configuration specified! You need to define a acl_mapping. "
                "For further reference, please see HelpOnStorageConfiguration.")

        if self.secrets is None:  # admin did not setup a real secret
            raise error.ConfigurationError(
                "No secret configured! You need to set secrets = 'somelongsecretstring' in your wiki config.")

        if self.interwikiname is None:  # admin did not setup a real interwikiname
            raise error.ConfigurationError(
                "No interwikiname configured! "
                "You need to set interwikiname = u'YourUniqueStableInterwikiName' in your wiki config.")

        secret_key_names = ['security/ticket', ]
        if self.textchas:
            secret_key_names.append('security/textcha')

        secret_min_length = 10
        if isinstance(self.secrets, str):
            if len(self.secrets) < secret_min_length:
                raise error.ConfigurationError(
                    "The secrets = '...' wiki config setting is a way too short string "
                    "(minimum length is {0} chars)!".format(secret_min_length))
            # for lazy people: set all required secrets to same value
            secrets = {}
            for key in secret_key_names:
                secrets[key] = self.secrets
            self.secrets = secrets

        # we check if we have all secrets we need and that they have minimum length
        for secret_key_name in secret_key_names:
            try:
                secret = self.secrets[secret_key_name]
                if len(secret) < secret_min_length:
                    raise ValueError
            except (KeyError, ValueError):
                raise error.ConfigurationError(
                    "You must set a (at least {0} chars long) secret string for secrets['{1}']!".format(
                        secret_min_length, secret_key_name))

        from passlib.context import CryptContext
        try:
            self.cache.pwd_context = CryptContext(**self.passlib_crypt_context)
        except ValueError as err:
            raise error.ConfigurationError("passlib_crypt_context configuration is invalid [{0}].".format(err))
Example #15
0
 def _parse_acl(self, acl, default=''):
     return AccessControlList([
         acl,
     ],
                              default=default,
                              valid=self.valid_rights)
Example #16
0
 def testhasACL(self):
     acl = AccessControlList(valid=app.cfg.acl_rights_contents)
     assert not acl.has_acl()
     acl = AccessControlList(["All:read", ], valid=app.cfg.acl_rights_contents)
     assert acl.has_acl()
Example #17
0
 def testhasACL(self):
     acl = AccessControlList(valid=app.cfg.acl_rights_contents)
     assert not acl.has_acl()
     acl = AccessControlList(["All:read", ], valid=app.cfg.acl_rights_contents)
     assert acl.has_acl()
Example #18
0
class AclWrapperBackend(object):
    """
    The AMW is bound to a specific request. The actual backend is retrieved
    from the config upon request initialization. Any method that is in some
    way relevant to security needs to be wrapped in order to ensure the user
    has the permissions necessary to perform the desired action.
    Note: This may *not* inherit from MoinMoin.storage.Backend because that would
    break our __getattr__ attribute 'redirects' (which are necessary because a backend
    implementor may decide to use his own helper functions which the items and revisions
    will still try to call).
    """
    def __init__(self,
                 cfg,
                 backend,
                 hierarchic=False,
                 before=u"",
                 default=u"",
                 after=u"",
                 valid=None):
        """
        @type backend: Some object that implements the storage API.
        @param backend: The unprotected backend that we want to protect.
        @type hierarchic: bool
        @param hierarchic: Indicate whether we want to process ACLs in hierarchic mode.
        @type before: unicode
        @param before: ACL to be applied before all the other ACLs.
        @type default: unicode
        @param default: If no ACL information is given on the item in question, use this default.
        @type after: unicode
        @param after: ACL to be applied after all the other ACLs.
        @type valid: list of strings or None
        @param valid: If a list is given, only strings in the list are treated as valid acl privilege descriptors.
                      If None is give, the global wiki default is used.
        """
        self.cfg = cfg
        self.backend = backend
        self.hierarchic = hierarchic
        self.valid = valid
        self.before = AccessControlList(cfg, [before],
                                        default=default,
                                        valid=valid)
        self.default = AccessControlList(cfg, [default],
                                         default=default,
                                         valid=valid)
        self.after = AccessControlList(cfg, [after],
                                       default=default,
                                       valid=valid)

    def __getattr__(self, attr):
        # Attributes that this backend does not define itself are just looked
        # up on the real backend.
        return getattr(self.backend, attr)

    def search_items(self, searchterm):
        """
        @see: Backend.search_items.__doc__
        """
        for item in self.backend.search_items(searchterm):
            if self._may(item.name, READ):
                # The item returned needs to be wrapped because otherwise the
                # item's methods (like create_revision) wouldn't be wrapped.
                wrapped_item = AclWrapperItem(item, self)
                yield wrapped_item

    def get_item(self, itemname):
        """
        @see: Backend.get_item.__doc__
        """
        if not self._may(itemname, READ):
            raise AccessDeniedError(flaskg.user.name, READ, itemname)
        real_item = self.backend.get_item(itemname)
        # Wrap the item here as well.
        wrapped_item = AclWrapperItem(real_item, self)
        return wrapped_item

    def has_item(self, itemname):
        """
        @see: Backend.has_item.__doc__
        """
        # We do not hide the sheer existance of items. When trying
        # to create an item with the same name, the user would notice anyway.
        return self.backend.has_item(itemname)

    def create_item(self, itemname):
        """
        @see: Backend.create_item.__doc__
        """
        if not self._may(itemname, CREATE):
            raise AccessDeniedError(flaskg.user.name, CREATE, itemname)
        real_item = self.backend.create_item(itemname)
        # Wrap item.
        wrapped_item = AclWrapperItem(real_item, self)
        return wrapped_item

    def iteritems(self):
        """
        @see: Backend.iteritems.__doc__
        """
        for item in self.backend.iteritems():
            if self._may(item.name, READ):
                yield AclWrapperItem(item, self)

    def history(self, reverse=True):
        """
        @see: Backend.history.__doc__
        """
        for revision in self.backend.history(reverse):
            if self._may(revision.item.name, READ):
                # The revisions returned here should only be StoredRevisions.
                # We wrap them nevertheless to be sure. Esp. revision.item
                # would otherwise give access to an unwrapped item.
                item = revision.item
                item = AclWrapperItem(item, self)
                revision = AclWrapperRevision(revision, item)
                yield revision

    def _get_acl(self, itemname):
        """
        Get ACL strings from the last revision's metadata and return ACL object.
        """
        try:
            item = self.backend.get_item(itemname)
            # we always use the ACLs set on the latest revision:
            current_rev = item.get_revision(-1)
            acls = current_rev[ACL]
        except (NoSuchItemError, NoSuchRevisionError, KeyError):
            # do not use default acl here
            acls = []
        if not isinstance(acls, (tuple, list)):
            acls = (acls, )
        default = self.default.default
        return AccessControlList(self.cfg,
                                 acls,
                                 default=default,
                                 valid=self.valid)

    def _may(self, itemname, right):
        """ Check if self.username may have <right> access on item <itemname>.

        For hierarchic=False we just check the item in question.

        For hierarchic=True, we check each item in the hierarchy. We
        start with the deepest item and recurse to the top of the tree.
        If one of those permits, True is returned.
        This is done *only* if there is *no ACL at all* (not even an empty one)
        on the items we 'recurse over'.

        For both configurations, we check `before` before the item/default
        acl and `after` after the item/default acl, of course.

        `default` is only used if there is no ACL on the item (and none on
        any of the item's parents when using hierarchic.)

        @param itemname: item to get permissions from
        @param right: the right to check

        @rtype: bool
        @return: True if you have permission or False
        """
        username = flaskg.user.name

        allowed = self.before.may(username, right)
        if allowed is not None:
            return allowed

        if self.hierarchic:
            items = itemname.split('/')  # create item hierarchy list
            some_acl = False
            for i in range(len(items), 0, -1):
                # Create the next pagename in the hierarchy
                # starting at the leaf, going to the root
                name = '/'.join(items[:i])
                acl = self._get_acl(name)
                if acl.has_acl():
                    some_acl = True
                    allowed = acl.may(username, right)
                    if allowed is not None:
                        return allowed
                    # If the item has an acl (even one that doesn't match) we *do not*
                    # check the parents. We only check the parents if there's no acl on
                    # the item at all.
                    break
            if not some_acl:
                allowed = self.default.may(username, right)
                if allowed is not None:
                    return allowed
        else:
            acl = self._get_acl(itemname)
            if acl.has_acl():
                allowed = acl.may(username, right)
                if allowed is not None:
                    return allowed
            else:
                allowed = self.default.may(username, right)
                if allowed is not None:
                    return allowed

        allowed = self.after.may(username, right)
        if allowed is not None:
            return allowed

        return False