mirror of
https://salsa.debian.org/freeipa-team/freeipa.git
synced 2025-02-25 18:55:28 -06:00
Adds delegation listing and creation to the GUI.
This commit is contained in:
@@ -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:
|
||||
|
@@ -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):
|
||||
|
@@ -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):
|
||||
|
@@ -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'
|
||||
@@ -66,6 +81,21 @@ class TestACI(unittest.TestCase):
|
||||
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)
|
||||
self.assertEqual(dest_dn, self.aci.dest_group)
|
||||
self.assertEqual(name, self.aci.name)
|
||||
self.assertEqual(src_dn, self.aci.source_group)
|
||||
|
||||
def testInvalidParse(self):
|
||||
try:
|
||||
self.aci.parse_acistr('foo bar')
|
||||
|
@@ -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())
|
||||
|
86
ipa-server/ipa-gui/ipagui/forms/delegate.py
Normal file
86
ipa-server/ipa-gui/ipagui/forms/delegate.py
Normal file
@@ -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)
|
@@ -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;
|
||||
}
|
||||
@@ -206,6 +206,19 @@ body {
|
||||
background: #eee;
|
||||
}
|
||||
|
||||
/*
|
||||
* Used for checkboxlist of aci attributes
|
||||
*/
|
||||
ul.requiredfield {
|
||||
background: #ffffff;
|
||||
}
|
||||
|
||||
ul.checkboxlist {
|
||||
padding: 0px;
|
||||
margin: 0px;
|
||||
list-style: none;
|
||||
}
|
||||
|
||||
/*
|
||||
* TableKit css
|
||||
*/
|
||||
|
168
ipa-server/ipa-gui/ipagui/subcontrollers/delegation.py
Normal file
168
ipa-server/ipa-gui/ipagui/subcontrollers/delegation.py
Normal file
@@ -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
|
||||
|
31
ipa-server/ipa-gui/ipagui/templates/delegategroupsearch.kid
Normal file
31
ipa-server/ipa-gui/ipagui/templates/delegategroupsearch.kid
Normal file
@@ -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>
|
16
ipa-server/ipa-gui/ipagui/templates/delegatelayout.kid
Normal file
16
ipa-server/ipa-gui/ipagui/templates/delegatelayout.kid
Normal file
@@ -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>
|
60
ipa-server/ipa-gui/ipagui/templates/delegatelist.kid
Normal file
60
ipa-server/ipa-gui/ipagui/templates/delegatelist.kid
Normal file
@@ -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>
|
15
ipa-server/ipa-gui/ipagui/templates/delegatenew.kid
Normal file
15
ipa-server/ipa-gui/ipagui/templates/delegatenew.kid
Normal file
@@ -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>
|
154
ipa-server/ipa-gui/ipagui/templates/delegatenewform.kid
Normal file
154
ipa-server/ipa-gui/ipagui/templates/delegatenewform.kid
Normal file
@@ -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>
|
@@ -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>
|
||||
|
@@ -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):
|
||||
|
@@ -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)
|
||||
|
Reference in New Issue
Block a user