[PATCH 0/3 V2] authorization: Backend changes

From: Aline Manera <alinefm@linux.vnet.ibm.com> V1 -> V2: - Add "access" elements to describe role/view for each tab - Return a role map in /login For each tab, a role will be returned. That way we have more flexibility to change user role per tab - Add "access" parameter to VM.lookup() As the user will have full access to the VM assigned to it, return "access=full" for all them Aline Manera (3): authorization: Update /login to return user roles instead of sudo parameter authorization: Add "access" elements to tabs.xml to describe user view authorization: Add "access" parameter to VM resource config/ui/tabs.xml | 15 +++++++++++++++ plugins/sample/ui/config/tab-ext.xml | 3 +++ src/kimchi/auth.py | 19 ++++++++++++++----- src/kimchi/mockmodel.py | 3 ++- src/kimchi/model/vms.py | 3 ++- src/kimchi/utils.py | 15 +++++++++++++++ tests/test_rest.py | 6 ++++++ tests/utils.py | 6 +++--- 8 files changed, 60 insertions(+), 10 deletions(-) -- 1.9.3

From: Aline Manera <alinefm@linux.vnet.ibm.com> For now, Kimchi just supports 2 types of user roles: 'admin' - user has full control of Kimchi features and 'user' with limited access. But in future the idea is to have more and more roles so it is good to already provide the authorization support with that in mind. That way, instead of only returning if user has or not sudo permissions, the /login API will return the user role for each tab. If the user has sudo permissions he/she will have 'admin' role, otherwise, 'user' role. curl -H "Content-Type: application/json" -H "Accept: application/json" http://localhost:8010/login -d'{"username": "guest", "password": "guest-passwd"}' -X POST { "username": "guest", "roles": {"templates": "user", "host": "user", "storage": "user", "guests": "user", "network": "user"}, "groups": [] } curl -H "Content-Type: application/json" -H "Accept: application/json" http://localhost:8010/login -d'{"username": "sysadmin", "password": "sysadmin-passwd"}' -X POST { "username": "sysadmin", "roles": {"templates": "admin", "host": "admin", "storage": "admin", "guests": "admin", "network": "admin"}, "groups": [] } Having one role per tab, give us more flexibility to assign different roles to user. Signed-off-by: Aline Manera <alinefm@linux.vnet.ibm.com> --- src/kimchi/auth.py | 19 ++++++++++++++----- src/kimchi/utils.py | 15 +++++++++++++++ tests/test_rest.py | 6 ++++++ tests/utils.py | 6 +++--- 4 files changed, 38 insertions(+), 8 deletions(-) diff --git a/src/kimchi/auth.py b/src/kimchi/auth.py index 6a4a610..8d20966 100644 --- a/src/kimchi/auth.py +++ b/src/kimchi/auth.py @@ -32,14 +32,17 @@ from kimchi import template from kimchi.exception import InvalidOperation, OperationFailed -from kimchi.utils import run_command +from kimchi.utils import get_all_tabs, run_command USER_NAME = 'username' USER_GROUPS = 'groups' USER_SUDO = 'sudo' +USER_ROLES = 'roles' REFRESH = 'robot-refresh' +tabs = get_all_tabs() + def redirect_login(): url = "/login.html" @@ -62,7 +65,10 @@ def __init__(self, username): self.user = {} self.user[USER_NAME] = username self.user[USER_GROUPS] = None - self.user[USER_SUDO] = False + # 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') def get_groups(self): self.user[USER_GROUPS] = [g.gr_name for g in grp.getgrall() @@ -74,10 +80,13 @@ def has_sudo(self): p = multiprocessing.Process(target=self._has_sudo, args=(result,)) p.start() p.join() - self.user[USER_SUDO] = bool(result.value) - return self.user[USER_SUDO] + if result.value: + self.user[USER_ROLES] = dict.fromkeys(tabs, 'admin') + return result.value def _has_sudo(self, result): + result.value = False + _master, slave = pty.openpty() os.setsid() fcntl.ioctl(slave, termios.TIOCSCTTY, 0) @@ -94,7 +103,7 @@ def _has_sudo(self, result): self.user[USER_NAME]]) for line in out.split('\n'): if line and re.search("(ALL)", line): - result.value = 1 + result.value = True debug("User %s can run any command with sudo" % result.value) return diff --git a/src/kimchi/utils.py b/src/kimchi/utils.py index 97adbf8..798d9cd 100644 --- a/src/kimchi/utils.py +++ b/src/kimchi/utils.py @@ -27,6 +27,7 @@ import subprocess import traceback import urllib2 +import xml.etree.ElementTree as ET from multiprocessing import Process, Queue from threading import Timer @@ -108,6 +109,20 @@ def get_enabled_plugins(): except (TypeError, KeyError): continue +def get_all_tabs(): + files = [os.path.join(paths.prefix, 'config/ui/tabs.xml')] + + for plugin, _ in get_enabled_plugins(): + files.append(os.path.join(PluginPaths(plugin).ui_dir, + 'config/tab-ext.xml')) + + tabs = [] + for f in files: + root = ET.parse(f) + tabs.extend([t.text.lower() for t in root.iter('title')]) + + return tabs + def import_class(class_path): module_name, class_name = class_path.rsplit('.', 1) diff --git a/tests/test_rest.py b/tests/test_rest.py index 694d907..1a91eb3 100644 --- a/tests/test_rest.py +++ b/tests/test_rest.py @@ -1552,6 +1552,12 @@ def test_auth_session(self): req = json.dumps({'username': user, 'password': pw}) resp = self.request('/login', req, 'POST', hdrs) self.assertEquals(200, resp.status) + + user_info = json.loads(resp.read()) + self.assertEquals(sorted(user_info.keys()), + ['groups', 'roles', 'username']) + self.assertEquals(user_info['roles'], [u'admin']) + cookie = resp.getheader('set-cookie') hdrs['Cookie'] = cookie diff --git a/tests/utils.py b/tests/utils.py index fd9b23c..4853b7a 100644 --- a/tests/utils.py +++ b/tests/utils.py @@ -157,8 +157,8 @@ def patch_auth(sudo=True): def _get_groups(self): return None - def _has_sudo(self): - return sudo + def _has_sudo(self, result): + result.value = sudo def _authenticate(username, password, service="passwd"): try: @@ -170,7 +170,7 @@ def _authenticate(username, password, service="passwd"): import kimchi.auth kimchi.auth.authenticate = _authenticate kimchi.auth.User.get_groups = _get_groups - kimchi.auth.User.has_sudo = _has_sudo + kimchi.auth.User._has_sudo = _has_sudo def normalize_xml(xml_str): -- 1.9.3

