summaryrefslogtreecommitdiffstats
path: root/ipa-server
diff options
context:
space:
mode:
authorKevin McCarthy <kmccarth@redhat.com>2007-10-12 15:11:55 -0700
committerKevin McCarthy <kmccarth@redhat.com>2007-10-12 15:11:55 -0700
commit63f7cdf7f7e1c39b791dad6951fa39d9a6d58c9d (patch)
tree72f1bd539e6fcbbce99a31f4b6695c149e828c2a /ipa-server
parentaf0a1d989b1eb483ae3e76fa5a3008fda3fafb5e (diff)
downloadfreeipa-63f7cdf7f7e1c39b791dad6951fa39d9a6d58c9d.tar.gz
freeipa-63f7cdf7f7e1c39b791dad6951fa39d9a6d58c9d.tar.xz
freeipa-63f7cdf7f7e1c39b791dad6951fa39d9a6d58c9d.zip
Adds delegation listing and creation to the GUI.
Diffstat (limited to 'ipa-server')
-rw-r--r--ipa-server/ipa-gui/ipagui/controllers.py2
-rw-r--r--ipa-server/ipa-gui/ipagui/forms/delegate.py86
-rw-r--r--ipa-server/ipa-gui/ipagui/static/css/style.css17
-rw-r--r--ipa-server/ipa-gui/ipagui/subcontrollers/delegation.py168
-rw-r--r--ipa-server/ipa-gui/ipagui/templates/delegategroupsearch.kid31
-rw-r--r--ipa-server/ipa-gui/ipagui/templates/delegatelayout.kid16
-rw-r--r--ipa-server/ipa-gui/ipagui/templates/delegatelist.kid60
-rw-r--r--ipa-server/ipa-gui/ipagui/templates/delegatenew.kid15
-rw-r--r--ipa-server/ipa-gui/ipagui/templates/delegatenewform.kid154
-rw-r--r--ipa-server/ipa-gui/ipagui/templates/master.kid3
-rw-r--r--ipa-server/xmlrpc-server/funcs.py9
-rw-r--r--ipa-server/xmlrpc-server/ipaxmlrpc.py1
12 files changed, 560 insertions, 2 deletions
diff --git a/ipa-server/ipa-gui/ipagui/controllers.py b/ipa-server/ipa-gui/ipagui/controllers.py
index 340d6f9f6..f2b7bb909 100644
--- a/ipa-server/ipa-gui/ipagui/controllers.py
+++ b/ipa-server/ipa-gui/ipagui/controllers.py
@@ -14,12 +14,14 @@ import ipa.ipaclient
from subcontrollers.user import UserController
from subcontrollers.group import GroupController
+from subcontrollers.delegation import DelegationController
ipa.config.init_config()
class Root(controllers.RootController):
user = UserController()
group = GroupController()
+ delegate = DelegationController()
@expose(template="ipagui.templates.welcome")
@identity.require(identity.not_anonymous())
diff --git a/ipa-server/ipa-gui/ipagui/forms/delegate.py b/ipa-server/ipa-gui/ipagui/forms/delegate.py
new file mode 100644
index 000000000..3b4967d6c
--- /dev/null
+++ b/ipa-server/ipa-gui/ipagui/forms/delegate.py
@@ -0,0 +1,86 @@
+import turbogears
+from turbogears import validators, widgets
+
+from ipagui.forms.user import UserFields
+
+# TODO - get from config or somewhere
+aci_attrs = [
+ UserFields.givenname,
+ UserFields.sn,
+ UserFields.cn,
+ UserFields.title,
+ UserFields.displayname,
+ UserFields.initials,
+ UserFields.uid,
+ UserFields.userpassword,
+ UserFields.uidnumber,
+ UserFields.gidnumber,
+ UserFields.homedirectory,
+ UserFields.loginshell,
+ UserFields.gecos,
+ UserFields.mail,
+ UserFields.telephonenumber,
+ UserFields.facsimiletelephonenumber,
+ UserFields.mobile,
+ UserFields.pager,
+ UserFields.homephone,
+ UserFields.street,
+ UserFields.l,
+ UserFields.st,
+ UserFields.postalcode,
+ UserFields.ou,
+ UserFields.businesscategory,
+ UserFields.description,
+ UserFields.employeetype,
+ UserFields.manager,
+ UserFields.roomnumber,
+ UserFields.secretary,
+ UserFields.carlicense,
+ UserFields.labeleduri,
+]
+
+aci_checkbox_attrs = [(field.name, field.label) for field in aci_attrs]
+
+class DelegateFields():
+ name = widgets.TextField(name="name", label="ACI Name")
+
+ source_group_dn = widgets.HiddenField(name="source_group_dn")
+ dest_group_dn = widgets.HiddenField(name="dest_group_dn")
+
+ source_group_cn = widgets.HiddenField(name="source_group_cn",
+ label="People in Group")
+ dest_group_cn = widgets.HiddenField(name="dest_group_cn",
+ label="For People in Group")
+
+ attrs = widgets.CheckBoxList(name="attrs", label="Can Modify",
+ options=aci_checkbox_attrs, validator=validators.NotEmpty)
+
+class DelegateNewValidator(validators.Schema):
+ name = validators.String(not_empty=True)
+ source_group_dn = validators.String(not_empty=True,
+ messages = { 'empty': _("Please choose a group"), })
+ dest_group_dn = validators.String(not_empty=True,
+ messages = { 'empty': _("Please choose a group"), })
+ attrs = validators.NotEmpty(
+ messages = { 'empty': _("Please select at least one value"), })
+
+class DelegateNewForm(widgets.Form):
+ params = ['delegate', 'attr_list']
+
+ hidden_fields = [
+ DelegateFields.source_group_dn,
+ DelegateFields.dest_group_dn,
+ DelegateFields.source_group_cn,
+ DelegateFields.dest_group_cn,
+ ]
+
+ validator = DelegateNewValidator()
+
+ def __init__(self, *args, **kw):
+ super(DelegateNewForm,self).__init__(*args, **kw)
+ (self.template_c, self.template) = widgets.meta.load_kid_template(
+ "ipagui.templates.delegatenewform")
+ self.delegate = DelegateFields
+
+ def update_params(self, params):
+ super(DelegateNewForm,self).update_params(params)
diff --git a/ipa-server/ipa-gui/ipagui/static/css/style.css b/ipa-server/ipa-gui/ipagui/static/css/style.css
index ae845e863..fb97a67a2 100644
--- a/ipa-server/ipa-gui/ipagui/static/css/style.css
+++ b/ipa-server/ipa-gui/ipagui/static/css/style.css
@@ -77,7 +77,7 @@ body {
#main_content {
background:#fff;
float:right;
- width:85%;
+ width:82%;
min-height:500px;
border-left: 1px solid #000;
padding: 10px;
@@ -92,7 +92,7 @@ body {
#sidebar {
background:#ccc; /* should be same as #page */
float:left;
- width:10%;
+ width:13%;
padding: 5px;
font-size: medium;
}
@@ -207,6 +207,19 @@ body {
}
/*
+ * Used for checkboxlist of aci attributes
+ */
+ul.requiredfield {
+ background: #ffffff;
+}
+
+ul.checkboxlist {
+ padding: 0px;
+ margin: 0px;
+ list-style: none;
+}
+
+/*
* TableKit css
*/
diff --git a/ipa-server/ipa-gui/ipagui/subcontrollers/delegation.py b/ipa-server/ipa-gui/ipagui/subcontrollers/delegation.py
new file mode 100644
index 000000000..8adbc7da6
--- /dev/null
+++ b/ipa-server/ipa-gui/ipagui/subcontrollers/delegation.py
@@ -0,0 +1,168 @@
+import os
+from pickle import dumps, loads
+from base64 import b64encode, b64decode
+
+import cherrypy
+import turbogears
+from turbogears import controllers, expose, flash
+from turbogears import validators, validate
+from turbogears import widgets, paginate
+from turbogears import error_handler
+from turbogears import identity
+
+from ipacontroller import IPAController
+from ipa.entity import utf8_encode_values
+from ipa import ipaerror
+import ipagui.forms.delegate
+import ipa.aci
+
+import ldap.dn
+
+aci_fields = ['*', 'aci']
+
+delegate_new_form = ipagui.forms.delegate.DelegateNewForm()
+
+class DelegationController(IPAController):
+
+ @expose()
+ @identity.require(identity.not_anonymous())
+ def index(self, tg_errors=None):
+ raise turbogears.redirect("/delegate/list")
+
+ @expose("ipagui.templates.delegatenew")
+ @identity.require(identity.not_anonymous())
+ def new(self):
+ """Display delegate page"""
+ client = self.get_ipaclient()
+ delegate = {}
+ delegate['source_group_cn'] = "Please choose"
+ delegate['dest_group_cn'] = "Please choose"
+
+ return dict(form=delegate_new_form, delegate=delegate)
+
+ @expose()
+ @identity.require(identity.not_anonymous())
+ def create(self, **kw):
+ """Creates a new delegation"""
+ client = self.get_ipaclient()
+
+ tg_errors, kw = self.delegatecreatevalidate(**kw)
+ if tg_errors:
+ return dict(form=delegate_new_form, delegate=kw,
+ tg_template='ipagui.templates.delegatenew')
+
+ try:
+ new_aci = ipa.aci.ACI()
+ new_aci.name = kw.get('name')
+ new_aci.source_group = kw.get('source_group_dn')
+ new_aci.dest_group = kw.get('dest_group_dn')
+ new_aci.attrs = kw.get('attrs')
+
+ # not pulling down existing aci attributes
+ aci_entry = client.get_aci_entry(['dn'])
+ aci_entry.setValue('aci', new_aci.export_to_string())
+
+ # TODO - add a client.update_entry() call instead
+ client.update_group(aci_entry)
+ except ipaerror.IPAError, e:
+ turbogears.flash("Delgate add failed: " + str(e))
+ return dict(form=delegate_new_form, delegate=kw,
+ tg_template='ipagui.templates.delegatenew')
+
+ turbogears.flash("delegate created")
+ raise turbogears.redirect('/delegate/list')
+#
+# @expose("ipagui.templates.delegateedit")
+# @identity.require(identity.not_anonymous())
+# def edit(self):
+# """Display delegate page"""
+# client = self.get_ipaclient()
+#
+# return dict(userfields=ipagui.forms.user.UserFields())
+#
+# @expose()
+# @identity.require(identity.not_anonymous())
+# def update(self, **kw):
+# """Display delegate page"""
+# client = self.get_ipaclient()
+#
+# turbogears.flash("delegate updated")
+# raise turbogears.redirect('/delegate/list')
+
+ @expose("ipagui.templates.delegatelist")
+ @identity.require(identity.not_anonymous())
+ def list(self):
+ """Display delegate page"""
+ client = self.get_ipaclient()
+
+ aci_entry = client.get_aci_entry(aci_fields)
+ aci_str_list = aci_entry.getValues('aci')
+ if aci_str_list is None:
+ aci_str_list = []
+
+ aci_list = []
+ for aci_str in aci_str_list:
+ try:
+ aci = ipa.aci.ACI(aci_str)
+ aci_list.append(aci)
+ except SyntaxError:
+ # ignore aci_str's that ACI can't parse
+ pass
+ group_dn_to_cn = self.extract_group_cns(aci_list, client)
+
+ return dict(aci_list=aci_list, group_dn_to_cn=group_dn_to_cn)
+
+ @expose("ipagui.templates.delegategroupsearch")
+ @identity.require(identity.not_anonymous())
+ def group_search(self, **kw):
+ """Searches for groups and displays list of results in a table.
+ This method is used for the ajax search on the delegation pages."""
+ client = self.get_ipaclient()
+
+ groups = []
+ groups_counter = 0
+ searchlimit = 100
+ criteria = kw.get('criteria')
+ if criteria != None and len(criteria) > 0:
+ try:
+ groups = client.find_groups(criteria.encode('utf-8'), None,
+ searchlimit)
+ groups_counter = groups[0]
+ groups = groups[1:]
+ except ipaerror.IPAError, e:
+ turbogears.flash("search failed: " + str(e))
+
+ return dict(groups=groups, criteria=criteria,
+ which_group=kw.get('which_group'),
+ counter=groups_counter)
+
+ @validate(form=delegate_new_form)
+ @identity.require(identity.not_anonymous())
+ def delegatecreatevalidate(self, tg_errors=None, **kw):
+ return tg_errors, kw
+
+ def extract_group_cns(self, aci_list, client):
+ """Extracts all the cn's from a list of aci's and returns them as a hash
+ from group_dn to group_cn.
+
+ It first tries to cheat by looking at the first rdn for the
+ group dn. If that's not cn for some reason, it looks up the group."""
+ group_dn_to_cn = {}
+ for aci in aci_list:
+ for dn in (aci.source_group, aci.dest_group):
+ if not group_dn_to_cn.has_key(dn):
+ rdn_list = ldap.dn.str2dn(dn)
+ first_rdn = rdn_list[0]
+ for (type,value,junk) in first_rdn:
+ if type == "cn":
+ group_dn_to_cn[dn] = value
+ break;
+ else:
+ try:
+ group = client.get_entry_by_dn(dn, ['cn'])
+ group_dn_to_cn[dn] = group.getValue('cn')
+ except ipaerror.IPAError, e:
+ group_dn_to_cn[dn] = 'unknown'
+
+ return group_dn_to_cn
+
diff --git a/ipa-server/ipa-gui/ipagui/templates/delegategroupsearch.kid b/ipa-server/ipa-gui/ipagui/templates/delegategroupsearch.kid
new file mode 100644
index 000000000..f97355f8b
--- /dev/null
+++ b/ipa-server/ipa-gui/ipagui/templates/delegategroupsearch.kid
@@ -0,0 +1,31 @@
+<div xmlns:py="http://purl.org/kid/ns#">
+
+<?python
+from ipagui.helpers import ipahelper
+?>
+ <div py:if='(groups != None) and (len(groups) > 0)'>
+ <div id="search-results-count">
+ ${len(groups)} results returned:
+ <span py:if="counter &lt; 0">
+ (truncated)
+ </span>
+ </div>
+
+ <div py:for="group in groups">
+ <?python
+ group_dn_esc = ipahelper.javascript_string_escape(group.dn)
+ group_cn_esc = ipahelper.javascript_string_escape(group.cn)
+ which_group_esc = ipahelper.javascript_string_escape(which_group)
+ ?>
+
+ ${group.cn}
+ <a href=""
+ onclick="selectGroup('${which_group_esc}', '${group_dn_esc}', '${group_cn_esc}');
+ return false;"
+ >select</a>
+ </div>
+ </div>
+ <div py:if='(groups != None) and (len(groups) == 0)'>
+ No results found for "${criteria}"
+ </div>
+</div>
diff --git a/ipa-server/ipa-gui/ipagui/templates/delegatelayout.kid b/ipa-server/ipa-gui/ipagui/templates/delegatelayout.kid
new file mode 100644
index 000000000..6cec389cb
--- /dev/null
+++ b/ipa-server/ipa-gui/ipagui/templates/delegatelayout.kid
@@ -0,0 +1,16 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml" xmlns:py="http://purl.org/kid/ns#"
+ py:extends="'master.kid'">
+<head>
+</head>
+
+<body py:match="item.tag=='{http://www.w3.org/1999/xhtml}body'" py:attrs="item.items()">
+ <div id="main_content">
+ <div id="status_block" py:if="value_of('tg_flash', None)"
+ py:content="XML(tg_flash)"></div>
+
+ <div py:replace="[item.text]+item[:]"></div>
+ </div>
+</body>
+
+</html>
diff --git a/ipa-server/ipa-gui/ipagui/templates/delegatelist.kid b/ipa-server/ipa-gui/ipagui/templates/delegatelist.kid
new file mode 100644
index 000000000..c88b6e31f
--- /dev/null
+++ b/ipa-server/ipa-gui/ipagui/templates/delegatelist.kid
@@ -0,0 +1,60 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml" xmlns:py="http://purl.org/kid/ns#"
+ py:extends="'delegatelayout.kid'">
+<head>
+<meta content="text/html; charset=utf-8" http-equiv="Content-Type" py:replace="''"/>
+<title>Delegations</title>
+</head>
+<body>
+ <script type="text/javascript" charset="utf-8" src="${tg.url('/static/javascript/tablekit.js')}"></script>
+
+ <h2>Delegations</h2>
+
+ <table id="resultstable" class="sortable resizable">
+ <thead>
+ <tr>
+ <th>Name</th>
+ <th>People in Group</th>
+ <th>Can Modify</th>
+ <th>For People in Group</th>
+ <th>Action</th>
+ </tr>
+ </thead>
+ <tbody>
+ <tr py:for='aci in aci_list'>
+ <?python
+ source_cn = group_dn_to_cn.get(aci.source_group)
+ dest_cn = group_dn_to_cn.get(aci.dest_group)
+ ?>
+ <td>
+ ${aci.name}
+ </td>
+ <td>
+ <a href="${tg.url('/group/show', cn=source_cn)}"
+ >${source_cn}</a>
+ </td>
+ <td>
+ ${", ".join(aci.attrs)}
+ </td>
+ <td>
+ <a href="${tg.url('/group/show', cn=dest_cn)}"
+ >${dest_cn}</a>
+ </td>
+ <td>
+ <a href="${tg.url('/delegate/edit')}">edit</a> (TODO)<br />
+ </td>
+ </tr>
+ </tbody>
+ </table>
+
+ <table border="0">
+ <tbody>
+ <tr>
+ <td>
+ <a href="${tg.url('/delegate/new')}">add new delegation</a><br />
+ </td>
+ </tr>
+ </tbody>
+ </table>
+</body>
+</html>
diff --git a/ipa-server/ipa-gui/ipagui/templates/delegatenew.kid b/ipa-server/ipa-gui/ipagui/templates/delegatenew.kid
new file mode 100644
index 000000000..71d9e7e2a
--- /dev/null
+++ b/ipa-server/ipa-gui/ipagui/templates/delegatenew.kid
@@ -0,0 +1,15 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml" xmlns:py="http://purl.org/kid/ns#"
+ py:extends="'delegatelayout.kid'">
+<head>
+<meta content="text/html; charset=utf-8" http-equiv="Content-Type" py:replace="''"/>
+<title>Add Delegation</title>
+</head>
+<body>
+
+ <h2>Add Delegation</h2>
+
+ ${form.display(action=tg.url("/delegate/create"), value=delegate)}
+
+</body>
+</html>
diff --git a/ipa-server/ipa-gui/ipagui/templates/delegatenewform.kid b/ipa-server/ipa-gui/ipagui/templates/delegatenewform.kid
new file mode 100644
index 000000000..95f93b5b4
--- /dev/null
+++ b/ipa-server/ipa-gui/ipagui/templates/delegatenewform.kid
@@ -0,0 +1,154 @@
+<div xmlns:py="http://purl.org/kid/ns#"
+ class="simpleroster">
+
+ <?python searchurl = tg.url('/delegate/group_search') ?>
+
+ <script type="text/javascript">
+
+ function enterDoSearch(e, which_group) {
+ var keyPressed;
+ if (window.event) {
+ keyPressed = window.event.keyCode;
+ } else {
+ keyPressed = e.which;
+ }
+
+ if (keyPressed == 13) {
+ return doSearch(which_group);
+ } else {
+ return true;
+ }
+ }
+
+ function doSearch(which_group) {
+ $(which_group + '_searchresults').update("Searching...");
+ new Ajax.Updater(which_group + '_searchresults',
+ '${searchurl}',
+ { asynchronous:true,
+ parameters: { criteria: $(which_group + '_criteria').value,
+ which_group: which_group},
+ evalScripts: true });
+ return false;
+ }
+
+ function selectGroup(which_group, group_dn, group_cn) {
+ group_dn_field = $('form_' + which_group + '_group_dn');
+ group_cn_field = $('form_' + which_group + '_group_cn');
+ group_cn_span = $(which_group + '_group_cn');
+
+ group_dn_field.value = group_dn;
+ group_cn_field.value = group_cn;
+ group_cn_span.update(group_cn);
+
+ new Effect.Fade($(which_group + '_searcharea'), {duration: 0.25});
+ new Effect.Appear($(which_group + '_change_link'), {duration: 0.25});
+ }
+ </script>
+
+ <form action="${action}" name="${name}" method="${method}" class="tableform">
+
+ <table class="formtable" cellpadding="2" cellspacing="0" border="0">
+ <tr>
+ <td>
+ <input type="submit" class="submitbutton" name="submit"
+ value="Add Delegation"/>
+ </td>
+ </tr>
+ </table>
+
+ <div py:for="field in hidden_fields"
+ py:replace="field.display(value_for(field), **params_for(field))"
+ />
+
+ <table class="formtable" cellpadding="2" cellspacing="0" border="0">
+ <tr>
+ <th valign="top">
+ <label class="fieldlabel" for="${delegate.name.field_id}"
+ py:content="delegate.name.label" />:
+ </th>
+ <td>
+ <span py:replace="delegate.name.display(value_for(delegate.name))" />
+ <span py:if="tg.errors.get('name')" class="fielderror"
+ py:content="tg.errors.get('name')" />
+ </td>
+ </tr>
+ <tr>
+ <th valign="top">
+ <label class="fieldlabel" for="${delegate.source_group_cn.field_id}"
+ py:content="delegate.source_group_cn.label" />:
+ </th>
+ <td>
+ <div>
+ <span id='source_group_cn'>${value_for(delegate.source_group_cn)}</span>
+ <a href="#" id='source_change_link'
+ onclick="new Effect.Appear($('source_searcharea'), {duration: 0.25});
+ new Effect.Fade(this, {duration: 0.25});
+ return false;">change</a>
+ <span py:if="tg.errors.get('source_group_dn')" class="fielderror"
+ py:content="tg.errors.get('source_group_dn')" />
+ </div>
+ <div id="source_searcharea" style="display:none">
+ <div>
+ <input id="source_criteria" type="text"
+ onkeypress="return enterDoSearch(event, 'source');" />
+ <input type="button" value="Find"
+ onclick="return doSearch('source');"
+ />
+ </div>
+ <div id="source_searchresults">
+ </div>
+ </div>
+ </td>
+ </tr>
+ <tr>
+ <th valign="top">
+ <label class="fieldlabel" for="${delegate.attrs.field_id}"
+ py:content="delegate.attrs.label" />:
+ </th>
+ <td valign="top">
+ <span py:if="tg.errors.get('attrs')" class="fielderror"
+ py:content="tg.errors.get('attrs')" />
+ <span py:replace="delegate.attrs.display(value_for(delegate.attrs))" />
+ </td>
+ </tr>
+ <tr>
+ <th valign="top">
+ <label class="fieldlabel" for="${delegate.dest_group_cn.field_id}"
+ py:content="delegate.dest_group_cn.label" />:
+ </th>
+ <td>
+ <div>
+ <span id='dest_group_cn'>${value_for(delegate.dest_group_cn)}</span>
+ <a href="#" id='dest_change_link'
+ onclick="new Effect.Appear($('dest_searcharea'), {duration: 0.25});
+ new Effect.Fade(this, {duration: 0.25});
+ return false;">change</a>
+ <span py:if="tg.errors.get('dest_group_dn')" class="fielderror"
+ py:content="tg.errors.get('dest_group_dn')" />
+ </div>
+ <div id="dest_searcharea" style="display:none">
+ <div>
+ <input id="dest_criteria" type="text"
+ onkeypress="return enterDoSearch(event, 'dest');" />
+ <input type="button" value="Find"
+ onclick="return doSearch('dest');"
+ />
+ </div>
+ <div id="dest_searchresults">
+ </div>
+ </div>
+ </td>
+ </tr>
+ </table>
+
+ <table class="formtable" cellpadding="2" cellspacing="0" border="0">
+ <tr>
+ <td>
+ <input type="submit" class="submitbutton" name="submit"
+ value="Add Delegation"/>
+ </td>
+ </tr>
+ </table>
+
+ </form>
+</div>
diff --git a/ipa-server/ipa-gui/ipagui/templates/master.kid b/ipa-server/ipa-gui/ipagui/templates/master.kid
index 52b88e37c..2926c4f90 100644
--- a/ipa-server/ipa-gui/ipagui/templates/master.kid
+++ b/ipa-server/ipa-gui/ipagui/templates/master.kid
@@ -77,6 +77,9 @@
<a href="${tg.url('/')}">Manage Policy</a><br/>
<a href="${tg.url('/')}">Self Service</a><br/>
</p>
+ <p>
+ <a href="${tg.url('/delegate/list')}">Delegation Mgmt</a><br/>
+ </p>
</div>
<div py:replace="[item.text]+item[:]"></div>
diff --git a/ipa-server/xmlrpc-server/funcs.py b/ipa-server/xmlrpc-server/funcs.py
index 517d54a78..0dd0c2c5d 100644
--- a/ipa-server/xmlrpc-server/funcs.py
+++ b/ipa-server/xmlrpc-server/funcs.py
@@ -43,6 +43,7 @@ except ImportError:
# Need a global to store this between requests
_LDAPPool = None
+ACIContainer = "cn=accounts"
DefaultUserContainer = "cn=users,cn=accounts"
DefaultGroupContainer = "cn=groups,cn=accounts"
@@ -315,6 +316,14 @@ class IPAServer:
return (exact_match_filter, partial_match_filter)
+# Higher-level API
+
+ def get_aci_entry(self, sattrs=None, opts=None):
+ """Returns the entry containing access control ACIs."""
+
+ dn="%s,%s" % (ACIContainer, self.basedn)
+ return self.get_entry_by_dn(dn, sattrs, opts)
+
# General searches
def get_entry_by_dn (self, dn, sattrs=None, opts=None):
diff --git a/ipa-server/xmlrpc-server/ipaxmlrpc.py b/ipa-server/xmlrpc-server/ipaxmlrpc.py
index 805dbf070..3872ee21c 100644
--- a/ipa-server/xmlrpc-server/ipaxmlrpc.py
+++ b/ipa-server/xmlrpc-server/ipaxmlrpc.py
@@ -317,6 +317,7 @@ def handler(req, profiling=False):
try:
f = funcs.IPAServer()
h = ModXMLRPCRequestHandler()
+ h.register_function(f.get_aci_entry)
h.register_function(f.get_entry_by_dn)
h.register_function(f.get_entry_by_cn)
h.register_function(f.get_user_by_uid)