mirror of
https://salsa.debian.org/freeipa-team/freeipa.git
synced 2024-12-28 18:01:23 -06:00
Add UI for service principal creation and keytab retrieval
This commit is contained in:
parent
c397041bfa
commit
15b7dc6ff9
@ -385,6 +385,20 @@ class IPAClient:
|
||||
def add_service_principal(self, princ_name):
|
||||
return self.transport.add_service_principal(princ_name)
|
||||
|
||||
def find_service_principal(self, criteria, sattrs=None, searchlimit=0, timelimit=-1):
|
||||
"""Return a list: counter followed by a Entity object for each host that
|
||||
matches the criteria. If the results are truncated, counter will
|
||||
be set to -1"""
|
||||
result = self.transport.find_service_principal(criteria, sattrs, searchlimit, timelimit)
|
||||
counter = result[0]
|
||||
|
||||
hosts = [counter]
|
||||
for attrs in result[1:]:
|
||||
if attrs is not None:
|
||||
hosts.append(entity.Entity(attrs))
|
||||
|
||||
return hosts
|
||||
|
||||
def get_keytab(self, princ_name):
|
||||
return self.transport.get_keytab(princ_name)
|
||||
|
||||
|
@ -703,6 +703,24 @@ class RPCClient:
|
||||
|
||||
return ipautil.unwrap_binary_data(result)
|
||||
|
||||
def find_service_principal (self, criteria, sattrs=None, searchlimit=0, timelimit=-1):
|
||||
"""Return a list: counter followed by a Entity object for each host that
|
||||
matches the criteria. If the results are truncated, counter will
|
||||
be set to -1"""
|
||||
|
||||
server = self.setup_server()
|
||||
try:
|
||||
# None values are not allowed in XML-RPC
|
||||
if sattrs is None:
|
||||
sattrs = "__NONE__"
|
||||
result = server.find_service_principal(criteria, sattrs, searchlimit, timelimit)
|
||||
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)
|
||||
|
||||
def get_keytab(self, princ_name):
|
||||
server = self.setup_server()
|
||||
|
||||
|
@ -19,6 +19,7 @@ from subcontrollers.group import GroupController
|
||||
from subcontrollers.delegation import DelegationController
|
||||
from subcontrollers.policy import PolicyController
|
||||
from subcontrollers.ipapolicy import IPAPolicyController
|
||||
from subcontrollers.principal import PrincipalController
|
||||
|
||||
ipa.config.init_config()
|
||||
|
||||
@ -31,6 +32,7 @@ class Root(controllers.RootController):
|
||||
delegate = DelegationController()
|
||||
policy = PolicyController()
|
||||
ipapolicy = IPAPolicyController()
|
||||
principal = PrincipalController()
|
||||
|
||||
@expose(template="ipagui.templates.welcome")
|
||||
@identity.require(identity.not_anonymous())
|
||||
|
39
ipa-server/ipa-gui/ipagui/forms/principal.py
Normal file
39
ipa-server/ipa-gui/ipagui/forms/principal.py
Normal file
@ -0,0 +1,39 @@
|
||||
import turbogears
|
||||
from turbogears import validators, widgets
|
||||
from tg_expanding_form_widget.tg_expanding_form_widget import ExpandingForm
|
||||
|
||||
class PrincipalFields(object):
|
||||
hostname = widgets.TextField(name="hostname", label="Host Name")
|
||||
service = widgets.SingleSelectField(name="service",
|
||||
label="Service Type",
|
||||
options = [
|
||||
("cifs", "cifs"),
|
||||
("dhcp", "dhcp"),
|
||||
("dns", "dns"),
|
||||
("host", "host"),
|
||||
("HTTP", "HTTP"),
|
||||
("ldap", "ldap"),
|
||||
("other", "other"),
|
||||
("rpc", "rpc"),
|
||||
("snmp", "snmp")
|
||||
],
|
||||
attrs=dict(onchange="toggleOther(this.id)"))
|
||||
other = widgets.TextField(name="other", label="Other Service", attrs=dict(size=10))
|
||||
|
||||
class PrincipalNewValidator(validators.Schema):
|
||||
hostname = validators.String(not_empty=True)
|
||||
service = validators.String(not_empty=True)
|
||||
other = validators.String(not_empty=False)
|
||||
|
||||
class PrincipalNewForm(widgets.Form):
|
||||
params = ['principal_fields']
|
||||
|
||||
validator = PrincipalNewValidator()
|
||||
|
||||
def __init__(self, *args, **kw):
|
||||
super(PrincipalNewForm,self).__init__(*args, **kw)
|
||||
(self.template_c, self.template) = widgets.meta.load_kid_template("ipagui.templates.principalnewform")
|
||||
self.principal_fields = PrincipalFields
|
||||
|
||||
def update_params(self, params):
|
||||
super(PrincipalNewForm,self).update_params(params)
|
153
ipa-server/ipa-gui/ipagui/subcontrollers/principal.py
Normal file
153
ipa-server/ipa-gui/ipagui/subcontrollers/principal.py
Normal file
@ -0,0 +1,153 @@
|
||||
import os
|
||||
from pickle import dumps, loads
|
||||
from base64 import b64encode, b64decode
|
||||
import copy
|
||||
import logging
|
||||
|
||||
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.principal
|
||||
|
||||
import ldap.dn
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
principal_new_form = ipagui.forms.principal.PrincipalNewForm()
|
||||
principal_fields = ['*']
|
||||
|
||||
class PrincipalController(IPAController):
|
||||
|
||||
@expose()
|
||||
@identity.require(identity.in_group("admins"))
|
||||
def index(self, tg_errors=None):
|
||||
raise turbogears.redirect("/principal/list")
|
||||
|
||||
@expose("ipagui.templates.principalnew")
|
||||
@identity.require(identity.in_group("admins"))
|
||||
def new(self, tg_errors=None):
|
||||
"""Displays the new service principal form"""
|
||||
if tg_errors:
|
||||
turbogears.flash("There were validation errors.<br/>" +
|
||||
"Please see the messages below for details.")
|
||||
|
||||
client = self.get_ipaclient()
|
||||
|
||||
return dict(form=principal_new_form, principal={})
|
||||
|
||||
@expose()
|
||||
@identity.require(identity.in_group("admins"))
|
||||
def create(self, **kw):
|
||||
"""Creates a service principal group"""
|
||||
self.restrict_post()
|
||||
client = self.get_ipaclient()
|
||||
|
||||
if kw.get('submit') == 'Cancel':
|
||||
turbogears.flash("Add principal cancelled")
|
||||
raise turbogears.redirect('/')
|
||||
|
||||
tg_errors, kw = self.principalcreatevalidate(**kw)
|
||||
if tg_errors:
|
||||
turbogears.flash("There were validation errors.<br/>" +
|
||||
"Please see the messages below for details.")
|
||||
return dict(form=principal_new_form, principal=kw,
|
||||
tg_template='ipagui.templates.principalnew')
|
||||
|
||||
principal_name = ""
|
||||
hostname = kw.get('hostname')
|
||||
#
|
||||
# Create the principal itself
|
||||
#
|
||||
try:
|
||||
if kw.get('service') == "other":
|
||||
service = kw.get('other')
|
||||
if not service:
|
||||
turbogears.flash("Service type must be provided")
|
||||
return dict(form=principal_new_form, principal=kw,
|
||||
tg_template='ipagui.templates.principalnew')
|
||||
else:
|
||||
service = kw.get('service')
|
||||
|
||||
# The realm is added by add_service_principal
|
||||
principal_name = utf8_encode_values(service + "/" + kw.get('hostname'))
|
||||
|
||||
rv = client.add_service_principal(principal_name)
|
||||
except ipaerror.exception_for(ipaerror.LDAP_DUPLICATE):
|
||||
turbogears.flash("Service principal '%s' already exists" %
|
||||
principal_name)
|
||||
return dict(form=principal_new_form, principal=kw,
|
||||
tg_template='ipagui.templates.principalnew')
|
||||
except ipaerror.IPAError, e:
|
||||
turbogears.flash("Service principal add failed: " + str(e) + "<br/>" + e.detail[0]['desc'])
|
||||
return dict(form=principal_new_form, principal=kw,
|
||||
tg_template='ipagui.templates.principalnew')
|
||||
|
||||
turbogears.flash("%s added!" % principal_name)
|
||||
raise turbogears.redirect('/principal/list', hostname=hostname)
|
||||
|
||||
@expose("ipagui.templates.principallist")
|
||||
@identity.require(identity.not_anonymous())
|
||||
def list(self, **kw):
|
||||
"""Searches for service principals and displays list of results"""
|
||||
client = self.get_ipaclient()
|
||||
|
||||
principals = None
|
||||
counter = 0
|
||||
hostname = kw.get('hostname')
|
||||
if hostname != None and len(hostname) > 0:
|
||||
try:
|
||||
principals = client.find_service_principal(hostname.encode('utf-8'), principal_fields, 0, 2)
|
||||
counter = principals[0]
|
||||
principals = principals[1:]
|
||||
|
||||
if counter == -1:
|
||||
turbogears.flash("These results are truncated.<br />" +
|
||||
"Please refine your search and try again.")
|
||||
|
||||
# For each entry break out service type and hostname
|
||||
for i in range(len(principals)):
|
||||
(service,host) = principals[i].krbprincipalname.split('/')
|
||||
h = host.split('@')
|
||||
principals[i].setValue('service', service)
|
||||
principals[i].setValue('hostname', h[0])
|
||||
|
||||
except ipaerror.IPAError, e:
|
||||
turbogears.flash("principal list failed: " + str(e) + "<br/>" + e.detail[0]['desc'])
|
||||
raise turbogears.redirect("/principal/list")
|
||||
|
||||
return dict(principals=principals, hostname=hostname, fields=ipagui.forms.principal.PrincipalFields())
|
||||
|
||||
@expose()
|
||||
@identity.require(identity.not_anonymous())
|
||||
def show(self, **kw):
|
||||
"""Returns the keytab for a given principal"""
|
||||
client = self.get_ipaclient()
|
||||
|
||||
principal = kw.get('principal')
|
||||
if principal != None and len(principal) > 0:
|
||||
try:
|
||||
p = principal.split('@')
|
||||
keytab = client.get_keytab(p[0].encode('utf-8'))
|
||||
|
||||
cherrypy.response.headers['Content-Type'] = "application/x-download"
|
||||
cherrypy.response.headers['Content-Disposition'] = 'attachment; filename=krb5.keytab'
|
||||
cherrypy.response.headers['Content-Length'] = len(keytab)
|
||||
cherrypy.response.body = keytab
|
||||
return cherrypy.response.body
|
||||
except ipaerror.IPAError, e:
|
||||
turbogears.flash("keytab retrieval failed: " + str(e) + "<br/>" + e.detail[0]['desc'])
|
||||
raise turbogears.redirect("/principal/list")
|
||||
raise turbogears.redirect("/principal/list")
|
||||
|
||||
@validate(form=principal_new_form)
|
||||
@identity.require(identity.not_anonymous())
|
||||
def principalcreatevalidate(self, tg_errors=None, **kw):
|
||||
return tg_errors, kw
|
@ -78,10 +78,14 @@
|
||||
<li><a href="${tg.url('/group/list')}">Find Groups</a></li>
|
||||
</ul>
|
||||
<ul py:if="'admins' in tg.identity.groups">
|
||||
<li><a href="${tg.url('/principal/new')}">Add Service Principal</a></li>
|
||||
<li><a href="${tg.url('/principal/list')}">Find Service Principal</a></li>
|
||||
</ul>
|
||||
<ul py:if="'admins' in tg.identity.groups">
|
||||
<li><a href="${tg.url('/policy/index')}">Manage Policy</a></li>
|
||||
</ul>
|
||||
<ul>
|
||||
<li><a href="${tg.url('/user/edit/', principal=tg.identity.user.display_name)}">Self Service</a></li>
|
||||
<li py:if="not tg.identity.anonymous"><a href="${tg.url('/user/edit/', principal=tg.identity.user.display_name)}">Self Service</a></li>
|
||||
</ul>
|
||||
<ul py:if="'admins' in tg.identity.groups">
|
||||
<li><a href="${tg.url('/delegate/list')}">Delegations</a></li>
|
||||
|
19
ipa-server/ipa-gui/ipagui/templates/principallayout.kid
Normal file
19
ipa-server/ipa-gui/ipagui/templates/principallayout.kid
Normal file
@ -0,0 +1,19 @@
|
||||
<!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="details">
|
||||
<div id="alertbox" py:if="value_of('tg_flash', None)">
|
||||
<p py:content="XML(tg_flash)"></p></div>
|
||||
|
||||
<div py:replace="[item.text]+item[:]"></div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</body>
|
||||
|
||||
</html>
|
64
ipa-server/ipa-gui/ipagui/templates/principallist.kid
Normal file
64
ipa-server/ipa-gui/ipagui/templates/principallist.kid
Normal file
@ -0,0 +1,64 @@
|
||||
<!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="'principallayout.kid'">
|
||||
<head>
|
||||
<meta content="text/html; charset=utf-8" http-equiv="Content-Type" py:replace="''"/>
|
||||
<title>Find Service Principals</title>
|
||||
</head>
|
||||
<body>
|
||||
<h1>Find Service Principals</h1>
|
||||
<script type="text/javascript" charset="utf-8" src="${tg.url('/static/javascript/tablekit.js')}"></script>
|
||||
<div id="search">
|
||||
<form action="${tg.url('/principal/list')}" method="get">
|
||||
<input id="hostname" type="text" name="hostname" value="${hostname}" />
|
||||
<input class="searchbutton" type="submit" value="Find Hosts"/>
|
||||
</form>
|
||||
<script type="text/javascript">
|
||||
document.getElementById("hostname").focus();
|
||||
</script>
|
||||
</div>
|
||||
<div py:if='(principals != None) and (len(principals) > 0)'>
|
||||
<h2>${len(principals)} results returned:</h2>
|
||||
<table id="resultstable" class="details sortable resizable" cellspacing="0">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>
|
||||
Hostname
|
||||
</th>
|
||||
<th>
|
||||
Service
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr py:for="principal in principals">
|
||||
<td>
|
||||
<a href="${tg.url('/principal/show',principal=principal.krbprincipalname)}"
|
||||
>${principal.hostname}</a>
|
||||
</td>
|
||||
<td>
|
||||
${principal.service}
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<div id="alertbox" py:if='(principals != None) and (len(principals) == 0)'>
|
||||
<p id="alertbox">No results found for "${hostname}"</p>
|
||||
</div>
|
||||
|
||||
<div class="instructions" py:if='principals == None'>
|
||||
<p>
|
||||
Exact matches are listed first, followed by partial matches. If your search
|
||||
is too broad, you will get a warning that the search returned too many
|
||||
results. Try being more specific.
|
||||
</p>
|
||||
<p>
|
||||
The results that come back are sortable. Simply click on a column
|
||||
header to sort on that header. A triangle will indicate the sorted
|
||||
column, along with its direction. Clicking and dragging between headers
|
||||
will allow you to resize the header.
|
||||
</p>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
13
ipa-server/ipa-gui/ipagui/templates/principalnew.kid
Normal file
13
ipa-server/ipa-gui/ipagui/templates/principalnew.kid
Normal file
@ -0,0 +1,13 @@
|
||||
<!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="'principallayout.kid'">
|
||||
<head>
|
||||
<meta content="text/html; charset=utf-8" http-equiv="Content-Type" py:replace="''"/>
|
||||
<title>Add Service Principal</title>
|
||||
</head>
|
||||
<body>
|
||||
<h1>Add Service Principal</h1>
|
||||
|
||||
${form.display(action=tg.url('/principal/create'), value=principal)}
|
||||
</body>
|
||||
</html>
|
98
ipa-server/ipa-gui/ipagui/templates/principalnewform.kid
Normal file
98
ipa-server/ipa-gui/ipagui/templates/principalnewform.kid
Normal file
@ -0,0 +1,98 @@
|
||||
<div xmlns:py="http://purl.org/kid/ns#"
|
||||
class="simpleroster">
|
||||
<form action="${action}" name="${name}" method="${method}" class="tableform"
|
||||
onsubmit="preSubmit()" >
|
||||
|
||||
<input type="submit" class="submitbutton" name="submit" value="Add Principal"/>
|
||||
|
||||
<?python
|
||||
from ipagui.helpers import ipahelper
|
||||
?>
|
||||
|
||||
<script type="text/javascript" charset="utf-8"
|
||||
src="${tg.url('/static/javascript/dynamicedit.js')}"></script>
|
||||
|
||||
<?python searchurl = tg.url('/principal/edit_search') ?>
|
||||
|
||||
<script type="text/javascript">
|
||||
function toggleOther(field) {
|
||||
otherField = document.getElementById('form_other');
|
||||
var e=document.getElementById(field).value;
|
||||
if ( e == "other") {
|
||||
otherField.disabled = false;
|
||||
} else {
|
||||
otherField.disabled =true;
|
||||
}
|
||||
}
|
||||
|
||||
function doSearch() {
|
||||
$('searchresults').update("Searching...");
|
||||
new Ajax.Updater('searchresults',
|
||||
'${searchurl}',
|
||||
{ asynchronous:true,
|
||||
parameters: { criteria: $('criteria').value },
|
||||
evalScripts: true });
|
||||
return false;
|
||||
}
|
||||
</script>
|
||||
|
||||
<div py:for="field in hidden_fields"
|
||||
py:replace="field.display(value_for(field), **params_for(field))"
|
||||
/>
|
||||
|
||||
<h2 class="formsection">Service Principal Details</h2>
|
||||
<table class="formtable" cellpadding="2" cellspacing="0" border="0">
|
||||
<tr>
|
||||
<th>
|
||||
<label class="fieldlabel" for="${principal_fields.hostname.field_id}"
|
||||
py:content="principal_fields.hostname.label" />:
|
||||
</th>
|
||||
<td>
|
||||
<span py:replace="principal_fields.hostname.display(value_for(principal_fields.hostname))" />
|
||||
<span py:if="tg.errors.get('hostname')" class="fielderror"
|
||||
py:content="tg.errors.get('hostname')" />
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<th>
|
||||
<label class="fieldlabel" for="${principal_fields.service.field_id}"
|
||||
py:content="principal_fields.service.label" />:
|
||||
</th>
|
||||
<td>
|
||||
<span py:replace="principal_fields.service.display(value_for(principal_fields.service))" />
|
||||
<span py:if="tg.errors.get('service')" class="fielderror"
|
||||
py:content="tg.errors.get('service')" />
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<th>
|
||||
<label class="fieldlabel" for="${principal_fields.other.field_id}"
|
||||
py:content="principal_fields.other.label" />:
|
||||
</th>
|
||||
<td>
|
||||
<span py:replace="principal_fields.other.display(value_for(principal_fields.other))" />
|
||||
<span py:if="tg.errors.get('other')" class="fielderror"
|
||||
py:content="tg.errors.get('other')" />
|
||||
<script type="text/javascript">
|
||||
var e=document.getElementById('form_service').value;
|
||||
if ( e != "other") {
|
||||
document.getElementById('form_other').disabled = true;
|
||||
}
|
||||
</script>
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
</table>
|
||||
|
||||
<hr />
|
||||
|
||||
<input type="submit" class="submitbutton" name="submit" value="Add Principal"/>
|
||||
|
||||
</form>
|
||||
|
||||
</div>
|
@ -1358,7 +1358,78 @@ class IPAServer:
|
||||
finally:
|
||||
self.releaseConnection(conn)
|
||||
return res
|
||||
|
||||
|
||||
def find_service_principal(self, criteria, sattrs, searchlimit=-1,
|
||||
timelimit=-1, opts=None):
|
||||
"""Returns a list: counter followed by the results.
|
||||
If the results are truncated, counter will be set to -1."""
|
||||
|
||||
config = self.get_ipa_config(opts)
|
||||
if timelimit < 0:
|
||||
timelimit = float(config.get('ipasearchtimelimit'))
|
||||
if searchlimit < 0:
|
||||
searchlimit = float(config.get('ipasearchrecordslimit'))
|
||||
|
||||
search_fields = ["krbprincipalname"]
|
||||
|
||||
criteria = self.__safe_filter(criteria)
|
||||
criteria_words = re.split(r'\s+', criteria)
|
||||
criteria_words = filter(lambda value:value!="", criteria_words)
|
||||
if len(criteria_words) == 0:
|
||||
return [0]
|
||||
|
||||
(exact_match_filter, partial_match_filter) = self.__generate_match_filters(
|
||||
search_fields, criteria_words)
|
||||
|
||||
#
|
||||
# further constrain search to just the objectClass
|
||||
# TODO - need to parameterize this into generate_match_filters,
|
||||
# and work it into the field-specification search feature
|
||||
#
|
||||
exact_match_filter = "(&(objectclass=krbPrincipalAux)(!(objectClass=person))(!(krbprincipalname=kadmin/*))%s)" % exact_match_filter
|
||||
partial_match_filter = "(&(objectclass=krbPrincipalAux)(!(objectClass=person))(!(krbprincipalname=kadmin/*))%s)" % partial_match_filter
|
||||
print exact_match_filter
|
||||
print partial_match_filter
|
||||
|
||||
conn = self.getConnection(opts)
|
||||
try:
|
||||
try:
|
||||
exact_results = conn.getListAsync(self.basedn, self.scope,
|
||||
exact_match_filter, sattrs, 0, None, None, timelimit,
|
||||
searchlimit)
|
||||
except ipaerror.exception_for(ipaerror.LDAP_NOT_FOUND):
|
||||
exact_results = [0]
|
||||
|
||||
try:
|
||||
partial_results = conn.getListAsync(self.basedn, self.scope,
|
||||
partial_match_filter, sattrs, 0, None, None, timelimit,
|
||||
searchlimit)
|
||||
except ipaerror.exception_for(ipaerror.LDAP_NOT_FOUND):
|
||||
partial_results = [0]
|
||||
finally:
|
||||
self.releaseConnection(conn)
|
||||
|
||||
exact_counter = exact_results[0]
|
||||
partial_counter = partial_results[0]
|
||||
|
||||
exact_results = exact_results[1:]
|
||||
partial_results = partial_results[1:]
|
||||
|
||||
# Remove exact matches from the partial_match list
|
||||
exact_dns = set(map(lambda e: e.dn, exact_results))
|
||||
partial_results = filter(lambda e: e.dn not in exact_dns,
|
||||
partial_results)
|
||||
|
||||
if (exact_counter == -1) or (partial_counter == -1):
|
||||
counter = -1
|
||||
else:
|
||||
counter = len(exact_results) + len(partial_results)
|
||||
|
||||
entries = [counter]
|
||||
for e in exact_results + partial_results:
|
||||
entries.append(self.convert_entry(e))
|
||||
|
||||
return entries
|
||||
|
||||
def get_keytab(self, name, opts=None):
|
||||
"""get a keytab"""
|
||||
|
@ -360,6 +360,7 @@ def handler(req, profiling=False):
|
||||
h.register_function(f.get_password_policy)
|
||||
h.register_function(f.update_password_policy)
|
||||
h.register_function(f.add_service_principal)
|
||||
h.register_function(f.find_service_principal)
|
||||
h.register_function(f.get_keytab)
|
||||
h.handle_request(req)
|
||||
finally:
|
||||
|
Loading…
Reference in New Issue
Block a user