Blob Blame History Raw
From 063f3ed388c96959f284f6e2b9e08a0328481197 Mon Sep 17 00:00:00 2001
From: Steve Milner <smilner@redhat.com>
Date: Fri, 18 Nov 2016 10:12:58 -0500
Subject: [PATCH] Removed the new auth module.

The new auth module doesn't support etcd 2.3+ yet. Since it is a net new
module in this version it has been disabled until an update becomes
available.

See: https://github.com/jplana/python-etcd/issues/210
---
 src/etcd/auth.py            | 255 --------------------------------------------
 src/etcd/tests/test_auth.py | 161 ----------------------------
 2 files changed, 416 deletions(-)
 delete mode 100644 src/etcd/auth.py
 delete mode 100644 src/etcd/tests/test_auth.py

diff --git a/src/etcd/auth.py b/src/etcd/auth.py
deleted file mode 100644
index 796772d..0000000
--- a/src/etcd/auth.py
+++ /dev/null
@@ -1,255 +0,0 @@
-import json
-
-import logging
-import etcd
-
-_log = logging.getLogger(__name__)
-
-
-class EtcdAuthBase(object):
-    entity = 'example'
-
-    def __init__(self, client, name):
-        self.client = client
-        self.name = name
-        self.uri = "{}/auth/{}s/{}".format(self.client.version_prefix,
-                                           self.entity, self.name)
-
-    @property
-    def names(self):
-        key = "{}s".format(self.entity)
-        uri = "{}/auth/{}".format(self.client.version_prefix, key)
-        response = self.client.api_execute(uri, self.client._MGET)
-        return json.loads(response.data.decode('utf-8'))[key]
-
-    def read(self):
-        try:
-            response = self.client.api_execute(self.uri, self.client._MGET)
-        except etcd.EtcdInsufficientPermissions as e:
-            _log.error("Any action on the authorization requires the root role")
-            raise
-        except etcd.EtcdKeyNotFound:
-            _log.info("%s '%s' not found", self.entity, self.name)
-            raise
-        except Exception as e:
-            _log.error("Failed to fetch %s in %s%s: %r",
-                       self.entity, self.client._base_uri,
-                       self.client.version_prefix, e)
-            raise etcd.EtcdException(
-                "Could not fetch {} '{}'".format(self.entity, self.name))
-
-        self._from_net(response.data)
-
-    def write(self):
-        try:
-            r = self.__class__(self.client, self.name)
-            r.read()
-        except etcd.EtcdKeyNotFound:
-            r = None
-        try:
-            for payload in self._to_net(r):
-                response = self.client.api_execute_json(self.uri,
-                                                        self.client._MPUT,
-                                                        params=payload)
-                # This will fail if the response is an error
-                self._from_net(response.data)
-        except etcd.EtcdInsufficientPermissions as e:
-            _log.error("Any action on the authorization requires the root role")
-            raise
-        except Exception as e:
-            _log.error("Failed to write %s '%s'", self.entity, self.name)
-            # TODO: fine-grained exception handling
-            raise etcd.EtcdException(
-                "Could not write {} '{}': {}".format(self.entity,
-                                                     self.name, e))
-
-    def delete(self):
-        try:
-            _ = self.client.api_execute(self.uri, self.client._MDELETE)
-        except etcd.EtcdInsufficientPermissions as e:
-            _log.error("Any action on the authorization requires the root role")
-            raise
-        except etcd.EtcdKeyNotFound:
-            _log.info("%s '%s' not found", self.entity, self.name)
-            raise
-        except Exception as e:
-            _log.error("Failed to delete %s in %s%s: %r",
-                       self.entity, self._base_uri, self.version_prefix, e)
-            raise etcd.EtcdException(
-                "Could not delete {} '{}'".format(self.entity, self.name))
-
-    def _from_net(self, data):
-        raise NotImplementedError()
-
-    def _to_net(self, old=None):
-        raise NotImplementedError()
-
-    @classmethod
-    def new(cls, client, data):
-        c = cls(client, data[cls.entity])
-        c._from_net(data)
-        return c
-
-
-class EtcdUser(EtcdAuthBase):
-    """Class to manage in a orm-like way etcd users"""
-    entity = 'user'
-
-    def __init__(self, client, name):
-        super(EtcdUser, self).__init__(client, name)
-        self._roles = set()
-        self._password = None
-
-    def _from_net(self, data):
-        d = json.loads(data.decode('utf-8'))
-        self.roles = d.get('roles', [])
-        self.name = d.get('user')
-
-    def _to_net(self, prevobj=None):
-        if prevobj is None:
-            retval = [{"user": self.name, "password": self._password,
-                       "roles": list(self.roles)}]
-        else:
-            retval = []
-            if self._password:
-                retval.append({"user": self.name, "password": self._password})
-            to_grant = list(self.roles - prevobj.roles)
-            to_revoke = list(prevobj.roles - self.roles)
-            if to_grant:
-                retval.append({"user": self.name, "grant": to_grant})
-            if to_revoke:
-                retval.append({"user": self.name, "revoke": to_revoke})
-        # Let's blank the password now
-        # Even if the user can't be written we don't want it to leak anymore.
-        self._password = None
-        return retval
-
-    @property
-    def roles(self):
-        return self._roles
-
-    @roles.setter
-    def roles(self, val):
-        self._roles = set(val)
-
-    @property
-    def password(self):
-        """Empty property for password."""
-        return None
-
-    @password.setter
-    def password(self, new_password):
-        """Change user's password."""
-        self._password = new_password
-
-    def __str__(self):
-        return json.dumps(self._to_net()[0])
-
-
-
-class EtcdRole(EtcdAuthBase):
-    entity = 'role'
-
-    def __init__(self, client, name):
-        super(EtcdRole, self).__init__(client, name)
-        self._read_paths = set()
-        self._write_paths = set()
-
-    def _from_net(self, data):
-        d = json.loads(data.decode('utf-8'))
-        self.name = d.get('role')
-
-        try:
-            kv = d["permissions"]["kv"]
-        except:
-            self._read_paths = set()
-            self._write_paths = set()
-            return
-
-        self._read_paths = set(kv.get('read', []))
-        self._write_paths = set(kv.get('write', []))
-
-    def _to_net(self, prevobj=None):
-        retval = []
-        if prevobj is None:
-            retval.append({
-                "role": self.name,
-                "permissions":
-                {
-                    "kv":
-                    {
-                        "read": list(self._read_paths),
-                        "write": list(self._write_paths)
-                    }
-                }
-            })
-        else:
-            to_grant = {
-                'read': list(self._read_paths - prevobj._read_paths),
-                'write': list(self._write_paths - prevobj._write_paths)
-            }
-            to_revoke = {
-                'read': list(prevobj._read_paths - self._read_paths),
-                'write': list(prevobj._write_paths - self._write_paths)
-            }
-            if [path for sublist in to_revoke.values() for path in sublist]:
-                retval.append({'role': self.name, 'revoke': {'kv': to_revoke}})
-            if [path for sublist in to_grant.values() for path in sublist]:
-                retval.append({'role': self.name, 'grant': {'kv': to_grant}})
-        return retval
-
-    def grant(self, path, permission):
-        if permission.upper().find('R') >= 0:
-            self._read_paths.add(path)
-        if permission.upper().find('W') >= 0:
-            self._write_paths.add(path)
-
-    def revoke(self, path, permission):
-        if permission.upper().find('R') >= 0 and \
-           path in self._read_paths:
-            self._read_paths.remove(path)
-        if permission.upper().find('W') >= 0 and \
-           path in self._write_paths:
-            self._write_paths.remove(path)
-
-    @property
-    def acls(self):
-        perms = {}
-        try:
-            for path in self._read_paths:
-                perms[path] = 'R'
-            for path in self._write_paths:
-                if path in perms:
-                    perms[path] += 'W'
-                else:
-                    perms[path] = 'W'
-        except:
-            pass
-        return perms
-
-    @acls.setter
-    def acls(self, acls):
-        self._read_paths = set()
-        self._write_paths = set()
-        for path, permission in acls.items():
-            self.grant(path, permission)
-
-    def __str__(self):
-        return json.dumps({"role": self.name, 'acls': self.acls})
-
-
-class Auth(object):
-    def __init__(self, client):
-        self.client = client
-        self.uri = "{}/auth/enable".format(self.client.version_prefix)
-
-    @property
-    def active(self):
-        resp = self.client.api_execute(self.uri, self.client._MGET)
-        return json.loads(resp.data.decode('utf-8'))['enabled']
-
-    @active.setter
-    def active(self, value):
-        if value != self.active:
-            method = value and self.client._MPUT or self.client._MDELETE
-            self.client.api_execute(self.uri, method)
diff --git a/src/etcd/tests/test_auth.py b/src/etcd/tests/test_auth.py
deleted file mode 100644
index fc6ce70..0000000
--- a/src/etcd/tests/test_auth.py
+++ /dev/null
@@ -1,161 +0,0 @@
-from etcd.tests.integration.test_simple import EtcdIntegrationTest
-from etcd import auth
-import etcd
-
-
-class TestEtcdAuthBase(EtcdIntegrationTest):
-    cl_size = 1
-
-    def setUp(self):
-        # Sets up the root user, toggles auth
-        u = auth.EtcdUser(self.client, 'root')
-        u.password = 'testpass'
-        u.write()
-        self.client = etcd.Client(port=6001, username='root',
-                                password='testpass')
-        self.unauth_client = etcd.Client(port=6001)
-        a = auth.Auth(self.client)
-        a.active = True
-
-    def tearDown(self):
-        u = auth.EtcdUser(self.client, 'test_user')
-        r = auth.EtcdRole(self.client, 'test_role')
-        try:
-            u.delete()
-        except:
-            pass
-        try:
-            r.delete()
-        except:
-            pass
-        a = auth.Auth(self.client)
-        a.active = False
-
-
-class EtcdUserTest(TestEtcdAuthBase):
-    def test_names(self):
-        u = auth.EtcdUser(self.client, 'test_user')
-        self.assertEquals(u.names, ['root'])
-
-    def test_read(self):
-        u = auth.EtcdUser(self.client, 'root')
-        # Reading an existing user succeeds
-        try:
-            u.read()
-        except Exception:
-            self.fail("reading the root user raised an exception")
-
-        # roles for said user are fetched
-        self.assertEquals(u.roles, set(['root']))
-
-        # The user is correctly rendered out
-        self.assertEquals(u._to_net(), [{'user': 'root', 'password': None,
-                                         'roles': ['root']}])
-
-        # An inexistent user raises the appropriate exception
-        u = auth.EtcdUser(self.client, 'user.does.not.exist')
-        self.assertRaises(etcd.EtcdKeyNotFound, u.read)
-
-        # Reading with an unauthenticated client raises an exception
-        u = auth.EtcdUser(self.unauth_client, 'root')
-        self.assertRaises(etcd.EtcdInsufficientPermissions, u.read)
-
-        # Generic errors are caught
-        c = etcd.Client(port=9999)
-        u = auth.EtcdUser(c, 'root')
-        self.assertRaises(etcd.EtcdException, u.read)
-
-    def test_write_and_delete(self):
-        # Create an user
-        u = auth.EtcdUser(self.client, 'test_user')
-        u.roles.add('guest')
-        u.roles.add('root')
-        # directly from my suitcase
-        u.password = '123456'
-        try:
-            u.write()
-        except:
-            self.fail("creating a user doesn't work")
-        # Password gets wiped
-        self.assertEquals(u.password, None)
-        u.read()
-        # Verify we can log in as this user and access the auth (it has the
-        # root role)
-        cl = etcd.Client(port=6001, username='test_user',
-                         password='123456')
-        ul = auth.EtcdUser(cl, 'root')
-        try:
-            ul.read()
-        except etcd.EtcdInsufficientPermissions:
-            self.fail("Reading auth with the new user is not possible")
-
-        self.assertEquals(u.name, "test_user")
-        self.assertEquals(u.roles, set(['guest', 'root']))
-        # set roles as a list, it works!
-        u.roles = ['guest', 'test_group']
-        try:
-            u.write()
-        except:
-            self.fail("updating a user you previously created fails")
-        u.read()
-        self.assertIn('test_group', u.roles)
-
-        # Unauthorized access is properly handled
-        ua = auth.EtcdUser(self.unauth_client, 'test_user')
-        self.assertRaises(etcd.EtcdInsufficientPermissions, ua.write)
-
-        # now let's test deletion
-        du = auth.EtcdUser(self.client, 'user.does.not.exist')
-        self.assertRaises(etcd.EtcdKeyNotFound, du.delete)
-
-        # Delete test_user
-        u.delete()
-        self.assertRaises(etcd.EtcdKeyNotFound, u.read)
-        # Permissions are properly handled
-        self.assertRaises(etcd.EtcdInsufficientPermissions, ua.delete)
-
-
-class EtcdRoleTest(TestEtcdAuthBase):
-    def test_names(self):
-        r = auth.EtcdRole(self.client, 'guest')
-        self.assertListEqual(r.names, [u'guest', u'root'])
-
-    def test_read(self):
-        r = auth.EtcdRole(self.client, 'guest')
-        try:
-            r.read()
-        except:
-            self.fail('Reading an existing role failed')
-
-        self.assertEquals(r.acls, {'*': 'RW'})
-        # We can actually skip most other read tests as they are common
-        # with EtcdUser
-
-    def test_write_and_delete(self):
-        r = auth.EtcdRole(self.client, 'test_role')
-        r.acls = {'*': 'R', '/test/*': 'RW'}
-        try:
-            r.write()
-        except:
-            self.fail("Writing a simple groups should not fail")
-
-        r1 = auth.EtcdRole(self.client, 'test_role')
-        r1.read()
-        self.assertEquals(r1.acls, r.acls)
-        r.revoke('/test/*', 'W')
-        r.write()
-        r1.read()
-        self.assertEquals(r1.acls, {'*': 'R', '/test/*': 'R'})
-        r.grant('/pub/*', 'RW')
-        r.write()
-        r1.read()
-        self.assertEquals(r1.acls['/pub/*'], 'RW')
-        # All other exceptions are tested by the user tests
-        r1.name = None
-        self.assertRaises(etcd.EtcdException, r1.write)
-        # ditto for delete
-        try:
-            r.delete()
-        except:
-            self.fail("A normal delete should not fail")
-        self.assertRaises(etcd.EtcdKeyNotFound, r.read)
-- 
2.7.4