summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--cobbler/modules/authz_ownership.py69
-rw-r--r--cobbler/remote.py5
-rw-r--r--tests/tests.py31
3 files changed, 85 insertions, 20 deletions
diff --git a/cobbler/modules/authz_ownership.py b/cobbler/modules/authz_ownership.py
index 3befc4a..ff5c016 100644
--- a/cobbler/modules/authz_ownership.py
+++ b/cobbler/modules/authz_ownership.py
@@ -50,6 +50,47 @@ def __parse_config():
alldata[g][o] = 1
return alldata
+def __authorize_kickstart(api_handle, user, user_groups, file):
+ # the authorization rules for kickstart editing are a bit
+ # of a special case. Non-admin users can edit a kickstart
+ # only if all objects that depend on that kickstart are
+ # editable by the user in question.
+ #
+ # Example:
+ # if Pinky owns ProfileA
+ # and the Brain owns ProfileB
+ # and both profiles use the same kickstart template
+ # and neither Pinky nor the Brain is an admin
+ # neither is allowed to edit the kickstart template
+ # because they would make unwanted changes to each other
+ #
+ # In the above scenario the UI will explain the problem
+ # and ask that the user asks the admin to resolve it if required.
+ # NOTE: this function is only called by authorize so admin users are
+ # cleared before this function is called.
+
+ lst = api_handle.find_profile(kickstart=kickstart, return_list=True)
+ lst.extend(api_handle.find_system(kickstart=kickstart, return_list=True))
+ for obj in lst:
+ if not __is_user_allowed(obj, user, user_groups):
+ return 0
+ return 1
+
+def __is_user_allowed(obj, user, user_groups):
+ if obj.owners == []:
+ # no ownership restrictions, cleared
+ return 1
+ for allowed in obj.owners:
+ if user == allowed:
+ # user match
+ return 1
+ # else look for a group match
+ for group in user_groups:
+ if group == allowed and user in user_groups[group]:
+ return 1
+ return 0
+
+
def authorize(api_handle,user,resource,arg1=None,arg2=None):
"""
@@ -60,10 +101,10 @@ def authorize(api_handle,user,resource,arg1=None,arg2=None):
user_groups = __parse_config()
# classify the type of operation
- save_or_remove = False
- for criteria in ["save_","remove_","modify_"]:
+ modify_operation = False
+ for criteria in ["save","remove","modify","write","edit"]:
if resource.find(criteria) != -1:
- save_or_remove = True
+ modify_operation = True
# FIXME: is everyone allowed to copy? I think so.
# FIXME: deal with the problem of deleted parents and promotion
@@ -83,14 +124,19 @@ def authorize(api_handle,user,resource,arg1=None,arg2=None):
# if the user isn't anywhere in the file, reject regardless
# they can still use read-only XMLRPC
return 0
- if not save_or_remove:
+ if not modify_operation:
# sufficient to allow access for non save/remove ops to all
# users for now, may want to refine later.
return 1
- # now we have a save_or_remove op, so we must check ownership
+ # now we have a modify_operation op, so we must check ownership
# of the object. remove ops pass in arg1 as a string name,
# saves pass in actual objects, so we must treat them differently.
+ # kickstarts are even more special so we call those out to another
+ # function, rather than going through the rest of the code here.
+
+ if resource.find("kickstart"):
+ return authorize_kickstart(api_handle,user,user_groups,arg1)
obj = None
if resource.find("remove") != -1:
@@ -109,18 +155,7 @@ def authorize(api_handle,user,resource,arg1=None,arg2=None):
if obj.owners is None or obj.owners == []:
return 1
- # otherwise, ownership by user/group
- for allowed in obj.owners:
- if user == allowed:
- # user match
- return 1
- for group in user_groups:
- if group == allowed and user in user_groups[group]:
- return 1
-
- # can't find user or group in ownership list and ownership is defined
- # so reject the operation
- return 0
+ return __is_user_allowed(user,group,user_groups)
if __name__ == "__main__":
diff --git a/cobbler/remote.py b/cobbler/remote.py
index 5e3fb83..ebb01f7 100644
--- a/cobbler/remote.py
+++ b/cobbler/remote.py
@@ -1050,7 +1050,10 @@ class CobblerReadWriteXMLRPCInterface(CobblerXMLRPCInterface):
"""
self.log("read_or_write_kickstart_template",name=kickstart_file,token=token)
- self.check_access(token,"read_or_write_kickstart_templates",kickstart_file,is_read)
+ if is_read:
+ self.check_access(token,"read_kickstart",kickstart_file)
+ else:
+ self.check_access(token,"modify_kickstart",kickstart_file)
if kickstart_file.find("..") != -1 or not kickstart_file.startswith("/"):
raise CX(_("tainted file location"))
diff --git a/tests/tests.py b/tests/tests.py
index c3cbea7..f9bc368 100644
--- a/tests/tests.py
+++ b/tests/tests.py
@@ -105,7 +105,9 @@ class Ownership(BootTest):
def test_ownership_params(self):
- # initially just test that we can set ownership on various components
+ fd = open("/tmp/test_cobbler_kickstart","w+")
+ fd.write("")
+ fd.close()
distro = self.api.find_distro(name="testdistro0")
profile = self.api.find_profile(name="testprofile0")
@@ -113,7 +115,8 @@ class Ownership(BootTest):
repo = self.api.find_repo(name="test_repo")
self.assertTrue(distro.set_owners(["superlab","basement1"]))
self.assertTrue(profile.set_owners(["superlab","basement1"]))
- self.assertTrue(system.set_owners(["superlab","basement1"]))
+ self.assertTrue(profile.set_kickstart("/tmp/test_cobbler_kickstart"))
+ self.assertTrue(system.set_owners(["superlab","basement1","basement3"]))
self.assertTrue(repo.set_owners([]))
self.api.add_distro(distro)
self.api.add_profile(profile)
@@ -136,17 +139,41 @@ class Ownership(BootTest):
fd.write("\n")
fd.write("[superlab]\n")
fd.write("superlab1 = 1\n")
+ fd.write("superlab2 = 1\n")
fd.write("\n")
fd.write("[basement]\n")
fd.write("basement1 = 1\n")
fd.write("basement2 = 1\n")
+ fd.write("basement3 = 1\n")
fd.close()
+
xo = self.api.find_distro("testdistro0")
xn = "testdistro0"
ro = self.api.find_repo("test_repo")
rn = "test_repo"
+ # WARNING: complex test explanation follows!
+ # we must ensure those who can edit the kickstart are only those
+ # who can edit all objects that depend on the said kickstart
+ # in this test, superlab & basement1 can edit test_profile0
+ # superlab & basement1/3 can edit test_system0
+ # the systems share a common kickstart record (in this case
+ # explicitly set, which is a bit arbitrary as they are parent/child
+ # nodes, but the concept is not limited to this).
+ # Therefore the correct result is that the following users can edit:
+ # admin1, superlab1, superlab2
+ # And these folks can't
+ # basement1, basement2
+ # Basement2 is rejected because the kickstart is shared by something
+ # basmeent2 can not edit.
+
+ for user in [ "admin1", "superlab1", "superlab2", "basement1" ]:
+ self.assertTrue(1==authorize(self.api, user, "modify_kickstart", xo), "%s can modify_kickstart" % user)
+
+ for user in [ "basement2", "dne" ]:
+ self.assertTrue(0==authorize(self.api, user, "modify_kickstart", xo), "%s can modify_kickstart" % user)
+
# ensure admin1 can edit (he's an admin) and do other tasks
# same applies to basement1 who is explicitly added as a user
# and superlab1 who is in a group in the ownership list