On 11/10/2014 05:09 AM, lvroyce(a)linux.vnet.ibm.com wrote:
From: Royce Lv <lvroyce(a)linux.vnet.ibm.com>
Put ldap validation in a single function to resue in authorization.
Tested:
1. LDAP:
GET /users?_user_id=a_valid_user_id
GET /users?_user_id=invalid_user_id
GET /groups
2. PAM:
GET /users
GET /groups
You need to adjust the docs/API.md to reflect those changes.
Signed-off-by: Royce Lv <lvroyce(a)linux.vnet.ibm.com>
---
src/kimchi/control/auth.py | 42 ++++++++++++++
src/kimchi/control/host.py | 14 -----
src/kimchi/i18n.py | 1 +
src/kimchi/model/auth.py | 136 +++++++++++++++++++++++++++++++++++++++++++++
src/kimchi/model/host.py | 19 -------
src/kimchi/model/vms.py | 4 +-
tests/test_rest.py | 4 +-
7 files changed, 183 insertions(+), 37 deletions(-)
create mode 100644 src/kimchi/control/auth.py
create mode 100644 src/kimchi/model/auth.py
diff --git a/src/kimchi/control/auth.py b/src/kimchi/control/auth.py
new file mode 100644
index 0000000..ebc019e
--- /dev/null
+++ b/src/kimchi/control/auth.py
Name it as users.py and groups.py
@@ -0,0 +1,42 @@
+#
+# Project Kimchi
+#
+# Copyright IBM, Corp. 2013-2014
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+
+from kimchi.control.base import SimpleCollection
+from kimchi.control.utils import get_class_name, model_fn, UrlSubNode
+from kimchi.template import render
+
+
+@UrlSubNode('users', True)
+class Users(SimpleCollection):
+ def __init__(self, model):
+ super(Users, self).__init__(model)
+ self.role_key = 'guests'
+
+ def get(self, filter_params):
+ res_list = []
+ get_list = getattr(self.model, model_fn(self, 'get_list'))
+ res_list = get_list(*self.model_args, **filter_params)
+ return render(get_class_name(self), res_list)
+
+
+@UrlSubNode('groups', True)
+class Groups(SimpleCollection):
+ def __init__(self, model):
+ super(Groups, self).__init__(model)
+ self.role_key = 'guests'
Split it in 2 files: users.py and groups.py as we have done one URI per
file to make maintenance easier.
diff --git a/src/kimchi/control/host.py b/src/kimchi/control/host.py
index 4362da7..5a185e3 100644
--- a/src/kimchi/control/host.py
+++ b/src/kimchi/control/host.py
@@ -36,8 +36,6 @@ class Host(Resource):
self.devices = Devices(self.model)
self.packagesupdate = PackagesUpdate(self.model)
self.repositories = Repositories(self.model)
- self.users = Users(self.model)
- self.groups = Groups(self.model)
self.swupdate = self.generate_action_handler_task('swupdate')
@property
@@ -159,15 +157,3 @@ class Repository(Resource):
@property
def data(self):
return self.info
-
-
-class Users(SimpleCollection):
- def __init__(self, model):
- super(Users, self).__init__(model)
- self.role_key = 'guests'
-
-
-class Groups(SimpleCollection):
- def __init__(self, model):
- super(Groups, self).__init__(model)
- self.role_key = 'guests'
diff --git a/src/kimchi/i18n.py b/src/kimchi/i18n.py
index f789259..4e5a59b 100644
--- a/src/kimchi/i18n.py
+++ b/src/kimchi/i18n.py
@@ -39,6 +39,7 @@ messages = {
"KCHAUTH0001E": _("Authentication failed for user
'%(username)s'. [Error code: %(code)s]"),
"KCHAUTH0002E": _("You are not authorized to access Kimchi"),
"KCHAUTH0003E": _("Specify %(item)s to login into Kimchi"),
+ "KCHAUTH0004E": _("User %(user_id)s not found with given LDAP
settings."),
"KCHDEVS0001E": _('Unknown "_cap" specified'),
"KCHDEVS0002E": _('"_passthrough" should be
"true" or "false"'),
diff --git a/src/kimchi/model/auth.py b/src/kimchi/model/auth.py
new file mode 100644
index 0000000..d52c919
--- /dev/null
+++ b/src/kimchi/model/auth.py
@@ -0,0 +1,136 @@
+#
+# Project Kimchi
+#
+# Copyright IBM, Corp. 2014
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+
+import grp
+import ldap
+import pwd
+
+from kimchi.config import config
+from kimchi.exception import NotFoundError
+
+
+class UsersModel(object):
+ def __init__(self, **args):
+ auth_type = config.get("authentication", "method")
+ for klass in UsersModel.__subclasses__():
+ if auth_type == klass.auth_type:
+ self.user = klass(**args)
+
+ def get_list(self, **args):
+ return self.user._get_list(**args)
+
+ def validate(self, user):
+ return self.user._validate(user)
+
+
+class PAMUsersModel(UsersModel):
+ auth_type = 'pam'
+
+ def __init__(self, **kargs):
+ pass
+
+ def _get_list(self):
+ return [user.pw_name for user in pwd.getpwall()
+ if user.pw_shell.rsplit("/")[-1] not in ["nologin",
"false"]]
+
+ def _validate(self, user):
+ try:
+ return user in self._get_list()
+ except:
+ return False
+
+
+class LDAPUsersModel(UsersModel):
+ auth_type = 'ldap'
+
+ def __init__(self, **kargs):
+ pass
+
+ def _get_list(self, _user_id=''):
+ return self._get_user(_user_id)
+
+ def _validate(self, user):
+ try:
+ self._get_user(user)
+ return True
+ except NotFoundError:
+ return False
+
+ def _get_user(self, _user_id):
+ ldap_server = config.get("authentication",
"ldap_server").strip('"')
+ ldap_search_base = config.get(
+ "authentication",
"ldap_search_base").strip('"')
+ ldap_search_filter = config.get(
+ "authentication", "ldap_search_filter",
+ vars={"username":
_user_id.encode("utf-8")}).strip('"')
+
+ connect = ldap.open(ldap_server)
+ try:
+ result = connect.search_s(
+ ldap_search_base, ldap.SCOPE_SUBTREE, ldap_search_filter)
+ if len(result) == 0:
+ raise NotFoundError("KCHAUTH0004E", {'user_id':
_user_id})
+ return result[0][1]
+ except ldap.NO_SUCH_OBJECT:
+ raise NotFoundError("KCHAUTH0004E", {'user_id':
_user_id})
+
+
Same here. One URI per file.
+class GroupsModel(object):
+ def __init__(self, **args):
+ auth_type = config.get("authentication", "method")
+ for klass in GroupsModel.__subclasses__():
+ if auth_type == klass.auth_type:
+ self.grp = klass(**args)
+
+
+ def get_list(self, **args):
+ if hasattr(self.grp, '_get_list'):
+ return self.grp._get_list(**args)
+ else:
+ return list()
+
+ def validate(self, gid):
+ return self.grp._validate(gid)
+
+
+class PAMGroupsModel(GroupsModel):
+ auth_type = 'pam'
+
+ def __init__(self, **kargs):
+ pass
+
+ def _get_list(self):
+ return [group.gr_name for group in grp.getgrall()]
+
+ def _validate(self, gid):
+ try:
+ grp.getgrnam(gid)
+ except KeyError:
+ return False
+ return True
+
+
+class LDAPGroupsModel(GroupsModel):
+ auth_type = 'ldap'
+
+ def __init__(self, **kargs):
+ pass
+
+ def _validate(self, gid):
+ return False
diff --git a/src/kimchi/model/host.py b/src/kimchi/model/host.py
index 8cddcdc..009f448 100644
--- a/src/kimchi/model/host.py
+++ b/src/kimchi/model/host.py
@@ -17,12 +17,10 @@
# License along with this library; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
-import grp
import libvirt
import os
import time
import platform
-import pwd
from collections import defaultdict
import psutil
@@ -457,20 +455,3 @@ class RepositoryModel(object):
raise InvalidOperation('KCHREPOS0014E')
return self._repositories.removeRepository(repo_id)
-
-
-class UsersModel(object):
- def __init__(self, **kargs):
- pass
-
- def get_list(self):
- return [user.pw_name for user in pwd.getpwall()
- if user.pw_shell.rsplit("/")[-1] not in ["nologin",
"false"]]
-
-
-class GroupsModel(object):
- def __init__(self, **kargs):
- pass
-
- def get_list(self):
- return [group.gr_name for group in grp.getgrall()]
diff --git a/src/kimchi/model/vms.py b/src/kimchi/model/vms.py
index f2e4ae3..8f14f9e 100644
--- a/src/kimchi/model/vms.py
+++ b/src/kimchi/model/vms.py
@@ -249,8 +249,8 @@ class VMModel(object):
self.conn = kargs['conn']
self.objstore = kargs['objstore']
self.vmscreenshot = VMScreenshotModel(**kargs)
- self.users = import_class('kimchi.model.host.UsersModel')(**kargs)
- self.groups = import_class('kimchi.model.host.GroupsModel')(**kargs)
+ self.users = import_class('kimchi.model.auth.UsersModel')(**kargs)
+ self.groups = import_class('kimchi.model.auth.GroupsModel')(**kargs)
def update(self, name, params):
dom = self.get_vm(name, self.conn)
diff --git a/tests/test_rest.py b/tests/test_rest.py
index 9bc930f..420bfd0 100644
--- a/tests/test_rest.py
+++ b/tests/test_rest.py
@@ -277,7 +277,7 @@ class RestTests(unittest.TestCase):
self.assertEquals(params[key], vm[key])
# change only VM users - groups are not changed (default is empty)
- resp = self.request('/host/users', '{}', 'GET')
+ resp = self.request('/users', '{}', 'GET')
users = json.loads(resp.read())
req = json.dumps({'users': users})
resp = self.request('/vms/∨м-црdαtеd', req, 'PUT')
@@ -286,7 +286,7 @@ class RestTests(unittest.TestCase):
self.assertEquals(users, info['users'])
# change only VM groups - users are not changed (default is empty)
- resp = self.request('/host/groups', '{}', 'GET')
+ resp = self.request('/groups', '{}', 'GET')
groups = json.loads(resp.read())
req = json.dumps({'groups': groups})
resp = self.request('/vms/∨м-црdαtеd', req, 'PUT')