mirror of
https://salsa.debian.org/freeipa-team/freeipa.git
synced 2025-02-25 18:55:28 -06:00
Merging with upstream
This commit is contained in:
commit
873bbbd2de
@ -7,6 +7,10 @@ install:
|
|||||||
install -m 755 ipa-finduser $(SBINDIR)
|
install -m 755 ipa-finduser $(SBINDIR)
|
||||||
install -m 755 ipa-usermod $(SBINDIR)
|
install -m 755 ipa-usermod $(SBINDIR)
|
||||||
install -m 755 ipa-deluser $(SBINDIR)
|
install -m 755 ipa-deluser $(SBINDIR)
|
||||||
|
install -m 755 ipa-addgroup $(SBINDIR)
|
||||||
|
install -m 755 ipa-delgroup $(SBINDIR)
|
||||||
|
install -m 755 ipa-findgroup $(SBINDIR)
|
||||||
|
install -m 755 ipa-groupmod $(SBINDIR)
|
||||||
|
|
||||||
clean:
|
clean:
|
||||||
rm -f *~ *.pyc
|
rm -f *~ *.pyc
|
||||||
|
@ -50,7 +50,9 @@ def main():
|
|||||||
client = ipaclient.IPAClient()
|
client = ipaclient.IPAClient()
|
||||||
users = client.find_users(args[1], sattrs=['dn','uid','cn','homeDirectory'])
|
users = client.find_users(args[1], sattrs=['dn','uid','cn','homeDirectory'])
|
||||||
|
|
||||||
if len(users) == 0:
|
counter = users[0]
|
||||||
|
users = users[1:]
|
||||||
|
if counter == 0:
|
||||||
print "No entries found for", args[1]
|
print "No entries found for", args[1]
|
||||||
return 0
|
return 0
|
||||||
|
|
||||||
|
@ -59,6 +59,9 @@ def main():
|
|||||||
except ipa.ipaerror.IPAError, e:
|
except ipa.ipaerror.IPAError, e:
|
||||||
print "%s" % e.message
|
print "%s" % e.message
|
||||||
return 1
|
return 1
|
||||||
|
except kerberos.GSSError, e:
|
||||||
|
print "Could not initialize GSSAPI: %s/%s" % (e[0][0][0], e[0][1][0])
|
||||||
|
return 1
|
||||||
|
|
||||||
if options.gecos:
|
if options.gecos:
|
||||||
user.setValue('gecos', options.gecos)
|
user.setValue('gecos', options.gecos)
|
||||||
@ -79,6 +82,9 @@ def main():
|
|||||||
except xmlrpclib.ProtocolError, e:
|
except xmlrpclib.ProtocolError, e:
|
||||||
print "Unable to connect to IPA server: %s" % (e.errmsg)
|
print "Unable to connect to IPA server: %s" % (e.errmsg)
|
||||||
return 1
|
return 1
|
||||||
|
except ipa.ipaerror.IPAError, e:
|
||||||
|
print "%s" % (e.message)
|
||||||
|
return 1
|
||||||
|
|
||||||
return 0
|
return 0
|
||||||
|
|
||||||
|
@ -30,6 +30,7 @@ import stat
|
|||||||
from string import lower
|
from string import lower
|
||||||
import re
|
import re
|
||||||
import xmlrpclib
|
import xmlrpclib
|
||||||
|
import datetime
|
||||||
|
|
||||||
def realm_to_suffix(realm_name):
|
def realm_to_suffix(realm_name):
|
||||||
s = realm_name.split(".")
|
s = realm_name.split(".")
|
||||||
@ -233,3 +234,100 @@ def unwrap_binary_data(data):
|
|||||||
else:
|
else:
|
||||||
return data
|
return data
|
||||||
|
|
||||||
|
class GeneralizedTimeZone(datetime.tzinfo):
|
||||||
|
"""This class is a basic timezone wrapper for the offset specified
|
||||||
|
in a Generalized Time. It is dst-ignorant."""
|
||||||
|
def __init__(self,offsetstr="Z"):
|
||||||
|
super(GeneralizedTimeZone, self).__init__()
|
||||||
|
|
||||||
|
self.name = offsetstr
|
||||||
|
self.houroffset = 0
|
||||||
|
self.minoffset = 0
|
||||||
|
|
||||||
|
if offsetstr == "Z":
|
||||||
|
self.houroffset = 0
|
||||||
|
self.minoffset = 0
|
||||||
|
else:
|
||||||
|
if (len(offsetstr) >= 3) and re.match(r'[-+]\d\d', offsetstr):
|
||||||
|
self.houroffset = int(offsetstr[0:3])
|
||||||
|
offsetstr = offsetstr[3:]
|
||||||
|
if (len(offsetstr) >= 2) and re.match(r'\d\d', offsetstr):
|
||||||
|
self.minoffset = int(offsetstr[0:2])
|
||||||
|
offsetstr = offsetstr[2:]
|
||||||
|
if len(offsetstr) > 0:
|
||||||
|
raise ValueError()
|
||||||
|
if self.houroffset < 0:
|
||||||
|
self.minoffset *= -1
|
||||||
|
|
||||||
|
def utcoffset(self, dt):
|
||||||
|
return datetime.timedelta(hours=self.houroffset, minutes=self.minoffset)
|
||||||
|
|
||||||
|
def dst(self, dt):
|
||||||
|
return datetime.timedelta(0)
|
||||||
|
|
||||||
|
def tzname(self, dt):
|
||||||
|
return self.name
|
||||||
|
|
||||||
|
|
||||||
|
def parse_generalized_time(timestr):
|
||||||
|
"""Parses are Generalized Time string (as specified in X.680),
|
||||||
|
returning a datetime object. Generalized Times are stored inside
|
||||||
|
the krbPasswordExpiration attribute in LDAP.
|
||||||
|
|
||||||
|
This method doesn't attempt to be perfect wrt timezones. If python
|
||||||
|
can't be bothered to implement them, how can we..."""
|
||||||
|
|
||||||
|
if len(timestr) < 8:
|
||||||
|
return None
|
||||||
|
try:
|
||||||
|
date = timestr[:8]
|
||||||
|
time = timestr[8:]
|
||||||
|
|
||||||
|
year = int(date[:4])
|
||||||
|
month = int(date[4:6])
|
||||||
|
day = int(date[6:8])
|
||||||
|
|
||||||
|
hour = min = sec = msec = 0
|
||||||
|
tzone = None
|
||||||
|
|
||||||
|
if (len(time) >= 2) and re.match(r'\d', time[0]):
|
||||||
|
hour = int(time[:2])
|
||||||
|
time = time[2:]
|
||||||
|
if len(time) >= 2 and (time[0] == "," or time[0] == "."):
|
||||||
|
hour_fraction = "."
|
||||||
|
time = time[1:]
|
||||||
|
while (len(time) > 0) and re.match(r'\d', time[0]):
|
||||||
|
hour_fraction += time[0]
|
||||||
|
time = time[1:]
|
||||||
|
total_secs = int(float(hour_fraction) * 3600)
|
||||||
|
min, sec = divmod(total_secs, 60)
|
||||||
|
|
||||||
|
if (len(time) >= 2) and re.match(r'\d', time[0]):
|
||||||
|
min = int(time[:2])
|
||||||
|
time = time[2:]
|
||||||
|
if len(time) >= 2 and (time[0] == "," or time[0] == "."):
|
||||||
|
min_fraction = "."
|
||||||
|
time = time[1:]
|
||||||
|
while (len(time) > 0) and re.match(r'\d', time[0]):
|
||||||
|
min_fraction += time[0]
|
||||||
|
time = time[1:]
|
||||||
|
sec = int(float(min_fraction) * 60)
|
||||||
|
|
||||||
|
if (len(time) >= 2) and re.match(r'\d', time[0]):
|
||||||
|
sec = int(time[:2])
|
||||||
|
time = time[2:]
|
||||||
|
if len(time) >= 2 and (time[0] == "," or time[0] == "."):
|
||||||
|
sec_fraction = "."
|
||||||
|
time = time[1:]
|
||||||
|
while (len(time) > 0) and re.match(r'\d', time[0]):
|
||||||
|
sec_fraction += time[0]
|
||||||
|
time = time[1:]
|
||||||
|
msec = int(float(sec_fraction) * 1000000)
|
||||||
|
|
||||||
|
if (len(time) > 0):
|
||||||
|
tzone = GeneralizedTimeZone(time)
|
||||||
|
|
||||||
|
return datetime.datetime(year, month, day, hour, min, sec, msec, tzone)
|
||||||
|
|
||||||
|
except ValueError:
|
||||||
|
return None
|
||||||
|
@ -21,6 +21,7 @@ import sys
|
|||||||
sys.path.insert(0, ".")
|
sys.path.insert(0, ".")
|
||||||
|
|
||||||
import unittest
|
import unittest
|
||||||
|
import datetime
|
||||||
|
|
||||||
import ipautil
|
import ipautil
|
||||||
|
|
||||||
@ -207,6 +208,102 @@ class TestCIDict(unittest.TestCase):
|
|||||||
self.assert_(item in items)
|
self.assert_(item in items)
|
||||||
items.discard(item)
|
items.discard(item)
|
||||||
|
|
||||||
|
class TestTimeParser(unittest.TestCase):
|
||||||
|
def setUp(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def tearDown(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def testSimple(self):
|
||||||
|
timestr = "20070803"
|
||||||
|
|
||||||
|
time = ipautil.parse_generalized_time(timestr)
|
||||||
|
self.assertEqual(2007, time.year)
|
||||||
|
self.assertEqual(8, time.month)
|
||||||
|
self.assertEqual(3, time.day)
|
||||||
|
self.assertEqual(0, time.hour)
|
||||||
|
self.assertEqual(0, time.minute)
|
||||||
|
self.assertEqual(0, time.second)
|
||||||
|
|
||||||
|
def testHourMinSec(self):
|
||||||
|
timestr = "20051213141205"
|
||||||
|
|
||||||
|
time = ipautil.parse_generalized_time(timestr)
|
||||||
|
self.assertEqual(2005, time.year)
|
||||||
|
self.assertEqual(12, time.month)
|
||||||
|
self.assertEqual(13, time.day)
|
||||||
|
self.assertEqual(14, time.hour)
|
||||||
|
self.assertEqual(12, time.minute)
|
||||||
|
self.assertEqual(5, time.second)
|
||||||
|
|
||||||
|
def testFractions(self):
|
||||||
|
timestr = "2003092208.5"
|
||||||
|
|
||||||
|
time = ipautil.parse_generalized_time(timestr)
|
||||||
|
self.assertEqual(2003, time.year)
|
||||||
|
self.assertEqual(9, time.month)
|
||||||
|
self.assertEqual(22, time.day)
|
||||||
|
self.assertEqual(8, time.hour)
|
||||||
|
self.assertEqual(30, time.minute)
|
||||||
|
self.assertEqual(0, time.second)
|
||||||
|
|
||||||
|
timestr = "199203301544,25"
|
||||||
|
|
||||||
|
time = ipautil.parse_generalized_time(timestr)
|
||||||
|
self.assertEqual(1992, time.year)
|
||||||
|
self.assertEqual(3, time.month)
|
||||||
|
self.assertEqual(30, time.day)
|
||||||
|
self.assertEqual(15, time.hour)
|
||||||
|
self.assertEqual(44, time.minute)
|
||||||
|
self.assertEqual(15, time.second)
|
||||||
|
|
||||||
|
timestr = "20060401185912,8"
|
||||||
|
|
||||||
|
time = ipautil.parse_generalized_time(timestr)
|
||||||
|
self.assertEqual(2006, time.year)
|
||||||
|
self.assertEqual(4, time.month)
|
||||||
|
self.assertEqual(1, time.day)
|
||||||
|
self.assertEqual(18, time.hour)
|
||||||
|
self.assertEqual(59, time.minute)
|
||||||
|
self.assertEqual(12, time.second)
|
||||||
|
self.assertEqual(800000, time.microsecond)
|
||||||
|
|
||||||
|
def testTimeZones(self):
|
||||||
|
timestr = "20051213141205Z"
|
||||||
|
|
||||||
|
time = ipautil.parse_generalized_time(timestr)
|
||||||
|
self.assertEqual(0, time.tzinfo.houroffset)
|
||||||
|
self.assertEqual(0, time.tzinfo.minoffset)
|
||||||
|
offset = time.tzinfo.utcoffset(None)
|
||||||
|
self.assertEqual(0, offset.seconds)
|
||||||
|
|
||||||
|
timestr = "20051213141205+0500"
|
||||||
|
|
||||||
|
time = ipautil.parse_generalized_time(timestr)
|
||||||
|
self.assertEqual(5, time.tzinfo.houroffset)
|
||||||
|
self.assertEqual(0, time.tzinfo.minoffset)
|
||||||
|
offset = time.tzinfo.utcoffset(None)
|
||||||
|
self.assertEqual(5 * 60 * 60, offset.seconds)
|
||||||
|
|
||||||
|
timestr = "20051213141205-0500"
|
||||||
|
|
||||||
|
time = ipautil.parse_generalized_time(timestr)
|
||||||
|
self.assertEqual(-5, time.tzinfo.houroffset)
|
||||||
|
self.assertEqual(0, time.tzinfo.minoffset)
|
||||||
|
# NOTE - the offset is always positive - it's minutes
|
||||||
|
# _east_ of UTC
|
||||||
|
offset = time.tzinfo.utcoffset(None)
|
||||||
|
self.assertEqual((24 - 5) * 60 * 60, offset.seconds)
|
||||||
|
|
||||||
|
timestr = "20051213141205-0930"
|
||||||
|
|
||||||
|
time = ipautil.parse_generalized_time(timestr)
|
||||||
|
self.assertEqual(-9, time.tzinfo.houroffset)
|
||||||
|
self.assertEqual(-30, time.tzinfo.minoffset)
|
||||||
|
offset = time.tzinfo.utcoffset(None)
|
||||||
|
self.assertEqual(((24 - 9) * 60 * 60) - (30 * 60), offset.seconds)
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
unittest.main()
|
unittest.main()
|
||||||
|
@ -50,6 +50,7 @@ rm -rf %{buildroot}
|
|||||||
|
|
||||||
%attr(755,root,root) %{plugin_dir}/libipa_pwd_extop.so
|
%attr(755,root,root) %{plugin_dir}/libipa_pwd_extop.so
|
||||||
%attr(755,root,root) %{plugin_dir}/libipa-memberof-plugin.so
|
%attr(755,root,root) %{plugin_dir}/libipa-memberof-plugin.so
|
||||||
|
%attr(755,root,root) %{plugin_dir}/libipa-dna-plugin.so
|
||||||
|
|
||||||
|
|
||||||
%changelog
|
%changelog
|
||||||
|
@ -50,6 +50,7 @@ rm -rf %{buildroot}
|
|||||||
|
|
||||||
%attr(755,root,root) %{plugin_dir}/libipa_pwd_extop.so
|
%attr(755,root,root) %{plugin_dir}/libipa_pwd_extop.so
|
||||||
%attr(755,root,root) %{plugin_dir}/libipa-memberof-plugin.so
|
%attr(755,root,root) %{plugin_dir}/libipa-memberof-plugin.so
|
||||||
|
%attr(755,root,root) %{plugin_dir}/libipa-dna-plugin.so
|
||||||
|
|
||||||
|
|
||||||
%changelog
|
%changelog
|
||||||
|
@ -17,6 +17,7 @@ import ipa.ipaclient
|
|||||||
import ipa.user
|
import ipa.user
|
||||||
import xmlrpclib
|
import xmlrpclib
|
||||||
import forms.user
|
import forms.user
|
||||||
|
from helpers import userhelper
|
||||||
from ipa import ipaerror
|
from ipa import ipaerror
|
||||||
|
|
||||||
ipa.config.init_config()
|
ipa.config.init_config()
|
||||||
@ -47,6 +48,14 @@ class Root(controllers.RootController):
|
|||||||
def index(self):
|
def index(self):
|
||||||
return dict()
|
return dict()
|
||||||
|
|
||||||
|
@expose()
|
||||||
|
def topsearch(self, **kw):
|
||||||
|
if kw.get('searchtype') == "Users":
|
||||||
|
return self.userlist(uid=kw.get('searchvalue'))
|
||||||
|
else:
|
||||||
|
return self.index()
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
########
|
########
|
||||||
# User #
|
# User #
|
||||||
@ -107,7 +116,7 @@ class Root(controllers.RootController):
|
|||||||
def userupdate(self, **kw):
|
def userupdate(self, **kw):
|
||||||
"""Updates an existing user"""
|
"""Updates an existing user"""
|
||||||
restrict_post()
|
restrict_post()
|
||||||
if kw.get('submit') == 'Cancel':
|
if kw.get('submit') == 'Cancel Edit':
|
||||||
turbogears.flash("Edit user cancelled")
|
turbogears.flash("Edit user cancelled")
|
||||||
raise turbogears.redirect('/usershow', uid=kw.get('uid'))
|
raise turbogears.redirect('/usershow', uid=kw.get('uid'))
|
||||||
|
|
||||||
@ -188,7 +197,7 @@ class Root(controllers.RootController):
|
|||||||
def userindex(self):
|
def userindex(self):
|
||||||
raise turbogears.redirect("/userlist")
|
raise turbogears.redirect("/userlist")
|
||||||
|
|
||||||
@expose()
|
# @expose()
|
||||||
def generate_password(self):
|
def generate_password(self):
|
||||||
password = ""
|
password = ""
|
||||||
generator = random.SystemRandom()
|
generator = random.SystemRandom()
|
||||||
@ -203,6 +212,9 @@ class Root(controllers.RootController):
|
|||||||
if (len(givenname) == 0) or (len(sn) == 0):
|
if (len(givenname) == 0) or (len(sn) == 0):
|
||||||
return ""
|
return ""
|
||||||
|
|
||||||
|
givenname = givenname.lower()
|
||||||
|
sn = sn.lower()
|
||||||
|
|
||||||
uid = givenname[0] + sn[:7]
|
uid = givenname[0] + sn[:7]
|
||||||
try:
|
try:
|
||||||
client.get_user_by_uid(uid)
|
client.get_user_by_uid(uid)
|
||||||
@ -244,6 +256,9 @@ class Root(controllers.RootController):
|
|||||||
if (len(givenname) == 0) or (len(sn) == 0):
|
if (len(givenname) == 0) or (len(sn) == 0):
|
||||||
return ""
|
return ""
|
||||||
|
|
||||||
|
givenname = givenname.lower()
|
||||||
|
sn = sn.lower()
|
||||||
|
|
||||||
# TODO - get from config
|
# TODO - get from config
|
||||||
domain = "freeipa.org"
|
domain = "freeipa.org"
|
||||||
|
|
||||||
|
@ -3,29 +3,41 @@ from turbogears import validators, widgets
|
|||||||
|
|
||||||
class UserFields():
|
class UserFields():
|
||||||
uid = widgets.TextField(name="uid", label="Login")
|
uid = widgets.TextField(name="uid", label="Login")
|
||||||
userpassword = widgets.TextField(name="userpassword", label="Password")
|
userpassword = widgets.PasswordField(name="userpassword", label="Password")
|
||||||
|
userpassword_confirm = widgets.PasswordField(name="userpassword_confirm",
|
||||||
|
label="Confirm Password")
|
||||||
uidnumber = widgets.TextField(name="uidnumber", label="UID")
|
uidnumber = widgets.TextField(name="uidnumber", label="UID")
|
||||||
gidnumber = widgets.TextField(name="gidnumber", label="GID")
|
gidnumber = widgets.TextField(name="gidnumber", label="GID")
|
||||||
givenname = widgets.TextField(name="givenname", label="First name")
|
givenname = widgets.TextField(name="givenname", label="First name")
|
||||||
sn = widgets.TextField(name="sn", label="Last name")
|
sn = widgets.TextField(name="sn", label="Last name")
|
||||||
mail = widgets.TextField(name="mail", label="E-mail address")
|
mail = widgets.TextField(name="mail", label="E-mail address")
|
||||||
telephonenumber = widgets.TextField(name="telephonenumber", label="Phone")
|
telephonenumber = widgets.TextField(name="telephonenumber", label="Phone")
|
||||||
nsAccountLock = widgets.CheckBox(name="nsAccountLock", label="Account Deactivated")
|
# nsAccountLock = widgets.CheckBox(name="nsAccountLock", label="Account Deactivated")
|
||||||
|
nsAccountLock = widgets.SingleSelectField(name="nsAccountLock",
|
||||||
uid.validator = validators.PlainText(not_empty=True)
|
label="Account Status",
|
||||||
userpassword.validator = validators.String(not_empty=True)
|
options = [("", "active"), ("true", "inactive")])
|
||||||
givenname.validator = validators.String(not_empty=True)
|
|
||||||
sn.validator = validators.String(not_empty=True)
|
|
||||||
mail.validator = validators.Email(not_empty=True)
|
|
||||||
# validators.PhoneNumber may be a bit too picky, requiring an area code
|
|
||||||
telephonenumber.validator = validators.PlainText(not_empty=True)
|
|
||||||
|
|
||||||
uid_hidden = widgets.HiddenField(name="uid")
|
uid_hidden = widgets.HiddenField(name="uid")
|
||||||
uidnumber_hidden = widgets.HiddenField(name="uidnumber")
|
uidnumber_hidden = widgets.HiddenField(name="uidnumber")
|
||||||
gidnumber_hidden = widgets.HiddenField(name="gidnumber")
|
gidnumber_hidden = widgets.HiddenField(name="gidnumber")
|
||||||
|
krbPasswordExpiration_hidden = widgets.HiddenField(name="krbPasswordExpiration")
|
||||||
|
|
||||||
user_orig = widgets.HiddenField(name="user_orig")
|
user_orig = widgets.HiddenField(name="user_orig")
|
||||||
|
|
||||||
|
class UserNewValidator(validators.Schema):
|
||||||
|
uid = validators.PlainText(not_empty=True)
|
||||||
|
userpassword = validators.String(not_empty=False)
|
||||||
|
userpassword_confirm = validators.String(not_empty=False)
|
||||||
|
givenname = validators.String(not_empty=True)
|
||||||
|
sn = validators.String(not_empty=True)
|
||||||
|
mail = validators.Email(not_empty=True)
|
||||||
|
# validators.PhoneNumber may be a bit too picky, requiring an area code
|
||||||
|
# telephonenumber = validators.PlainText(not_empty=False)
|
||||||
|
|
||||||
|
chained_validators = [
|
||||||
|
validators.FieldsMatch('userpassword', 'userpassword_confirm')
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
class UserNewForm(widgets.Form):
|
class UserNewForm(widgets.Form):
|
||||||
params = ['user']
|
params = ['user']
|
||||||
@ -34,6 +46,8 @@ class UserNewForm(widgets.Form):
|
|||||||
UserFields.uidnumber, UserFields.gidnumber,
|
UserFields.uidnumber, UserFields.gidnumber,
|
||||||
UserFields.sn, UserFields.mail]
|
UserFields.sn, UserFields.mail]
|
||||||
|
|
||||||
|
validator = UserNewValidator()
|
||||||
|
|
||||||
def __init__(self, *args, **kw):
|
def __init__(self, *args, **kw):
|
||||||
super(UserNewForm,self).__init__(*args, **kw)
|
super(UserNewForm,self).__init__(*args, **kw)
|
||||||
(self.template_c, self.template) = widgets.meta.load_kid_template("ipagui.templates.usernewform")
|
(self.template_c, self.template) = widgets.meta.load_kid_template("ipagui.templates.usernewform")
|
||||||
@ -46,6 +60,18 @@ class UserNewForm(widgets.Form):
|
|||||||
def has_foo(self):
|
def has_foo(self):
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
class UserEditValidator(validators.Schema):
|
||||||
|
userpassword = validators.String(not_empty=False)
|
||||||
|
userpassword_confirm = validators.String(not_empty=False)
|
||||||
|
givenname = validators.String(not_empty=True)
|
||||||
|
sn = validators.String(not_empty=True)
|
||||||
|
mail = validators.Email(not_empty=True)
|
||||||
|
# validators.PhoneNumber may be a bit too picky, requiring an area code
|
||||||
|
# telephonenumber = validators.PlainText(not_empty=False)
|
||||||
|
|
||||||
|
chained_validators = [
|
||||||
|
validators.FieldsMatch('userpassword', 'userpassword_confirm')
|
||||||
|
]
|
||||||
|
|
||||||
class UserEditForm(widgets.Form):
|
class UserEditForm(widgets.Form):
|
||||||
params = ['user']
|
params = ['user']
|
||||||
@ -53,8 +79,11 @@ class UserEditForm(widgets.Form):
|
|||||||
fields = [UserFields.givenname, UserFields.sn, UserFields.mail,
|
fields = [UserFields.givenname, UserFields.sn, UserFields.mail,
|
||||||
UserFields.uid_hidden, UserFields.user_orig,
|
UserFields.uid_hidden, UserFields.user_orig,
|
||||||
UserFields.uidnumber_hidden, UserFields.gidnumber_hidden,
|
UserFields.uidnumber_hidden, UserFields.gidnumber_hidden,
|
||||||
|
UserFields.krbPasswordExpiration_hidden,
|
||||||
]
|
]
|
||||||
|
|
||||||
|
validator = UserEditValidator()
|
||||||
|
|
||||||
def __init__(self, *args, **kw):
|
def __init__(self, *args, **kw):
|
||||||
super(UserEditForm,self).__init__(*args, **kw)
|
super(UserEditForm,self).__init__(*args, **kw)
|
||||||
(self.template_c, self.template) = widgets.meta.load_kid_template("ipagui.templates.usereditform")
|
(self.template_c, self.template) = widgets.meta.load_kid_template("ipagui.templates.usereditform")
|
||||||
|
1
ipa-server/ipa-gui/ipagui/helpers/__init__.py
Normal file
1
ipa-server/ipa-gui/ipagui/helpers/__init__.py
Normal file
@ -0,0 +1 @@
|
|||||||
|
# __init__.py
|
29
ipa-server/ipa-gui/ipagui/helpers/userhelper.py
Normal file
29
ipa-server/ipa-gui/ipagui/helpers/userhelper.py
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
import sys
|
||||||
|
import datetime
|
||||||
|
|
||||||
|
from ipa import ipautil
|
||||||
|
|
||||||
|
def password_expires_in(datestr):
|
||||||
|
"""Returns the number of days that password expires in. Returns a negative number
|
||||||
|
if the password is already expired."""
|
||||||
|
if (datestr == None) or (datestr == ""):
|
||||||
|
return sys.maxint
|
||||||
|
|
||||||
|
expdate = ipautil.parse_generalized_time(datestr)
|
||||||
|
if not expdate:
|
||||||
|
return sys.maxint
|
||||||
|
|
||||||
|
delta = expdate - datetime.datetime.now()
|
||||||
|
return delta.days
|
||||||
|
|
||||||
|
def password_is_expired(days):
|
||||||
|
return days < 0
|
||||||
|
|
||||||
|
def password_expires_soon(days):
|
||||||
|
return (not password_is_expired(days)) and (days < 7)
|
||||||
|
|
||||||
|
def account_status_display(status):
|
||||||
|
if status == "true":
|
||||||
|
return "inactive"
|
||||||
|
else:
|
||||||
|
return "active"
|
@ -17,6 +17,7 @@ body {
|
|||||||
background:#ccc; /* should be same as #sidebar */
|
background:#ccc; /* should be same as #sidebar */
|
||||||
margin:0 auto;
|
margin:0 auto;
|
||||||
width:100%;
|
width:100%;
|
||||||
|
clear:both;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -24,9 +25,26 @@ body {
|
|||||||
background:#fff;
|
background:#fff;
|
||||||
}
|
}
|
||||||
|
|
||||||
#header h1 {
|
#header #logo {
|
||||||
padding:5px;
|
float:left;
|
||||||
margin:0;
|
}
|
||||||
|
|
||||||
|
#header #headerinfo {
|
||||||
|
text-align:right;
|
||||||
|
padding-right:10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#header #headerinfo #login {
|
||||||
|
}
|
||||||
|
|
||||||
|
#header #headerinfo #topsearch {
|
||||||
|
padding-top: 15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.searchtext {
|
||||||
|
background-color:#E5F1F4;
|
||||||
|
border:1px solid #8E8E8E;
|
||||||
|
color:#444444;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -75,7 +93,11 @@ body {
|
|||||||
float:left;
|
float:left;
|
||||||
width:10%;
|
width:10%;
|
||||||
padding: 5px;
|
padding: 5px;
|
||||||
font-size: small;
|
font-size: medium;
|
||||||
|
}
|
||||||
|
|
||||||
|
#sidebar p {
|
||||||
|
line-height: 150%;
|
||||||
}
|
}
|
||||||
|
|
||||||
#sidebar h2 {
|
#sidebar h2 {
|
||||||
@ -140,6 +162,12 @@ body {
|
|||||||
font-weight: bolder;
|
font-weight: bolder;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.warning_message {
|
||||||
|
font-size: 120%;
|
||||||
|
color: #ee0000;
|
||||||
|
font-weight: bolder;
|
||||||
|
}
|
||||||
|
|
||||||
.fielderror {
|
.fielderror {
|
||||||
color: red;
|
color: red;
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
|
@ -24,35 +24,66 @@
|
|||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div id="page">
|
|
||||||
<div id="header">
|
<div id="header">
|
||||||
|
<div id="logo">
|
||||||
<a href="${tg.url('/')}"><img
|
<a href="${tg.url('/')}"><img
|
||||||
src="${tg.url('/static/images/logo.png')}"
|
src="${tg.url('/static/images/logo.png')}"
|
||||||
border="0"
|
border="0"
|
||||||
/></a>
|
/></a>
|
||||||
</div>
|
</div>
|
||||||
|
<div id="headerinfo">
|
||||||
|
<div id="login">
|
||||||
|
Logged in as: ace
|
||||||
|
</div>
|
||||||
|
<div id="topsearch">
|
||||||
|
<form action="${tg.url('/topsearch')}" method="post">
|
||||||
|
<select name="searchtype">
|
||||||
|
<option>Users</option>
|
||||||
|
<option>Groups</option>
|
||||||
|
</select>
|
||||||
|
<input class="searchtext" id="topsearchbox" type="text"
|
||||||
|
name="searchvalue"
|
||||||
|
value="Type search terms here."
|
||||||
|
onfocus="clearsearch()" />
|
||||||
|
<input type="submit" value="Search"/>
|
||||||
|
</form>
|
||||||
|
<script type="text/javascript">
|
||||||
|
function clearsearch() {
|
||||||
|
topsearchbox = document.getElementById('topsearchbox');
|
||||||
|
topsearchbox.onfocus = null;
|
||||||
|
topsearchbox.value = "";
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div id="page">
|
||||||
<div id="nav"><!--
|
<div id="nav"><!--
|
||||||
This used to have links. Keeping around in case we move them back...
|
This used to have links. Keeping around in case we move them back...
|
||||||
--></div>
|
--></div>
|
||||||
|
|
||||||
<div id="sidebar">
|
<div id="sidebar">
|
||||||
<h2>Tasks</h2>
|
<h2>Tasks</h2>
|
||||||
|
<p>
|
||||||
<a href="${tg.url('/usernew')}">Add Person</a><br/>
|
<a href="${tg.url('/usernew')}">Add Person</a><br/>
|
||||||
<a href="${tg.url('/userlist')}">Find People</a><br/>
|
<a href="${tg.url('/userlist')}">Find People</a><br/>
|
||||||
<br />
|
</p>
|
||||||
|
<p>
|
||||||
<a href="${tg.url('/groupindex')}">Add Group</a><br/>
|
<a href="${tg.url('/groupindex')}">Add Group</a><br/>
|
||||||
<a href="${tg.url('/groupindex')}">Find Groups</a><br/>
|
<a href="${tg.url('/groupindex')}">Find Groups</a><br/>
|
||||||
<br />
|
</p>
|
||||||
|
<p>
|
||||||
<a href="${tg.url('/')}">Manage Policy</a><br/>
|
<a href="${tg.url('/')}">Manage Policy</a><br/>
|
||||||
<a href="${tg.url('/')}">Self Service</a><br/>
|
<a href="${tg.url('/')}">Self Service</a><br/>
|
||||||
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div py:replace="[item.text]+item[:]"></div>
|
<div py:replace="[item.text]+item[:]"></div>
|
||||||
|
|
||||||
|
|
||||||
<div id="footer">
|
<div id="footer">
|
||||||
<a href="http://www.freeipa.com/">Powered by FreeIPA</a>
|
<a href="http://www.freeipa.com/" target="_blank">Powered by FreeIPA</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
@ -8,6 +8,24 @@
|
|||||||
<body>
|
<body>
|
||||||
<h2>Edit Person</h2>
|
<h2>Edit Person</h2>
|
||||||
|
|
||||||
|
<?python
|
||||||
|
from ipagui.helpers import userhelper
|
||||||
|
pw_expires_days = userhelper.password_expires_in(user.get("krbPasswordExpiration"))
|
||||||
|
pw_expires_soon = userhelper.password_expires_soon(pw_expires_days)
|
||||||
|
pw_is_expired = userhelper.password_is_expired(pw_expires_days)
|
||||||
|
if pw_expires_days != 1:
|
||||||
|
days_suffix = "s"
|
||||||
|
else:
|
||||||
|
days_suffix = ""
|
||||||
|
?>
|
||||||
|
|
||||||
|
<div py:if='pw_expires_soon' class="warning_message">
|
||||||
|
Password will expire in ${pw_expires_days} day${days_suffix}
|
||||||
|
</div>
|
||||||
|
<div py:if='pw_is_expired' class="warning_message">
|
||||||
|
Password has expired
|
||||||
|
</div>
|
||||||
|
|
||||||
${form.display(action="userupdate", value=user)}
|
${form.display(action="userupdate", value=user)}
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
@ -56,8 +56,9 @@
|
|||||||
<span py:replace="user.userpassword.display(value_for(user.userpassword))" />
|
<span py:replace="user.userpassword.display(value_for(user.userpassword))" />
|
||||||
<span py:if="tg.errors.get('userpassword')" class="fielderror"
|
<span py:if="tg.errors.get('userpassword')" class="fielderror"
|
||||||
py:content="tg.errors.get('userpassword')" />
|
py:content="tg.errors.get('userpassword')" />
|
||||||
<span id="password_text">********</span>
|
|
||||||
|
|
||||||
|
<!--
|
||||||
|
<span id="password_text">********</span>
|
||||||
<input id="genpassword_button" type="button" value="Generate Password"
|
<input id="genpassword_button" type="button" value="Generate Password"
|
||||||
disabled="true"
|
disabled="true"
|
||||||
onclick="new Ajax.Request('${tg.url('/generate_password')}',
|
onclick="new Ajax.Request('${tg.url('/generate_password')}',
|
||||||
@ -89,6 +90,20 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
-->
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
|
||||||
|
<tr>
|
||||||
|
<th valign="top">
|
||||||
|
<label class="fieldlabel" for="${user.userpassword_confirm.field_id}"
|
||||||
|
py:content="user.userpassword_confirm.label" />:
|
||||||
|
</th>
|
||||||
|
<td valign="top">
|
||||||
|
<span py:replace="user.userpassword_confirm.display(
|
||||||
|
value_for(user.userpassword_confirm))" />
|
||||||
|
<span py:if="tg.errors.get('userpassword_confirm')" class="fielderror"
|
||||||
|
py:content="tg.errors.get('userpassword_confirm')" />
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
|
||||||
@ -158,11 +173,13 @@
|
|||||||
<tr>
|
<tr>
|
||||||
<th>
|
<th>
|
||||||
<br />
|
<br />
|
||||||
<input type="submit" class="submitbutton" name="submit" value="Submit"/>
|
<input type="submit" class="submitbutton" name="submit"
|
||||||
|
value="Update Person"/>
|
||||||
</th>
|
</th>
|
||||||
<td>
|
<td>
|
||||||
<br />
|
<br />
|
||||||
<input type="submit" class="submitbutton" name="submit" value="Cancel" />
|
<input type="submit" class="submitbutton" name="submit"
|
||||||
|
value="Cancel Edit" />
|
||||||
</td>
|
</td>
|
||||||
<td></td>
|
<td></td>
|
||||||
</tr>
|
</tr>
|
||||||
|
@ -6,20 +6,18 @@
|
|||||||
<title>Find People</title>
|
<title>Find People</title>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<h2>Find People</h2>
|
|
||||||
<div id="search">
|
<div id="search">
|
||||||
<form action="${tg.url('/userlist')}" method="post">
|
<form action="${tg.url('/userlist')}" method="post">
|
||||||
Search:
|
|
||||||
<input id="uid" type="text" name="uid" value="${uid}" />
|
<input id="uid" type="text" name="uid" value="${uid}" />
|
||||||
<input type="submit" />
|
<input type="submit" value="Find People"/>
|
||||||
</form>
|
</form>
|
||||||
<script type="text/javascript">
|
<script type="text/javascript">
|
||||||
document.getElementById("uid").focus();
|
document.getElementById("uid").focus();
|
||||||
</script>
|
</script>
|
||||||
</div>
|
</div>
|
||||||
<div py:if='users != None'>
|
<div py:if='(users != None) and (len(users) > 0)'>
|
||||||
<h2>${len(users)} results returned:</h2>
|
<h2>${len(users)} results returned:</h2>
|
||||||
<table id="resultstable" py:if='len(users) > 0'>
|
<table id="resultstable">
|
||||||
<tr>
|
<tr>
|
||||||
<th>
|
<th>
|
||||||
<label class="fieldlabel" py:content="fields.uid.label" />
|
<label class="fieldlabel" py:content="fields.uid.label" />
|
||||||
@ -61,9 +59,9 @@
|
|||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
</table>
|
</table>
|
||||||
<div py:if='len(users) == 0'>
|
|
||||||
No results found.
|
|
||||||
</div>
|
</div>
|
||||||
|
<div py:if='(users != None) and (len(users) == 0)'>
|
||||||
|
<h2>No results found for "${uid}"</h2>
|
||||||
</div>
|
</div>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
@ -27,6 +27,9 @@
|
|||||||
<span py:if="tg.errors.get('sn')" class="fielderror"
|
<span py:if="tg.errors.get('sn')" class="fielderror"
|
||||||
py:content="tg.errors.get('sn')" />
|
py:content="tg.errors.get('sn')" />
|
||||||
<script type="text/javascript">
|
<script type="text/javascript">
|
||||||
|
var uid_suggest = ""
|
||||||
|
var mail_suggest = ""
|
||||||
|
|
||||||
function autofill(self) {
|
function autofill(self) {
|
||||||
givenname = document.getElementById('form_givenname');
|
givenname = document.getElementById('form_givenname');
|
||||||
sn = document.getElementById('form_sn');
|
sn = document.getElementById('form_sn');
|
||||||
@ -35,22 +38,24 @@
|
|||||||
}
|
}
|
||||||
uid = document.getElementById('form_uid');
|
uid = document.getElementById('form_uid');
|
||||||
mail = document.getElementById('form_mail');
|
mail = document.getElementById('form_mail');
|
||||||
if (uid.value == "") {
|
if ((uid.value == "") || (uid.value == uid_suggest)) {
|
||||||
new Ajax.Request('${tg.url('/suggest_uid')}', {
|
new Ajax.Request('${tg.url('/suggest_uid')}', {
|
||||||
method: 'get',
|
method: 'get',
|
||||||
parameters: {'givenname': givenname.value, 'sn': sn.value},
|
parameters: {'givenname': givenname.value, 'sn': sn.value},
|
||||||
onSuccess: function(transport) {
|
onSuccess: function(transport) {
|
||||||
uid.value = transport.responseText;
|
uid.value = transport.responseText;
|
||||||
|
uid_suggest = uid.value;
|
||||||
new Effect.Highlight(uid);
|
new Effect.Highlight(uid);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
if (mail.value == "") {
|
if ((mail.value == "") || (mail.value == mail_suggest)) {
|
||||||
new Ajax.Request('${tg.url('/suggest_email')}', {
|
new Ajax.Request('${tg.url('/suggest_email')}', {
|
||||||
method: 'get',
|
method: 'get',
|
||||||
parameters: {'givenname': givenname.value, 'sn': sn.value},
|
parameters: {'givenname': givenname.value, 'sn': sn.value},
|
||||||
onSuccess: function(transport) {
|
onSuccess: function(transport) {
|
||||||
mail.value = transport.responseText;
|
mail.value = transport.responseText;
|
||||||
|
mail_suggest = mail.value;
|
||||||
new Effect.Highlight(mail);
|
new Effect.Highlight(mail);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@ -87,6 +92,7 @@
|
|||||||
<span py:if="tg.errors.get('userpassword')" class="fielderror"
|
<span py:if="tg.errors.get('userpassword')" class="fielderror"
|
||||||
py:content="tg.errors.get('userpassword')" />
|
py:content="tg.errors.get('userpassword')" />
|
||||||
|
|
||||||
|
<!--
|
||||||
<input type="button" value="Generate Password"
|
<input type="button" value="Generate Password"
|
||||||
onclick="new Ajax.Request('${tg.url('/generate_password')}',
|
onclick="new Ajax.Request('${tg.url('/generate_password')}',
|
||||||
{
|
{
|
||||||
@ -96,6 +102,20 @@
|
|||||||
transport.responseText;
|
transport.responseText;
|
||||||
}
|
}
|
||||||
});" />
|
});" />
|
||||||
|
-->
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
|
||||||
|
<tr>
|
||||||
|
<th>
|
||||||
|
<label class="fieldlabel" for="${user.userpassword_confirm.field_id}"
|
||||||
|
py:content="user.userpassword_confirm.label" />:
|
||||||
|
</th>
|
||||||
|
<td>
|
||||||
|
<span py:replace="user.userpassword_confirm.display(
|
||||||
|
value_for(user.userpassword_confirm))" />
|
||||||
|
<span py:if="tg.errors.get('userpassword_confirm')" class="fielderror"
|
||||||
|
py:content="tg.errors.get('userpassword_confirm')" />
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
|
||||||
@ -167,15 +187,11 @@
|
|||||||
|
|
||||||
<table class="formtable" cellpadding="2" cellspacing="0" border="0">
|
<table class="formtable" cellpadding="2" cellspacing="0" border="0">
|
||||||
<tr>
|
<tr>
|
||||||
<th>
|
<th></th>
|
||||||
<br />
|
|
||||||
<input type="submit" class="submitbutton" name="submit" value="Submit"/>
|
|
||||||
</th>
|
|
||||||
<td>
|
<td>
|
||||||
<br />
|
<br />
|
||||||
<input type="submit" class="submitbutton" name="submit" value="Cancel" />
|
<input type="submit" class="submitbutton" name="submit" value="Add Person"/>
|
||||||
</td>
|
</td>
|
||||||
<td></td>
|
|
||||||
</tr>
|
</tr>
|
||||||
</table>
|
</table>
|
||||||
|
|
||||||
|
@ -8,6 +8,24 @@
|
|||||||
<body>
|
<body>
|
||||||
<h2>View Person</h2>
|
<h2>View Person</h2>
|
||||||
|
|
||||||
|
<?python
|
||||||
|
from ipagui.helpers import userhelper
|
||||||
|
pw_expires_days = userhelper.password_expires_in(user.get("krbPasswordExpiration"))
|
||||||
|
pw_expires_soon = userhelper.password_expires_soon(pw_expires_days)
|
||||||
|
pw_is_expired = userhelper.password_is_expired(pw_expires_days)
|
||||||
|
if pw_expires_days != 1:
|
||||||
|
days_suffix = "s"
|
||||||
|
else:
|
||||||
|
days_suffix = ""
|
||||||
|
?>
|
||||||
|
|
||||||
|
<div py:if='pw_expires_soon' class="warning_message">
|
||||||
|
Password will expire in ${pw_expires_days} day${days_suffix}
|
||||||
|
</div>
|
||||||
|
<div py:if='pw_is_expired' class="warning_message">
|
||||||
|
Password has expired
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="formsection">Identity Details</div>
|
<div class="formsection">Identity Details</div>
|
||||||
<table class="formtable" cellpadding="2" cellspacing="0" border="0">
|
<table class="formtable" cellpadding="2" cellspacing="0" border="0">
|
||||||
<tr>
|
<tr>
|
||||||
@ -68,7 +86,7 @@
|
|||||||
<th>
|
<th>
|
||||||
<label class="fieldlabel" py:content="fields.nsAccountLock.label" />:
|
<label class="fieldlabel" py:content="fields.nsAccountLock.label" />:
|
||||||
</th>
|
</th>
|
||||||
<td>${user.get("nsAccountLock")}</td>
|
<td>${userhelper.account_status_display(user.get("nsAccountLock"))}</td>
|
||||||
</tr>
|
</tr>
|
||||||
</table>
|
</table>
|
||||||
|
|
||||||
|
@ -2,7 +2,8 @@
|
|||||||
Required packages:
|
Required packages:
|
||||||
|
|
||||||
krb5-server
|
krb5-server
|
||||||
fedora-ds-base / fedora-ds-base-devel
|
fedora-ds-base
|
||||||
|
fedora-ds-base-devel
|
||||||
openldap-clients
|
openldap-clients
|
||||||
krb5-server-ldap
|
krb5-server-ldap
|
||||||
cyrus-sasl-gssapi
|
cyrus-sasl-gssapi
|
||||||
@ -13,12 +14,40 @@ openssl-devel
|
|||||||
|
|
||||||
Installation example:
|
Installation example:
|
||||||
|
|
||||||
TEMPORARY: (until fedora ds scripts are fixed)
|
TEMPORARY: until bug https://bugzilla.redhat.com/show_bug.cgi?id=248169 is
|
||||||
please use the fedora-ds.init.patch under share/ to patch your init scripts before
|
fixed.
|
||||||
running ipa-server-install
|
|
||||||
|
|
||||||
cd ipa-install
|
Please apply the fedora-ds.init.patch in freeipa/ipa-server/ipa-install/share/
|
||||||
make install
|
to patch your init scripts before running ipa-server-install. This tells
|
||||||
cd ..
|
FDS where to find its kerberos keytab.
|
||||||
/usr/sbin/ipa-server-install -u fds -r FREEIPA.ORG -p freeipa -m ipafree
|
|
||||||
|
|
||||||
|
Things done as root are denoted by #. Things done as a unix user are denoted
|
||||||
|
by %.
|
||||||
|
|
||||||
|
# cd freeipa
|
||||||
|
# patch -p0 < ipa-server/ipa-install/share/fedora-ds.init.patch
|
||||||
|
|
||||||
|
Now to do the installation.
|
||||||
|
|
||||||
|
# cd freeipa
|
||||||
|
# make install
|
||||||
|
# /usr/sbin/ipa-server-install -u fds -r FREEIPA.ORG -p freeipa -P ipafree
|
||||||
|
|
||||||
|
For more verbose output add the -d flag
|
||||||
|
|
||||||
|
You have a basic working system with one super administrator (named admin).
|
||||||
|
|
||||||
|
To create another administrative user:
|
||||||
|
|
||||||
|
% kinit admin@FREEIPA.ORG
|
||||||
|
% /usr/sbin/ipa-adduser -f Test -l User test
|
||||||
|
% ldappasswd -Y GSSAPI -h localhost -s password uid=test,cn=users,cn=accounts,dc=freeipa,dc=org
|
||||||
|
% /usr/sbin/ipa-groupmod -a test admins
|
||||||
|
|
||||||
|
An admin user is just a regular user in the group admin.
|
||||||
|
|
||||||
|
Now you can destroy the old ticket and log in as test:
|
||||||
|
|
||||||
|
% kdestroy
|
||||||
|
% kinit test@FREEIPA.ORG
|
||||||
|
% /usr/sbin/ipa-finduser test
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
SUBDIRS=ipa-pwd-extop ipa-memberof
|
SUBDIRS=ipa-pwd-extop ipa-memberof dna
|
||||||
|
|
||||||
all:
|
all:
|
||||||
@for subdir in $(SUBDIRS); do \
|
@for subdir in $(SUBDIRS); do \
|
||||||
|
34
ipa-server/ipa-slapi-plugins/dna/Makefile
Normal file
34
ipa-server/ipa-slapi-plugins/dna/Makefile
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
DIRSRV ?= dirsrv
|
||||||
|
PREFIX ?= $(DESTDIR)/usr
|
||||||
|
LIBDIR ?= $(PREFIX)/lib/$(DIRSRV)/plugins
|
||||||
|
LIB64DIR ?= $(PREFIX)/lib64/$(DIRSRV)/plugins
|
||||||
|
SHAREDIR = $(DESTDIR)/usr/share/ipa
|
||||||
|
|
||||||
|
SONAME = libipa-dna-plugin.so
|
||||||
|
LDFLAGS += -llber
|
||||||
|
CFLAGS ?= -g -Wall -Wshadow
|
||||||
|
CFLAGS += -I/usr/include/$(DIRSRV) -I/usr/include/nss3 -I/usr/include/mozldap -I/usr/include/nspr4 -fPIC -DPIC
|
||||||
|
|
||||||
|
OBJS = $(patsubst %.c,%.o,$(wildcard *.c))
|
||||||
|
|
||||||
|
all: $(OBJS)
|
||||||
|
$(CC) $(LDFLAGS) $(OBJS) -Wl,-soname -Wl,$(SONAME) -shared -o $(SONAME)
|
||||||
|
|
||||||
|
%.o: %.c
|
||||||
|
$(CC) $(CFLAGS) -c -o $@ $<
|
||||||
|
|
||||||
|
install:
|
||||||
|
-mkdir -p $(LIBDIR)
|
||||||
|
if [ -e $(PREFIX)/lib/$(DIRSRV) ]; then \
|
||||||
|
install -m 644 $(SONAME) $(LIBDIR); \
|
||||||
|
else \
|
||||||
|
install -m 644 $(SONAME) $(LIB64DIR); \
|
||||||
|
fi
|
||||||
|
install -m 644 *.ldif $(SHAREDIR)
|
||||||
|
|
||||||
|
clean:
|
||||||
|
rm -f *.o
|
||||||
|
rm -f $(SONAME)
|
||||||
|
rm -f *~
|
||||||
|
|
||||||
|
|
13
ipa-server/ipa-slapi-plugins/dna/dna-conf.ldif
Normal file
13
ipa-server/ipa-slapi-plugins/dna/dna-conf.ldif
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
dn: cn=ipa-dna,cn=plugins,cn=config
|
||||||
|
objectclass: top
|
||||||
|
objectclass: nsSlapdPlugin
|
||||||
|
objectclass: extensibleObject
|
||||||
|
cn: ipa-dna
|
||||||
|
nsslapd-pluginpath: libipa-dna-plugin
|
||||||
|
nsslapd-plugininitfunc: dna_init
|
||||||
|
nsslapd-plugintype: postoperation
|
||||||
|
nsslapd-pluginenabled: on
|
||||||
|
nsslapd-pluginid: ipa-dna
|
||||||
|
nsslapd-pluginversion: 1.0
|
||||||
|
nsslapd-pluginvendor: Red Hat
|
||||||
|
nsslapd-plugindescription: Distributed numeric assignment plugin
|
1174
ipa-server/ipa-slapi-plugins/dna/dna.c
Normal file
1174
ipa-server/ipa-slapi-plugins/dna/dna.c
Normal file
File diff suppressed because it is too large
Load Diff
@ -24,7 +24,7 @@ import tempfile
|
|||||||
import shutil
|
import shutil
|
||||||
import logging
|
import logging
|
||||||
import pwd
|
import pwd
|
||||||
from util import *
|
from ipa.ipautil import *
|
||||||
|
|
||||||
|
|
||||||
SHARE_DIR = "/usr/share/ipa/"
|
SHARE_DIR = "/usr/share/ipa/"
|
||||||
|
@ -35,13 +35,14 @@ import cStringIO
|
|||||||
import time
|
import time
|
||||||
import operator
|
import operator
|
||||||
import struct
|
import struct
|
||||||
|
import ldap.sasl
|
||||||
from ldap.controls import LDAPControl,DecodeControlTuples,EncodeControlTuples
|
from ldap.controls import LDAPControl,DecodeControlTuples,EncodeControlTuples
|
||||||
from ldap.modlist import modifyModlist
|
|
||||||
|
|
||||||
from ldap.ldapobject import SimpleLDAPObject
|
from ldap.ldapobject import SimpleLDAPObject
|
||||||
|
|
||||||
from ipa import ipaerror, ipautil
|
from ipa import ipaerror, ipautil
|
||||||
|
|
||||||
|
# Global variable to define SASL auth
|
||||||
|
sasl_auth = ldap.sasl.sasl({},'GSSAPI')
|
||||||
|
|
||||||
class Entry:
|
class Entry:
|
||||||
"""This class represents an LDAP Entry object. An LDAP entry consists of a DN
|
"""This class represents an LDAP Entry object. An LDAP entry consists of a DN
|
||||||
and a list of attributes. Each attribute consists of a name and a list of
|
and a list of attributes. Each attribute consists of a name and a list of
|
||||||
@ -196,22 +197,34 @@ class IPAdmin(SimpleLDAPObject):
|
|||||||
raise ipaerror.gen_exception(ipaerror.LDAP_DATABASE_ERROR, None, e)
|
raise ipaerror.gen_exception(ipaerror.LDAP_DATABASE_ERROR, None, e)
|
||||||
|
|
||||||
def __localinit__(self):
|
def __localinit__(self):
|
||||||
|
"""If a CA certificate is provided then it is assumed that we are
|
||||||
|
doing SSL client authentication with proxy auth.
|
||||||
|
|
||||||
|
If a CA certificate is not present then it is assumed that we are
|
||||||
|
using a forwarded kerberos ticket for SASL auth. SASL provides
|
||||||
|
its own encryption.
|
||||||
|
"""
|
||||||
|
if self.cacert is not None:
|
||||||
SimpleLDAPObject.__init__(self,'ldaps://%s:%d' % (self.host,self.port))
|
SimpleLDAPObject.__init__(self,'ldaps://%s:%d' % (self.host,self.port))
|
||||||
|
else:
|
||||||
|
SimpleLDAPObject.__init__(self,'ldap://%s:%d' % (self.host,self.port))
|
||||||
|
|
||||||
def __init__(self,host,port,cacert,bindcert,bindkey,proxydn=None):
|
def __init__(self,host,port,cacert,bindcert,bindkey,proxydn=None):
|
||||||
"""We just set our instance variables and wrap the methods - the real work is
|
"""We just set our instance variables and wrap the methods - the real
|
||||||
done in __localinit__ and __initPart2 - these are separated out this way so
|
work is done in __localinit__ and __initPart2 - these are separated
|
||||||
that we can call them from places other than instance creation e.g. when
|
out this way so that we can call them from places other than
|
||||||
using the start command, we just need to reconnect, not create a new instance"""
|
instance creation e.g. when we just need to reconnect, not create a
|
||||||
|
new instance"""
|
||||||
# ldap.set_option(ldap.OPT_DEBUG_LEVEL,255)
|
# ldap.set_option(ldap.OPT_DEBUG_LEVEL,255)
|
||||||
|
if cacert is not None:
|
||||||
ldap.set_option(ldap.OPT_X_TLS_CACERTFILE,cacert)
|
ldap.set_option(ldap.OPT_X_TLS_CACERTFILE,cacert)
|
||||||
ldap.set_option(ldap.OPT_X_TLS_CERTFILE,bindcert)
|
ldap.set_option(ldap.OPT_X_TLS_CERTFILE,bindcert)
|
||||||
ldap.set_option(ldap.OPT_X_TLS_KEYFILE,bindkey)
|
ldap.set_option(ldap.OPT_X_TLS_KEYFILE,bindkey)
|
||||||
|
|
||||||
self.__wrapmethods()
|
self.__wrapmethods()
|
||||||
self.port = port or 389
|
self.port = port or 389
|
||||||
self.sslport = 0
|
|
||||||
self.host = host
|
self.host = host
|
||||||
|
self.cacert = cacert
|
||||||
self.bindcert = bindcert
|
self.bindcert = bindcert
|
||||||
self.bindkey = bindkey
|
self.bindkey = bindkey
|
||||||
self.proxydn = proxydn
|
self.proxydn = proxydn
|
||||||
@ -251,6 +264,12 @@ class IPAdmin(SimpleLDAPObject):
|
|||||||
def set_proxydn(self, proxydn):
|
def set_proxydn(self, proxydn):
|
||||||
self.proxydn = proxydn
|
self.proxydn = proxydn
|
||||||
|
|
||||||
|
def set_keytab(self, keytab):
|
||||||
|
if keytab is not None:
|
||||||
|
os.environ["KRB5CCNAME"] = keytab
|
||||||
|
self.sasl_interactive_bind_s("", sasl_auth)
|
||||||
|
self.proxydn = None
|
||||||
|
|
||||||
def getEntry(self,*args):
|
def getEntry(self,*args):
|
||||||
"""This wraps the search function. It is common to just get one entry"""
|
"""This wraps the search function. It is common to just get one entry"""
|
||||||
|
|
||||||
@ -346,6 +365,7 @@ class IPAdmin(SimpleLDAPObject):
|
|||||||
sctrl = self.__get_server_controls__()
|
sctrl = self.__get_server_controls__()
|
||||||
|
|
||||||
try:
|
try:
|
||||||
|
if sctrl is not None:
|
||||||
self.set_option(ldap.OPT_SERVER_CONTROLS, sctrl)
|
self.set_option(ldap.OPT_SERVER_CONTROLS, sctrl)
|
||||||
self.add_s(*args)
|
self.add_s(*args)
|
||||||
except ldap.ALREADY_EXISTS:
|
except ldap.ALREADY_EXISTS:
|
||||||
@ -366,6 +386,7 @@ class IPAdmin(SimpleLDAPObject):
|
|||||||
raise ipaerror.gen_exception(ipaerror.LDAP_EMPTY_MODLIST)
|
raise ipaerror.gen_exception(ipaerror.LDAP_EMPTY_MODLIST)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
|
if sctrl is not None:
|
||||||
self.set_option(ldap.OPT_SERVER_CONTROLS, sctrl)
|
self.set_option(ldap.OPT_SERVER_CONTROLS, sctrl)
|
||||||
self.modify_s(dn, modlist)
|
self.modify_s(dn, modlist)
|
||||||
# this is raised when a 'delete' attribute isn't found.
|
# this is raised when a 'delete' attribute isn't found.
|
||||||
@ -428,6 +449,7 @@ class IPAdmin(SimpleLDAPObject):
|
|||||||
modlist.append((operation, "nsAccountlock", "true"))
|
modlist.append((operation, "nsAccountlock", "true"))
|
||||||
|
|
||||||
try:
|
try:
|
||||||
|
if sctrl is not None:
|
||||||
self.set_option(ldap.OPT_SERVER_CONTROLS, sctrl)
|
self.set_option(ldap.OPT_SERVER_CONTROLS, sctrl)
|
||||||
self.modify_s(dn, modlist)
|
self.modify_s(dn, modlist)
|
||||||
except ldap.LDAPError, e:
|
except ldap.LDAPError, e:
|
||||||
@ -440,6 +462,7 @@ class IPAdmin(SimpleLDAPObject):
|
|||||||
sctrl = self.__get_server_controls__()
|
sctrl = self.__get_server_controls__()
|
||||||
|
|
||||||
try:
|
try:
|
||||||
|
if sctrl is not None:
|
||||||
self.set_option(ldap.OPT_SERVER_CONTROLS, sctrl)
|
self.set_option(ldap.OPT_SERVER_CONTROLS, sctrl)
|
||||||
self.delete_s(*args)
|
self.delete_s(*args)
|
||||||
except ldap.LDAPError, e:
|
except ldap.LDAPError, e:
|
||||||
|
@ -29,7 +29,7 @@ import os
|
|||||||
import pwd
|
import pwd
|
||||||
import socket
|
import socket
|
||||||
import time
|
import time
|
||||||
from util import *
|
from ipa.ipautil import *
|
||||||
|
|
||||||
def host_to_domain(fqdn):
|
def host_to_domain(fqdn):
|
||||||
s = fqdn.split(".")
|
s = fqdn.split(".")
|
||||||
|
@ -47,19 +47,30 @@ DefaultGroupContainer = "cn=groups,cn=accounts"
|
|||||||
# this is not anticipated.
|
# this is not anticipated.
|
||||||
class IPAConnPool:
|
class IPAConnPool:
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.numentries = 0
|
|
||||||
self.freelist = []
|
self.freelist = []
|
||||||
|
|
||||||
def getConn(self, host, port, bindca, bindcert, bindkey, proxydn=None):
|
def getConn(self, host, port, bindca, bindcert, bindkey, proxydn=None, keytab=None):
|
||||||
self.numentries = self.numentries + 1
|
conn = None
|
||||||
if len(self.freelist) > 0:
|
if len(self.freelist) > 0:
|
||||||
conn = self.freelist.pop()
|
for i in range(len(self.freelist)):
|
||||||
else:
|
c = self.freelist[i]
|
||||||
|
if ((c.host == host) and (c.port == port)):
|
||||||
|
conn = self.freelist.pop(i)
|
||||||
|
break
|
||||||
|
if conn is None:
|
||||||
conn = ipaserver.ipaldap.IPAdmin(host,port,bindca,bindcert,bindkey)
|
conn = ipaserver.ipaldap.IPAdmin(host,port,bindca,bindcert,bindkey)
|
||||||
|
if proxydn is not None:
|
||||||
conn.set_proxydn(proxydn)
|
conn.set_proxydn(proxydn)
|
||||||
|
else:
|
||||||
|
conn.set_keytab(keytab)
|
||||||
return conn
|
return conn
|
||||||
|
|
||||||
def releaseConn(self, conn):
|
def releaseConn(self, conn):
|
||||||
|
# We can't re-use SASL connections. If proxydn is None it means
|
||||||
|
# we have a keytab set. See ipaldap.set_keytab
|
||||||
|
if conn.proxydn is None:
|
||||||
|
conn.unbind_s()
|
||||||
|
else:
|
||||||
self.freelist.append(conn)
|
self.freelist.append(conn)
|
||||||
|
|
||||||
class IPAServer:
|
class IPAServer:
|
||||||
@ -68,7 +79,8 @@ class IPAServer:
|
|||||||
global _LDAPPool
|
global _LDAPPool
|
||||||
# FIXME, this needs to be auto-discovered
|
# FIXME, this needs to be auto-discovered
|
||||||
self.host = 'localhost'
|
self.host = 'localhost'
|
||||||
self.port = 636
|
self.port = 389
|
||||||
|
self.sslport = 636
|
||||||
self.bindcert = "/usr/share/ipa/cert.pem"
|
self.bindcert = "/usr/share/ipa/cert.pem"
|
||||||
self.bindkey = "/usr/share/ipa/key.pem"
|
self.bindkey = "/usr/share/ipa/key.pem"
|
||||||
self.bindca = "/usr/share/ipa/cacert.asc"
|
self.bindca = "/usr/share/ipa/cacert.asc"
|
||||||
@ -79,24 +91,84 @@ class IPAServer:
|
|||||||
self.basedn = ipa.ipautil.realm_to_suffix(ipa.config.config.get_realm())
|
self.basedn = ipa.ipautil.realm_to_suffix(ipa.config.config.get_realm())
|
||||||
self.scope = ldap.SCOPE_SUBTREE
|
self.scope = ldap.SCOPE_SUBTREE
|
||||||
self.princ = None
|
self.princ = None
|
||||||
|
self.keytab = None
|
||||||
|
|
||||||
def set_principal(self, princ):
|
def set_principal(self, princ):
|
||||||
self.princ = princ
|
self.princ = princ
|
||||||
|
|
||||||
|
def set_keytab(self, keytab):
|
||||||
|
self.keytab = keytab
|
||||||
|
|
||||||
def get_dn_from_principal(self, princ):
|
def get_dn_from_principal(self, princ):
|
||||||
"""Given a kerberls principal get the LDAP uid"""
|
"""Given a kerberos principal get the LDAP uid"""
|
||||||
global _LDAPPool
|
global _LDAPPool
|
||||||
|
|
||||||
filter = "(krbPrincipalName=" + princ + ")"
|
filter = "(krbPrincipalName=" + princ + ")"
|
||||||
# The only anonymous search we should have
|
# The only anonymous search we should have
|
||||||
m1 = _LDAPPool.getConn(self.host,self.port,self.bindca,self.bindcert,self.bindkey,None)
|
conn = _LDAPPool.getConn(self.host,self.sslport,self.bindca,self.bindcert,self.bindkey,None,None)
|
||||||
try:
|
try:
|
||||||
ent = m1.getEntry(self.basedn, self.scope, filter, ['dn'])
|
ent = conn.getEntry(self.basedn, self.scope, filter, ['dn'])
|
||||||
finally:
|
finally:
|
||||||
_LDAPPool.releaseConn(m1)
|
_LDAPPool.releaseConn(conn)
|
||||||
|
|
||||||
return "dn:" + ent.dn
|
return "dn:" + ent.dn
|
||||||
|
|
||||||
|
def __setup_connection(self, opts):
|
||||||
|
"""Set up common things done in the connection.
|
||||||
|
If there is a keytab then return None as the proxy dn and the keytab
|
||||||
|
otherwise return the proxy dn and None as the keytab.
|
||||||
|
|
||||||
|
We only want one or the other used at one time and we prefer
|
||||||
|
the keytab. So if there is a keytab, return that and None for
|
||||||
|
proxy dn to make calling getConn() easier.
|
||||||
|
"""
|
||||||
|
|
||||||
|
if opts:
|
||||||
|
if opts.get('keytab'):
|
||||||
|
self.set_keytab(opts['keytab'])
|
||||||
|
self.set_principal(None)
|
||||||
|
else:
|
||||||
|
self.set_keytab(None)
|
||||||
|
self.set_principal(opts['remoteuser'])
|
||||||
|
else:
|
||||||
|
self.set_keytab(None)
|
||||||
|
# The caller should have already set the principal
|
||||||
|
|
||||||
|
if self.princ is not None:
|
||||||
|
return self.get_dn_from_principal(self.princ), None
|
||||||
|
else:
|
||||||
|
return None, self.keytab
|
||||||
|
|
||||||
|
def getConnection(self, opts):
|
||||||
|
"""Wrapper around IPAConnPool.getConn() so we don't have to pass
|
||||||
|
around self.* every time a connection is needed.
|
||||||
|
|
||||||
|
For SASL connections (where we have a keytab) we can't set
|
||||||
|
the SSL variables for certificates. It confuses the ldap
|
||||||
|
module.
|
||||||
|
"""
|
||||||
|
global _LDAPPool
|
||||||
|
|
||||||
|
(proxy_dn, keytab) = self.__setup_connection(opts)
|
||||||
|
|
||||||
|
if keytab is not None:
|
||||||
|
bindca = None
|
||||||
|
bindcert = None
|
||||||
|
bindkey = None
|
||||||
|
port = self.port
|
||||||
|
else:
|
||||||
|
bindca = self.bindca
|
||||||
|
bindcert = self.bindcert
|
||||||
|
bindkey = self.bindkey
|
||||||
|
port = self.sslport
|
||||||
|
|
||||||
|
return _LDAPPool.getConn(self.host,port,bindca,bindcert,bindkey,proxy_dn,keytab)
|
||||||
|
|
||||||
|
def releaseConnection(self, conn):
|
||||||
|
global _LDAPPool
|
||||||
|
|
||||||
|
_LDAPPool.releaseConn(conn)
|
||||||
|
|
||||||
def convert_entry(self, ent):
|
def convert_entry(self, ent):
|
||||||
entry = dict(ent.data)
|
entry = dict(ent.data)
|
||||||
entry['dn'] = ent.dn
|
entry['dn'] = ent.dn
|
||||||
@ -110,24 +182,17 @@ class IPAServer:
|
|||||||
entry[key] = value[0]
|
entry[key] = value[0]
|
||||||
return entry
|
return entry
|
||||||
|
|
||||||
|
|
||||||
def __get_entry (self, base, filter, sattrs=None, opts=None):
|
def __get_entry (self, base, filter, sattrs=None, opts=None):
|
||||||
"""Get a specific entry. Return as a dict of values.
|
"""Get a specific entry. Return as a dict of values.
|
||||||
Multi-valued fields are represented as lists.
|
Multi-valued fields are represented as lists.
|
||||||
"""
|
"""
|
||||||
global _LDAPPool
|
|
||||||
ent=""
|
ent=""
|
||||||
|
|
||||||
if opts:
|
conn = self.getConnection(opts)
|
||||||
self.set_principal(opts['remoteuser'])
|
|
||||||
|
|
||||||
dn = self.get_dn_from_principal(self.princ)
|
|
||||||
|
|
||||||
m1 = _LDAPPool.getConn(self.host,self.port,self.bindca,self.bindcert,self.bindkey,dn)
|
|
||||||
try:
|
try:
|
||||||
ent = m1.getEntry(base, self.scope, filter, sattrs)
|
ent = conn.getEntry(base, self.scope, filter, sattrs)
|
||||||
finally:
|
finally:
|
||||||
_LDAPPool.releaseConn(m1)
|
self.releaseConnection(conn)
|
||||||
|
|
||||||
return self.convert_entry(ent)
|
return self.convert_entry(ent)
|
||||||
|
|
||||||
@ -137,8 +202,6 @@ class IPAServer:
|
|||||||
oldentry is a dict
|
oldentry is a dict
|
||||||
newentry is a dict
|
newentry is a dict
|
||||||
"""
|
"""
|
||||||
global _LDAPPool
|
|
||||||
|
|
||||||
oldentry = self.convert_scalar_values(oldentry)
|
oldentry = self.convert_scalar_values(oldentry)
|
||||||
newentry = self.convert_scalar_values(newentry)
|
newentry = self.convert_scalar_values(newentry)
|
||||||
|
|
||||||
@ -150,16 +213,11 @@ class IPAServer:
|
|||||||
except KeyError, e:
|
except KeyError, e:
|
||||||
raise ipaerror.gen_exception(ipaerror.LDAP_MISSING_DN)
|
raise ipaerror.gen_exception(ipaerror.LDAP_MISSING_DN)
|
||||||
|
|
||||||
if opts:
|
conn = self.getConnection(opts)
|
||||||
self.set_principal(opts['remoteuser'])
|
|
||||||
|
|
||||||
proxydn = self.get_dn_from_principal(self.princ)
|
|
||||||
|
|
||||||
m1 = _LDAPPool.getConn(self.host,self.port,self.bindca,self.bindcert,self.bindkey,proxydn)
|
|
||||||
try:
|
try:
|
||||||
res = m1.updateEntry(moddn, oldentry, newentry)
|
res = conn.updateEntry(moddn, oldentry, newentry)
|
||||||
finally:
|
finally:
|
||||||
_LDAPPool.releaseConn(m1)
|
self.releaseConnection(conn)
|
||||||
return res
|
return res
|
||||||
|
|
||||||
def __safe_filter(self, criteria):
|
def __safe_filter(self, criteria):
|
||||||
@ -234,8 +292,6 @@ class IPAServer:
|
|||||||
attribute name and the value is either a string or in the case
|
attribute name and the value is either a string or in the case
|
||||||
of a multi-valued field a list of values. user_container sets
|
of a multi-valued field a list of values. user_container sets
|
||||||
where in the tree the user is placed."""
|
where in the tree the user is placed."""
|
||||||
global _LDAPPool
|
|
||||||
|
|
||||||
if user_container is None:
|
if user_container is None:
|
||||||
user_container = DefaultUserContainer
|
user_container = DefaultUserContainer
|
||||||
|
|
||||||
@ -288,16 +344,11 @@ class IPAServer:
|
|||||||
for u in user:
|
for u in user:
|
||||||
entry.setValues(u, user[u])
|
entry.setValues(u, user[u])
|
||||||
|
|
||||||
if opts:
|
conn = self.getConnection(opts)
|
||||||
self.set_principal(opts['remoteuser'])
|
|
||||||
|
|
||||||
dn = self.get_dn_from_principal(self.princ)
|
|
||||||
|
|
||||||
m1 = _LDAPPool.getConn(self.host,self.port,self.bindca,self.bindcert,self.bindkey,dn)
|
|
||||||
try:
|
try:
|
||||||
res = m1.addEntry(entry)
|
res = conn.addEntry(entry)
|
||||||
finally:
|
finally:
|
||||||
_LDAPPool.releaseConn(m1)
|
self.releaseConnection(conn)
|
||||||
return res
|
return res
|
||||||
|
|
||||||
def get_add_schema (self):
|
def get_add_schema (self):
|
||||||
@ -348,20 +399,13 @@ class IPAServer:
|
|||||||
"""Return a list containing a User object for each
|
"""Return a list containing a User object for each
|
||||||
existing user.
|
existing user.
|
||||||
"""
|
"""
|
||||||
global _LDAPPool
|
|
||||||
|
|
||||||
if opts:
|
|
||||||
self.set_principal(opts['remoteuser'])
|
|
||||||
|
|
||||||
dn = self.get_dn_from_principal(self.princ)
|
|
||||||
|
|
||||||
filter = "(objectclass=posixAccount)"
|
filter = "(objectclass=posixAccount)"
|
||||||
|
|
||||||
m1 = _LDAPPool.getConn(self.host,self.port,self.bindca,self.bindcert,self.bindkey,dn)
|
conn = self.getConnection(opts)
|
||||||
try:
|
try:
|
||||||
all_users = m1.getList(self.basedn, self.scope, filter, None)
|
all_users = conn.getList(self.basedn, self.scope, filter, None)
|
||||||
finally:
|
finally:
|
||||||
_LDAPPool.releaseConn(m1)
|
self.releaseConnection(conn)
|
||||||
|
|
||||||
users = []
|
users = []
|
||||||
for u in all_users:
|
for u in all_users:
|
||||||
@ -372,13 +416,6 @@ class IPAServer:
|
|||||||
def find_users (self, criteria, sattrs=None, opts=None):
|
def find_users (self, criteria, sattrs=None, opts=None):
|
||||||
"""Returns a list: counter followed by the results.
|
"""Returns a list: counter followed by the results.
|
||||||
If the results are truncated, counter will be set to -1."""
|
If the results are truncated, counter will be set to -1."""
|
||||||
global _LDAPPool
|
|
||||||
|
|
||||||
if opts:
|
|
||||||
self.set_principal(opts['remoteuser'])
|
|
||||||
|
|
||||||
dn = self.get_dn_from_principal(self.princ)
|
|
||||||
|
|
||||||
# Assume the list of fields to search will come from a central
|
# Assume the list of fields to search will come from a central
|
||||||
# configuration repository. A good format for that would be
|
# configuration repository. A good format for that would be
|
||||||
# a comma-separated list of fields
|
# a comma-separated list of fields
|
||||||
@ -389,26 +426,26 @@ class IPAServer:
|
|||||||
criteria_words = re.split(r'\s+', criteria)
|
criteria_words = re.split(r'\s+', criteria)
|
||||||
criteria_words = filter(lambda value:value!="", criteria_words)
|
criteria_words = filter(lambda value:value!="", criteria_words)
|
||||||
if len(criteria_words) == 0:
|
if len(criteria_words) == 0:
|
||||||
return []
|
return [0]
|
||||||
|
|
||||||
(exact_match_filter, partial_match_filter) = self.__generate_match_filters(
|
(exact_match_filter, partial_match_filter) = self.__generate_match_filters(
|
||||||
search_fields, criteria_words)
|
search_fields, criteria_words)
|
||||||
|
|
||||||
m1 = _LDAPPool.getConn(self.host,self.port,self.bindca,self.bindcert,self.bindkey,dn)
|
conn = self.getConnection(opts)
|
||||||
try:
|
try:
|
||||||
try:
|
try:
|
||||||
exact_results = m1.getListAsync(self.basedn, self.scope,
|
exact_results = conn.getListAsync(self.basedn, self.scope,
|
||||||
exact_match_filter, sattrs)
|
exact_match_filter, sattrs)
|
||||||
except ipaerror.exception_for(ipaerror.LDAP_NOT_FOUND):
|
except ipaerror.exception_for(ipaerror.LDAP_NOT_FOUND):
|
||||||
exact_results = [0]
|
exact_results = [0]
|
||||||
|
|
||||||
try:
|
try:
|
||||||
partial_results = m1.getListAsync(self.basedn, self.scope,
|
partial_results = conn.getListAsync(self.basedn, self.scope,
|
||||||
partial_match_filter, sattrs)
|
partial_match_filter, sattrs)
|
||||||
except ipaerror.exception_for(ipaerror.LDAP_NOT_FOUND):
|
except ipaerror.exception_for(ipaerror.LDAP_NOT_FOUND):
|
||||||
partial_results = [0]
|
partial_results = [0]
|
||||||
finally:
|
finally:
|
||||||
_LDAPPool.releaseConn(m1)
|
self.releaseConnection(conn)
|
||||||
|
|
||||||
exact_counter = exact_results[0]
|
exact_counter = exact_results[0]
|
||||||
partial_counter = partial_results[0]
|
partial_counter = partial_results[0]
|
||||||
@ -450,13 +487,6 @@ class IPAServer:
|
|||||||
def mark_user_deleted (self, uid, opts=None):
|
def mark_user_deleted (self, uid, opts=None):
|
||||||
"""Mark a user as inactive in LDAP. We aren't actually deleting
|
"""Mark a user as inactive in LDAP. We aren't actually deleting
|
||||||
users here, just making it so they can't log in, etc."""
|
users here, just making it so they can't log in, etc."""
|
||||||
global _LDAPPool
|
|
||||||
|
|
||||||
if opts:
|
|
||||||
self.set_principal(opts['remoteuser'])
|
|
||||||
|
|
||||||
proxydn = self.get_dn_from_principal(self.princ)
|
|
||||||
|
|
||||||
user = self.get_user_by_uid(uid, ['dn', 'uid', 'nsAccountlock'], opts)
|
user = self.get_user_by_uid(uid, ['dn', 'uid', 'nsAccountlock'], opts)
|
||||||
|
|
||||||
# Are we doing an add or replace operation?
|
# Are we doing an add or replace operation?
|
||||||
@ -467,11 +497,11 @@ class IPAServer:
|
|||||||
else:
|
else:
|
||||||
has_key = False
|
has_key = False
|
||||||
|
|
||||||
m1 = _LDAPPool.getConn(self.host,self.port,self.bindca,self.bindcert,self.bindkey,proxydn)
|
conn = self.getConnection(opts)
|
||||||
try:
|
try:
|
||||||
res = m1.inactivateEntry(user['dn'], has_key)
|
res = conn.inactivateEntry(user['dn'], has_key)
|
||||||
finally:
|
finally:
|
||||||
_LDAPPool.releaseConn(m1)
|
self.releaseConnection(conn)
|
||||||
return res
|
return res
|
||||||
|
|
||||||
def delete_user (self, uid, opts=None):
|
def delete_user (self, uid, opts=None):
|
||||||
@ -483,18 +513,15 @@ class IPAServer:
|
|||||||
The memberOf plugin handles removing the user from any other
|
The memberOf plugin handles removing the user from any other
|
||||||
groups.
|
groups.
|
||||||
"""
|
"""
|
||||||
if opts:
|
|
||||||
self.set_principal(opts['remoteuser'])
|
|
||||||
|
|
||||||
dn = self.get_dn_from_principal(self.princ)
|
|
||||||
|
|
||||||
user_dn = self.get_user_by_uid(uid, ['dn', 'uid', 'objectclass'], opts)
|
user_dn = self.get_user_by_uid(uid, ['dn', 'uid', 'objectclass'], opts)
|
||||||
if user_dn is None:
|
if user_dn is None:
|
||||||
raise ipaerror.gen_exception(ipaerror.LDAP_NOT_FOUND)
|
raise ipaerror.gen_exception(ipaerror.LDAP_NOT_FOUND)
|
||||||
|
|
||||||
m1 = _LDAPPool.getConn(self.host,self.port,self.bindca,self.bindcert,self.bindkey,dn)
|
conn = self.getConnection(opts)
|
||||||
res = m1.deleteEntry(user_dn['dn'])
|
try:
|
||||||
_LDAPPool.releaseConn(m1)
|
res = conn.deleteEntry(user_dn['dn'])
|
||||||
|
finally:
|
||||||
|
self.releaseConnection(conn)
|
||||||
return res
|
return res
|
||||||
|
|
||||||
# Group support
|
# Group support
|
||||||
@ -532,8 +559,6 @@ class IPAServer:
|
|||||||
attribute name and the value is either a string or in the case
|
attribute name and the value is either a string or in the case
|
||||||
of a multi-valued field a list of values. group_container sets
|
of a multi-valued field a list of values. group_container sets
|
||||||
where in the tree the group is placed."""
|
where in the tree the group is placed."""
|
||||||
global _LDAPPool
|
|
||||||
|
|
||||||
if group_container is None:
|
if group_container is None:
|
||||||
group_container = DefaultGroupContainer
|
group_container = DefaultGroupContainer
|
||||||
|
|
||||||
@ -554,38 +579,26 @@ class IPAServer:
|
|||||||
for g in group:
|
for g in group:
|
||||||
entry.setValues(g, group[g])
|
entry.setValues(g, group[g])
|
||||||
|
|
||||||
if opts:
|
conn = self.getConnection(opts)
|
||||||
self.set_principal(opts['remoteuser'])
|
|
||||||
|
|
||||||
dn = self.get_dn_from_principal(self.princ)
|
|
||||||
|
|
||||||
m1 = _LDAPPool.getConn(self.host,self.port,self.bindca,self.bindcert,self.bindkey,dn)
|
|
||||||
try:
|
try:
|
||||||
res = m1.addEntry(entry)
|
res = conn.addEntry(entry)
|
||||||
finally:
|
finally:
|
||||||
_LDAPPool.releaseConn(m1)
|
self.releaseConnection(conn)
|
||||||
|
|
||||||
def find_groups (self, criteria, sattrs=None, opts=None):
|
def find_groups (self, criteria, sattrs=None, opts=None):
|
||||||
"""Return a list containing a User object for each
|
"""Return a list containing a User object for each
|
||||||
existing group that matches the criteria.
|
existing group that matches the criteria.
|
||||||
"""
|
"""
|
||||||
global _LDAPPool
|
|
||||||
|
|
||||||
if opts:
|
|
||||||
self.set_principal(opts['remoteuser'])
|
|
||||||
|
|
||||||
dn = self.get_dn_from_principal(self.princ)
|
|
||||||
|
|
||||||
criteria = self.__safe_filter(criteria)
|
criteria = self.__safe_filter(criteria)
|
||||||
|
|
||||||
filter = "(&(cn=%s)(objectClass=posixGroup))" % criteria
|
filter = "(&(cn=%s)(objectClass=posixGroup))" % criteria
|
||||||
m1 = _LDAPPool.getConn(self.host,self.port,self.bindca,self.bindcert,self.bindkey,dn)
|
conn = self.getConnection(opts)
|
||||||
try:
|
try:
|
||||||
results = m1.getList(self.basedn, self.scope, filter, sattrs)
|
results = conn.getList(self.basedn, self.scope, filter, sattrs)
|
||||||
except ipaerror.exception_for(ipaerror.LDAP_NOT_FOUND):
|
except ipaerror.exception_for(ipaerror.LDAP_NOT_FOUND):
|
||||||
results = []
|
results = []
|
||||||
finally:
|
finally:
|
||||||
_LDAPPool.releaseConn(m1)
|
self.releaseConnection(conn)
|
||||||
|
|
||||||
groups = []
|
groups = []
|
||||||
for u in results:
|
for u in results:
|
||||||
@ -599,9 +612,6 @@ class IPAServer:
|
|||||||
group is the cn of the group to be added to
|
group is the cn of the group to be added to
|
||||||
"""
|
"""
|
||||||
|
|
||||||
if opts:
|
|
||||||
self.set_principal(opts['remoteuser'])
|
|
||||||
|
|
||||||
old_group = self.get_group_by_cn(group, None, opts)
|
old_group = self.get_group_by_cn(group, None, opts)
|
||||||
if old_group is None:
|
if old_group is None:
|
||||||
raise ipaerror.gen_exception(ipaerror.LDAP_NOT_FOUND)
|
raise ipaerror.gen_exception(ipaerror.LDAP_NOT_FOUND)
|
||||||
@ -652,9 +662,6 @@ class IPAServer:
|
|||||||
group is the cn of the group to be removed from
|
group is the cn of the group to be removed from
|
||||||
"""
|
"""
|
||||||
|
|
||||||
if opts:
|
|
||||||
self.set_principal(opts['remoteuser'])
|
|
||||||
|
|
||||||
old_group = self.get_group_by_cn(group, None, opts)
|
old_group = self.get_group_by_cn(group, None, opts)
|
||||||
if old_group is None:
|
if old_group is None:
|
||||||
raise ipaerror.gen_exception(ipaerror.LDAP_NOT_FOUND)
|
raise ipaerror.gen_exception(ipaerror.LDAP_NOT_FOUND)
|
||||||
@ -718,19 +725,16 @@ class IPAServer:
|
|||||||
The memberOf plugin handles removing the group from any other
|
The memberOf plugin handles removing the group from any other
|
||||||
groups.
|
groups.
|
||||||
"""
|
"""
|
||||||
if opts:
|
|
||||||
self.set_principal(opts['remoteuser'])
|
|
||||||
|
|
||||||
dn = self.get_dn_from_principal(self.princ)
|
|
||||||
|
|
||||||
group = self.get_group_by_cn(group_cn, ['dn', 'cn'], opts)
|
group = self.get_group_by_cn(group_cn, ['dn', 'cn'], opts)
|
||||||
|
|
||||||
if len(group) != 1:
|
if len(group) != 1:
|
||||||
raise ipaerror.gen_exception(ipaerror.LDAP_NOT_FOUND)
|
raise ipaerror.gen_exception(ipaerror.LDAP_NOT_FOUND)
|
||||||
|
|
||||||
m1 = _LDAPPool.getConn(self.host,self.port,self.bindca,self.bindcert,self.bindkey,dn)
|
conn = self.getConnection(opts)
|
||||||
res = m1.deleteEntry(group[0]['dn'])
|
try:
|
||||||
_LDAPPool.releaseConn(m1)
|
res = conn.deleteEntry(group[0]['dn'])
|
||||||
|
finally:
|
||||||
|
self.releaseConnection(conn)
|
||||||
return res
|
return res
|
||||||
|
|
||||||
def add_group_to_group(self, group, tgroup, opts=None):
|
def add_group_to_group(self, group, tgroup, opts=None):
|
||||||
@ -739,9 +743,6 @@ class IPAServer:
|
|||||||
tgroup is the cn of the group to be added to
|
tgroup is the cn of the group to be added to
|
||||||
"""
|
"""
|
||||||
|
|
||||||
if opts:
|
|
||||||
self.set_principal(opts['remoteuser'])
|
|
||||||
|
|
||||||
old_group = self.get_group_by_cn(tgroup, None, opts)
|
old_group = self.get_group_by_cn(tgroup, None, opts)
|
||||||
if old_group is None:
|
if old_group is None:
|
||||||
raise ipaerror.gen_exception(ipaerror.LDAP_NOT_FOUND)
|
raise ipaerror.gen_exception(ipaerror.LDAP_NOT_FOUND)
|
||||||
|
@ -126,13 +126,19 @@ class ModXMLRPCRequestHandler(object):
|
|||||||
def register_instance(self,instance):
|
def register_instance(self,instance):
|
||||||
self.register_module(instance)
|
self.register_module(instance)
|
||||||
|
|
||||||
def _marshaled_dispatch(self, data, remoteuser):
|
def _marshaled_dispatch(self, data, req):
|
||||||
"""Dispatches an XML-RPC method from marshalled (XML) data."""
|
"""Dispatches an XML-RPC method from marshalled (XML) data."""
|
||||||
|
|
||||||
params, method = loads(data)
|
params, method = loads(data)
|
||||||
|
|
||||||
|
# Populate the Apache environment variables
|
||||||
|
req.add_common_vars()
|
||||||
|
|
||||||
opts={}
|
opts={}
|
||||||
opts['remoteuser'] = remoteuser
|
opts['remoteuser'] = req.user
|
||||||
|
|
||||||
|
if req.subprocess_env.get("KRB5CCNAME") is not None:
|
||||||
|
opts['keytab'] = req.subprocess_env.get("KRB5CCNAME")
|
||||||
|
|
||||||
# Tack onto the end of the passed-in arguments any options we also
|
# Tack onto the end of the passed-in arguments any options we also
|
||||||
# need
|
# need
|
||||||
@ -263,7 +269,7 @@ class ModXMLRPCRequestHandler(object):
|
|||||||
req.allow_methods(['POST'],1)
|
req.allow_methods(['POST'],1)
|
||||||
raise apache.SERVER_RETURN, apache.HTTP_METHOD_NOT_ALLOWED
|
raise apache.SERVER_RETURN, apache.HTTP_METHOD_NOT_ALLOWED
|
||||||
|
|
||||||
response = self._marshaled_dispatch(req.read(), req.user)
|
response = self._marshaled_dispatch(req.read(), req)
|
||||||
|
|
||||||
req.content_type = "text/xml"
|
req.content_type = "text/xml"
|
||||||
req.set_content_length(len(response))
|
req.set_content_length(len(response))
|
||||||
|
Loading…
Reference in New Issue
Block a user