From: Aline Manera <alinefm@linux.vnet.ibm.com>
For now, Kimchi just supports 2 types of user roles: 'admin' - user has full control of Kimchi features and 'user' with limited access. But in future the idea is to have more and more roles so it is good to already provide the authorization support with that in mind.
That way, instead of only returning if user has or not sudo permissions, the /login API will return the user role for each tab.
If the user has sudo permissions he/she will have 'admin' role, otherwise, 'user' role.
curl -H "Content-Type: application/json" -H "Accept: application/json" http://localhost:8010/login -d'{"username": "guest", "password": "guest-passwd"}' -X POST
{ "username": "guest", "roles": {"templates": "user", "host": "user", "storage": "user", "guests": "user", "network": "user"}, "groups": [] }
curl -H "Content-Type: application/json" -H "Accept: application/json" http://localhost:8010/login -d'{"username": "sysadmin", "password": "sysadmin-passwd"}' -X POST
{ "username": "sysadmin", "roles": {"templates": "admin", "host": "admin", "storage": "admin", "guests": "admin", "network": "admin"}, "groups": [] }
Having one role per tab, give us more flexibility to assign different roles to user.
Signed-off-by: Aline Manera <alinefm@linux.vnet.ibm.com> --- src/kimchi/auth.py | 19 ++++++++++++++----- src/kimchi/utils.py | 15 +++++++++++++++ tests/test_rest.py | 6 ++++++ tests/utils.py | 6 +++--- 4 files changed, 38 insertions(+), 8 deletions(-)
diff --git a/src/kimchi/auth.py b/src/kimchi/auth.py index 6a4a610..8d20966 100644 --- a/src/kimchi/auth.py +++ b/src/kimchi/auth.py @@ -32,14 +32,17 @@
from kimchi import template from kimchi.exception import InvalidOperation, OperationFailed -from kimchi.utils import run_command +from kimchi.utils import get_all_tabs, run_command
USER_NAME = 'username' USER_GROUPS = 'groups' USER_SUDO = 'sudo' +USER_ROLES = 'roles' REFRESH = 'robot-refresh'
+tabs = get_all_tabs() +
def redirect_login(): url = "/login.html" @@ -62,7 +65,10 @@ def __init__(self, username): self.user = {} self.user[USER_NAME] = username self.user[USER_GROUPS] = None - self.user[USER_SUDO] = False + # 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')
def get_groups(self): self.user[USER_GROUPS] = [g.gr_name for g in grp.getgrall() @@ -74,10 +80,13 @@ def has_sudo(self): p = multiprocessing.Process(target=self._has_sudo, args=(result,)) p.start() p.join() - self.user[USER_SUDO] = bool(result.value) - return self.user[USER_SUDO] + if result.value: + self.user[USER_ROLES] = dict.fromkeys(tabs, 'admin') + return result.value
def _has_sudo(self, result): + result.value = False + _master, slave = pty.openpty() os.setsid() fcntl.ioctl(slave, termios.TIOCSCTTY, 0) @@ -94,7 +103,7 @@ def _has_sudo(self, result): self.user[USER_NAME]]) for line in out.split('\n'): if line and re.search("(ALL)", line): - result.value = 1 + result.value = True debug("User %s can run any command with sudo" % result.value) return diff --git a/src/kimchi/utils.py b/src/kimchi/utils.py index 97adbf8..798d9cd 100644 --- a/src/kimchi/utils.py +++ b/src/kimchi/utils.py @@ -27,6 +27,7 @@ import subprocess import traceback import urllib2 +import xml.etree.ElementTree as ET from multiprocessing import Process, Queue from threading import Timer
@@ -108,6 +109,20 @@ def get_enabled_plugins(): except (TypeError, KeyError): continue
+def get_all_tabs(): + files = [os.path.join(paths.prefix, 'config/ui/tabs.xml')] + + for plugin, _ in get_enabled_plugins(): + files.append(os.path.join(PluginPaths(plugin).ui_dir, + 'config/tab-ext.xml')) + + tabs = [] + for f in files: + root = ET.parse(f) + tabs.extend([t.text.lower() for t in root.iter('title')])
On 07/16/2014 03:44 AM, alinefm@linux.vnet.ibm.com wrote: please use getiterator. iter is not available on rhel6 --- a/src/kimchi/utils.py +++ b/src/kimchi/utils.py @@ -119,7 +119,7 @@ def get_all_tabs(): tabs = [] for f in files: root = ET.parse(f) - tabs.extend([t.text.lower() for t in root.iter('title')]) + tabs.extend([t.text.lower() for t in root.getiterator('title')])
+ + return tabs +
def import_class(class_path): module_name, class_name = class_path.rsplit('.', 1) diff --git a/tests/test_rest.py b/tests/test_rest.py index 694d907..1a91eb3 100644 --- a/tests/test_rest.py +++ b/tests/test_rest.py @@ -1552,6 +1552,12 @@ def test_auth_session(self): req = json.dumps({'username': user, 'password': pw}) resp = self.request('/login', req, 'POST', hdrs) self.assertEquals(200, resp.status) + + user_info = json.loads(resp.read()) + self.assertEquals(sorted(user_info.keys()), + ['groups', 'roles', 'username']) + self.assertEquals(user_info['roles'], [u'admin']) + cookie = resp.getheader('set-cookie') hdrs['Cookie'] = cookie
diff --git a/tests/utils.py b/tests/utils.py index fd9b23c..4853b7a 100644 --- a/tests/utils.py +++ b/tests/utils.py @@ -157,8 +157,8 @@ def patch_auth(sudo=True): def _get_groups(self): return None
- def _has_sudo(self): - return sudo + def _has_sudo(self, result): + result.value = sudo
def _authenticate(username, password, service="passwd"): try: @@ -170,7 +170,7 @@ def _authenticate(username, password, service="passwd"): import kimchi.auth kimchi.auth.authenticate = _authenticate kimchi.auth.User.get_groups = _get_groups - kimchi.auth.User.has_sudo = _has_sudo + kimchi.auth.User._has_sudo = _has_sudo
def normalize_xml(xml_str):
-- Thanks and best regards! Sheldon Feng(冯少合)<shaohef@linux.vnet.ibm.com> IBM Linux Technology Center

