[Freeipa-devel] [PATCH] delegation list and create GUI

Kevin McCarthy kmccarth at redhat.com
Fri Oct 12 22:13:16 UTC 2007


This is the delegation ACI listing and creation GUI.  Editing is still
todo.

Sorry, this patch got a bit bigger than I intended, but the listing and
creation were intertwined.

-Kevin

-------------- next part --------------
# HG changeset patch
# User Kevin McCarthy <kmccarth at redhat.com>
# Date 1192227115 25200
# Node ID 2ffed88489759ced4ba285c3f1d00f84eb3eeebe
# Parent  dcfb5974ee278ca2b94052ea19897310ecd7531c
Adds delegation listing and creation to the GUI.

diff -r dcfb5974ee27 -r 2ffed8848975 ipa-python/aci.py
--- a/ipa-python/aci.py	Fri Oct 12 10:11:57 2007 -0700
+++ b/ipa-python/aci.py	Fri Oct 12 15:11:55 2007 -0700
@@ -16,6 +16,7 @@
 #
 
 import re
+import urllib
 
 class ACI:
     """
@@ -25,10 +26,10 @@ class ACI:
     """
 
     def __init__(self,acistr=None):
+        self.name = ''
         self.source_group = ''
         self.dest_group = ''
         self.attrs = []
-        self.name = ''
         if acistr is not None:
             self.parse_acistr(acistr)
 
@@ -40,15 +41,15 @@ class ACI:
         # dn's aren't typed in, but searched for, and the search results
         # will return escaped dns
 
-        acistr = ('(targetattr = "%s")' +
+        acistr = ('(targetattr="%s")' +
                   '(targetfilter="(memberOf=%s)")' +
                   '(version 3.0;' +
                   'acl "%s";' +
                   'allow (write) ' +
-                  'groupdn="%s";)') % (attrs_str,
+                  'groupdn="ldap:///%s";)') % (attrs_str,
                                        self.dest_group,
                                        self.name,
-                                       self.source_group)
+                                       urllib.quote(self.source_group, "/=, "))
         return acistr
 
     def _match(self, prefix, inputstr):
@@ -89,7 +90,7 @@ class ACI:
     def parse_acistr(self, acistr):
         """Parses the acistr.  If the string isn't recognized, a SyntaxError
            is raised."""
-        acistr = self._match('(targetattr = ', acistr)
+        acistr = self._match('(targetattr=', acistr)
         (attrstr, acistr) = self._match_str(acistr)
         self.attrs = attrstr.split(' || ')
 
@@ -107,7 +108,8 @@ class ACI:
 
         acistr = self._match(';allow (write) groupdn=', acistr)
         (src_dn_str, acistr) = self._match_str(acistr)
-        self.source_group = src_dn_str
+        src_dn_str = self._match('ldap:///', src_dn_str)
+        self.source_group = urllib.unquote(src_dn_str)
 
         acistr = self._match(';)', acistr)
         if len(acistr) > 0:
diff -r dcfb5974ee27 -r 2ffed8848975 ipa-python/ipaclient.py
--- a/ipa-python/ipaclient.py	Fri Oct 12 10:11:57 2007 -0700
+++ b/ipa-python/ipaclient.py	Fri Oct 12 15:11:55 2007 -0700
@@ -54,6 +54,14 @@ class IPAClient:
         if self.local:
             self.transport.set_krbccache(krbccache)
 
+# Higher-level API
+
+    def get_aci_entry(self, sattrs=None):
+        """Returns the entry containing access control ACIs."""
+
+        result = self.transport.get_aci_entry(sattrs)
+        return entity.Entity(result)
+
 # General searches
 
     def get_entry_by_dn(self,dn,sattrs=None):
diff -r dcfb5974ee27 -r 2ffed8848975 ipa-python/rpcclient.py
--- a/ipa-python/rpcclient.py	Fri Oct 12 10:11:57 2007 -0700
+++ b/ipa-python/rpcclient.py	Fri Oct 12 15:11:55 2007 -0700
@@ -67,6 +67,23 @@ class RPCClient:
     
         return obj 
 
