diff options
Diffstat (limited to 'ipa-server/ipa-gui/ipagui')
-rw-r--r-- | ipa-server/ipa-gui/ipagui/controllers.py | 2 | ||||
-rw-r--r-- | ipa-server/ipa-gui/ipagui/forms/delegate.py | 86 | ||||
-rw-r--r-- | ipa-server/ipa-gui/ipagui/static/css/style.css | 17 | ||||
-rw-r--r-- | ipa-server/ipa-gui/ipagui/subcontrollers/delegation.py | 168 | ||||
-rw-r--r-- | ipa-server/ipa-gui/ipagui/templates/delegategroupsearch.kid | 31 | ||||
-rw-r--r-- | ipa-server/ipa-gui/ipagui/templates/delegatelayout.kid | 16 | ||||
-rw-r--r-- | ipa-server/ipa-gui/ipagui/templates/delegatelist.kid | 60 | ||||
-rw-r--r-- | ipa-server/ipa-gui/ipagui/templates/delegatenew.kid | 15 | ||||
-rw-r--r-- | ipa-server/ipa-gui/ipagui/templates/delegatenewform.kid | 154 | ||||
-rw-r--r-- | ipa-server/ipa-gui/ipagui/templates/master.kid | 3 |
10 files changed, 550 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 < 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> |