On 07/16/2014 02:50 AM, Sheldon wrote:
From: Aline Manera <alinefm@linux.vnet.ibm.com>
For now, Kimchi just supports 2 types of user roles: 'admin' - user has full control of Kimchi features and 'user' with limited access. But in future the idea is to have more and more roles so it is good to already provide the authorization support with that in mind.
That way, instead of only returning if user has or not sudo permissions, the /login API will return the user role for each tab.
If the user has sudo permissions he/she will have 'admin' role, otherwise, 'user' role.
curl -H "Content-Type: application/json" -H "Accept: application/json" http://localhost:8010/login -d'{"username": "guest", "password": "guest-passwd"}' -X POST
{ "username": "guest", "roles": {"templates": "user", "host": "user", "storage": "user", "guests": "user", "network": "user"}, "groups": [] }
curl -H "Content-Type: application/json" -H "Accept: application/json" http://localhost:8010/login -d'{"username": "sysadmin", "password": "sysadmin-passwd"}' -X POST
{ "username": "sysadmin", "roles": {"templates": "admin", "host": "admin", "storage": "admin", "guests": "admin", "network": "admin"}, "groups": [] }
Having one role per tab, give us more flexibility to assign different roles to user.
Signed-off-by: Aline Manera <alinefm@linux.vnet.ibm.com> --- src/kimchi/auth.py | 19 ++++++++++++++----- src/kimchi/utils.py | 15 +++++++++++++++ tests/test_rest.py | 6 ++++++ tests/utils.py | 6 +++--- 4 files changed, 38 insertions(+), 8 deletions(-)
diff --git a/src/kimchi/auth.py b/src/kimchi/auth.py index 6a4a610..8d20966 100644 --- a/src/kimchi/auth.py +++ b/src/kimchi/auth.py @@ -32,14 +32,17 @@
from kimchi import template from kimchi.exception import InvalidOperation, OperationFailed -from kimchi.utils import run_command +from kimchi.utils import get_all_tabs, run_command
USER_NAME = 'username' USER_GROUPS = 'groups' USER_SUDO = 'sudo' +USER_ROLES = 'roles' REFRESH = 'robot-refresh'
+tabs = get_all_tabs() +
def redirect_login(): url = "/login.html" @@ -62,7 +65,10 @@ def __init__(self, username): self.user = {} self.user[USER_NAME] = username self.user[USER_GROUPS] = None - self.user[USER_SUDO] = False + # 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')
def get_groups(self): self.user[USER_GROUPS] = [g.gr_name for g in grp.getgrall() @@ -74,10 +80,13 @@ def has_sudo(self): p = multiprocessing.Process(target=self._has_sudo, args=(result,)) p.start() p.join() - self.user[USER_SUDO] = bool(result.value) - return self.user[USER_SUDO] + if result.value: + self.user[USER_ROLES] = dict.fromkeys(tabs, 'admin') + return result.value
def _has_sudo(self, result): + result.value = False + _master, slave = pty.openpty() os.setsid() fcntl.ioctl(slave, termios.TIOCSCTTY, 0) @@ -94,7 +103,7 @@ def _has_sudo(self, result): self.user[USER_NAME]]) for line in out.split('\n'): if line and re.search("(ALL)", line): - result.value = 1 + result.value = True debug("User %s can run any command with sudo" % result.value) return diff --git a/src/kimchi/utils.py b/src/kimchi/utils.py index 97adbf8..798d9cd 100644 --- a/src/kimchi/utils.py +++ b/src/kimchi/utils.py @@ -27,6 +27,7 @@ import subprocess import traceback import urllib2 +import xml.etree.ElementTree as ET from multiprocessing import Process, Queue from threading import Timer
@@ -108,6 +109,20 @@ def get_enabled_plugins(): except (TypeError, KeyError): continue
+def get_all_tabs(): + files = [os.path.join(paths.prefix, 'config/ui/tabs.xml')] + + for plugin, _ in get_enabled_plugins(): + files.append(os.path.join(PluginPaths(plugin).ui_dir, + 'config/tab-ext.xml')) + + tabs = [] + for f in files: + root = ET.parse(f) + tabs.extend([t.text.lower() for t in root.iter('title')])
On 07/16/2014 03:44 AM, alinefm@linux.vnet.ibm.com wrote: please use getiterator. iter is not available on rhel6
--- a/src/kimchi/utils.py +++ b/src/kimchi/utils.py @@ -119,7 +119,7 @@ def get_all_tabs(): tabs = [] for f in files: root = ET.parse(f) - tabs.extend([t.text.lower() for t in root.iter('title')]) + tabs.extend([t.text.lower() for t in root.getiterator('title')])
ACK. Thanks!
+ + return tabs +
def import_class(class_path): module_name, class_name = class_path.rsplit('.', 1) diff --git a/tests/test_rest.py b/tests/test_rest.py index 694d907..1a91eb3 100644 --- a/tests/test_rest.py +++ b/tests/test_rest.py @@ -1552,6 +1552,12 @@ def test_auth_session(self): req = json.dumps({'username': user, 'password': pw}) resp = self.request('/login', req, 'POST', hdrs) self.assertEquals(200, resp.status) + + user_info = json.loads(resp.read()) + self.assertEquals(sorted(user_info.keys()), + ['groups', 'roles', 'username']) + self.assertEquals(user_info['roles'], [u'admin']) + cookie = resp.getheader('set-cookie') hdrs['Cookie'] = cookie
diff --git a/tests/utils.py b/tests/utils.py index fd9b23c..4853b7a 100644 --- a/tests/utils.py +++ b/tests/utils.py @@ -157,8 +157,8 @@ def patch_auth(sudo=True): def _get_groups(self): return None
- def _has_sudo(self): - return sudo + def _has_sudo(self, result): + result.value = sudo
def _authenticate(username, password, service="passwd"): try: @@ -170,7 +170,7 @@ def _authenticate(username, password, service="passwd"): import kimchi.auth kimchi.auth.authenticate = _authenticate kimchi.auth.User.get_groups = _get_groups - kimchi.auth.User.has_sudo = _has_sudo + kimchi.auth.User._has_sudo = _has_sudo
def normalize_xml(xml_str):