+# Higher-level API
+
+    def get_aci_entry(self, sattrs=None):
+        """Returns the entry containing access control ACIs."""
+        server = self.setup_server()
+        if sattrs is None:
+            sattrs = "__NONE__"
+        try:
+            result = server.get_aci_entry(sattrs)
+        except xmlrpclib.Fault, fault:
+            raise ipaerror.gen_exception(fault.faultCode, fault.faultString)
+        except socket.error, (value, msg):
+            raise xmlrpclib.Fault(value, msg)
+
+        return ipautil.unwrap_binary_data(result)
+
+
 # General searches
 
     def get_entry_by_dn(self,dn,sattrs=None):
diff -r dcfb5974ee27 -r 2ffed8848975 ipa-python/test/test_aci.py
--- a/ipa-python/test/test_aci.py	Fri Oct 12 10:11:57 2007 -0700
+++ b/ipa-python/test/test_aci.py	Fri Oct 12 15:11:55 2007 -0700
@@ -22,15 +22,16 @@ sys.path.insert(0, ".")
 
 import unittest
 import aci
+import urllib
 
 
 class TestACI(unittest.TestCase):
-    acitemplate = ('(targetattr = "%s")' +
+    acitemplate = ('(targetattr="%s")' +
                '(targetfilter="(memberOf=%s)")' +
                '(version 3.0;' +
                'acl "%s";' +
                'allow (write) ' +
-               'groupdn="%s";)')
+               'groupdn="ldap:///%s";)')
 
     def setUp(self):
         self.aci = aci.ACI()
@@ -52,6 +53,20 @@ class TestACI(unittest.TestCase):
 
         self.assertEqual(aci, exportaci)
 
+    def testURLEncodedExport(self):
+        self.aci.source_group = 'cn=foo " bar, dc=freeipa, dc=org'
+        self.aci.dest_group = 'cn=bar, dc=freeipa, dc=org'
+        self.aci.name = 'this is a "name'
+        self.aci.attrs = ['field1', 'field2', 'field3']
+
+        exportaci = self.aci.export_to_string()
+        aci = TestACI.acitemplate % ('field1 || field2 || field3',
+                                     self.aci.dest_group,
+                                     'this is a "name',
+                                     urllib.quote(self.aci.source_group, "/=, "))
+
+        self.assertEqual(aci, exportaci)
+
     def testSimpleParse(self):
         attr_str = 'field3 || field4 || field5'
         dest_dn = 'cn=dest\\"group, dc=freeipa, dc=org'
@@ -59,6 +74,21 @@ class TestACI(unittest.TestCase):
         src_dn = 'cn=srcgroup, dc=freeipa, dc=org'
 
         acistr = TestACI.acitemplate % (attr_str, dest_dn, name, src_dn)
+        self.aci.parse_acistr(acistr)
+
+        self.assertEqual(['field3', 'field4', 'field5'], self.aci.attrs)
+        self.assertEqual(dest_dn, self.aci.dest_group)
+        self.assertEqual(name, self.aci.name)
+        self.assertEqual(src_dn, self.aci.source_group)
+
+    def testUrlEncodedParse(self):
+        attr_str = 'field3 || field4 || field5'
+        dest_dn = 'cn=dest\\"group, dc=freeipa, dc=org'
+        name = 'my name'
+        src_dn = 'cn=src " group, dc=freeipa, dc=org'
+
+        acistr = TestACI.acitemplate % (attr_str, dest_dn, name, 
+                urllib.quote(src_dn, "/=, "))
         self.aci.parse_acistr(acistr)
 
         self.assertEqual(['field3', 'field4', 'field5'], self.aci.attrs)
