On 2014年11月12日 19:53, Aline Manera wrote:
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.
ACK
>
> 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.
ACK
> 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.
ACK
> +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')