On 2014年07月16日 03:44, alinefm@linux.vnet.ibm.com wrote:
From: Aline Manera <alinefm@linux.vnet.ibm.com>
For now, Kimchi just supports 2 types of user roles: 'admin' - user has full control of Kimchi features and 'user' with limited access. But in future the idea is to have more and more roles so it is good to already provide the authorization support with that in mind.
That way, instead of only returning if user has or not sudo permissions, the /login API will return the user role for each tab.
If the user has sudo permissions he/she will have 'admin' role, otherwise, 'user' role.
curl -H "Content-Type: application/json" -H "Accept: application/json" http://localhost:8010/login -d'{"username": "guest", "password": "guest-passwd"}' -X POST
{ "username": "guest", "roles": {"templates": "user", "host": "user", "storage": "user", "guests": "user", "network": "user"}, "groups": [] }
curl -H "Content-Type: application/json" -H "Accept: application/json" http://localhost:8010/login -d'{"username": "sysadmin", "password": "sysadmin-passwd"}' -X POST
{ "username": "sysadmin", "roles": {"templates": "admin", "host": "admin", "storage": "admin", "guests": "admin", "network": "admin"}, "groups": [] }
Having one role per tab, give us more flexibility to assign different roles to user.
Signed-off-by: Aline Manera <alinefm@linux.vnet.ibm.com> --- src/kimchi/auth.py | 19 ++++++++++++++----- src/kimchi/utils.py | 15 +++++++++++++++ tests/test_rest.py | 6 ++++++ tests/utils.py | 6 +++--- 4 files changed, 38 insertions(+), 8 deletions(-)
diff --git a/src/kimchi/auth.py b/src/kimchi/auth.py index 6a4a610..8d20966 100644 --- a/src/kimchi/auth.py +++ b/src/kimchi/auth.py @@ -32,14 +32,17 @@
from kimchi import template from kimchi.exception import InvalidOperation, OperationFailed -from kimchi.utils import run_command +from kimchi.utils import get_all_tabs, run_command
USER_NAME = 'username' USER_GROUPS = 'groups' USER_SUDO = 'sudo' +USER_ROLES = 'roles' REFRESH = 'robot-refresh'
+tabs = get_all_tabs() +
def redirect_login(): url = "/login.html" @@ -62,7 +65,10 @@ def __init__(self, username): self.user = {} self.user[USER_NAME] = username self.user[USER_GROUPS] = None - self.user[USER_SUDO] = False + # 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')
def get_groups(self): self.user[USER_GROUPS] = [g.gr_name for g in grp.getgrall() @@ -74,10 +80,13 @@ def has_sudo(self): p = multiprocessing.Process(target=self._has_sudo, args=(result,)) p.start() p.join() - self.user[USER_SUDO] = bool(result.value) - return self.user[USER_SUDO] + if result.value: + self.user[USER_ROLES] = dict.fromkeys(tabs, 'admin') + return result.value Since we introduced rolls in each tab. I think when judging whether a user's role of a tab, we need to depend on the access assigned to him rather than see if he has "sudo". "sudo" judgement divide users of two kinds: 'users' of all tabs OR 'admin' of all tabs.Also I would like we stop mapping system users to kimchi users--kimchi needs its own user/role/group maintaining to isolate security risks as they did in openstack/ovirt.
def _has_sudo(self, result): + result.value = False + _master, slave = pty.openpty() os.setsid() fcntl.ioctl(slave, termios.TIOCSCTTY, 0) @@ -94,7 +103,7 @@ def _has_sudo(self, result): self.user[USER_NAME]]) for line in out.split('\n'): if line and re.search("(ALL)", line): - result.value = 1 + result.value = True debug("User %s can run any command with sudo" % result.value) return diff --git a/src/kimchi/utils.py b/src/kimchi/utils.py index 97adbf8..798d9cd 100644 --- a/src/kimchi/utils.py +++ b/src/kimchi/utils.py @@ -27,6 +27,7 @@ import subprocess import traceback import urllib2 +import xml.etree.ElementTree as ET from multiprocessing import Process, Queue from threading import Timer
@@ -108,6 +109,20 @@ def get_enabled_plugins(): except (TypeError, KeyError): continue
+def get_all_tabs(): + files = [os.path.join(paths.prefix, 'config/ui/tabs.xml')] + + for plugin, _ in get_enabled_plugins(): + files.append(os.path.join(PluginPaths(plugin).ui_dir, + 'config/tab-ext.xml')) + + tabs = [] + for f in files: + root = ET.parse(f) + tabs.extend([t.text.lower() for t in root.iter('title')]) + + return tabs +
def import_class(class_path): module_name, class_name = class_path.rsplit('.', 1) diff --git a/tests/test_rest.py b/tests/test_rest.py index 694d907..1a91eb3 100644 --- a/tests/test_rest.py +++ b/tests/test_rest.py @@ -1552,6 +1552,12 @@ def test_auth_session(self): req = json.dumps({'username': user, 'password': pw}) resp = self.request('/login', req, 'POST', hdrs) self.assertEquals(200, resp.status) + + user_info = json.loads(resp.read()) + self.assertEquals(sorted(user_info.keys()), + ['groups', 'roles', 'username']) + self.assertEquals(user_info['roles'], [u'admin']) + cookie = resp.getheader('set-cookie') hdrs['Cookie'] = cookie
diff --git a/tests/utils.py b/tests/utils.py index fd9b23c..4853b7a 100644 --- a/tests/utils.py +++ b/tests/utils.py @@ -157,8 +157,8 @@ def patch_auth(sudo=True): def _get_groups(self): return None
- def _has_sudo(self): - return sudo + def _has_sudo(self, result): + result.value = sudo
def _authenticate(username, password, service="passwd"): try: @@ -170,7 +170,7 @@ def _authenticate(username, password, service="passwd"): import kimchi.auth kimchi.auth.authenticate = _authenticate kimchi.auth.User.get_groups = _get_groups - kimchi.auth.User.has_sudo = _has_sudo + kimchi.auth.User._has_sudo = _has_sudo
def normalize_xml(xml_str):