diff -r dcfb5974ee27 -r 2ffed8848975 ipa-server/ipa-gui/ipagui/controllers.py
--- a/ipa-server/ipa-gui/ipagui/controllers.py	Fri Oct 12 10:11:57 2007 -0700
+++ b/ipa-server/ipa-gui/ipagui/controllers.py	Fri Oct 12 15:11:55 2007 -0700
@@ -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 -r dcfb5974ee27 -r 2ffed8848975 ipa-server/ipa-gui/ipagui/forms/delegate.py
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ipa-server/ipa-gui/ipagui/forms/delegate.py	Fri Oct 12 15:11:55 2007 -0700
@@ -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 -r dcfb5974ee27 -r 2ffed8848975 ipa-server/ipa-gui/ipagui/static/css/style.css
--- a/ipa-server/ipa-gui/ipagui/static/css/style.css	Fri Oct 12 10:11:57 2007 -0700
+++ b/ipa-server/ipa-gui/ipagui/static/css/style.css	Fri Oct 12 15:11:55 2007 -0700
@@ -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 -r dcfb5974ee27 -r 2ffed8848975 ipa-server/ipa-gui/ipagui/subcontrollers/delegation.py
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ipa-server/ipa-gui/ipagui/subcontrollers/delegation.py	Fri Oct 12 15:11:55 2007 -0700
@@ -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 -r dcfb5974ee27 -r 2ffed8848975 ipa-server/ipa-gui/ipagui/templates/delegategroupsearch.kid
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ipa-server/ipa-gui/ipagui/templates/delegategroupsearch.kid	Fri Oct 12 15:11:55 2007 -0700
@@ -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 < 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 -r dcfb5974ee27 -r 2ffed8848975 ipa-server/ipa-gui/ipagui/templates/delegatelayout.kid
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ipa-server/ipa-gui/ipagui/templates/delegatelayout.kid	Fri Oct 12 15:11:55 2007 -0700
@@ -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 -r dcfb5974ee27 -r 2ffed8848975 ipa-server/ipa-gui/ipagui/templates/delegatelist.kid
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ipa-server/ipa-gui/ipagui/templates/delegatelist.kid	Fri Oct 12 15:11:55 2007 -0700
@@ -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 -r dcfb5974ee27 -r 2ffed8848975 ipa-server/ipa-gui/ipagui/templates/delegatenew.kid
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ipa-server/ipa-gui/ipagui/templates/delegatenew.kid	Fri Oct 12 15:11:55 2007 -0700
@@ -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 -r dcfb5974ee27 -r 2ffed8848975 ipa-server/ipa-gui/ipagui/templates/delegatenewform.kid
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ipa-server/ipa-gui/ipagui/templates/delegatenewform.kid	Fri Oct 12 15:11:55 2007 -0700
@@ -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 -r dcfb5974ee27 -r 2ffed8848975 ipa-server/ipa-gui/ipagui/templates/master.kid
--- a/ipa-server/ipa-gui/ipagui/templates/master.kid	Fri Oct 12 10:11:57 2007 -0700
+++ b/ipa-server/ipa-gui/ipagui/templates/master.kid	Fri Oct 12 15:11:55 2007 -0700
@@ -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 -r dcfb5974ee27 -r 2ffed8848975 ipa-server/xmlrpc-server/funcs.py
--- a/ipa-server/xmlrpc-server/funcs.py	Fri Oct 12 10:11:57 2007 -0700
+++ b/ipa-server/xmlrpc-server/funcs.py	Fri Oct 12 15:11:55 2007 -0700
@@ -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"
 
@@ -343,6 +344,14 @@ class IPAServer:
         partial_match_filter += ")"
 
         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
 
diff -r dcfb5974ee27 -r 2ffed8848975 ipa-server/xmlrpc-server/ipaxmlrpc.py
--- a/ipa-server/xmlrpc-server/ipaxmlrpc.py	Fri Oct 12 10:11:57 2007 -0700
+++ b/ipa-server/xmlrpc-server/ipaxmlrpc.py	Fri Oct 12 15:11:55 2007 -0700
@@ -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)
-------------- next part --------------
A non-text attachment was scrubbed...
Name: smime.p7s
Type: application/x-pkcs7-signature
Size: 4054 bytes
Desc: not available
URL: <http://listman.redhat.com/archives/freeipa-devel/attachments/20071012/30d21467/attachment.bin>


More information about the Freeipa-devel mailing list