[PATCH] [Kimchi] Remove role_key parameter
by Aline Manera
Signed-off-by: Aline Manera <alinefm(a)linux.vnet.ibm.com>
---
control/cpuinfo.py | 3 +--
control/groups.py | 3 +--
control/host.py | 7 +------
control/interfaces.py | 4 +---
control/networks.py | 4 +---
control/peers.py | 3 +--
control/storagepools.py | 4 +---
control/storageservers.py | 5 +----
control/templates.py | 4 +---
control/users.py | 3 +--
control/vms.py | 6 +-----
11 files changed, 11 insertions(+), 35 deletions(-)
diff --git a/control/cpuinfo.py b/control/cpuinfo.py
index f795b3f..322ff8f 100644
--- a/control/cpuinfo.py
+++ b/control/cpuinfo.py
@@ -1,7 +1,7 @@
#
# Project Kimchi
#
-# Copyright IBM Corp, 2015-2016
+# Copyright IBM Corp, 2015-2017
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
@@ -25,7 +25,6 @@ class CPUInfo(Resource):
def __init__(self, model):
super(CPUInfo, self).__init__(model)
self.admin_methods = ['GET']
- self.role_key = 'host'
self.uri_fmt = "/host/cpuinfo"
@property
diff --git a/control/groups.py b/control/groups.py
index 8c5c4c6..1e037bd 100644
--- a/control/groups.py
+++ b/control/groups.py
@@ -1,7 +1,7 @@
#
# Project Kimchi
#
-# Copyright IBM Corp, 2015-2016
+# Copyright IBM Corp, 2015-2017
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
@@ -25,4 +25,3 @@ from wok.control.utils import UrlSubNode
class Groups(SimpleCollection):
def __init__(self, model):
super(Groups, self).__init__(model)
- self.role_key = 'guests'
diff --git a/control/host.py b/control/host.py
index 6d52bc1..b1204fe 100644
--- a/control/host.py
+++ b/control/host.py
@@ -1,7 +1,7 @@
#
# Project Kimchi
#
-# Copyright IBM Corp, 2015-2016
+# Copyright IBM Corp, 2015-2017
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
@@ -30,7 +30,6 @@ from wok.plugins.kimchi.utils import is_s390x
class Host(Resource):
def __init__(self, model, id=None):
super(Host, self).__init__(model, id)
- self.role_key = 'host'
self.admin_methods = ['GET', 'POST']
self.uri_fmt = '/host/%s'
self.devices = Devices(self.model)
@@ -46,7 +45,6 @@ class Host(Resource):
class VolumeGroups(Collection):
def __init__(self, model):
super(VolumeGroups, self).__init__(model)
- self.role_key = 'host'
self.uri_fmt = "/host/vgs"
self.admin_methods = ['GET']
self.resource = VolumeGroup
@@ -55,7 +53,6 @@ class VolumeGroups(Collection):
class VolumeGroup(Resource):
def __init__(self, model, id=None):
super(VolumeGroup, self).__init__(model, id)
- self.role_key = 'host'
self.uri_fmt = "/host/vgs/%s"
self.admin_methods = ['GET']
@@ -89,7 +86,6 @@ class Device(Resource):
class Partitions(Collection):
def __init__(self, model):
super(Partitions, self).__init__(model)
- self.role_key = 'storage'
self.admin_methods = ['GET']
self.resource = Partition
@@ -112,7 +108,6 @@ class Partitions(Collection):
class Partition(Resource):
def __init__(self, model, id):
- self.role_key = 'storage'
self.admin_methods = ['GET']
super(Partition, self).__init__(model, id)
diff --git a/control/interfaces.py b/control/interfaces.py
index c65839a..7b6127a 100644
--- a/control/interfaces.py
+++ b/control/interfaces.py
@@ -1,7 +1,7 @@
#
# Project Kimchi
#
-# Copyright IBM Corp, 2015-2016
+# Copyright IBM Corp, 2015-2017
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
@@ -25,7 +25,6 @@ from wok.control.utils import UrlSubNode
class Interfaces(Collection):
def __init__(self, model):
super(Interfaces, self).__init__(model)
- self.role_key = 'network'
self.admin_methods = ['GET']
self.resource = Interface
@@ -33,7 +32,6 @@ class Interfaces(Collection):
class Interface(Resource):
def __init__(self, model, ident):
super(Interface, self).__init__(model, ident)
- self.role_key = 'network'
self.admin_methods = ['GET']
self.uri_fmt = "/interfaces/%s"
diff --git a/control/networks.py b/control/networks.py
index 1b8012e..d743873 100644
--- a/control/networks.py
+++ b/control/networks.py
@@ -1,7 +1,7 @@
#
# Project Kimchi
#
-# Copyright IBM Corp, 2015-2016
+# Copyright IBM Corp, 2015-2017
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
@@ -39,7 +39,6 @@ NETWORK_REQUESTS = {
class Networks(Collection):
def __init__(self, model):
super(Networks, self).__init__(model)
- self.role_key = 'network'
self.admin_methods = ['POST']
self.resource = Network
@@ -51,7 +50,6 @@ class Networks(Collection):
class Network(Resource):
def __init__(self, model, ident):
super(Network, self).__init__(model, ident)
- self.role_key = 'network'
self.admin_methods = ['PUT', 'POST', 'DELETE']
self.uri_fmt = "/networks/%s"
self.activate = self.generate_action_handler('activate')
diff --git a/control/peers.py b/control/peers.py
index c0d83ac..5eaacdd 100644
--- a/control/peers.py
+++ b/control/peers.py
@@ -1,7 +1,7 @@
#
# Project Kimchi
#
-# Copyright IBM Corp, 2015-2016
+# Copyright IBM Corp, 2015-2017
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
@@ -25,5 +25,4 @@ from wok.control.utils import UrlSubNode
class Peers(SimpleCollection):
def __init__(self, model):
super(Peers, self).__init__(model)
- self.role_key = 'peers'
self.admin_methods = ['GET']
diff --git a/control/storagepools.py b/control/storagepools.py
index e188aae..26aab7d 100644
--- a/control/storagepools.py
+++ b/control/storagepools.py
@@ -1,7 +1,7 @@
#
# Project Kimchi
#
-# Copyright IBM Corp, 2015-2016
+# Copyright IBM Corp, 2015-2017
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
@@ -47,7 +47,6 @@ STORAGEPOOL_REQUESTS = {
class StoragePools(Collection):
def __init__(self, model):
super(StoragePools, self).__init__(model)
- self.role_key = 'storage'
self.admin_methods = ['POST']
self.resource = StoragePool
isos = IsoPool(model)
@@ -95,7 +94,6 @@ class StoragePools(Collection):
class StoragePool(Resource):
def __init__(self, model, ident):
super(StoragePool, self).__init__(model, ident)
- self.role_key = 'storage'
self.admin_methods = ['PUT', 'POST', 'DELETE']
self.uri_fmt = "/storagepools/%s"
self.activate = self.generate_action_handler('activate')
diff --git a/control/storageservers.py b/control/storageservers.py
index 9f9cade..9d18514 100644
--- a/control/storageservers.py
+++ b/control/storageservers.py
@@ -1,7 +1,7 @@
#
# Project Kimchi
#
-# Copyright IBM Corp, 2015-2016
+# Copyright IBM Corp, 2015-2017
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
@@ -26,7 +26,6 @@ from wok.control.utils import get_class_name, model_fn, UrlSubNode
class StorageServers(Collection):
def __init__(self, model):
super(StorageServers, self).__init__(model)
- self.role_key = 'storage'
self.admin_methods = ['GET']
self.resource = StorageServer
@@ -34,7 +33,6 @@ class StorageServers(Collection):
class StorageServer(Resource):
def __init__(self, model, ident):
super(StorageServer, self).__init__(model, ident)
- self.role_key = 'storage'
self.admin_methods = ['GET']
self.storagetargets = StorageTargets(self.model,
self.ident.decode("utf-8"))
@@ -47,7 +45,6 @@ class StorageServer(Resource):
class StorageTargets(Collection):
def __init__(self, model, server):
super(StorageTargets, self).__init__(model)
- self.role_key = 'storage'
self.admin_methods = ['GET']
self.server = server
self.resource_args = [self.server, ]
diff --git a/control/templates.py b/control/templates.py
index ca36be3..a5d7dd5 100644
--- a/control/templates.py
+++ b/control/templates.py
@@ -1,7 +1,7 @@
#
# Project Kimchi
#
-# Copyright IBM Corp, 2015-2016
+# Copyright IBM Corp, 2015-2017
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
@@ -40,7 +40,6 @@ TEMPLATE_REQUESTS = {
class Templates(Collection):
def __init__(self, model):
super(Templates, self).__init__(model)
- self.role_key = 'templates'
self.admin_methods = ['GET', 'POST']
self.resource = Template
@@ -52,7 +51,6 @@ class Templates(Collection):
class Template(Resource):
def __init__(self, model, ident):
super(Template, self).__init__(model, ident)
- self.role_key = 'templates'
self.admin_methods = ['PUT', 'POST', 'DELETE']
self.uri_fmt = "/templates/%s"
self.clone = self.generate_action_handler('clone')
diff --git a/control/users.py b/control/users.py
index e551920..c71b621 100644
--- a/control/users.py
+++ b/control/users.py
@@ -1,7 +1,7 @@
#
# Project Kimchi
#
-# Copyright IBM Corp, 2015-2016
+# Copyright IBM Corp, 2015-2017
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
@@ -26,7 +26,6 @@ from wok.template import render
class Users(SimpleCollection):
def __init__(self, model):
super(Users, self).__init__(model)
- self.role_key = 'guests'
def get(self, filter_params):
res_list = []
diff --git a/control/vms.py b/control/vms.py
index 645cb40..80e48e1 100644
--- a/control/vms.py
+++ b/control/vms.py
@@ -1,7 +1,7 @@
#
# Project Kimchi
#
-# Copyright IBM Corp, 2015-2016
+# Copyright IBM Corp, 2015-2017
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
@@ -52,7 +52,6 @@ class VMs(AsyncCollection):
def __init__(self, model):
super(VMs, self).__init__(model)
self.resource = VM
- self.role_key = 'guests'
self.admin_methods = ['POST']
# set user log messages and make sure all parameters are present
@@ -63,7 +62,6 @@ class VMs(AsyncCollection):
class VM(Resource):
def __init__(self, model, ident):
super(VM, self).__init__(model, ident)
- self.role_key = 'guests'
self.screenshot = VMScreenShot(model, ident)
self.virtviewerfile = VMVirtViewerFile(model, ident)
self.uri_fmt = '/vms/%s'
@@ -98,7 +96,6 @@ class VM(Resource):
class VMScreenShot(Resource):
def __init__(self, model, ident):
super(VMScreenShot, self).__init__(model, ident)
- self.role_key = 'guests'
def get(self):
self.lookup()
@@ -109,7 +106,6 @@ class VMScreenShot(Resource):
class VMVirtViewerFile(Resource):
def __init__(self, model, ident):
super(VMVirtViewerFile, self).__init__(model, ident)
- self.role_key = 'guests'
@property
def data(self):
--
2.7.4
7 years, 8 months
[PATCH] [Wok 0/2] Remove dhparams generation from build
by Lucio Correia
This patch moves dhparams.pem file generation to package post-install and
server initialization when running on development mode (i.e. running from
source)
It also fix an issue on nginx configure reloading. If nginx was not up and
running, reload command failed, causing tests to break or forcing user to
restart nginx manually.
Lucio Correia (2):
Generate dhparams in post-install and development mode
Make sure nginx is running before reloading its config
Makefile.am | 2 --
contrib/DEBIAN/control.in | 1 -
contrib/DEBIAN/postinst | 3 +++
contrib/wok.spec.fedora.in | 4 +++-
contrib/wok.spec.suse.in | 4 +++-
src/Makefile.am | 8 +-------
src/wok/proxy.py | 20 ++++++++++++++++----
7 files changed, 26 insertions(+), 16 deletions(-)
--
2.7.4
7 years, 8 months
[PATCH] [Wok] Do not link user role with UI tabs
by Aline Manera
Thinking about providing more granularity on authorization (ie, grant
access to a normal user to create a VM on Kimchi, for example) the user
role (sysadmin or normal user) was linked to the UI tabs instead
of the API itself
It can cause multiple issues, for example:
- different plugins with the same tab name (the case of Ginger and
Kimchi) will get the authorization settings merged
- what about an API in use for different tabs?
To avoid those problems, remove the role_key parameter and also do not
link UI tabs with user role.
Signed-off-by: Aline Manera <alinefm(a)linux.vnet.ibm.com>
---
src/wok/auth.py | 86 +++++++++++++++++++++++-------------------------
src/wok/control/base.py | 16 ++++-----
src/wok/control/logs.py | 3 +-
src/wok/control/utils.py | 8 ++---
tests/test_server.py | 7 ++--
tests/utils.py | 18 ++++------
ui/js/src/wok.login.js | 4 +--
ui/js/src/wok.main.js | 9 +++--
8 files changed, 67 insertions(+), 84 deletions(-)
diff --git a/src/wok/auth.py b/src/wok/auth.py
index 6b1c160..976d729 100644
--- a/src/wok/auth.py
+++ b/src/wok/auth.py
@@ -1,7 +1,7 @@
#
# Project Wok
#
-# Copyright IBM Corp, 2015-2016
+# Copyright IBM Corp, 2015-2017
#
# Code derived from Project Kimchi
#
@@ -35,13 +35,11 @@ import urllib2
from wok import template
from wok.config import config
from wok.exception import InvalidOperation, OperationFailed
-from wok.utils import get_all_tabs, run_command
+from wok.utils import run_command
USER_NAME = 'username'
+USER_ROLE = 'role'
USER_GROUPS = 'groups'
-USER_ROLES = 'roles'
-
-tabs = get_all_tabs()
def redirect_login():
@@ -60,6 +58,20 @@ def debug(msg):
class User(object):
+ def __init__(self, username):
+ self.name = username
+ self.groups = self._get_groups()
+ # after adding support to change user roles that info should be read
+ # from a specific objstore and fallback to default only if any entry is
+ # found
+ self.role = self._get_role()
+
+ def _get_groups(self):
+ pass
+
+ def _get_role(self):
+ pass
+
@classmethod
def get(cls, auth_args):
auth_type = auth_args.pop('auth_type')
@@ -78,29 +90,23 @@ class PAMUser(User):
auth_type = "pam"
def __init__(self, username):
- self.user = {}
- self.user[USER_NAME] = username
- self.user[USER_GROUPS] = None
- # after adding support to change user roles that info should be read
- # from a specific objstore and fallback to default only if any entry is
- # found
- self.user[USER_ROLES] = dict.fromkeys(tabs, 'user')
+ super(PAMUser, self).__init__(username)
- def get_groups(self):
- out, err, rc = run_command(['id', '-Gn', self.user[USER_NAME]])
+ def _get_groups(self):
+ out, err, rc = run_command(['id', '-Gn', self.name])
if rc == 0:
- self.user[USER_GROUPS] = out.rstrip().split(" ")
+ return out.rstrip().split(" ")
- return self.user[USER_GROUPS]
+ return None
- def get_roles(self):
+ def _get_role(self):
if self.has_sudo():
# after adding support to change user roles that info should be
# read from a specific objstore and fallback to default only if
# any entry is found
- self.user[USER_ROLES] = dict.fromkeys(tabs, 'admin')
+ return 'admin'
- return self.user[USER_ROLES]
+ return 'user'
def has_sudo(self):
result = multiprocessing.Value('i', 0, lock=False)
@@ -117,16 +123,16 @@ class PAMUser(User):
os.setsid()
fcntl.ioctl(slave, termios.TIOCSCTTY, 0)
- out, err, exit = run_command(['sudo', '-l', '-U', self.user[USER_NAME],
+ out, err, exit = run_command(['sudo', '-l', '-U', self.name,
'sudo'])
if exit == 0:
- debug("User %s is allowed to run sudo" % self.user[USER_NAME])
+ debug("User %s is allowed to run sudo" % self.name)
# sudo allows a wide range of configurations, such as controlling
# which binaries the user can execute with sudo.
# For now, we will just check whether the user is allowed to run
# any command with sudo.
out, err, exit = run_command(['sudo', '-l', '-U',
- self.user[USER_NAME]])
+ self.name])
for line in out.split('\n'):
if line and re.search("(ALL)", line):
result.value = True
@@ -134,12 +140,9 @@ class PAMUser(User):
result.value)
return
debug("User %s can only run some commands with sudo" %
- self.user[USER_NAME])
+ self.name)
else:
- debug("User %s is not allowed to run sudo" % self.user[USER_NAME])
-
- def get_user(self):
- return self.user
+ debug("User %s is not allowed to run sudo" % self.name)
@staticmethod
def authenticate(username, password, service="passwd"):
@@ -189,12 +192,7 @@ class LDAPUser(User):
auth_type = "ldap"
def __init__(self, username):
- self.user = {}
- self.user[USER_NAME] = username
- self.user[USER_GROUPS] = list()
- # FIXME: user roles will be changed according roles assignment after
- # objstore is integrated
- self.user[USER_ROLES] = dict.fromkeys(tabs, 'user')
+ super(PAMUser, self).__init__(username)
@staticmethod
def authenticate(username, password):
@@ -227,19 +225,16 @@ class LDAPUser(User):
arg = {"username": username, "code": e.message}
raise OperationFailed("WOKAUTH0001E", arg)
- def get_groups(self):
- return self.user[USER_GROUPS]
+ def _get_groups(self):
+ return None
- def get_roles(self):
+ def _get_role(self):
admin_ids = config.get(
"authentication", "ldap_admin_id").strip('"').split(',')
for admin_id in admin_ids:
- if self.user[USER_NAME] == admin_id.strip():
- self.user[USER_ROLES] = dict.fromkeys(tabs, 'admin')
- return self.user[USER_ROLES]
-
- def get_user(self):
- return self.user
+ if self.name == admin_id.strip():
+ return 'admin'
+ return 'user'
def from_browser():
@@ -313,11 +308,12 @@ def login(username, password, **kwargs):
cherrypy.session.acquire_lock()
cherrypy.session.regenerate()
cherrypy.session[USER_NAME] = username
- cherrypy.session[USER_GROUPS] = user.get_groups()
- cherrypy.session[USER_ROLES] = user.get_roles()
+ cherrypy.session[USER_GROUPS] = user.groups
+ cherrypy.session[USER_ROLE] = user.role
cherrypy.session[template.REFRESH] = time.time()
cherrypy.session.release_lock()
- return user.get_user()
+ return {USER_NAME: user.name, USER_GROUPS: user.groups,
+ USER_ROLE: user.role}
def logout():
diff --git a/src/wok/control/base.py b/src/wok/control/base.py
index 786ecb7..3070e53 100644
--- a/src/wok/control/base.py
+++ b/src/wok/control/base.py
@@ -2,7 +2,7 @@
#
# Project Wok
#
-# Copyright IBM Corp, 2015-2016
+# Copyright IBM Corp, 2015-2017
#
# Code derived from Project Kimchi
#
@@ -27,7 +27,7 @@ import urllib2
import wok.template
from wok.asynctask import save_request_log_id
-from wok.auth import USER_GROUPS, USER_NAME, USER_ROLES
+from wok.auth import USER_GROUPS, USER_NAME, USER_ROLE
from wok.control.utils import get_class_name, internal_redirect, model_fn
from wok.control.utils import parse_request, validate_method
from wok.control.utils import validate_params
@@ -66,7 +66,6 @@ class Resource(object):
self.model = model
self.ident = ident
self.model_args = (ident,)
- self.role_key = None
self.admin_methods = []
self.log_map = {}
self.log_args = {
@@ -124,7 +123,7 @@ class Resource(object):
status = 500
method = 'POST'
- validate_method((method), self.role_key, self.admin_methods)
+ validate_method((method), self.admin_methods)
try:
request = parse_request()
validate_params(request, self, action_name)
@@ -191,8 +190,7 @@ class Resource(object):
details = None
status = 500
- method = validate_method(('GET', 'DELETE', 'PUT'),
- self.role_key, self.admin_methods)
+ method = validate_method(('GET', 'DELETE', 'PUT'), self.admin_methods)
try:
self.lookup()
@@ -222,7 +220,7 @@ class Resource(object):
def is_authorized(self):
user_name = cherrypy.session.get(USER_NAME, '')
user_groups = cherrypy.session.get(USER_GROUPS, [])
- user_role = cherrypy.session.get(USER_ROLES, {}).get(self.role_key)
+ user_role = cherrypy.session.get(USER_ROLE, None)
users = self.data.get("users", None)
groups = self.data.get("groups", None)
@@ -331,7 +329,6 @@ class Collection(object):
self.resource = Resource
self.resource_args = []
self.model_args = []
- self.role_key = None
self.admin_methods = []
self.log_map = {}
self.log_args = {}
@@ -432,8 +429,7 @@ class Collection(object):
status = 500
params = {}
- method = validate_method(('GET', 'POST'),
- self.role_key, self.admin_methods)
+ method = validate_method(('GET', 'POST'), self.admin_methods)
try:
if method == 'GET':
diff --git a/src/wok/control/logs.py b/src/wok/control/logs.py
index 4844b89..739270c 100644
--- a/src/wok/control/logs.py
+++ b/src/wok/control/logs.py
@@ -1,7 +1,7 @@
#
# Project Wok
#
-# Copyright IBM Corp, 2016
+# Copyright IBM Corp, 2016-2017
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
@@ -28,7 +28,6 @@ from wok.control.utils import UrlSubNode
class Logs(SimpleCollection):
def __init__(self, model):
super(Logs, self).__init__(model)
- self.role_key = 'logs'
self.admin_methods = ['GET']
def get(self, filter_params):
diff --git a/src/wok/control/utils.py b/src/wok/control/utils.py
index 987dd00..ef7b054 100644
--- a/src/wok/control/utils.py
+++ b/src/wok/control/utils.py
@@ -1,7 +1,7 @@
#
# Project Wok
#
-# Copyright IBM Corp, 2015-2016
+# Copyright IBM Corp, 2015-2017
#
# Code derived from Project Kimchi
#
@@ -24,7 +24,7 @@ import cherrypy
import json
from jsonschema import Draft3Validator, ValidationError, FormatChecker
-from wok.auth import USER_ROLES
+from wok.auth import USER_ROLE
from wok.exception import InvalidParameter, OperationFailed
from wok.utils import import_module, listPathModules
@@ -41,12 +41,12 @@ def model_fn(cls, fn_name):
return '%s_%s' % (get_class_name(cls), fn_name)
-def validate_method(allowed, role_key, admin_methods):
+def validate_method(allowed, admin_methods):
method = cherrypy.request.method.upper()
if method not in allowed:
raise cherrypy.HTTPError(405)
- user_role = cherrypy.session.get(USER_ROLES, {}).get(role_key)
+ user_role = cherrypy.session.get(USER_ROLE, None)
if user_role and user_role != 'admin' and method in admin_methods:
raise cherrypy.HTTPError(403)
diff --git a/tests/test_server.py b/tests/test_server.py
index 5541a4a..9c12b27 100644
--- a/tests/test_server.py
+++ b/tests/test_server.py
@@ -224,10 +224,9 @@ class ServerTests(unittest.TestCase):
user_info = json.loads(resp.read())
self.assertEquals(sorted(user_info.keys()),
- ['groups', 'roles', 'username'])
- roles = user_info['roles']
- for tab, role in roles.iteritems():
- self.assertEquals(role, u'admin')
+ ['groups', 'role', 'username'])
+ role = user_info['role']
+ self.assertEquals(role, u'admin')
cookie = resp.getheader('set-cookie')
hdrs['Cookie'] = cookie
diff --git a/tests/utils.py b/tests/utils.py
index f26aeff..739434f 100644
--- a/tests/utils.py
+++ b/tests/utils.py
@@ -34,7 +34,7 @@ import unittest
import wok.server
-from wok.auth import User, USER_NAME, USER_GROUPS, USER_ROLES, tabs
+from wok.auth import User
from wok.config import config
from wok.exception import NotFoundError, OperationFailed
from wok.utils import wok_log
@@ -140,21 +140,15 @@ class FakeUser(User):
sudo = True
def __init__(self, username):
- self.user = {}
- self.user[USER_NAME] = username
- self.user[USER_GROUPS] = None
- self.user[USER_ROLES] = dict.fromkeys(tabs, 'user')
+ super(FakeUser, self).__init__(username)
- def get_groups(self):
+ def _get_groups(self):
return sorted([group.gr_name for group in grp.getgrall()])[0:3]
- def get_roles(self):
+ def _get_role(self):
if self.sudo:
- self.user[USER_ROLES] = dict.fromkeys(tabs, 'admin')
- return self.user[USER_ROLES]
-
- def get_user(self):
- return self.user
+ return 'admin'
+ return 'user'
@staticmethod
def authenticate(username, password, service="passwd"):
diff --git a/ui/js/src/wok.login.js b/ui/js/src/wok.login.js
index fa2a98a..666a339 100644
--- a/ui/js/src/wok.login.js
+++ b/ui/js/src/wok.login.js
@@ -1,7 +1,7 @@
/*
* Project Wok
*
- * Copyright IBM Corp, 2015-2016
+ * Copyright IBM Corp, 2015-2017
*
* Code derived from Project Kimchi
*
@@ -78,7 +78,7 @@ wok.login_main = function() {
var lastPage = wok.cookie.get('lastPage');
var next_url = lastPage ? lastPage.replace(/\"/g,'') : "/";
}
- wok.cookie.set('roles',JSON.stringify(data.roles));
+ wok.cookie.set('user_role', data.role);
window.location.replace(window.location.pathname.replace(/\/+login.html/, '') + next_url);
}, function(jqXHR, textStatus, errorThrown) {
if (jqXHR.responseText == "") {
diff --git a/ui/js/src/wok.main.js b/ui/js/src/wok.main.js
index 658974a..c67e97c 100644
--- a/ui/js/src/wok.main.js
+++ b/ui/js/src/wok.main.js
@@ -1,7 +1,7 @@
/*
* Project Wok
*
- * Copyright IBM Corp, 2015-2016
+ * Copyright IBM Corp, 2015-2017
*
* Code derived from Project Kimchi
*
@@ -91,12 +91,11 @@ wok.main = function() {
var titleKey = tab.find('title').text();
var title = i18n[titleKey] ? i18n[titleKey] : titleKey;
var path = tab.find('path').text();
- var roles = wok.cookie.get('roles');
+ var user_role = wok.cookie.get('user_role');
var order = tab.find('order').text();
- if (roles) {
- var role = JSON.parse(roles)[titleKey.toLowerCase()];
- var mode = tab.find('[role="' + role + '"]').attr('mode');
+ if (user_role) {
+ var mode = tab.find('[role=' + user_role + ']').attr('mode');
wok.tabMode[titleKey.toLowerCase()] = mode;
if (mode != 'none') {
tabs.push({
--
2.7.4
7 years, 8 months
[PATCH v6] [WoK 0/6] 'reload' API implementation
by dhbarboza82@gmail.com
From: Daniel Henrique Barboza <danielhb(a)linux.vnet.ibm.com>
v6:
- added user log messages in /config/reload API
v5:
- added 'self.admin_methods = ['POST'] in control/config.py
v4:
- fixed a '()' in test_api.py
- changed the positioning of the notification message in i18n.py
v3:
- 'add_notification' is now being called before the reload command
v2:
- changed API name to 'reload'
- added test_api tests
This patch set implements a new WOK API called 'reload' under
/config/reload.
To test it:
$ curl -k -u danielhb -H "Content-Type: application/json" -H "Accept: application/json" -X POST 'https://localhost:8001/config/reload' -d'{}'
Enter host password for user 'danielhb':
{
"proxy_port":"8001",
"websockets_port":"64667",
"version":"2.3.0-55.git5c9127b",
"auth":"pam",
"server_root":""
}
This will reload WoK and all its plug-ins.
Daniel Henrique Barboza (6):
reload API: doc changes
reload API: control and model changes
reload API: new file tests/test_config_model.py
reload API: added rest API tests
reload API: adding notification before reloading operation
reload API: adding user log messages
docs/API/config.md | 2 +-
src/wok/control/config.py | 13 ++++++++++++-
src/wok/i18n.py | 3 +++
src/wok/model/config.py | 22 +++++++++++++++++++++-
tests/test_api.py | 9 ++++++++-
tests/test_config_model.py | 41 +++++++++++++++++++++++++++++++++++++++++
6 files changed, 86 insertions(+), 4 deletions(-)
create mode 100644 tests/test_config_model.py
--
2.7.4
7 years, 8 months
[PATCH] [Wok] Bug fix #147: Block authentication request after too many failures
by ramonn@linux.vnet.ibm.com
From: Ramon Medeiros <ramonn(a)linux.vnet.ibm.com>
To prevent brute force attack, creates a mechanism to allow 3 tries
first. After that, a timeout will start and will be added 30 seconds for
each failed try in a row.
Signed-off-by: Ramon Medeiros <ramonn(a)linux.vnet.ibm.com>
---
src/wok/i18n.py | 2 ++
src/wok/root.py | 66 ++++++++++++++++++++++++++++++++++++++++++++----
ui/js/src/wok.login.js | 21 +++++++++------
ui/pages/i18n.json.tmpl | 5 +++-
ui/pages/login.html.tmpl | 6 ++---
5 files changed, 82 insertions(+), 18 deletions(-)
diff --git a/src/wok/i18n.py b/src/wok/i18n.py
index e454e31..7d595b8 100644
--- a/src/wok/i18n.py
+++ b/src/wok/i18n.py
@@ -41,7 +41,9 @@ messages = {
"WOKAUTH0001E": _("Authentication failed for user '%(username)s'. [Error code: %(code)s]"),
"WOKAUTH0002E": _("You are not authorized to access Wok. Please, login first."),
"WOKAUTH0003E": _("Specify %(item)s to login into Wok."),
+ "WOKAUTH0004E": _("You have failed to login in too much attempts. Please, wait for %(seconds)s seconds to try again."),
"WOKAUTH0005E": _("Invalid LDAP configuration: %(item)s : %(value)s"),
+ "WOKAUTH0006E": _("The username or password you entered is incorrect. Please try again."),
"WOKLOG0001E": _("Invalid filter parameter. Filter parameters allowed: %(filters)s"),
"WOKLOG0002E": _("Creation of log file failed: %(err)s"),
diff --git a/src/wok/root.py b/src/wok/root.py
index 080b7f0..d314d25 100644
--- a/src/wok/root.py
+++ b/src/wok/root.py
@@ -1,7 +1,7 @@
#
# Project Wok
#
-# Copyright IBM Corp, 2015-2016
+# Copyright IBM Corp, 2015-2017
#
# Code derived from Project Kimchi
#
@@ -21,7 +21,9 @@
import cherrypy
import json
+import re
import os
+import time
from distutils.version import LooseVersion
from wok import auth
@@ -31,7 +33,7 @@ from wok.config import paths as wok_paths
from wok.control import sub_nodes
from wok.control.base import Resource
from wok.control.utils import parse_request
-from wok.exception import MissingParameter
+from wok.exception import MissingParameter, UnauthorizedError
from wok.reqlogger import log_request
@@ -48,7 +50,8 @@ class Root(Resource):
super(Root, self).__init__(model)
self._handled_error = ['error_page.400', 'error_page.404',
'error_page.405', 'error_page.406',
- 'error_page.415', 'error_page.500']
+ 'error_page.415', 'error_page.500',
+ 'error_page.403', 'error_page.401']
if not dev_env:
self._cp_config = dict([(key, self.error_production_handler)
@@ -146,6 +149,7 @@ class WokRoot(Root):
self.domain = 'wok'
self.messages = messages
self.extends = None
+ self.failed_logins = {}
# set user log messages and make sure all parameters are present
self.log_map = ROOT_REQUESTS
@@ -153,6 +157,13 @@ class WokRoot(Root):
@cherrypy.expose
def login(self, *args):
+ def _raise_timeout(user_id):
+ length = self.failed_logins[user_ip_sid]["count"]
+ timeout = (length - 3) * 30
+ details = e = UnauthorizedError("WOKAUTH0004E",
+ {"seconds": timeout})
+ log_request(code, params, details, method, 403)
+ raise cherrypy.HTTPError(403, e.message)
details = None
method = 'POST'
code = self.getRequestMessage(method, 'login')
@@ -161,17 +172,62 @@ class WokRoot(Root):
params = parse_request()
username = params['username']
password = params['password']
+
+ # no data passed: raise error
+ if len(username + password) == 0:
+ raise KeyError
+
except KeyError, item:
details = e = MissingParameter('WOKAUTH0003E', {'item': str(item)})
log_request(code, params, details, method, 400)
raise cherrypy.HTTPError(400, e.message)
+ # get authentication info
+ remote_ip = cherrypy.request.remote.ip
+ session_id = str(cherrypy.session.originalid)
+ user_ip_sid = re.escape(username + remote_ip + session_id)
+
+ # check for repetly
+ count = self.failed_logins.get(user_ip_sid, {"count": 0}).get("count")
+ if count >= 3:
+
+ # verify if timeout is still valid
+ last_try = self.failed_logins[user_ip_sid]["time"]
+ if time.time() < (last_try + ((count - 3) * 30)):
+ _raise_timeout(user_ip_sid)
+
try:
status = 200
user_info = auth.login(username, password)
+
+ # user logged sucessfuly: reset counters
+ if self.failed_logins.get(user_ip_sid) != None:
+ self.failed_logins.remove(user_ip_sid)
except cherrypy.HTTPError, e:
- status = e.status
- raise
+
+ # store time and prevent too much tries
+ if self.failed_logins.get(user_ip_sid) == None:
+ self.failed_logins[user_ip_sid] = {"time": time.time(),
+ "ip": remote_ip,
+ "session_id": session_id,
+ "username": username,
+ "count": 1}
+ else:
+
+ # tries take more than 30 seconds between each one: do not
+ # increase count
+ if (time.time() -
+ self.failed_logins[user_ip_sid]["time"]) < 30:
+
+ self.failed_logins[user_ip_sid]["time"] = time.time()
+ self.failed_logins[user_ip_sid]["count"] += 1
+
+ # more than 3 fails: raise error
+ if self.failed_logins[user_ip_sid]["count"] > 3:
+ _raise_timeout(user_ip_sid)
+
+ details = e = MissingParameter('WOKAUTH0006E')
+ raise cherrypy.HTTPError(401, e.message)
finally:
log_request(code, params, details, method, status)
diff --git a/ui/js/src/wok.login.js b/ui/js/src/wok.login.js
index fa2a98a..d9daacf 100644
--- a/ui/js/src/wok.login.js
+++ b/ui/js/src/wok.login.js
@@ -1,7 +1,7 @@
/*
* Project Wok
*
- * Copyright IBM Corp, 2015-2016
+ * Copyright IBM Corp, 2015-2017
*
* Code derived from Project Kimchi
*
@@ -19,6 +19,10 @@
*/
wok.login_main = function() {
"use strict";
+ var i18n;
+ wok.getI18n(function(i18nObj){
+ i18n = i18nObj;
+ }, false, "i18n.json", true);
// verify if language is available
var selectedLanguage = wok.lang.get();
@@ -50,7 +54,8 @@ wok.login_main = function() {
var query = window.location.search;
var error = /.*error=(.*?)(&|$)/g.exec(query);
if (error && error[1] === "sessionTimeout") {
- $("#messSession").show();
+ $("#errorArea").html(i18n["WOKAUT0001E"]);
+ $("#errorArea").show();
}
var userNameBox = $('#username');
@@ -82,13 +87,13 @@ wok.login_main = function() {
window.location.replace(window.location.pathname.replace(/\/+login.html/, '') + next_url);
}, function(jqXHR, textStatus, errorThrown) {
if (jqXHR.responseText == "") {
- $("#messUserPass").hide();
- $("#missServer").show();
- } else {
- $("#missServer").hide();
- $("#messUserPass").show();
+ $("#errorArea").html(i18n["WOKAUT0002E"]);
+ $("#errorArea").show();
+ } else if ((jqXHR.responseJSON != undefined) &&
+ ! (jqXHR.responseJSON["reason"] == undefined)) {
+ $("#errorArea").html(jqXHR.responseJSON["reason"]);
+ $("#errorArea").show();
}
- $("#messSession").hide();
$("#logging").hide();
$("#login").show();
});
diff --git a/ui/pages/i18n.json.tmpl b/ui/pages/i18n.json.tmpl
index ba29532..4329ad0 100644
--- a/ui/pages/i18n.json.tmpl
+++ b/ui/pages/i18n.json.tmpl
@@ -1,7 +1,7 @@
#*
* Project Wok
*
- * Copyright IBM Corp, 2014-2016
+ * Copyright IBM Corp, 2014-2017
*
* Code derived from Project Kimchi
*
@@ -39,6 +39,9 @@
"WOKHOST6001M": "$_("Max:")",
+ "WOKAUT0001E": "$_("Session timeout, please re-login.")",
+ "WOKAUT0002E": "$_("Server unreachable")",
+
"WOKSETT0001M": "$_("Application")",
"WOKSETT0002M": "$_("User")",
"WOKSETT0003M": "$_("Request")",
diff --git a/ui/pages/login.html.tmpl b/ui/pages/login.html.tmpl
index f5a4b2d..6f967cf 100644
--- a/ui/pages/login.html.tmpl
+++ b/ui/pages/login.html.tmpl
@@ -1,7 +1,7 @@
#*
* Project Wok
*
- * Copyright IBM Corp, 2014-2016
+ * Copyright IBM Corp, 2014-2017
*
* Code derived from Project Kimchi
*
@@ -104,9 +104,7 @@
<div class="container">
<div id="login-window" class="login-area row">
<div class="err-area">
- <div id="messUserPass" class="alert alert-danger" style="display: none;">$_("The username or password you entered is incorrect. Please try again.")</div>
- <div id="messSession" class="alert alert-danger" style="display: none;">$_("Session timeout, please re-login.")</div>
- <div id="missServer" class="alert alert-danger" style="display: none;">$_("Server unreachable.")</div>
+ <div id="errorArea" class="alert alert-danger" style="display: none;"></div>
</div>
<form id="form-login" class="form-horizontal" method="post">
<div class="form-group">
--
2.10.1 (Apple Git-78)
7 years, 8 months
[PATCH v2][Wok] Bug fix #147: Block authentication request after too many failures
by Ramon Medeiros
To prevent brute force attack, creates a mechanism to allow 3 tries
first. After that, a timeout will start and will be added 30 seconds for
each failed try in a row.
Signed-off-by: Ramon Medeiros <ramonn(a)linux.vnet.ibm.com>
---
Changes:
v2:
Set timeout by user, ip and session id. This will avoid trouble with
users using the same ip, like NAT.
Only count as failed login, tries with less than 30 seconds.
Does not store all try: store last time try.
src/wok/i18n.py | 1 +
src/wok/root.py | 57 +++++++++++++++++++++++++++++++++++++++++++++---
ui/js/src/wok.login.js | 10 ++++++++-
ui/pages/login.html.tmpl | 3 ++-
4 files changed, 66 insertions(+), 5 deletions(-)
diff --git a/src/wok/i18n.py b/src/wok/i18n.py
index e454e31..21cc4ea 100644
--- a/src/wok/i18n.py
+++ b/src/wok/i18n.py
@@ -41,6 +41,7 @@ messages = {
"WOKAUTH0001E": _("Authentication failed for user '%(username)s'. [Error code: %(code)s]"),
"WOKAUTH0002E": _("You are not authorized to access Wok. Please, login first."),
"WOKAUTH0003E": _("Specify %(item)s to login into Wok."),
+ "WOKAUTH0004E": _("You have failed to login in too much attempts. Please, wait for %(seconds)s seconds to try again."),
"WOKAUTH0005E": _("Invalid LDAP configuration: %(item)s : %(value)s"),
"WOKLOG0001E": _("Invalid filter parameter. Filter parameters allowed: %(filters)s"),
diff --git a/src/wok/root.py b/src/wok/root.py
index 080b7f0..4cabfdf 100644
--- a/src/wok/root.py
+++ b/src/wok/root.py
@@ -1,7 +1,7 @@
#
# Project Wok
#
-# Copyright IBM Corp, 2015-2016
+# Copyright IBM Corp, 2015-2017
#
# Code derived from Project Kimchi
#
@@ -21,7 +21,9 @@
import cherrypy
import json
+import re
import os
+import time
from distutils.version import LooseVersion
from wok import auth
@@ -31,7 +33,7 @@ from wok.config import paths as wok_paths
from wok.control import sub_nodes
from wok.control.base import Resource
from wok.control.utils import parse_request
-from wok.exception import MissingParameter
+from wok.exception import MissingParameter, UnauthorizedError
from wok.reqlogger import log_request
@@ -48,7 +50,8 @@ class Root(Resource):
super(Root, self).__init__(model)
self._handled_error = ['error_page.400', 'error_page.404',
'error_page.405', 'error_page.406',
- 'error_page.415', 'error_page.500']
+ 'error_page.415', 'error_page.500',
+ 'error_page.403']
if not dev_env:
self._cp_config = dict([(key, self.error_production_handler)
@@ -146,6 +149,7 @@ class WokRoot(Root):
self.domain = 'wok'
self.messages = messages
self.extends = None
+ self.failed_logins = {}
# set user log messages and make sure all parameters are present
self.log_map = ROOT_REQUESTS
@@ -153,6 +157,13 @@ class WokRoot(Root):
@cherrypy.expose
def login(self, *args):
+ def _raise_timeout(user_id):
+ length = self.failed_logins[user_ip_sid]["count"]
+ timeout = (length - 3) * 30
+ details = e = UnauthorizedError("WOKAUTH0004E",
+ {"seconds": timeout})
+ log_request(code, params, details, method, 403)
+ raise cherrypy.HTTPError(403, e.message)
details = None
method = 'POST'
code = self.getRequestMessage(method, 'login')
@@ -166,10 +177,50 @@ class WokRoot(Root):
log_request(code, params, details, method, 400)
raise cherrypy.HTTPError(400, e.message)
+ # get authentication info
+ remote_ip = cherrypy.request.remote.ip
+ session_id = str(cherrypy.session.originalid)
+ user_ip_sid = re.escape(username + remote_ip + session_id)
+
+ # check for repetly
+ count = self.failed_logins.get(user_ip_sid, {"count": 0}).get("count")
+ if count >= 3:
+
+ # verify if timeout is still valid
+ last_try = self.failed_logins[user_ip_sid]["time"]
+ if time.time() < (last_try + ((count - 3) * 30)):
+ _raise_timeout(user_ip_sid)
+
try:
status = 200
user_info = auth.login(username, password)
+
+ # user logged sucessfuly: reset counters
+ if self.failed_logins.get(user_ip_sid) != None:
+ self.failed_logins.remove(user_ip_sid)
except cherrypy.HTTPError, e:
+
+ # store time and prevent too much tries
+ if self.failed_logins.get(user_ip_sid) == None:
+ self.failed_logins[user_ip_sid] = {"time": time.time(),
+ "ip": remote_ip,
+ "session_id": session_id,
+ "username": username,
+ "count": 1}
+ else:
+
+ # tries take more than 30 seconds between each one: do not
+ # increase count
+ if (time.time() -
+ self.failed_logins[user_ip_sid]["time"]) < 30:
+
+ self.failed_logins[user_ip_sid]["time"] = time.time()
+ self.failed_logins[user_ip_sid]["count"] += 1
+
+ # more than 3 fails: raise error
+ if self.failed_logins[user_ip_sid]["count"] > 3:
+ _raise_timeout(user_ip_sid)
+
status = e.status
raise
finally:
diff --git a/ui/js/src/wok.login.js b/ui/js/src/wok.login.js
index fa2a98a..12c6880 100644
--- a/ui/js/src/wok.login.js
+++ b/ui/js/src/wok.login.js
@@ -1,7 +1,7 @@
/*
* Project Wok
*
- * Copyright IBM Corp, 2015-2016
+ * Copyright IBM Corp, 2015-2017
*
* Code derived from Project Kimchi
*
@@ -84,9 +84,17 @@ wok.login_main = function() {
if (jqXHR.responseText == "") {
$("#messUserPass").hide();
$("#missServer").show();
+ $("#timeoutError").hide();
+ } else if ((jqXHR.responseJSON != undefined) &&
+ ! (jqXHR.responseJSON["reason"] == undefined)) {
+ $("#messUserPass").hide();
+ $("#missServer").hide();
+ $("#timeoutError").html(jqXHR.responseJSON["reason"]);
+ $("#timeoutError").show();
} else {
$("#missServer").hide();
$("#messUserPass").show();
+ $("#timeoutError").hide();
}
$("#messSession").hide();
$("#logging").hide();
diff --git a/ui/pages/login.html.tmpl b/ui/pages/login.html.tmpl
index f5a4b2d..d25910c 100644
--- a/ui/pages/login.html.tmpl
+++ b/ui/pages/login.html.tmpl
@@ -1,7 +1,7 @@
#*
* Project Wok
*
- * Copyright IBM Corp, 2014-2016
+ * Copyright IBM Corp, 2014-2017
*
* Code derived from Project Kimchi
*
@@ -107,6 +107,7 @@
<div id="messUserPass" class="alert alert-danger" style="display: none;">$_("The username or password you entered is incorrect. Please try again.")</div>
<div id="messSession" class="alert alert-danger" style="display: none;">$_("Session timeout, please re-login.")</div>
<div id="missServer" class="alert alert-danger" style="display: none;">$_("Server unreachable.")</div>
+ <div id="timeoutError" class="alert alert-danger" style="display: none;">$_("Timeout error")</div>
</div>
<form id="form-login" class="form-horizontal" method="post">
<div class="form-group">
--
2.7.4
7 years, 8 months
[PATCH v5] [WoK 0/5] 'reload' API implementation
by dhbarboza82@gmail.com
From: Daniel Henrique Barboza <danielhb(a)linux.vnet.ibm.com>
v5:
- added 'self.admin_methods = ['POST'] in control/config.py
v4:
- fixed a '()' in test_api.py
- changed the positioning of the notification message in i18n.py
v3:
- 'add_notification' is now being called before the reload command
v2:
- changed API name to 'reload'
- added test_api tests
This patch set implements a new WOK API called 'reload' under
/config/reload.
To test it:
$ curl -k -u danielhb -H "Content-Type: application/json" -H "Accept: application/json" -X POST 'https://localhost:8001/config/reload' -d'{}'
Enter host password for user 'danielhb':
{
"proxy_port":"8001",
"websockets_port":"64667",
"version":"2.3.0-55.git5c9127b",
"auth":"pam",
"server_root":""
}
This will reload WoK and all its plug-ins.
Daniel Henrique Barboza (5):
reload API: doc changes
reload API: control and model changes
reload API: new file tests/test_config_model.py
reload API: added rest API tests
reload API: adding notification before reloading operation
docs/API/config.md | 2 +-
src/wok/control/config.py | 5 ++++-
src/wok/i18n.py | 2 ++
src/wok/model/config.py | 22 +++++++++++++++++++++-
tests/test_api.py | 9 ++++++++-
tests/test_config_model.py | 41 +++++++++++++++++++++++++++++++++++++++++
6 files changed, 77 insertions(+), 4 deletions(-)
create mode 100644 tests/test_config_model.py
--
2.7.4
7 years, 8 months
[PATCH] [WoK 0/9] '/config/plugins' API implementation
by dhbarboza82@gmail.com
From: Daniel Henrique Barboza <danielhb(a)linux.vnet.ibm.com>
This patch set implements the '/config/plugins' API. The idea
of this API is to replace the current '/plugins' API while
adding a new attribute called 'enabled' in its return value,
representing the current state of the plug-in. It will also
return all the plug-ins, regardless of whether it is loaded
or not.
New actions /config/plugin/*name*/enable and
/config/plugin/*name*/disable were implemented to enable/disable
the plug-in *name* by editing the plug-in .conf file.
This is the API in action:
[danielhb@arthas wok_all_plugins]$ curl -k -u danielhb -H "Content-Type: application/json" -H "Accept: application/json" -X GET 'https://localhost:8001/config/plugins'Enter host password for user 'danielhb':
[
{
"enabled":true,
"name":"gingerbase"
},
{
"enabled":true,
"name":"kimchi"
},
{
"enabled":true,
"name":"ginger"
}
][danielhb@arthas wok_all_plugins]$
[danielhb@arthas wok_all_plugins]$ curl -k -u danielhb -H "Content-Type: application/json" -H "Accept: application/json" -X POST 'https://localhost:8001/config/plugins/ginger/disable' -d'{}'
Enter host password for user 'danielhb':
{
"enabled":false,
"name":"ginger"
}[danielhb@arthas wok_all_plugins]$
[danielhb@arthas wok_all_plugins]$ curl -k -u danielhb -H "Content-Type: applicationjson" -H "Accept: application/json" -X POST 'https://localhost:8001/config/plugins/ginger/enable' -d'{}'
Enter host password for user 'danielhb':
{
"enabled":true,
"name":"ginger"
}[danielhb@arthas wok_all_plugins]$
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Daniel Henrique Barboza (9):
/config/plugins API: docs changes
/config/plugins API: utils changes
/config/plugins: changes in config model
/config/plugins API: model changes
/config/plugins API: test changes
Removing old /plugins API
/config/plugins: changing existing UI calls
/config/plugins: exclude 'sample' plug-in when not in test_mode
Removing configobj, use file operations to edit conf files
docs/API/config.md | 31 +++++++++++++++++++++++
docs/API/plugins.md | 13 ----------
src/wok/control/config.py | 24 ++++++++++++++++--
src/wok/control/plugins.py | 29 ---------------------
src/wok/i18n.py | 1 +
src/wok/model/plugins.py | 32 +++++++++++++++++------
src/wok/server.py | 7 +++++-
src/wok/utils.py | 63 ++++++++++++++++++++++++++++++++++++++++------
tests/test_api.py | 47 +++++++++++++++++++++++++++++++++-
tests/test_utils.py | 56 ++++++++++++++++++++++++++++++++++++++++-
ui/js/src/wok.api.js | 4 +--
ui/js/src/wok.logos.js | 11 +++++---
ui/js/src/wok.main.js | 12 ++++++---
13 files changed, 258 insertions(+), 72 deletions(-)
delete mode 100644 docs/API/plugins.md
delete mode 100644 src/wok/control/plugins.py
--
2.7.4
7 years, 8 months
[PATCH][Kimchi] Bug fix #1029: Unable to create a snapshot on a running guest
by Ramon Medeiros
Libvirt supports live snapshot. This patch just removes the limitation
to run snapshot only on turned off guests
Signed-off-by: Ramon Medeiros <ramonn(a)linux.vnet.ibm.com>
---
i18n.py | 3 +--
model/vmsnapshots.py | 7 ++-----
ui/js/src/kimchi.guest_edit_main.js | 3 ---
3 files changed, 3 insertions(+), 10 deletions(-)
diff --git a/i18n.py b/i18n.py
index da1391b..c1f27a0 100644
--- a/i18n.py
+++ b/i18n.py
@@ -1,7 +1,7 @@
#
# Project Kimchi
#
-# Copyright IBM Corp, 2015-2016
+# Copyright IBM Corp, 2015-2017
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
@@ -343,7 +343,6 @@ messages = {
"KCHVMSTOR0020E": _("On s390x arch 'format' must be specified while attaching disk to virtual machine"),
"KCHVMSTOR0021E": _("Virtual disk already exists on the system: %(disk_path)s"),
- "KCHSNAP0001E": _("Virtual machine '%(vm)s' must be stopped before creating a snapshot of it."),
"KCHSNAP0002E": _("Unable to create snapshot '%(name)s' on virtual machine '%(vm)s'. Details: %(err)s"),
"KCHSNAP0003E": _("Snapshot '%(name)s' does not exist on virtual machine '%(vm)s'."),
"KCHSNAP0004E": _("Unable to retrieve snapshot '%(name)s' on virtual machine '%(vm)s'. Details: %(err)s"),
diff --git a/model/vmsnapshots.py b/model/vmsnapshots.py
index 6589306..8c30a53 100644
--- a/model/vmsnapshots.py
+++ b/model/vmsnapshots.py
@@ -1,7 +1,7 @@
#
# Project Kimchi
#
-# Copyright IBM Corp, 2015-2016
+# Copyright IBM Corp, 2015-2017
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
@@ -28,7 +28,7 @@ from wok.exception import InvalidOperation, NotFoundError, OperationFailed
from wok.xmlutils.utils import xpath_get_text
from wok.model.tasks import TaskModel
-from wok.plugins.kimchi.model.vms import DOM_STATE_MAP, VMModel
+from wok.plugins.kimchi.model.vms import VMModel
from wok.plugins.kimchi.model.vmstorages import VMStorageModel, VMStoragesModel
@@ -57,9 +57,6 @@ class VMSnapshotsModel(object):
"""
if params is None:
params = {}
- vir_dom = VMModel.get_vm(vm_name, self.conn)
- if DOM_STATE_MAP[vir_dom.info()[0]] != u'shutoff':
- raise InvalidOperation('KCHSNAP0001E', {'vm': vm_name})
# if the VM has a non-CDROM disk with type 'raw', abort.
for storage_name in self.vmstorages.get_list(vm_name):
diff --git a/ui/js/src/kimchi.guest_edit_main.js b/ui/js/src/kimchi.guest_edit_main.js
index 1682a58..b47d293 100644
--- a/ui/js/src/kimchi.guest_edit_main.js
+++ b/ui/js/src/kimchi.guest_edit_main.js
@@ -961,9 +961,6 @@ kimchi.guest_edit_main = function() {
addOngoingItem(task);
});
});
- if (kimchi.thisVMState === "running") {
- $("button", "#form-guest-edit-snapshot").remove();
- }
};
var initContent = function(guest) {
--
2.7.4
7 years, 8 months
[PATCH v2][Kimchi 0/2] Bug fix #1029: Unable to create a snapshot on a running guest
by Ramon Medeiros
Changes:
v2:
Add unit tests
Ramon Medeiros (2):
Bug fix #1029: Unable to create a snapshot on a running guest
Add test to live snapshot
i18n.py | 1 -
model/vmsnapshots.py | 7 ++-----
tests/test_model.py | 6 ++++--
tests/test_rest.py | 19 +++++++++++++++++++
ui/js/src/kimchi.guest_edit_main.js | 3 ---
5 files changed, 25 insertions(+), 11 deletions(-)
--
2.7.4
7 years, 8 months