On 07/16/2014 04:15 AM, Royce Lv wrote:
On 2014年07月16日 03:44, alinefm@linux.vnet.ibm.com wrote:
From: Aline Manera <alinefm@linux.vnet.ibm.com>
For now, Kimchi just supports 2 types of user roles: 'admin' - user has full control of Kimchi features and 'user' with limited access. But in future the idea is to have more and more roles so it is good to already provide the authorization support with that in mind.
That way, instead of only returning if user has or not sudo permissions, the /login API will return the user role for each tab.
If the user has sudo permissions he/she will have 'admin' role, otherwise, 'user' role.
curl -H "Content-Type: application/json" -H "Accept: application/json" http://localhost:8010/login -d'{"username": "guest", "password": "guest-passwd"}' -X POST
{ "username": "guest", "roles": {"templates": "user", "host": "user", "storage": "user", "guests": "user", "network": "user"}, "groups": [] }
curl -H "Content-Type: application/json" -H "Accept: application/json" http://localhost:8010/login -d'{"username": "sysadmin", "password": "sysadmin-passwd"}' -X POST
{ "username": "sysadmin", "roles": {"templates": "admin", "host": "admin", "storage": "admin", "guests": "admin", "network": "admin"}, "groups": [] }
Having one role per tab, give us more flexibility to assign different roles to user.
Signed-off-by: Aline Manera <alinefm@linux.vnet.ibm.com> --- src/kimchi/auth.py | 19 ++++++++++++++----- src/kimchi/utils.py | 15 +++++++++++++++ tests/test_rest.py | 6 ++++++ tests/utils.py | 6 +++--- 4 files changed, 38 insertions(+), 8 deletions(-)
diff --git a/src/kimchi/auth.py b/src/kimchi/auth.py index 6a4a610..8d20966 100644 --- a/src/kimchi/auth.py +++ b/src/kimchi/auth.py @@ -32,14 +32,17 @@
from kimchi import template from kimchi.exception import InvalidOperation, OperationFailed -from kimchi.utils import run_command +from kimchi.utils import get_all_tabs, run_command
USER_NAME = 'username' USER_GROUPS = 'groups' USER_SUDO = 'sudo' +USER_ROLES = 'roles' REFRESH = 'robot-refresh'
+tabs = get_all_tabs() +
def redirect_login(): url = "/login.html" @@ -62,7 +65,10 @@ def __init__(self, username): self.user = {} self.user[USER_NAME] = username self.user[USER_GROUPS] = None - self.user[USER_SUDO] = False + # 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')
def get_groups(self): self.user[USER_GROUPS] = [g.gr_name for g in grp.getgrall() @@ -74,10 +80,13 @@ def has_sudo(self): p = multiprocessing.Process(target=self._has_sudo, args=(result,)) p.start() p.join() - self.user[USER_SUDO] = bool(result.value) - return self.user[USER_SUDO] + if result.value: + self.user[USER_ROLES] = dict.fromkeys(tabs, 'admin') + return result.value Since we introduced rolls in each tab. I think when judging whether a user's role of a tab, we need to depend on the access assigned to him rather than see if he has "sudo". "sudo" judgement divide users of two kinds: 'users' of all tabs OR 'admin' of all tabs.
+ # 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
As I commented above, the idea is add supporting to change user roles instead of only checking sudo permissions But for now (for 1.3 release) we will only support 2 roles: admin and user and they will be set according to sudo permissions Also I would like we stop mapping system users to
kimchi users--kimchi needs its own user/role/group maintaining to isolate security risks as they did in openstack/ovirt.
We will continue using system users in Kimchi and only store the additional data we need to set user roles in Kimchi. As we have already discussed about.
def _has_sudo(self, result): + result.value = False + _master, slave = pty.openpty() os.setsid() fcntl.ioctl(slave, termios.TIOCSCTTY, 0) @@ -94,7 +103,7 @@ def _has_sudo(self, result): self.user[USER_NAME]]) for line in out.split('\n'): if line and re.search("(ALL)", line): - result.value = 1 + result.value = True debug("User %s can run any command with sudo" % result.value) return diff --git a/src/kimchi/utils.py b/src/kimchi/utils.py index 97adbf8..798d9cd 100644 --- a/src/kimchi/utils.py +++ b/src/kimchi/utils.py @@ -27,6 +27,7 @@ import subprocess import traceback import urllib2 +import xml.etree.ElementTree as ET from multiprocessing import Process, Queue from threading import Timer
@@ -108,6 +109,20 @@ def get_enabled_plugins(): except (TypeError, KeyError): continue
+def get_all_tabs(): + files = [os.path.join(paths.prefix, 'config/ui/tabs.xml')] + + for plugin, _ in get_enabled_plugins(): + files.append(os.path.join(PluginPaths(plugin).ui_dir, + 'config/tab-ext.xml')) + + tabs = [] + for f in files: + root = ET.parse(f) + tabs.extend([t.text.lower() for t in root.iter('title')]) + + return tabs +
def import_class(class_path): module_name, class_name = class_path.rsplit('.', 1) diff --git a/tests/test_rest.py b/tests/test_rest.py index 694d907..1a91eb3 100644 --- a/tests/test_rest.py +++ b/tests/test_rest.py @@ -1552,6 +1552,12 @@ def test_auth_session(self): req = json.dumps({'username': user, 'password': pw}) resp = self.request('/login', req, 'POST', hdrs) self.assertEquals(200, resp.status) + + user_info = json.loads(resp.read()) + self.assertEquals(sorted(user_info.keys()), + ['groups', 'roles', 'username']) + self.assertEquals(user_info['roles'], [u'admin']) + cookie = resp.getheader('set-cookie') hdrs['Cookie'] = cookie
diff --git a/tests/utils.py b/tests/utils.py index fd9b23c..4853b7a 100644 --- a/tests/utils.py +++ b/tests/utils.py @@ -157,8 +157,8 @@ def patch_auth(sudo=True): def _get_groups(self): return None
- def _has_sudo(self): - return sudo + def _has_sudo(self, result): + result.value = sudo
def _authenticate(username, password, service="passwd"): try: @@ -170,7 +170,7 @@ def _authenticate(username, password, service="passwd"): import kimchi.auth kimchi.auth.authenticate = _authenticate kimchi.auth.User.get_groups = _get_groups - kimchi.auth.User.has_sudo = _has_sudo + kimchi.auth.User._has_sudo = _has_sudo
def normalize_xml(xml_str):

