mirror of
https://salsa.debian.org/freeipa-team/freeipa.git
synced 2025-02-25 18:55:28 -06:00
Merge.
This commit is contained in:
commit
d2378f13d0
@ -65,6 +65,9 @@ def main():
|
||||
if counter == 0:
|
||||
print "No entries found for", args[1]
|
||||
return 2
|
||||
elif counter == -1:
|
||||
print "These results are truncated."
|
||||
print "Please revine your search and try again."
|
||||
|
||||
for ent in groups:
|
||||
try:
|
||||
|
@ -90,6 +90,9 @@ def main():
|
||||
if counter == 0:
|
||||
print "No entries found for", args[1]
|
||||
return 2
|
||||
elif counter == -1:
|
||||
print "These results are truncated."
|
||||
print "Please revine your search and try again."
|
||||
|
||||
for ent in users:
|
||||
attr = ent.attrList()
|
||||
|
@ -117,9 +117,6 @@ class IPAClient:
|
||||
|
||||
user_dict = user.toDict()
|
||||
|
||||
# dn is set on the server-side
|
||||
del user_dict['dn']
|
||||
|
||||
# convert to a regular dict before sending
|
||||
result = self.transport.add_user(user_dict, user_container)
|
||||
return result
|
||||
@ -386,6 +383,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)
|
||||
|
||||
|
@ -177,3 +177,8 @@ CONFIG_DEFAULT_GROUP = gen_error_code(
|
||||
CONFIGURATION_CATEGORY,
|
||||
0x0002,
|
||||
"You cannot remove the default users group.")
|
||||
|
||||
CONFIG_INVALID_OC = gen_error_code(
|
||||
CONFIGURATION_CATEGORY,
|
||||
0x0003,
|
||||
"Invalid object class.")
|
||||
|
@ -25,14 +25,11 @@ import logging
|
||||
import subprocess
|
||||
from random import Random
|
||||
from time import gmtime
|
||||
import os
|
||||
import os, sys, traceback, readline
|
||||
import stat
|
||||
import socket
|
||||
import readline
|
||||
import traceback
|
||||
|
||||
from types import *
|
||||
|
||||
from string import lower
|
||||
import re
|
||||
import xmlrpclib
|
||||
import datetime
|
||||
@ -82,7 +79,7 @@ def run(args, stdin=None):
|
||||
logging.info(stderr)
|
||||
|
||||
if p.returncode != 0:
|
||||
raise self.CalledProcessError(p.returncode, ' '.join(args))
|
||||
raise CalledProcessError(p.returncode, ' '.join(args))
|
||||
|
||||
def file_exists(filename):
|
||||
try:
|
||||
@ -121,24 +118,24 @@ class CIDict(dict):
|
||||
self.update(default or {})
|
||||
|
||||
def __getitem__(self,key):
|
||||
return super(CIDict,self).__getitem__(lower(key))
|
||||
return super(CIDict,self).__getitem__(string.lower(key))
|
||||
|
||||
def __setitem__(self,key,value):
|
||||
lower_key = lower(key)
|
||||
lower_key = string.lower(key)
|
||||
self._keys[lower_key] = key
|
||||
return super(CIDict,self).__setitem__(lower(key),value)
|
||||
return super(CIDict,self).__setitem__(string.lower(key),value)
|
||||
|
||||
def __delitem__(self,key):
|
||||
lower_key = lower(key)
|
||||
lower_key = string.lower(key)
|
||||
del self._keys[lower_key]
|
||||
return super(CIDict,self).__delitem__(lower(key))
|
||||
return super(CIDict,self).__delitem__(string.lower(key))
|
||||
|
||||
def update(self,dict):
|
||||
for key in dict.keys():
|
||||
self[key] = dict[key]
|
||||
|
||||
def has_key(self,key):
|
||||
return super(CIDict, self).has_key(lower(key))
|
||||
return super(CIDict, self).has_key(string.lower(key))
|
||||
|
||||
def get(self,key,failobj=None):
|
||||
try:
|
||||
@ -373,7 +370,7 @@ def format_list(items, quote=None, page_width=80):
|
||||
'''
|
||||
left_quote = right_quote = ''
|
||||
num_items = len(items)
|
||||
if not num_items: return text
|
||||
if not num_items: return ""
|
||||
|
||||
if quote is not None:
|
||||
if type(quote) in StringTypes:
|
||||
@ -458,7 +455,7 @@ def read_pairs_file(filename):
|
||||
fd = open(filename)
|
||||
text = fd.read()
|
||||
text = comment_re.sub('', text) # kill comments
|
||||
pairs = ipautil.parse_key_value_pairs(text)
|
||||
pairs = parse_key_value_pairs(text)
|
||||
if fd != sys.stdin: fd.close()
|
||||
return pairs
|
||||
|
||||
@ -470,7 +467,7 @@ def read_items_file(filename):
|
||||
fd = open(filename)
|
||||
text = fd.read()
|
||||
text = comment_re.sub('', text) # kill comments
|
||||
items = ipautil.parse_items(text)
|
||||
items = parse_items(text)
|
||||
if fd != sys.stdin: fd.close()
|
||||
return items
|
||||
|
||||
@ -568,11 +565,6 @@ class AttributeValueCompleter:
|
||||
readline.set_completer_delims(self.prev_completer_delims)
|
||||
readline.set_completer(self.prev_completer)
|
||||
|
||||
def _debug(self):
|
||||
print >> output_fd, "lhs='%s' lhs_complete=%s operator='%s' operator_complete=%s rhs='%s'" % \
|
||||
(self.lhs, self.lhs_complete, self.operator, self.operator_complete, self.rhs)
|
||||
|
||||
|
||||
def parse_input(self):
|
||||
'''We are looking for 3 tokens: <lhs,op,rhs>
|
||||
Extract as much of each token as possible.
|
||||
|
@ -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()
|
||||
|
||||
|
@ -130,6 +130,15 @@ dnl ---------------------------------------------------------------------------
|
||||
|
||||
PKG_CHECK_MODULES(MOZLDAP, mozldap > 6)
|
||||
|
||||
dnl ---------------------------------------------------------------------------
|
||||
dnl - Check for OpenSSL Crypto library
|
||||
dnl ---------------------------------------------------------------------------
|
||||
dnl This is a very simple check, we should probably check also for MD4_Init and
|
||||
dnl probably also the version we are using is recent enough
|
||||
SSL_LIBS=
|
||||
AC_CHECK_LIB(crypto, DES_set_key_unchecked, [SSL_LIBS="-lcrypto"])
|
||||
AC_SUBST(SSL_LIBS)
|
||||
|
||||
dnl ---------------------------------------------------------------------------
|
||||
dnl - Check for Python
|
||||
dnl ---------------------------------------------------------------------------
|
||||
@ -251,5 +260,6 @@ echo "
|
||||
cflags: ${CFLAGS}
|
||||
LDAP libs: ${LDAP_LIBS}
|
||||
KRB5 libs: ${KRB5_LIBS}
|
||||
OpenSSL libs: ${SSL_LIBS}
|
||||
Maintainer mode: ${USE_MAINTAINER_MODE}
|
||||
"
|
||||
|
@ -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())
|
||||
|
@ -7,6 +7,7 @@ app_PYTHON = \
|
||||
ipapolicy.py \
|
||||
user.py \
|
||||
delegate.py \
|
||||
principal.py \
|
||||
$(NULL)
|
||||
|
||||
EXTRA_DIST = \
|
||||
|
@ -41,11 +41,12 @@ class GroupNewForm(widgets.Form):
|
||||
|
||||
|
||||
class GroupEditValidator(validators.Schema):
|
||||
cn = validators.String(not_empty=True)
|
||||
cn = validators.String(not_empty=False)
|
||||
gidnumber = validators.Int(not_empty=False)
|
||||
description = validators.String(not_empty=False)
|
||||
|
||||
pre_validators = [
|
||||
validators.RequireIfPresent(required='cn', present='editprotected'),
|
||||
validators.RequireIfPresent(required='gidnumber', present='editprotected'),
|
||||
]
|
||||
|
||||
|
@ -1,5 +1,6 @@
|
||||
import turbogears
|
||||
from turbogears import validators, widgets
|
||||
from tg_expanding_form_widget.tg_expanding_form_widget import ExpandingForm
|
||||
|
||||
class IPAPolicyFields(object):
|
||||
# From cn=ipaConfig
|
||||
@ -12,6 +13,11 @@ class IPAPolicyFields(object):
|
||||
ipadefaultprimarygroup = widgets.TextField(name="ipadefaultprimarygroup", label="Default Users group")
|
||||
ipamaxusernamelength = widgets.TextField(name="ipamaxusernamelength", label="Max. Username Length", attrs=dict(size=3,maxlength=3))
|
||||
ipapwdexpadvnotify = widgets.TextField(name="ipapwdexpadvnotify", label="Password Expiration Notification (days)", attrs=dict(size=3,maxlength=3))
|
||||
ipauserobjectclasses = widgets.TextField(name="ipauserobjectclasses", label="Default User Object Classes", attrs=dict(size=50))
|
||||
userobjectclasses = ExpandingForm(name="userobjectclasses", label="Default User Object Classes", fields=[ipauserobjectclasses])
|
||||
ipagroupobjectclasses = widgets.TextField(name="ipagroupobjectclasses", label="Default Group Object Classes", attrs=dict(size=50))
|
||||
groupobjectclasses = ExpandingForm(name="groupobjectclasses", label="Default User Object Classes", fields=[ipagroupobjectclasses])
|
||||
ipadefaultemaildomain = widgets.TextField(name="ipadefaultemaildomain", label="Default E-mail Domain", attrs=dict(size=20))
|
||||
|
||||
ipapolicy_orig = widgets.HiddenField(name="ipapolicy_orig")
|
||||
|
||||
@ -34,6 +40,10 @@ class IPAPolicyValidator(validators.Schema):
|
||||
ipahomesrootdir = validators.String(not_empty=True)
|
||||
ipadefaultloginshell = validators.String(not_empty=True)
|
||||
ipadefaultprimarygroup = validators.String(not_empty=True)
|
||||
ipauserobjectclasses = validators.ForEach(validators.String(not_empty=True))
|
||||
ipagroupobjectclasses = validators.ForEach(validators.String(not_empty=True))
|
||||
ipadefaultemaildomain = validators.String(not_empty=True)
|
||||
|
||||
krbmaxpwdlife = validators.Number(not_empty=True)
|
||||
krbminpwdlife = validators.Number(not_empty=True)
|
||||
krbpwdmindiffchars = validators.Number(not_empty=True)
|
||||
|
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)
|
@ -11,7 +11,7 @@ class UserFields(object):
|
||||
displayname = widgets.TextField(name="displayname", label="Display Name")
|
||||
initials = widgets.TextField(name="initials", label="Initials")
|
||||
|
||||
uid = widgets.TextField(name="uid", label="Login")
|
||||
uid = widgets.TextField(name="uid", label="Login", attrs=dict(onchange="warnRDN(this.id)"))
|
||||
userpassword = widgets.PasswordField(name="userpassword", label="Password")
|
||||
userpassword_confirm = widgets.PasswordField(name="userpassword_confirm",
|
||||
label="Confirm Password")
|
||||
@ -56,9 +56,7 @@ class UserFields(object):
|
||||
label="Account Status",
|
||||
options = [("", "active"), ("true", "inactive")])
|
||||
|
||||
uid_hidden = widgets.HiddenField(name="uid")
|
||||
uidnumber_hidden = widgets.HiddenField(name="uidnumber")
|
||||
gidnumber_hidden = widgets.HiddenField(name="gidnumber")
|
||||
uid_hidden = widgets.HiddenField(name="uid_hidden")
|
||||
krbPasswordExpiration_hidden = widgets.HiddenField(name="krbPasswordExpiration")
|
||||
editprotected_hidden = widgets.HiddenField(name="editprotected")
|
||||
|
||||
@ -111,11 +109,12 @@ class UserEditValidator(validators.Schema):
|
||||
givenname = validators.String(not_empty=True)
|
||||
sn = validators.String(not_empty=True)
|
||||
cn = validators.ForEach(validators.String(not_empty=True))
|
||||
mail = validators.Email(not_empty=True)
|
||||
mail = validators.Email(not_empty=False)
|
||||
uidnumber = validators.Int(not_empty=False)
|
||||
gidnumber = validators.Int(not_empty=False)
|
||||
|
||||
pre_validators = [
|
||||
validators.RequireIfPresent(required='uid', present='editprotected'),
|
||||
validators.RequireIfPresent(required='uidnumber', present='editprotected'),
|
||||
validators.RequireIfPresent(required='gidnumber', present='editprotected'),
|
||||
]
|
||||
|
@ -7,3 +7,34 @@ def javascript_string_escape(input):
|
||||
return re.sub(r'[\'\"\\]',
|
||||
lambda match: "\\%s" % match.group(),
|
||||
input)
|
||||
|
||||
def setup_mv_fields(field, fieldname):
|
||||
"""Given a field (must be a list) and field name, convert that
|
||||
field into a list of dictionaries of the form:
|
||||
[ { fieldname : v1}, { fieldname : v2 }, .. ]
|
||||
|
||||
This is how we pre-fill values for multi-valued fields.
|
||||
"""
|
||||
mvlist = []
|
||||
if field:
|
||||
for v in field:
|
||||
if v:
|
||||
mvlist.append({ fieldname : v } )
|
||||
if len(mvlist) == 0:
|
||||
# We need to return an empty value so something can be
|
||||
# displayed on the edit page. Otherwise only an Add link
|
||||
# will show, not an empty field.
|
||||
mvlist.append({ fieldname : '' } )
|
||||
return mvlist
|
||||
|
||||
def fix_incoming_fields(fields, fieldname, multifieldname):
|
||||
"""This is called by the update() function. It takes the incoming
|
||||
list of dictionaries and converts it into back into the original
|
||||
field, then removes the multiple field.
|
||||
"""
|
||||
fields[fieldname] = []
|
||||
for i in range(len(fields[multifieldname])):
|
||||
fields[fieldname].append(fields[multifieldname][i][fieldname])
|
||||
del(fields[multifieldname])
|
||||
|
||||
return fields
|
||||
|
@ -83,7 +83,7 @@ class ProxyIdentity(object):
|
||||
return self._user.groups
|
||||
except AttributeError:
|
||||
# Groups haven't been computed yet
|
||||
return None
|
||||
return []
|
||||
groups= property(_get_groups)
|
||||
|
||||
def logout(self):
|
||||
|
@ -339,14 +339,18 @@ table.formtable td input[type="text"], input#criteria {
|
||||
border: 1px inset #dcdcdc;
|
||||
font-size: medium;
|
||||
padding: 2px 1px;
|
||||
/*
|
||||
background-color: #f5faff;
|
||||
*/
|
||||
}
|
||||
|
||||
table.formtable td select {
|
||||
border: 1px inset #dcdcdc;
|
||||
font-size: small;
|
||||
padding: 2px 1px;
|
||||
/*
|
||||
background-color: #f5faff;
|
||||
*/
|
||||
}
|
||||
|
||||
p.empty-message {
|
||||
@ -402,3 +406,20 @@ ul.checkboxlist li input {
|
||||
.sortdesc {
|
||||
background-image: url(/static/images/down.gif) !important;
|
||||
}
|
||||
|
||||
.warning_message {
|
||||
font-size: 120%;
|
||||
/*
|
||||
color: #ee0000;
|
||||
*/
|
||||
font-weight: bolder;
|
||||
}
|
||||
|
||||
.fielderror {
|
||||
color: red !important;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.requiredfield {
|
||||
background-color: #eebbbb !important;
|
||||
}
|
||||
|
@ -9,6 +9,7 @@ app_PYTHON = \
|
||||
policy.py \
|
||||
user.py \
|
||||
delegation.py \
|
||||
principal.py \
|
||||
$(NULL)
|
||||
|
||||
EXTRA_DIST = \
|
||||
|
@ -267,14 +267,6 @@ class GroupController(IPAController):
|
||||
group_modified = True
|
||||
new_group.setValue('cn', kw['cn'])
|
||||
|
||||
if group_modified:
|
||||
rv = client.update_group(new_group)
|
||||
#
|
||||
# If the group update succeeds, but below operations fail, we
|
||||
if new_group.cn != kw.get('cn'):
|
||||
group_modified = True
|
||||
new_group.setValue('cn', kw['cn'])
|
||||
|
||||
if group_modified:
|
||||
rv = client.update_group(new_group)
|
||||
#
|
||||
@ -313,7 +305,7 @@ class GroupController(IPAController):
|
||||
kw['dnadd'] = failed_adds
|
||||
group_modified = True
|
||||
except ipaerror.IPAError, e:
|
||||
turbogears.flash("Group update failed: " + str(e) + "<br/>" + e.detail[0]['desc'])
|
||||
turbogears.flash("Updating group membership failed: " + str(e) + "<br/>" + e.detail[0]['desc'])
|
||||
return dict(form=group_edit_form, group=kw, members=member_dicts,
|
||||
tg_template='ipagui.templates.groupedit')
|
||||
|
||||
@ -331,7 +323,7 @@ class GroupController(IPAController):
|
||||
kw['dndel'] = failed_dels
|
||||
group_modified = True
|
||||
except ipaerror.IPAError, e:
|
||||
turbogears.flash("Group update failed: " + str(e) + "<br/>" + e.detail[0]['desc'])
|
||||
turbogears.flash("Updating group membership failed: " + str(e) + "<br/>" + e.detail[0]['desc'])
|
||||
return dict(form=group_edit_form, group=kw, members=member_dicts,
|
||||
tg_template='ipagui.templates.groupedit')
|
||||
|
||||
|
@ -17,6 +17,7 @@ from ipa.entity import utf8_encode_values
|
||||
from ipa import ipaerror
|
||||
import ipa.entity
|
||||
import ipagui.forms.ipapolicy
|
||||
from ipagui.helpers import ipahelper
|
||||
|
||||
import ldap.dn
|
||||
|
||||
@ -71,6 +72,15 @@ class IPAPolicyController(IPAController):
|
||||
# Combine the 2 dicts to make the form easier
|
||||
ipapolicy_dict.update(password_dict)
|
||||
|
||||
# Load potential multi-valued fields
|
||||
if isinstance(ipapolicy_dict.get('ipauserobjectclasses',''), str):
|
||||
ipapolicy_dict['ipauserobjectclasses'] = [ipapolicy_dict.get('ipauserobjectclasses')]
|
||||
ipapolicy_dict['userobjectclasses'] = ipahelper.setup_mv_fields(ipapolicy_dict.get('ipauserobjectclasses'), 'ipauserobjectclasses')
|
||||
|
||||
if isinstance(ipapolicy_dict.get('ipagroupobjectclasses',''), str):
|
||||
ipapolicy_dict['ipagroupobjectclasses'] = [ipapolicy_dict.get('ipagroupobjectclasses')]
|
||||
ipapolicy_dict['groupobjectclasses'] = ipahelper.setup_mv_fields(ipapolicy_dict.get('ipagroupobjectclasses'), 'ipagroupobjectclasses')
|
||||
|
||||
return dict(form=ipapolicy_edit_form, ipapolicy=ipapolicy_dict)
|
||||
except ipaerror.IPAError, e:
|
||||
turbogears.flash("IPA Policy edit failed: " + str(e) + "<br/>" + str(e.detail))
|
||||
@ -88,6 +98,10 @@ class IPAPolicyController(IPAController):
|
||||
turbogears.flash("Edit policy cancelled")
|
||||
raise turbogears.redirect('/ipapolicy/show')
|
||||
|
||||
# Fix incoming multi-valued fields we created for the form
|
||||
kw = ipahelper.fix_incoming_fields(kw, 'ipauserobjectclasses', 'userobjectclasses')
|
||||
kw = ipahelper.fix_incoming_fields(kw, 'ipagroupobjectclasses', 'groupobjectclasses')
|
||||
|
||||
tg_errors, kw = self.ipapolicyupdatevalidate(**kw)
|
||||
if tg_errors:
|
||||
turbogears.flash("There were validation errors.<br/>" +
|
||||
@ -132,6 +146,15 @@ class IPAPolicyController(IPAController):
|
||||
if new_ipapolicy.ipadefaultprimarygroup != kw.get('ipadefaultprimarygroup'):
|
||||
policy_modified = True
|
||||
new_ipapolicy.setValue('ipadefaultprimarygroup', kw.get('ipadefaultprimarygroup'))
|
||||
if new_ipapolicy.ipauserobjectclasses != kw.get('ipauserobjectclasses'):
|
||||
policy_modified = True
|
||||
new_ipapolicy.setValue('ipauserobjectclasses', kw.get('ipauserobjectclasses'))
|
||||
if new_ipapolicy.ipagroupobjectclasses != kw.get('ipagroupobjectclasses'):
|
||||
policy_modified = True
|
||||
new_ipapolicy.setValue('ipagroupobjectclasses', kw.get('ipagroupobjectclasses'))
|
||||
if new_ipapolicy.ipadefaultemaildomain != kw.get('ipadefaultemaildomain'):
|
||||
policy_modified = True
|
||||
new_ipapolicy.setValue('ipadefaultemaildomain', kw.get('ipadefaultemaildomain'))
|
||||
|
||||
if policy_modified:
|
||||
rv = client.update_ipa_config(new_ipapolicy)
|
||||
|
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
|
@ -18,6 +18,7 @@ from ipa.entity import utf8_encode_values
|
||||
from ipa import ipaerror
|
||||
import ipagui.forms.user
|
||||
import ipa.config
|
||||
from ipagui.helpers import ipahelper
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
@ -28,14 +29,20 @@ user_edit_form = ipagui.forms.user.UserEditForm()
|
||||
|
||||
user_fields = ['*', 'nsAccountLock']
|
||||
|
||||
email_domain = ipa.config.config.default_realm.lower()
|
||||
|
||||
class UserController(IPAController):
|
||||
|
||||
def __init__(self, *args, **kw):
|
||||
super(UserController,self).__init__(*args, **kw)
|
||||
# self.load_custom_fields()
|
||||
|
||||
def get_email_domain(self):
|
||||
client = self.get_ipaclient()
|
||||
|
||||
conf = client.get_ipa_config()
|
||||
email_domain = conf.ipadefaultemaildomain
|
||||
|
||||
return email_domain
|
||||
|
||||
def load_custom_fields(self):
|
||||
|
||||
client = self.get_ipaclient()
|
||||
@ -83,36 +90,6 @@ class UserController(IPAController):
|
||||
user_new_form.validator.add_field(s['field'], validator)
|
||||
user_edit_form.validator.add_field(s['field'], validator)
|
||||
|
||||
def setup_mv_fields(self, field, fieldname):
|
||||
"""Given a field (must be a list) and field name, convert that
|
||||
field into a list of dictionaries of the form:
|
||||
[ { fieldname : v1}, { fieldname : v2 }, .. ]
|
||||
|
||||
This is how we pre-fill values for multi-valued fields.
|
||||
"""
|
||||
mvlist = []
|
||||
if field is not None:
|
||||
for v in field:
|
||||
mvlist.append({ fieldname : v } )
|
||||
else:
|
||||
# We need to return an empty value so something can be
|
||||
# displayed on the edit page. Otherwise only an Add link
|
||||
# will show, not an empty field.
|
||||
mvlist.append({ fieldname : '' } )
|
||||
return mvlist
|
||||
|
||||
def fix_incoming_fields(self, fields, fieldname, multifieldname):
|
||||
"""This is called by the update() function. It takes the incoming
|
||||
list of dictionaries and converts it into back into the original
|
||||
field, then removes the multiple field.
|
||||
"""
|
||||
fields[fieldname] = []
|
||||
for i in range(len(fields[multifieldname])):
|
||||
fields[fieldname].append(fields[multifieldname][i][fieldname])
|
||||
del(fields[multifieldname])
|
||||
|
||||
return fields
|
||||
|
||||
@expose()
|
||||
def index(self):
|
||||
raise turbogears.redirect("/user/list")
|
||||
@ -142,12 +119,12 @@ class UserController(IPAController):
|
||||
tg_errors, kw = self.usercreatevalidate(**kw)
|
||||
|
||||
# Fix incoming multi-valued fields we created for the form
|
||||
kw = self.fix_incoming_fields(kw, 'cn', 'cns')
|
||||
kw = self.fix_incoming_fields(kw, 'telephonenumber', 'telephonenumbers')
|
||||
kw = self.fix_incoming_fields(kw, 'facsimiletelephonenumber', 'facsimiletelephonenumbers')
|
||||
kw = self.fix_incoming_fields(kw, 'mobile', 'mobiles')
|
||||
kw = self.fix_incoming_fields(kw, 'pager', 'pagers')
|
||||
kw = self.fix_incoming_fields(kw, 'homephone', 'homephones')
|
||||
kw = ipahelper.fix_incoming_fields(kw, 'cn', 'cns')
|
||||
kw = ipahelper.fix_incoming_fields(kw, 'telephonenumber', 'telephonenumbers')
|
||||
kw = ipahelper.fix_incoming_fields(kw, 'facsimiletelephonenumber', 'facsimiletelephonenumbers')
|
||||
kw = ipahelper.fix_incoming_fields(kw, 'mobile', 'mobiles')
|
||||
kw = ipahelper.fix_incoming_fields(kw, 'pager', 'pagers')
|
||||
kw = ipahelper.fix_incoming_fields(kw, 'homephone', 'homephones')
|
||||
|
||||
if tg_errors:
|
||||
turbogears.flash("There were validation errors.<br/>" +
|
||||
@ -325,32 +302,34 @@ class UserController(IPAController):
|
||||
# Load potential multi-valued fields
|
||||
if isinstance(user_dict['cn'], str):
|
||||
user_dict['cn'] = [user_dict['cn']]
|
||||
user_dict['cns'] = self.setup_mv_fields(user_dict['cn'], 'cn')
|
||||
user_dict['cns'] = ipahelper.setup_mv_fields(user_dict['cn'], 'cn')
|
||||
|
||||
if isinstance(user_dict.get('telephonenumber',''), str):
|
||||
user_dict['telephonenumber'] = [user_dict.get('telephonenumber'),'']
|
||||
user_dict['telephonenumbers'] = self.setup_mv_fields(user_dict.get('telephonenumber'), 'telephonenumber')
|
||||
user_dict['telephonenumber'] = [user_dict.get('telephonenumber')]
|
||||
user_dict['telephonenumbers'] = ipahelper.setup_mv_fields(user_dict.get('telephonenumber'), 'telephonenumber')
|
||||
|
||||
if isinstance(user_dict.get('facsimiletelephonenumber',''), str):
|
||||
user_dict['facsimiletelephonenumber'] = [user_dict.get('facsimiletelephonenumber'),'']
|
||||
user_dict['facsimiletelephonenumbers'] = self.setup_mv_fields(user_dict.get('facsimiletelephonenumber'), 'facsimiletelephonenumber')
|
||||
user_dict['facsimiletelephonenumber'] = [user_dict.get('facsimiletelephonenumber')]
|
||||
user_dict['facsimiletelephonenumbers'] = ipahelper.setup_mv_fields(user_dict.get('facsimiletelephonenumber'), 'facsimiletelephonenumber')
|
||||
|
||||
if isinstance(user_dict.get('mobile',''), str):
|
||||
user_dict['mobile'] = [user_dict.get('mobile'),'']
|
||||
user_dict['mobiles'] = self.setup_mv_fields(user_dict.get('mobile'), 'mobile')
|
||||
user_dict['mobile'] = [user_dict.get('mobile')]
|
||||
user_dict['mobiles'] = ipahelper.setup_mv_fields(user_dict.get('mobile'), 'mobile')
|
||||
|
||||
if isinstance(user_dict.get('pager',''), str):
|
||||
user_dict['pager'] = [user_dict.get('pager'),'']
|
||||
user_dict['pagers'] = self.setup_mv_fields(user_dict.get('pager'), 'pager')
|
||||
user_dict['pager'] = [user_dict.get('pager')]
|
||||
user_dict['pagers'] = ipahelper.setup_mv_fields(user_dict.get('pager'), 'pager')
|
||||
|
||||
if isinstance(user_dict.get('homephone',''), str):
|
||||
user_dict['homephone'] = [user_dict.get('homephone'),'']
|
||||
user_dict['homephones'] = self.setup_mv_fields(user_dict.get('homephone'), 'homephone')
|
||||
user_dict['homephone'] = [user_dict.get('homephone')]
|
||||
user_dict['homephones'] = ipahelper.setup_mv_fields(user_dict.get('homephone'), 'homephone')
|
||||
|
||||
# Edit shouldn't fill in the password field.
|
||||
if user_dict.has_key('userpassword'):
|
||||
del(user_dict['userpassword'])
|
||||
|
||||
user_dict['uid_hidden'] = user_dict.get('uid')
|
||||
|
||||
user_groups = client.get_groups_by_member(user.dn, ['dn', 'cn'])
|
||||
user_groups.sort(self.sort_by_cn)
|
||||
user_groups_dicts = map(lambda group: group.toDict(), user_groups)
|
||||
@ -398,17 +377,20 @@ class UserController(IPAController):
|
||||
self.restrict_post()
|
||||
client = self.get_ipaclient()
|
||||
|
||||
if not kw.get('uid'):
|
||||
kw['uid'] = kw.get('uid_hidden')
|
||||
|
||||
if kw.get('submit') == 'Cancel Edit':
|
||||
turbogears.flash("Edit user cancelled")
|
||||
raise turbogears.redirect('/user/show', uid=kw.get('uid'))
|
||||
|
||||
# Fix incoming multi-valued fields we created for the form
|
||||
kw = self.fix_incoming_fields(kw, 'cn', 'cns')
|
||||
kw = self.fix_incoming_fields(kw, 'telephonenumber', 'telephonenumbers')
|
||||
kw = self.fix_incoming_fields(kw, 'facsimiletelephonenumber', 'facsimiletelephonenumbers')
|
||||
kw = self.fix_incoming_fields(kw, 'mobile', 'mobiles')
|
||||
kw = self.fix_incoming_fields(kw, 'pager', 'pagers')
|
||||
kw = self.fix_incoming_fields(kw, 'homephone', 'homephones')
|
||||
kw = ipahelper.fix_incoming_fields(kw, 'cn', 'cns')
|
||||
kw = ipahelper.fix_incoming_fields(kw, 'telephonenumber', 'telephonenumbers')
|
||||
kw = ipahelper.fix_incoming_fields(kw, 'facsimiletelephonenumber', 'facsimiletelephonenumbers')
|
||||
kw = ipahelper.fix_incoming_fields(kw, 'mobile', 'mobiles')
|
||||
kw = ipahelper.fix_incoming_fields(kw, 'pager', 'pagers')
|
||||
kw = ipahelper.fix_incoming_fields(kw, 'homephone', 'homephones')
|
||||
|
||||
# admins and editors can update anybody. A user can only update
|
||||
# themselves. We need this check because it is very easy to guess
|
||||
@ -430,6 +412,12 @@ class UserController(IPAController):
|
||||
user_groups=user_groups_dicts,
|
||||
tg_template='ipagui.templates.useredit')
|
||||
|
||||
# We don't want to inadvertantly add this to a record
|
||||
try:
|
||||
del kw['uid_hidden']
|
||||
except KeyError:
|
||||
pass
|
||||
|
||||
password_change = False
|
||||
user_modified = False
|
||||
|
||||
@ -488,6 +476,7 @@ class UserController(IPAController):
|
||||
new_user.setValue('uidnumber', str(kw.get('uidnumber')))
|
||||
new_user.setValue('gidnumber', str(kw.get('gidnumber')))
|
||||
new_user.setValue('homedirectory', str(kw.get('homedirectory')))
|
||||
new_user.setValue('uid', str(kw.get('uid')))
|
||||
|
||||
for custom_field in user_edit_form.custom_fields:
|
||||
new_user.setValue(custom_field.name,
|
||||
@ -750,13 +739,13 @@ class UserController(IPAController):
|
||||
givenname = givenname.lower()
|
||||
sn = sn.lower()
|
||||
|
||||
email = "%s.%s@%s" % (givenname, sn, email_domain)
|
||||
email = "%s.%s@%s" % (givenname, sn, self.get_email_domain())
|
||||
try:
|
||||
client.get_user_by_email(email)
|
||||
except ipaerror.exception_for(ipaerror.LDAP_NOT_FOUND):
|
||||
return email
|
||||
|
||||
email = "%s@%s" % (self.suggest_uid(givenname, sn), email_domain)
|
||||
email = "%s@%s" % (self.suggest_uid(givenname, sn), self.get_email_domain())
|
||||
try:
|
||||
client.get_user_by_email(email)
|
||||
except ipaerror.exception_for(ipaerror.LDAP_NOT_FOUND):
|
||||
|
@ -27,6 +27,10 @@ app_DATA = \
|
||||
master.kid \
|
||||
policyindex.kid \
|
||||
policylayout.kid \
|
||||
principallayout.kid \
|
||||
principallist.kid \
|
||||
principalnewform.kid \
|
||||
principalnew.kid \
|
||||
usereditform.kid \
|
||||
useredit.kid \
|
||||
userlayout.kid \
|
||||
|
@ -180,4 +180,10 @@
|
||||
</script>
|
||||
|
||||
</form>
|
||||
|
||||
|
||||
<script type="text/javascript">
|
||||
document.getElementById("form_name").focus();
|
||||
</script>
|
||||
|
||||
</div>
|
||||
|
@ -97,6 +97,10 @@ from ipagui.helpers import ipahelper
|
||||
|
||||
</form>
|
||||
|
||||
<script type="text/javascript">
|
||||
document.getElementById("form_cn").focus();
|
||||
</script>
|
||||
|
||||
<script type="text/javascript">
|
||||
/*
|
||||
* This section restores the contents of the add and remove lists
|
||||
|
@ -15,6 +15,8 @@ from ipagui.helpers import ipahelper
|
||||
|
||||
<script type="text/javascript" charset="utf-8"
|
||||
src="${tg.url('/static/javascript/dynamicedit.js')}"></script>
|
||||
<script type="text/javascript" charset="utf-8"
|
||||
src="${tg.url('/tg_widgets/tg_expanding_form_widget/javascript/expanding_form.js')}"></script>
|
||||
|
||||
<div py:for="field in hidden_fields"
|
||||
py:replace="field.display(value_for(field), **params_for(field))"
|
||||
@ -170,7 +172,90 @@ from ipagui.helpers import ipahelper
|
||||
py:content="tg.errors.get('ipadefaultprimarygroup')" />
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>
|
||||
<label class="fieldlabel" py:content="ipapolicy_fields.ipadefaultemaildomain.label" />:
|
||||
</th>
|
||||
<td>
|
||||
<span py:replace="ipapolicy_fields.ipadefaultemaildomain.display(value_for(ipapolicy_fields.ipadefaultemaildomain))" />
|
||||
<span py:if="tg.errors.get('ipadefaultemaildomain')" class="fielderror"
|
||||
py:content="tg.errors.get('ipadefaultemaildomain')" />
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>
|
||||
<label class="fieldlabel" for="${ipapolicy_fields.userobjectclasses.field_id}"
|
||||
py:content="ipapolicy_fields.userobjectclasses.label" />:
|
||||
</th>
|
||||
<td colspan="3">
|
||||
<table class="formtable" cellpadding="2" cellspacing="0" border="0" id="${ipapolicy_fields.userobjectclasses.field_id}">
|
||||
<tbody>
|
||||
<?python repetition = 0
|
||||
fld_index = 0
|
||||
fld_error = tg.errors.get('ipauserobjectclasses')
|
||||
?>
|
||||
<tr py:for="fld in value_for(ipapolicy_fields.ipauserobjectclasses)"
|
||||
id="${ipapolicy_fields.userobjectclasses.field_id}_${repetition}"
|
||||
class="${ipapolicy_fields.userobjectclasses.field_class}">
|
||||
|
||||
<td py:for="field in ipapolicy_fields.userobjectclasses.fields">
|
||||
<span><input class="textfield" type="text" id="${ipapolicy_fields.userobjectclasses.field_id}_${repetition}_ipauserobjectclasses" name="userobjectclasses-${repetition}.ipauserobjectclasses" value="${fld}"/></span>
|
||||
<span py:if="fld_error and fld_error[fld_index]" class="fielderror"
|
||||
py:content="tg.errors.get('ipauserobjectclasses')" />
|
||||
</td>
|
||||
<?python fld_index = fld_index + 1 ?>
|
||||
<td>
|
||||
<a
|
||||
href="javascript:ExpandingForm.removeItem('${ipapolicy_fields.userobjectclasses.field_id}_${repetition}')">Remove</a>
|
||||
</td>
|
||||
<?python repetition = repetition + 1?>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<a id="${ipapolicy_fields.userobjectclasses.field_id}_doclink" href="javascript:ExpandingForm.addItem('${ipapolicy_fields.userobjectclasses.field_id}');">Add User Object Class</a>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>
|
||||
<label class="fieldlabel" for="${ipapolicy_fields.groupobjectclasses.field_id}"
|
||||
py:content="ipapolicy_fields.groupobjectclasses.label" />:
|
||||
</th>
|
||||
<td colspan="3">
|
||||
<table class="formtable" cellpadding="2" cellspacing="0" border="0" id="${ipapolicy_fields.groupobjectclasses.field_id}">
|
||||
<tbody>
|
||||
<?python repetition = 0
|
||||
fld_index = 0
|
||||
fld_error = tg.errors.get('ipagroupobjectclasses')
|
||||
?>
|
||||
<tr py:for="fld in value_for(ipapolicy_fields.ipagroupobjectclasses)"
|
||||
id="${ipapolicy_fields.groupobjectclasses.field_id}_${repetition}"
|
||||
class="${ipapolicy_fields.groupobjectclasses.field_class}">
|
||||
|
||||
<td py:for="field in ipapolicy_fields.groupobjectclasses.fields">
|
||||
<span><input class="textfield" type="text" id="${ipapolicy_fields.groupobjectclasses.field_id}_${repetition}_ipagroupobjectclasses" name="groupobjectclasses-${repetition}.ipagroupobjectclasses" value="${fld}"/></span>
|
||||
<span py:if="fld_error and fld_error[fld_index]" class="fielderror"
|
||||
py:content="tg.errors.get('ipagroupobjectclasses')" />
|
||||
</td>
|
||||
<?python fld_index = fld_index + 1 ?>
|
||||
<td>
|
||||
<a
|
||||
href="javascript:ExpandingForm.removeItem('${ipapolicy_fields.groupobjectclasses.field_id}_${repetition}')">Remove</a>
|
||||
</td>
|
||||
<?python repetition = repetition + 1?>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<a id="${ipapolicy_fields.groupobjectclasses.field_id}_doclink" href="javascript:ExpandingForm.addItem('${ipapolicy_fields.groupobjectclasses.field_id}');">Add Group Object Class</a>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<hr/>
|
||||
|
||||
<input type="submit" class="submitbutton" name="submit"
|
||||
value="Update Policy"/>
|
||||
<input type="submit" class="submitbutton" name="submit"
|
||||
value="Cancel Edit" />
|
||||
</form>
|
||||
|
||||
</div>
|
||||
|
@ -15,6 +15,9 @@ edit_url = tg.url('/ipapolicy/edit')
|
||||
<script type="text/javascript" charset="utf-8" src="${tg.url('/static/javascript/tablekit.js')}"></script>
|
||||
|
||||
<h1>Manage IPA Policy</h1>
|
||||
<input class="submitbutton" type="button"
|
||||
onclick="document.location.href='${edit_url}'"
|
||||
value="Edit Policy" />
|
||||
|
||||
<h2 class="formsection">Search</h2>
|
||||
<table class="formtable" cellpadding="2" cellspacing="0" border="0">
|
||||
@ -109,6 +112,52 @@ edit_url = tg.url('/ipapolicy/edit')
|
||||
</th>
|
||||
<td>${ipapolicy.get("ipadefaultprimarygroup")}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>
|
||||
<label class="fieldlabel" py:content="fields.ipadefaultemaildomain.label" />:
|
||||
</th>
|
||||
<td>${ipapolicy.get("ipadefaultemaildomain")}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>
|
||||
<label class="fieldlabel" py:content="fields.ipauserobjectclasses.label" />:
|
||||
</th>
|
||||
<td>
|
||||
<table cellpadding="2" cellspacing="0" border="0">
|
||||
<tbody>
|
||||
<?python
|
||||
index = 0
|
||||
values = ipapolicy.get("ipauserobjectclasses", '')
|
||||
if isinstance(values, str):
|
||||
values = [values]
|
||||
?>
|
||||
<tr py:for="index in range(len(values))">
|
||||
<td>${values[index]}</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>
|
||||
<label class="fieldlabel" py:content="fields.ipagroupobjectclasses.label" />:
|
||||
</th>
|
||||
<td>
|
||||
<table cellpadding="2" cellspacing="0" border="0">
|
||||
<tbody>
|
||||
<?python
|
||||
index = 0
|
||||
values = ipapolicy.get("ipagroupobjectclasses", '')
|
||||
if isinstance(values, str):
|
||||
values = [values]
|
||||
?>
|
||||
<tr py:for="index in range(len(values))">
|
||||
<td>${values[index]}</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
<hr />
|
||||
<input class="submitbutton" type="button"
|
||||
|
@ -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>
|
102
ipa-server/ipa-gui/ipagui/templates/principalnewform.kid
Normal file
102
ipa-server/ipa-gui/ipagui/templates/principalnewform.kid
Normal file
@ -0,0 +1,102 @@
|
||||
<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>
|
||||
|
||||
<script type="text/javascript">
|
||||
document.getElementById("form_hostname").focus();
|
||||
</script>
|
||||
|
||||
</div>
|
@ -38,12 +38,14 @@ from ipagui.helpers import ipahelper
|
||||
function toggleProtectedFields(checkbox) {
|
||||
passwordField = document.getElementById('form_userpassword');
|
||||
passwordConfirmField = document.getElementById('form_userpassword_confirm');
|
||||
uidField = document.getElementById('form_uid');
|
||||
uidnumberField = document.getElementById('form_uidnumber');
|
||||
gidnumberField = document.getElementById('form_gidnumber');
|
||||
homedirectoryField = document.getElementById('form_homedirectory');
|
||||
if (checkbox.checked) {
|
||||
passwordField.disabled = false;
|
||||
passwordConfirmField.disabled = false;
|
||||
uidField.disabled = false;
|
||||
uidnumberField.disabled = false;
|
||||
gidnumberField.disabled = false;
|
||||
homedirectoryField.disabled = false;
|
||||
@ -51,6 +53,7 @@ from ipagui.helpers import ipahelper
|
||||
} else {
|
||||
passwordField.disabled = true;
|
||||
passwordConfirmField.disabled = true;
|
||||
uidField.disabled = true;
|
||||
uidnumberField.disabled = true;
|
||||
gidnumberField.disabled = true;
|
||||
homedirectoryField.disabled = true;
|
||||
@ -58,6 +61,13 @@ from ipagui.helpers import ipahelper
|
||||
}
|
||||
}
|
||||
|
||||
function warnRDN() {
|
||||
if (confirm("Are you sure you want to change the login name?<br/>This can have unexpected results. A password change is required.")) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
function doSearch() {
|
||||
$('searchresults').update("Searching...");
|
||||
new Ajax.Updater('searchresults',
|
||||
@ -215,13 +225,21 @@ from ipagui.helpers import ipahelper
|
||||
py:content="tg.errors.get('nsAccountLock')" />
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<th>
|
||||
<label class="fieldlabel" for="${user_fields.uid.field_id}"
|
||||
py:content="user_fields.uid.label" />:
|
||||
</th>
|
||||
<td>
|
||||
${value_for(user_fields.uid)}
|
||||
<span py:replace="user_fields.uid.display(
|
||||
value_for(user_fields.uid))" />
|
||||
<span py:if="tg.errors.get('uid')" class="fielderror"
|
||||
py:content="tg.errors.get('uid')" />
|
||||
|
||||
<script type="text/javascript">
|
||||
document.getElementById('form_uid').disabled = true;
|
||||
</script>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
|
@ -790,6 +790,10 @@ from ipagui.helpers import ipahelper
|
||||
|
||||
</form>
|
||||
|
||||
<script type="text/javascript">
|
||||
document.getElementById("form_title").focus();
|
||||
</script>
|
||||
|
||||
<script type="text/javascript">
|
||||
/*
|
||||
* This section restores the contents of the add and remove lists
|
||||
|
@ -11,7 +11,7 @@ edit_url = tg.url('/user/edit', uid=user.get('uid'))
|
||||
?>
|
||||
<h1>View User</h1>
|
||||
|
||||
<input py:if="'editors' in tg.identity.groups or 'admins' in tg.identity.groups"
|
||||
<input py:if="'editors' in tg.identity.groups or 'admins' in tg.identity.groups or tg.identity.display_name == user.get('uid')"
|
||||
class="submitbutton" type="button"
|
||||
onclick="document.location.href='${edit_url}'"
|
||||
value="Edit User" />
|
||||
@ -374,7 +374,7 @@ else:
|
||||
|
||||
<br/>
|
||||
<hr />
|
||||
<input py:if="'editors' in tg.identity.groups or 'admins' in tg.identity.groups"
|
||||
<input py:if="'editors' in tg.identity.groups or 'admins' in tg.identity.groups or tg.identity.display_name == user.get('uid')"
|
||||
class="submitbutton" type="button"
|
||||
onclick="document.location.href='${edit_url}'"
|
||||
value="Edit User" />
|
||||
|
@ -226,7 +226,7 @@ def read_dm_password():
|
||||
print "Certain directory server operations require an administrative user."
|
||||
print "This user is referred to as the Directory Manager and has full access"
|
||||
print "to the Directory for system management tasks."
|
||||
print "The password must be at least 8 characters long, and contain no spaces."
|
||||
print "The password must be at least 8 characters long."
|
||||
print ""
|
||||
#TODO: provide the option of generating a random password
|
||||
dm_password = read_password("Directory Manager")
|
||||
@ -288,7 +288,7 @@ def main():
|
||||
if options.setup_bind:
|
||||
if not bind.check_inst():
|
||||
print "--setup-bind was specified but bind is not installed on the system"
|
||||
print "Please install bind (you also need the package 'caching-nameserver') and restart the setup program"
|
||||
print "Please install bind (you may also need the package 'caching-nameserver') and restart the setup program"
|
||||
return "-Fatal Error-"
|
||||
|
||||
# check the hostname is correctly configured, it must be as the kldap
|
||||
|
@ -27,11 +27,16 @@ attributetypes: ( 2.16.840.1.113730.3.8.1.7 NAME 'ipaDefaultLoginShell' EQUALITY
|
||||
attributetypes: ( 2.16.840.1.113730.3.8.1.8 NAME 'ipaDefaultPrimaryGroup' EQUALITY caseExactMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 SINGLE-VALUE)
|
||||
## ipaMaxUsernameLength - maximum username length to allow in the UI
|
||||
attributetypes: ( 2.16.840.1.113730.3.8.1.9 NAME 'ipaMaxUsernameLength' EQUALITY integerMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE)
|
||||
## ipaPwdExpAdvNotify - time in days to send out paswwrod expiration notification before passwpord actually expires
|
||||
## ipaPwdExpAdvNotify - time in days to send out paswword expiration notification before passwpord actually expires
|
||||
attributetypes: ( 2.16.840.1.113730.3.8.1.10 NAME 'ipaPwdExpAdvNotify' EQUALITY integerMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE)
|
||||
# ipaUserObjectClasses - required objectclasses for users
|
||||
attributetypes: ( 2.16.840.1.113730.3.8.1.11 NAME 'ipaUserObjectClasses' SYNTAX 1.3.6.1.4.1.1466.115.121.1.15)
|
||||
# ipaGroupObjectClasses - required objectclasses for groups
|
||||
attributetypes: ( 2.16.840.1.113730.3.8.1.12 NAME 'ipaGroupObjectClasses' SYNTAX 1.3.6.1.4.1.1466.115.121.1.15)
|
||||
attributetypes: ( 2.16.840.1.113730.3.8.1.13 NAME 'ipaDefaultEmailDomain' EQUALITY caseIgnoreMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.15)
|
||||
###############################################
|
||||
##
|
||||
## ObjectClasses
|
||||
##
|
||||
## ipaGuiConfig - GUI config parameters objectclass
|
||||
objectClasses: ( 2.16.840.1.113730.3.8.2.1 NAME 'ipaGuiConfig' AUXILIARY MAY ( ipaUserSearchFields $ ipaGroupSearchFields $ ipaSearchTimeLimit $ ipaSearchRecordsLimit $ ipaCustomFields $ ipaHomesRootDir $ ipaDefaultLoginShell $ ipaDefaultPrimaryGroup $ ipaMaxUsernameLength $ ipaPwdExpAdvNotify ) )
|
||||
objectClasses: ( 2.16.840.1.113730.3.8.2.1 NAME 'ipaGuiConfig' AUXILIARY MAY ( ipaUserSearchFields $ ipaGroupSearchFields $ ipaSearchTimeLimit $ ipaSearchRecordsLimit $ ipaCustomFields $ ipaHomesRootDir $ ipaDefaultLoginShell $ ipaDefaultPrimaryGroup $ ipaMaxUsernameLength $ ipaPwdExpAdvNotify $ ipaUserObjectClasses $ ipaGroupObjectClasses $ ipaDefaultEmailDomain) )
|
||||
|
@ -147,6 +147,19 @@ ipaDefaultLoginShell: /bin/sh
|
||||
ipaDefaultPrimaryGroup: ipausers
|
||||
ipaMaxUsernameLength: 8
|
||||
ipaPwdExpAdvNotify: 4
|
||||
ipaGroupObjectClasses: top
|
||||
ipaGroupObjectClasses: groupofnames
|
||||
ipaGroupObjectClasses: posixGroup
|
||||
ipaGroupObjectClasses: inetUser
|
||||
ipaUserObjectClasses: top
|
||||
ipaUserObjectClasses: person
|
||||
ipaUserObjectClasses: organizationalPerson
|
||||
ipaUserObjectClasses: inetOrgPerson
|
||||
ipaUserObjectClasses: inetUser
|
||||
ipaUserObjectClasses: posixAccount
|
||||
ipaUserObjectClasses: krbPrincipalAux
|
||||
ipaUserObjectClasses: radiusprofile
|
||||
ipaDefaultEmailDomain: $DOMAIN
|
||||
|
||||
dn: cn=account inactivation,cn=accounts,$SUFFIX
|
||||
changetype: add
|
||||
|
@ -664,6 +664,8 @@ void handle_krb_packets(uint8_t *buf, ssize_t buflen,
|
||||
auth_context = NULL;
|
||||
krep.length = 0;
|
||||
krep.data = NULL;
|
||||
kdec.length = 0;
|
||||
kdec.data = NULL;
|
||||
kprincpw = NULL;
|
||||
context = NULL;
|
||||
ticket = NULL;
|
||||
@ -859,6 +861,7 @@ void handle_krb_packets(uint8_t *buf, ssize_t buflen,
|
||||
/* make sure password is cleared off before we free the memory */
|
||||
memset(kdec.data, 0, kdec.length);
|
||||
free(kdec.data);
|
||||
kdec.length = 0;
|
||||
|
||||
kpreply:
|
||||
|
||||
@ -867,6 +870,7 @@ kpreply:
|
||||
kdec.data = malloc(kdec.length);
|
||||
if (!kdec.data) {
|
||||
syslog(LOG_ERR, "Out of memory!");
|
||||
kdec.length = 0;
|
||||
goto done;
|
||||
}
|
||||
|
||||
|
@ -15,6 +15,7 @@ BuildRequires: openssl-devel
|
||||
BuildRequires: openldap-devel
|
||||
BuildRequires: krb5-devel
|
||||
BuildRequires: nss-devel
|
||||
BuildRequires: libcap-devel
|
||||
|
||||
Requires: ipa-python
|
||||
Requires: ipa-admintools
|
||||
@ -38,6 +39,7 @@ Requires: python-tgexpandingformwidget
|
||||
Requires: acl
|
||||
Requires: freeradius
|
||||
Requires: pyasn1
|
||||
Requires: libcap
|
||||
|
||||
%define httpd_conf /etc/httpd/conf.d
|
||||
%define plugin_dir %{_libdir}/dirsrv/plugins
|
||||
|
@ -11,6 +11,7 @@ INCLUDES = \
|
||||
$(MOZLDAP_CFLAGS) \
|
||||
$(LDAP_CFLAGS) \
|
||||
$(KRB5_CFLAGS) \
|
||||
$(SSL_CFLAGS) \
|
||||
$(WARN_CFLAGS) \
|
||||
$(NULL)
|
||||
|
||||
@ -27,6 +28,7 @@ libipa_pwd_extop_la_LDFLAGS = -avoid-version
|
||||
|
||||
libipa_pwd_extop_la_LIBADD = \
|
||||
$(KRB5_LIBS) \
|
||||
$(SSL_LIBS) \
|
||||
$(MOZLDAP_LIBS) \
|
||||
$(NULL)
|
||||
|
||||
|
@ -18,16 +18,15 @@
|
||||
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
#
|
||||
|
||||
import subprocess
|
||||
import string
|
||||
import tempfile
|
||||
import shutil
|
||||
import logging
|
||||
import pwd
|
||||
import glob
|
||||
import sys
|
||||
import os
|
||||
|
||||
from ipa import ipautil
|
||||
|
||||
from ipa.ipautil import *
|
||||
import service
|
||||
import installutils
|
||||
|
||||
@ -36,7 +35,7 @@ SERVER_ROOT_32 = "/usr/lib/dirsrv"
|
||||
|
||||
def ldap_mod(fd, dn, pwd):
|
||||
args = ["/usr/bin/ldapmodify", "-h", "127.0.0.1", "-xv", "-D", dn, "-w", pwd, "-f", fd.name]
|
||||
run(args)
|
||||
ipautil.run(args)
|
||||
|
||||
def realm_to_suffix(realm_name):
|
||||
s = realm_name.split(".")
|
||||
@ -44,7 +43,7 @@ def realm_to_suffix(realm_name):
|
||||
return ",".join(terms)
|
||||
|
||||
def find_server_root():
|
||||
if dir_exists(SERVER_ROOT_64):
|
||||
if ipautil.dir_exists(SERVER_ROOT_64):
|
||||
return SERVER_ROOT_64
|
||||
else:
|
||||
return SERVER_ROOT_32
|
||||
@ -83,7 +82,7 @@ def check_existing_installation():
|
||||
sys.exit(1)
|
||||
|
||||
try:
|
||||
run(["/sbin/service", "dirsrv", "stop"])
|
||||
ipautil.run(["/sbin/service", "dirsrv", "stop"])
|
||||
except:
|
||||
pass
|
||||
for d in dirs:
|
||||
@ -126,6 +125,7 @@ class DsInstance(service.Service):
|
||||
self.host_name = None
|
||||
self.dm_password = None
|
||||
self.sub_dict = None
|
||||
self.domain = None
|
||||
|
||||
def create_instance(self, ds_user, realm_name, host_name, dm_password, ro_replica=False):
|
||||
self.ds_user = ds_user
|
||||
@ -134,6 +134,7 @@ class DsInstance(service.Service):
|
||||
self.suffix = realm_to_suffix(self.realm_name)
|
||||
self.host_name = host_name
|
||||
self.dm_password = dm_password
|
||||
self.domain = host_name[host_name.find(".")+1:]
|
||||
self.__setup_sub_dict()
|
||||
|
||||
if ro_replica:
|
||||
@ -174,7 +175,7 @@ class DsInstance(service.Service):
|
||||
self.sub_dict = dict(FQHN=self.host_name, SERVERID=self.serverid,
|
||||
PASSWORD=self.dm_password, SUFFIX=self.suffix.lower(),
|
||||
REALM=self.realm_name, USER=self.ds_user,
|
||||
SERVER_ROOT=server_root)
|
||||
SERVER_ROOT=server_root, DOMAIN=self.domain)
|
||||
|
||||
def __create_ds_user(self):
|
||||
self.step("creating directory server user")
|
||||
@ -185,25 +186,25 @@ class DsInstance(service.Service):
|
||||
logging.debug("adding ds user %s" % self.ds_user)
|
||||
args = ["/usr/sbin/useradd", "-c", "DS System User", "-d", "/var/lib/dirsrv", "-M", "-r", "-s", "/sbin/nologin", self.ds_user]
|
||||
try:
|
||||
run(args)
|
||||
ipautil.run(args)
|
||||
logging.debug("done adding user")
|
||||
except ipautil.CalledProcessError, e:
|
||||
logging.critical("failed to add user %s" % e)
|
||||
|
||||
def __create_instance(self):
|
||||
self.step("creating directory server instance")
|
||||
inf_txt = template_str(INF_TEMPLATE, self.sub_dict)
|
||||
inf_txt = ipautil.template_str(INF_TEMPLATE, self.sub_dict)
|
||||
logging.debug(inf_txt)
|
||||
inf_fd = write_tmp_file(inf_txt)
|
||||
inf_fd = ipautil.write_tmp_file(inf_txt)
|
||||
logging.debug("writing inf template")
|
||||
if file_exists("/usr/sbin/setup-ds.pl"):
|
||||
if ipautil.file_exists("/usr/sbin/setup-ds.pl"):
|
||||
args = ["/usr/sbin/setup-ds.pl", "--silent", "--logfile", "-", "-f", inf_fd.name]
|
||||
logging.debug("calling setup-ds.pl")
|
||||
else:
|
||||
args = ["/usr/bin/ds_newinst.pl", inf_fd.name]
|
||||
logging.debug("calling ds_newinst.pl")
|
||||
try:
|
||||
run(args)
|
||||
ipautil.run(args)
|
||||
logging.debug("completed creating ds instance")
|
||||
except ipautil.CalledProcessError, e:
|
||||
logging.critical("failed to restart ds instance %s" % e)
|
||||
@ -217,19 +218,19 @@ class DsInstance(service.Service):
|
||||
|
||||
def __add_default_schemas(self):
|
||||
self.step("adding default schema")
|
||||
shutil.copyfile(SHARE_DIR + "60kerberos.ldif",
|
||||
shutil.copyfile(ipautil.SHARE_DIR + "60kerberos.ldif",
|
||||
schema_dirname(self.realm_name) + "60kerberos.ldif")
|
||||
shutil.copyfile(SHARE_DIR + "60samba.ldif",
|
||||
shutil.copyfile(ipautil.SHARE_DIR + "60samba.ldif",
|
||||
schema_dirname(self.realm_name) + "60samba.ldif")
|
||||
shutil.copyfile(SHARE_DIR + "60radius.ldif",
|
||||
shutil.copyfile(ipautil.SHARE_DIR + "60radius.ldif",
|
||||
schema_dirname(self.realm_name) + "60radius.ldif")
|
||||
shutil.copyfile(SHARE_DIR + "60ipaconfig.ldif",
|
||||
shutil.copyfile(ipautil.SHARE_DIR + "60ipaconfig.ldif",
|
||||
schema_dirname(self.realm_name) + "60ipaconfig.ldif")
|
||||
|
||||
def __add_memberof_module(self):
|
||||
self.step("enabling memboerof plugin")
|
||||
memberof_txt = template_file(SHARE_DIR + "memberof-conf.ldif", self.sub_dict)
|
||||
memberof_fd = write_tmp_file(memberof_txt)
|
||||
memberof_txt = ipautil.template_file(ipautil.SHARE_DIR + "memberof-conf.ldif", self.sub_dict)
|
||||
memberof_fd = ipautil.write_tmp_file(memberof_txt)
|
||||
try:
|
||||
ldap_mod(memberof_fd, "cn=Directory Manager", self.dm_password)
|
||||
except ipautil.CalledProcessError, e:
|
||||
@ -238,8 +239,8 @@ class DsInstance(service.Service):
|
||||
|
||||
def __init_memberof(self):
|
||||
self.step("initializing group membership")
|
||||
memberof_txt = template_file(SHARE_DIR + "memberof-task.ldif", self.sub_dict)
|
||||
memberof_fd = write_tmp_file(memberof_txt)
|
||||
memberof_txt = ipautil.template_file(ipautil.SHARE_DIR + "memberof-task.ldif", self.sub_dict)
|
||||
memberof_fd = ipautil.write_tmp_file(memberof_txt)
|
||||
try:
|
||||
ldap_mod(memberof_fd, "cn=Directory Manager", self.dm_password)
|
||||
except ipautil.CalledProcessError, e:
|
||||
@ -248,8 +249,8 @@ class DsInstance(service.Service):
|
||||
|
||||
def __add_referint_module(self):
|
||||
self.step("enabling referential integrity plugin")
|
||||
referint_txt = template_file(SHARE_DIR + "referint-conf.ldif", self.sub_dict)
|
||||
referint_fd = write_tmp_file(referint_txt)
|
||||
referint_txt = ipautil.template_file(ipautil.SHARE_DIR + "referint-conf.ldif", self.sub_dict)
|
||||
referint_fd = ipautil.write_tmp_file(referint_txt)
|
||||
try:
|
||||
ldap_mod(referint_fd, "cn=Directory Manager", self.dm_password)
|
||||
except ipautil.CalledProcessError, e:
|
||||
@ -258,8 +259,8 @@ class DsInstance(service.Service):
|
||||
|
||||
def __add_dna_module(self):
|
||||
self.step("enabling distributed numeric assignment plugin")
|
||||
dna_txt = template_file(SHARE_DIR + "dna-conf.ldif", self.sub_dict)
|
||||
dna_fd = write_tmp_file(dna_txt)
|
||||
dna_txt = ipautil.template_file(ipautil.SHARE_DIR + "dna-conf.ldif", self.sub_dict)
|
||||
dna_fd = ipautil.write_tmp_file(dna_txt)
|
||||
try:
|
||||
ldap_mod(dna_fd, "cn=Directory Manager", self.dm_password)
|
||||
except ipautil.CalledProcessError, e:
|
||||
@ -268,8 +269,8 @@ class DsInstance(service.Service):
|
||||
|
||||
def __config_uidgid_gen_first_master(self):
|
||||
self.step("configuring Posix uid/gid generation as first master")
|
||||
dna_txt = template_file(SHARE_DIR + "dna-posix.ldif", self.sub_dict)
|
||||
dna_fd = write_tmp_file(dna_txt)
|
||||
dna_txt = ipautil.template_file(ipautil.SHARE_DIR + "dna-posix.ldif", self.sub_dict)
|
||||
dna_fd = ipautil.write_tmp_file(dna_txt)
|
||||
try:
|
||||
ldap_mod(dna_fd, "cn=Directory Manager", self.dm_password)
|
||||
except ipautil.CalledProcessError, e:
|
||||
@ -278,8 +279,8 @@ class DsInstance(service.Service):
|
||||
|
||||
def __add_master_entry_first_master(self):
|
||||
self.step("adding master entry as first master")
|
||||
master_txt = template_file(SHARE_DIR + "master-entry.ldif", self.sub_dict)
|
||||
master_fd = write_tmp_file(master_txt)
|
||||
master_txt = ipautil.template_file(ipautil.SHARE_DIR + "master-entry.ldif", self.sub_dict)
|
||||
master_fd = ipautil.write_tmp_file(master_txt)
|
||||
try:
|
||||
ldap_mod(master_fd, "cn=Directory Manager", self.dm_password)
|
||||
except ipautil.CalledProcessError, e:
|
||||
@ -292,20 +293,20 @@ class DsInstance(service.Service):
|
||||
args = ["/usr/share/ipa/ipa-server-setupssl", self.dm_password,
|
||||
dirname, self.host_name]
|
||||
try:
|
||||
run(args)
|
||||
ipautil.run(args)
|
||||
logging.debug("done configuring ssl for ds instance")
|
||||
except ipautil.CalledProcessError, e:
|
||||
logging.critical("Failed to configure ssl in ds instance %s" % e)
|
||||
|
||||
def __add_default_layout(self):
|
||||
self.step("adding default layout")
|
||||
txt = template_file(SHARE_DIR + "bootstrap-template.ldif", self.sub_dict)
|
||||
inf_fd = write_tmp_file(txt)
|
||||
txt = ipautil.template_file(ipautil.SHARE_DIR + "bootstrap-template.ldif", self.sub_dict)
|
||||
inf_fd = ipautil.write_tmp_file(txt)
|
||||
logging.debug("adding default dfrom ipa.ipautil import *s layout")
|
||||
args = ["/usr/bin/ldapmodify", "-xv", "-D", "cn=Directory Manager",
|
||||
"-w", self.dm_password, "-f", inf_fd.name]
|
||||
try:
|
||||
run(args)
|
||||
ipautil.run(args)
|
||||
logging.debug("done adding default ds layout")
|
||||
except ipautil.CalledProcessError, e:
|
||||
print "Failed to add default ds layout", e
|
||||
@ -313,13 +314,13 @@ class DsInstance(service.Service):
|
||||
|
||||
def __create_indeces(self):
|
||||
self.step("creating indeces")
|
||||
txt = template_file(SHARE_DIR + "indeces.ldif", self.sub_dict)
|
||||
inf_fd = write_tmp_file(txt)
|
||||
txt = ipautil.template_file(ipautil.SHARE_DIR + "indeces.ldif", self.sub_dict)
|
||||
inf_fd = ipautil.write_tmp_file(txt)
|
||||
logging.debug("adding/updating indeces")
|
||||
args = ["/usr/bin/ldapmodify", "-xv", "-D", "cn=Directory Manager",
|
||||
"-w", self.dm_password, "-f", inf_fd.name]
|
||||
try:
|
||||
run(args)
|
||||
ipautil.run(args)
|
||||
logging.debug("done adding/updating indeces")
|
||||
except ipautil.CalledProcessError, e:
|
||||
logging.critical("Failed to add/update indeces %s" % str(e))
|
||||
@ -327,7 +328,7 @@ class DsInstance(service.Service):
|
||||
def __certmap_conf(self):
|
||||
self.step("configuring certmap.conf")
|
||||
dirname = config_dirname(self.realm_name)
|
||||
certmap_conf = template_file(SHARE_DIR+"certmap.conf.template", self.sub_dict)
|
||||
certmap_conf = ipautil.template_file(ipautil.SHARE_DIR + "certmap.conf.template", self.sub_dict)
|
||||
certmap_fd = open(dirname+"certmap.conf", "w+")
|
||||
certmap_fd.write(certmap_conf)
|
||||
certmap_fd.close()
|
||||
@ -335,7 +336,7 @@ class DsInstance(service.Service):
|
||||
def change_admin_password(self, password):
|
||||
logging.debug("Changing admin password")
|
||||
dirname = config_dirname(self.realm_name)
|
||||
if dir_exists("/usr/lib64/mozldap"):
|
||||
if ipautil.dir_exists("/usr/lib64/mozldap"):
|
||||
app = "/usr/lib64/mozldap/ldappasswd"
|
||||
else:
|
||||
app = "/usr/lib/mozldap/ldappasswd"
|
||||
@ -344,7 +345,7 @@ class DsInstance(service.Service):
|
||||
"-P", dirname+"/cert8.db", "-ZZZ", "-s", password,
|
||||
"uid=admin,cn=sysaccounts,cn=etc,"+self.suffix]
|
||||
try:
|
||||
run(args)
|
||||
ipautil.run(args)
|
||||
logging.debug("ldappasswd done")
|
||||
except ipautil.CalledProcessError, e:
|
||||
print "Unable to set admin password", e
|
||||
|
@ -356,13 +356,13 @@ class IPAdmin(SimpleLDAPObject):
|
||||
type, obj = self.result(res)
|
||||
except ldap.NO_SUCH_OBJECT:
|
||||
raise ipaerror.gen_exception(ipaerror.LDAP_NOT_FOUND,
|
||||
"no such entry for " + str(args))
|
||||
notfound(args))
|
||||
except ldap.LDAPError, e:
|
||||
raise ipaerror.gen_exception(ipaerror.LDAP_DATABASE_ERROR, None, e)
|
||||
|
||||
if not obj:
|
||||
raise ipaerror.gen_exception(ipaerror.LDAP_NOT_FOUND,
|
||||
"no such entry for " + str(args))
|
||||
notfound(args))
|
||||
elif isinstance(obj,Entry):
|
||||
return obj
|
||||
else: # assume list/tuple
|
||||
@ -386,7 +386,7 @@ class IPAdmin(SimpleLDAPObject):
|
||||
|
||||
if not obj:
|
||||
raise ipaerror.gen_exception(ipaerror.LDAP_NOT_FOUND,
|
||||
"no such entry for " + str(args))
|
||||
notfound(args))
|
||||
|
||||
all_users = []
|
||||
for s in obj:
|
||||
@ -424,7 +424,7 @@ class IPAdmin(SimpleLDAPObject):
|
||||
|
||||
if not entries:
|
||||
raise ipaerror.gen_exception(ipaerror.LDAP_NOT_FOUND,
|
||||
"no such entry for " + str(args))
|
||||
notfound(args))
|
||||
|
||||
if partial == 1:
|
||||
counter = -1
|
||||
@ -717,3 +717,16 @@ class IPAdmin(SimpleLDAPObject):
|
||||
"""Returns True if the given string is a DN, False otherwise."""
|
||||
return (dn.find("=") > 0)
|
||||
is_a_dn = staticmethod(is_a_dn)
|
||||
|
||||
|
||||
def notfound(args):
|
||||
"""Return a string suitable for displaying as an error when a
|
||||
search returns no results.
|
||||
|
||||
This just returns whatever is after the equals sign"""
|
||||
filter = args[2]
|
||||
try:
|
||||
target = re.match(r'\(.*=(.*)\)', filter).group(1)
|
||||
except:
|
||||
target = filter
|
||||
return "%s not found" % str(target)
|
||||
|
@ -330,6 +330,32 @@ class IPAServer:
|
||||
|
||||
return (exact_match_filter, partial_match_filter)
|
||||
|
||||
def __get_schema(self, opts=None):
|
||||
"""Retrieves the current LDAP schema from the LDAP server."""
|
||||
|
||||
schema_entry = self.__get_base_entry("", "objectclass=*", ['dn','subschemasubentry'], opts)
|
||||
schema_cn = schema_entry.get('subschemasubentry')
|
||||
schema = self.__get_base_entry(schema_cn, "objectclass=*", ['*'], opts)
|
||||
|
||||
return schema
|
||||
|
||||
def __get_objectclasses(self, opts=None):
|
||||
"""Returns a list of available objectclasses that the LDAP
|
||||
server supports. This parses out the syntax, attributes, etc
|
||||
and JUST returns a lower-case list of the names."""
|
||||
|
||||
schema = self.__get_schema(opts)
|
||||
|
||||
objectclasses = schema.get('objectclasses')
|
||||
|
||||
# Convert this list into something more readable
|
||||
result = []
|
||||
for i in range(len(objectclasses)):
|
||||
oc = objectclasses[i].lower().split(" ")
|
||||
result.append(oc[3].replace("'",""))
|
||||
|
||||
return result
|
||||
|
||||
# Higher-level API
|
||||
|
||||
def get_aci_entry(self, sattrs, opts=None):
|
||||
@ -424,6 +450,19 @@ class IPAServer:
|
||||
if self.__is_user_unique(user['uid'], opts) == 0:
|
||||
raise ipaerror.gen_exception(ipaerror.LDAP_DUPLICATE)
|
||||
|
||||
# dn is set here, not by the user
|
||||
try:
|
||||
del user['dn']
|
||||
except KeyError:
|
||||
pass
|
||||
|
||||
# No need to set empty fields, and they can cause issues when they
|
||||
# get to LDAP, like:
|
||||
# TypeError: ('expected a string in the list', None)
|
||||
for k in user.keys():
|
||||
if not user[k] or len(user[k]) == 0 or (len(user[k]) == 1 and '' in user[k]):
|
||||
del user[k]
|
||||
|
||||
dn="uid=%s,%s,%s" % (ldap.dn.escape_dn_chars(user['uid']),
|
||||
user_container,self.basedn)
|
||||
entry = ipaserver.ipaldap.Entry(dn)
|
||||
@ -468,8 +507,7 @@ class IPAServer:
|
||||
del user['gn']
|
||||
|
||||
# some required objectclasses
|
||||
entry.setValues('objectClass', 'top', 'person', 'organizationalPerson',
|
||||
'inetOrgPerson', 'inetUser', 'posixAccount', 'krbPrincipalAux', 'radiusprofile')
|
||||
entry.setValues('objectClass', (config.get('ipauserobjectclasses')))
|
||||
|
||||
# fill in our new entry with everything sent by the user
|
||||
for u in user:
|
||||
@ -477,8 +515,16 @@ class IPAServer:
|
||||
|
||||
conn = self.getConnection(opts)
|
||||
try:
|
||||
res = conn.addEntry(entry)
|
||||
self.add_user_to_group(user.get('uid'), group_dn, opts)
|
||||
try:
|
||||
res = conn.addEntry(entry)
|
||||
except TypeError, e:
|
||||
raise ipaerror.gen_exception(ipaerror.LDAP_DATABASE_ERROR, "There is a problem with one of the data types.")
|
||||
except Exception, e:
|
||||
raise ipaerror.gen_exception(ipaerror.LDAP_DATABASE_ERROR, e)
|
||||
try:
|
||||
self.add_user_to_group(user.get('uid'), group_dn, opts)
|
||||
except Exception, e:
|
||||
raise ipaerror.gen_exception(ipaerror.LDAP_DATABASE_ERROR, "The user was created but adding to group %s failed" % group_dn)
|
||||
finally:
|
||||
self.releaseConnection(conn)
|
||||
return res
|
||||
@ -867,6 +913,12 @@ class IPAServer:
|
||||
finally:
|
||||
self.releaseConnection(conn)
|
||||
|
||||
# Get our configuration
|
||||
config = self.get_ipa_config(opts)
|
||||
|
||||
# Make sure we have the latest object classes
|
||||
newentry['objectclass'] = uniq_list(newentry.get('objectclass') + config.get('ipauserobjectclasses'))
|
||||
|
||||
try:
|
||||
rv = self.update_entry(oldentry, newentry, opts)
|
||||
return rv
|
||||
@ -1026,13 +1078,15 @@ class IPAServer:
|
||||
if self.__is_group_unique(group['cn'], opts) == 0:
|
||||
raise ipaerror.gen_exception(ipaerror.LDAP_DUPLICATE)
|
||||
|
||||
# Get our configuration
|
||||
config = self.get_ipa_config(opts)
|
||||
|
||||
dn="cn=%s,%s,%s" % (ldap.dn.escape_dn_chars(group['cn']),
|
||||
group_container,self.basedn)
|
||||
entry = ipaserver.ipaldap.Entry(dn)
|
||||
|
||||
# some required objectclasses
|
||||
entry.setValues('objectClass', 'top', 'groupofnames', 'posixGroup',
|
||||
'inetUser')
|
||||
entry.setValues('objectClass', (config.get('ipagroupobjectclasses')))
|
||||
|
||||
# No need to explicitly set gidNumber. The dna_plugin will do this
|
||||
# for us if the value isn't provided by the user.
|
||||
@ -1357,23 +1411,32 @@ class IPAServer:
|
||||
try:
|
||||
res = conn.updateRDN(oldentry.get('dn'), "cn=" + newcn[0])
|
||||
newdn = oldentry.get('dn')
|
||||
newcn = newentry.get('cn')
|
||||
if isinstance(newcn, str):
|
||||
newcn = [newcn]
|
||||
|
||||
# Ick. Need to find the exact cn used in the old DN so we'll
|
||||
# walk the list of cns and skip the obviously bad ones:
|
||||
for c in oldentry.get('dn').split("cn="):
|
||||
if c and c != "groups" and not c.startswith("accounts"):
|
||||
newdn = newdn.replace("cn=%s" % c, "uid=%s" % newentry.get('cn')[0])
|
||||
newdn = newdn.replace("cn=%s" % c, "cn=%s," % newcn[0])
|
||||
break
|
||||
|
||||
# Now fix up the dns and cns so they aren't seen as having
|
||||
# changed.
|
||||
oldentry['dn'] = newdn
|
||||
newentry['dn'] = newdn
|
||||
oldentry['cn'] = newentry['cn']
|
||||
oldentry['cn'] = newentry.get('cn')
|
||||
newrdn = 1
|
||||
finally:
|
||||
self.releaseConnection(conn)
|
||||
|
||||
# Get our configuration
|
||||
config = self.get_ipa_config(opts)
|
||||
|
||||
# Make sure we have the latest object classes
|
||||
newentry['objectclass'] = uniq_list(newentry.get('objectclass') + config.get('ipagroupobjectclasses'))
|
||||
|
||||
try:
|
||||
rv = self.update_entry(oldentry, newentry, opts)
|
||||
return rv
|
||||
@ -1527,7 +1590,76 @@ 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
|
||||
|
||||
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"""
|
||||
@ -1592,6 +1724,19 @@ class IPAServer:
|
||||
except:
|
||||
raise
|
||||
|
||||
# Run through the list of User and Group object classes to make
|
||||
# sure they are all valid. This doesn't handle dependencies but it
|
||||
# will at least catch typos.
|
||||
classes = self.__get_objectclasses(opts)
|
||||
oc = newconfig['ipauserobjectclasses']
|
||||
for i in range(len(oc)):
|
||||
if not oc[i].lower() in classes:
|
||||
raise ipaerror.gen_exception(ipaerror.CONFIG_INVALID_OC)
|
||||
oc = newconfig['ipagroupobjectclasses']
|
||||
for i in range(len(oc)):
|
||||
if not oc[i].lower() in classes:
|
||||
raise ipaerror.gen_exception(ipaerror.CONFIG_INVALID_OC)
|
||||
|
||||
return self.update_entry(oldconfig, newconfig, opts)
|
||||
|
||||
def get_password_policy(self, opts=None):
|
||||
@ -1654,3 +1799,8 @@ def ldap_search_escape(match):
|
||||
return r'\00'
|
||||
else:
|
||||
return value
|
||||
|
||||
def uniq_list(x):
|
||||
"""Return a unique list, preserving order and ignoring case"""
|
||||
set = {}
|
||||
return [set.setdefault(e,e) for e in x if e.lower() not in set]
|
||||
|
@ -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.register_function(f.get_radius_client_by_ip_addr)
|
||||
h.register_function(f.add_radius_client)
|
||||
|
Loading…
Reference in New Issue
Block a user