From: Aline Manera <alinefm@linux.vnet.ibm.com> Kimchi has 2 user roles: "admin" with full control of Kimchi features and "user" with limited access To describe how each tab should be displayed to user, an "access" element was added to tabs.xml indicating which view mode each role has. <access role="..." mode="..."/> The "mode" attribute values are: - none: do not show the tab; - admin: full instance access; - read-only: read-only access; - byInstance: each resource will have its configuration sent by the backend ("access" parameter); The user will only be able to manage the guests he/she is assigned for, because that the guest tab has 'mode' == by-instance. That way each VM will have a new parameter "access" indicating if user has "full" or "read-only" access to this VM. As a user can edit a guest, he/she may need to know which networks and storage pools are configured, so set network and storage tab 'mode' to read-only. And as user should not perform any operation on host or templates, set their 'mode' attributes to 'none'. Signed-off-by: Aline Manera <alinefm@linux.vnet.ibm.com> --- config/ui/tabs.xml | 15 +++++++++++++++ plugins/sample/ui/config/tab-ext.xml | 3 +++ 2 files changed, 18 insertions(+) diff --git a/config/ui/tabs.xml b/config/ui/tabs.xml index b045521..f79684c 100644 --- a/config/ui/tabs.xml +++ b/config/ui/tabs.xml @@ -1,22 +1,37 @@ <?xml version="1.0" encoding="utf-8"?> <tabs> <tab> + <access role="admin" mode="admin"/> + <access role="user" mode="none"/> + <title>Host</title> <path>tabs/host.html</path> </tab> <tab> + <access role="admin" mode="admin"/> + <access role="user" mode="byInstance"/> + <title>Guests</title> <path>tabs/guests.html</path> </tab> <tab> + <access role="admin" mode="admin"/> + <access role="user" mode="none"/> + <title>Templates</title> <path>tabs/templates.html</path> </tab> <tab> + <access role="admin" mode="admin"/> + <access role="user" mode="read-only"/> + <title>Storage</title> <path>tabs/storage.html</path> </tab> <tab> + <access role="admin" mode="admin"/> + <access role="user" mode="read-only"/> + <title>Network</title> <path>tabs/network.html</path> </tab> diff --git a/plugins/sample/ui/config/tab-ext.xml b/plugins/sample/ui/config/tab-ext.xml index 8e0b3d3..a1fb1c2 100644 --- a/plugins/sample/ui/config/tab-ext.xml +++ b/plugins/sample/ui/config/tab-ext.xml @@ -1,6 +1,9 @@ <?xml version="1.0" encoding="utf-8"?> <tabs-ext> <tab> + <access role="admin" mode="admin"/> + <access role="user" mode="none"/> + <title>SampleTab</title> <path>plugins/sample/tab.html</path> </tab> -- 1.9.3

This role and mode infomation is stored in tabs.xml, If backend is using this information, I guess its difficult to update operations a role can perform. If we are using it in frontend, is that mean user can hack .js to change the access mode? I suppose roles and operations mappings need to be restored in backend db to make sure APIs are protected by authorization? On 2014年07月16日 03:44, alinefm@linux.vnet.ibm.com wrote:
From: Aline Manera <alinefm@linux.vnet.ibm.com>
Kimchi has 2 user roles: "admin" with full control of Kimchi features and "user" with limited access To describe how each tab should be displayed to user, an "access" element was added to tabs.xml indicating which view mode each role has.
<access role="..." mode="..."/>
The "mode" attribute values are:
- none: do not show the tab; - admin: full instance access; - read-only: read-only access; - byInstance: each resource will have its configuration sent by the backend ("access" parameter);
The user will only be able to manage the guests he/she is assigned for, because that the guest tab has 'mode' == by-instance. That way each VM will have a new parameter "access" indicating if user has "full" or "read-only" access to this VM.
As a user can edit a guest, he/she may need to know which networks and storage pools are configured, so set network and storage tab 'mode' to read-only.
And as user should not perform any operation on host or templates, set their 'mode' attributes to 'none'.
Signed-off-by: Aline Manera <alinefm@linux.vnet.ibm.com> --- config/ui/tabs.xml | 15 +++++++++++++++ plugins/sample/ui/config/tab-ext.xml | 3 +++ 2 files changed, 18 insertions(+)
diff --git a/config/ui/tabs.xml b/config/ui/tabs.xml index b045521..f79684c 100644 --- a/config/ui/tabs.xml +++ b/config/ui/tabs.xml @@ -1,22 +1,37 @@ <?xml version="1.0" encoding="utf-8"?> <tabs> <tab> + <access role="admin" mode="admin"/> + <access role="user" mode="none"/> + <title>Host</title> <path>tabs/host.html</path> </tab> <tab> + <access role="admin" mode="admin"/> + <access role="user" mode="byInstance"/> Maybe more role for guests? Virtualization admin can create/destroy, guest system admin can start and stop, guest user can just login and logout. + <title>Guests</title> <path>tabs/guests.html</path> </tab> <tab> + <access role="admin" mode="admin"/> + <access role="user" mode="none"/> I suppose also read-only for 'user', because user may want to browser templates available, check details of the template to make decision about which to use. + <title>Templates</title> <path>tabs/templates.html</path> </tab> <tab> + <access role="admin" mode="admin"/> + <access role="user" mode="read-only"/> + <title>Storage</title> <path>tabs/storage.html</path> </tab> <tab> + <access role="admin" mode="admin"/> + <access role="user" mode="read-only"/> + <title>Network</title> <path>tabs/network.html</path> </tab> diff --git a/plugins/sample/ui/config/tab-ext.xml b/plugins/sample/ui/config/tab-ext.xml index 8e0b3d3..a1fb1c2 100644 --- a/plugins/sample/ui/config/tab-ext.xml +++ b/plugins/sample/ui/config/tab-ext.xml @@ -1,6 +1,9 @@ <?xml version="1.0" encoding="utf-8"?> <tabs-ext> <tab> + <access role="admin" mode="admin"/> + <access role="user" mode="none"/> + <title>SampleTab</title> <path>plugins/sample/tab.html</path> </tab>

On 07/16/2014 04:38 AM, Royce Lv wrote:
This role and mode infomation is stored in tabs.xml, If backend is using this information, I guess its difficult to update operations a role can perform.
The content in the xml is static, ie, it will not change. It only describe which view a user role (admin or user) will get in Kimchi
If we are using it in frontend, is that mean user can hack .js to change the access mode? I suppose roles and operations mappings need to be restored in backend db to make sure APIs are protected by authorization?
ACK. But I don't think we need to store that info in a db as it is static. I will create a new API /authorization that returns the map presented in the xml. <tab>: {<role>: <mode>} Example: GET /authorization { "guests": {"admin": "admin", "user": "byInstance"}, "templates": {"admin": "admin", "user": "byInstance"} ... }
On 2014年07月16日 03:44, alinefm@linux.vnet.ibm.com wrote:
From: Aline Manera <alinefm@linux.vnet.ibm.com>
Kimchi has 2 user roles: "admin" with full control of Kimchi features and "user" with limited access To describe how each tab should be displayed to user, an "access" element was added to tabs.xml indicating which view mode each role has.
<access role="..." mode="..."/>
The "mode" attribute values are:
- none: do not show the tab; - admin: full instance access; - read-only: read-only access; - byInstance: each resource will have its configuration sent by the backend ("access" parameter);
The user will only be able to manage the guests he/she is assigned for, because that the guest tab has 'mode' == by-instance. That way each VM will have a new parameter "access" indicating if user has "full" or "read-only" access to this VM.
As a user can edit a guest, he/she may need to know which networks and storage pools are configured, so set network and storage tab 'mode' to read-only.
And as user should not perform any operation on host or templates, set their 'mode' attributes to 'none'.
Signed-off-by: Aline Manera <alinefm@linux.vnet.ibm.com> --- config/ui/tabs.xml | 15 +++++++++++++++ plugins/sample/ui/config/tab-ext.xml | 3 +++ 2 files changed, 18 insertions(+)
diff --git a/config/ui/tabs.xml b/config/ui/tabs.xml index b045521..f79684c 100644 --- a/config/ui/tabs.xml +++ b/config/ui/tabs.xml @@ -1,22 +1,37 @@ <?xml version="1.0" encoding="utf-8"?> <tabs> <tab> + <access role="admin" mode="admin"/> + <access role="user" mode="none"/> + <title>Host</title> <path>tabs/host.html</path> </tab> <tab> + <access role="admin" mode="admin"/> + <access role="user" mode="byInstance"/> Maybe more role for guests? Virtualization admin can create/destroy, guest system admin can start and stop, guest user can just login and logout.
For 1.3 only those 2 roles will be added. And then we can add more and more according to our needs.
+ <title>Guests</title> <path>tabs/guests.html</path> </tab> <tab> + <access role="admin" mode="admin"/> + <access role="user" mode="none"/> I suppose also read-only for 'user', because user may want to browser templates available, check details of the template to make decision about which to use.
A user will no be able to create a VM. So why he/she needs to know details about a template as the only proposal of templates is creating VMs? Let me explain you the full idea: - For now (1.3) we only have 2 roles: admin and user - admin role can do everything in kimchi - user role only can manage the VMs assigned to him After getting it done, we idea is add more roles and levels for users. Example: - user role only can get the console of a VM he/she is assigned for - user role only can start/stop/console of VM ... and so on Because that, the mode for a user role in the guests tab is set to "byInstance" as each VM must tell what kind of access the user has to it
+ <title>Templates</title> <path>tabs/templates.html</path> </tab> <tab> + <access role="admin" mode="admin"/> + <access role="user" mode="read-only"/> + <title>Storage</title> <path>tabs/storage.html</path> </tab> <tab> + <access role="admin" mode="admin"/> + <access role="user" mode="read-only"/> + <title>Network</title> <path>tabs/network.html</path> </tab> diff --git a/plugins/sample/ui/config/tab-ext.xml b/plugins/sample/ui/config/tab-ext.xml index 8e0b3d3..a1fb1c2 100644 --- a/plugins/sample/ui/config/tab-ext.xml +++ b/plugins/sample/ui/config/tab-ext.xml @@ -1,6 +1,9 @@ <?xml version="1.0" encoding="utf-8"?> <tabs-ext> <tab> + <access role="admin" mode="admin"/> + <access role="user" mode="none"/> + <title>SampleTab</title> <path>plugins/sample/tab.html</path> </tab>

On 07/16/2014 09:53 AM, Aline Manera wrote:
On 07/16/2014 04:38 AM, Royce Lv wrote:
This role and mode infomation is stored in tabs.xml, If backend is using this information, I guess its difficult to update operations a role can perform.
The content in the xml is static, ie, it will not change. It only describe which view a user role (admin or user) will get in Kimchi
If we are using it in frontend, is that mean user can hack .js to change the access mode? I suppose roles and operations mappings need to be restored in backend db to make sure APIs are protected by authorization?
ACK. But I don't think we need to store that info in a db as it is static.
I will create a new API /authorization that returns the map presented in the xml.
<tab>: {<role>: <mode>}
Example:
GET /authorization { "guests": {"admin": "admin", "user": "byInstance"}, "templates": {"admin": "admin", "user": "byInstance"} ... }
As we discussed today in the scrum meeting, we will keep the authorization map in the tabs.xml The frontend can not guarantee authorization, it must be done in backend, so even if a hacker change the JS to get the admin view, he/she will not be able to perform any operation (blocked by backend) The scrum meeting log can be found at http://kimchi-project.github.io/kimchi/meetings/kimchi.scrum.2014-07-16-13.0...
On 2014年07月16日 03:44, alinefm@linux.vnet.ibm.com wrote:
From: Aline Manera <alinefm@linux.vnet.ibm.com>
Kimchi has 2 user roles: "admin" with full control of Kimchi features and "user" with limited access To describe how each tab should be displayed to user, an "access" element was added to tabs.xml indicating which view mode each role has.
<access role="..." mode="..."/>
The "mode" attribute values are:
- none: do not show the tab; - admin: full instance access; - read-only: read-only access; - byInstance: each resource will have its configuration sent by the backend ("access" parameter);
The user will only be able to manage the guests he/she is assigned for, because that the guest tab has 'mode' == by-instance. That way each VM will have a new parameter "access" indicating if user has "full" or "read-only" access to this VM.
As a user can edit a guest, he/she may need to know which networks and storage pools are configured, so set network and storage tab 'mode' to read-only.
And as user should not perform any operation on host or templates, set their 'mode' attributes to 'none'.
Signed-off-by: Aline Manera <alinefm@linux.vnet.ibm.com> --- config/ui/tabs.xml | 15 +++++++++++++++ plugins/sample/ui/config/tab-ext.xml | 3 +++ 2 files changed, 18 insertions(+)
diff --git a/config/ui/tabs.xml b/config/ui/tabs.xml index b045521..f79684c 100644 --- a/config/ui/tabs.xml +++ b/config/ui/tabs.xml @@ -1,22 +1,37 @@ <?xml version="1.0" encoding="utf-8"?> <tabs> <tab> + <access role="admin" mode="admin"/> + <access role="user" mode="none"/> + <title>Host</title> <path>tabs/host.html</path> </tab> <tab> + <access role="admin" mode="admin"/> + <access role="user" mode="byInstance"/> Maybe more role for guests? Virtualization admin can create/destroy, guest system admin can start and stop, guest user can just login and logout.
For 1.3 only those 2 roles will be added. And then we can add more and more according to our needs.
+ <title>Guests</title> <path>tabs/guests.html</path> </tab> <tab> + <access role="admin" mode="admin"/> + <access role="user" mode="none"/> I suppose also read-only for 'user', because user may want to browser templates available, check details of the template to make decision about which to use.
A user will no be able to create a VM. So why he/she needs to know details about a template as the only proposal of templates is creating VMs?
Let me explain you the full idea: - For now (1.3) we only have 2 roles: admin and user - admin role can do everything in kimchi - user role only can manage the VMs assigned to him
After getting it done, we idea is add more roles and levels for users. Example: - user role only can get the console of a VM he/she is assigned for - user role only can start/stop/console of VM ... and so on
Because that, the mode for a user role in the guests tab is set to "byInstance" as each VM must tell what kind of access the user has to it
+ <title>Templates</title> <path>tabs/templates.html</path> </tab> <tab> + <access role="admin" mode="admin"/> + <access role="user" mode="read-only"/> + <title>Storage</title> <path>tabs/storage.html</path> </tab> <tab> + <access role="admin" mode="admin"/> + <access role="user" mode="read-only"/> + <title>Network</title> <path>tabs/network.html</path> </tab> diff --git a/plugins/sample/ui/config/tab-ext.xml b/plugins/sample/ui/config/tab-ext.xml index 8e0b3d3..a1fb1c2 100644 --- a/plugins/sample/ui/config/tab-ext.xml +++ b/plugins/sample/ui/config/tab-ext.xml @@ -1,6 +1,9 @@ <?xml version="1.0" encoding="utf-8"?> <tabs-ext> <tab> + <access role="admin" mode="admin"/> + <access role="user" mode="none"/> + <title>SampleTab</title> <path>plugins/sample/tab.html</path> </tab>
_______________________________________________ Kimchi-devel mailing list Kimchi-devel@ovirt.org http://lists.ovirt.org/mailman/listinfo/kimchi-devel

From: Aline Manera <alinefm@linux.vnet.ibm.com> As the guests tab has by-instance mode when a normal user is logged into Kimchi, each VM resource must specify the user access. By now, if a user has access to a VM he/she will have full access to it so add "access=full". Signed-off-by: Aline Manera <alinefm@linux.vnet.ibm.com> --- src/kimchi/mockmodel.py | 3 ++- src/kimchi/model/vms.py | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/kimchi/mockmodel.py b/src/kimchi/mockmodel.py index d1cec70..0e45d1e 100644 --- a/src/kimchi/mockmodel.py +++ b/src/kimchi/mockmodel.py @@ -1050,7 +1050,8 @@ def __init__(self, uuid, name, template_info): 'graphics': {'type': 'vnc', 'listen': '0.0.0.0', 'port': None}, 'users': ['user1', 'user2', 'root'], - 'groups': ['group1', 'group2', 'admin'] + 'groups': ['group1', 'group2', 'admin'], + 'access': 'full' } self.info['graphics'].update(template_info['graphics']) diff --git a/src/kimchi/model/vms.py b/src/kimchi/model/vms.py index 17bda04..8c0dcb1 100644 --- a/src/kimchi/model/vms.py +++ b/src/kimchi/model/vms.py @@ -395,7 +395,8 @@ def lookup(self, name): "listen": graphics_listen, "port": graphics_port}, 'users': users, - 'groups': groups + 'groups': groups, + 'access': 'full' } def _vm_get_disk_paths(self, dom): -- 1.9.3

Reviewed-by: Royce Lv<lvroyce@linux.vnet.ibm.com> On 2014年07月16日 03:44, alinefm@linux.vnet.ibm.com wrote:
From: Aline Manera <alinefm@linux.vnet.ibm.com>
As the guests tab has by-instance mode when a normal user is logged into Kimchi, each VM resource must specify the user access. By now, if a user has access to a VM he/she will have full access to it so add "access=full".
Signed-off-by: Aline Manera <alinefm@linux.vnet.ibm.com> --- src/kimchi/mockmodel.py | 3 ++- src/kimchi/model/vms.py | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-)
diff --git a/src/kimchi/mockmodel.py b/src/kimchi/mockmodel.py index d1cec70..0e45d1e 100644 --- a/src/kimchi/mockmodel.py +++ b/src/kimchi/mockmodel.py @@ -1050,7 +1050,8 @@ def __init__(self, uuid, name, template_info): 'graphics': {'type': 'vnc', 'listen': '0.0.0.0', 'port': None}, 'users': ['user1', 'user2', 'root'], - 'groups': ['group1', 'group2', 'admin'] + 'groups': ['group1', 'group2', 'admin'], + 'access': 'full' } self.info['graphics'].update(template_info['graphics'])
diff --git a/src/kimchi/model/vms.py b/src/kimchi/model/vms.py index 17bda04..8c0dcb1 100644 --- a/src/kimchi/model/vms.py +++ b/src/kimchi/model/vms.py @@ -395,7 +395,8 @@ def lookup(self, name): "listen": graphics_listen, "port": graphics_port}, 'users': users, - 'groups': groups + 'groups': groups, + 'access': 'full' }
def _vm_get_disk_paths(self, dom):
participants (4)
-
Aline Manera
-
alinefm@linux.vnet.ibm.com
-
Royce Lv
-
Sheldon