- Abstracted client class to work directly or over RPC

- Add mod_auth_kerb and cyrus-sasl-gssapi to Requires
- Remove references to admin server in ipa-server-setupssl
- Generate a client certificate for the XML-RPC server to connect to LDAP with
- Create a keytab for Apache
- Create an ldif with a test user
- Provide a certmap.conf for doing SSL client authentication
- Update tools to use kerberos
- Add User class
This commit is contained in:
rcritten@redhat.com 2007-08-06 10:05:53 -04:00
parent 66ab69d0b2
commit 993f76fe60
26 changed files with 877 additions and 322 deletions

View File

@ -1,6 +1,6 @@
Name: freeipa-admintools Name: freeipa-admintools
Version: 0.1.0 Version: 0.1.0
Release: 1%{?dist} Release: 3%{?dist}
Summary: FreeIPA authentication server Summary: FreeIPA authentication server
Group: System Environment/Base Group: System Environment/Base
@ -36,7 +36,12 @@ rm -rf %{buildroot}
%changelog %changelog
* Mon Aug 5 2007 Rob Crittenden <rcritten@redhat.com> - 0.1.0-3
- Abstracted client class to work directly or over RPC
* Wed Aug 1 2007 Rob Crittenden <rcritten@redhat.com> - 0.1.0-2
- Update tools to do kerberos
- Add User class
* Fri Jul 27 2007 Karl MacMillan <kmacmill@localhost.localdomain> - 0.1.0-1 * Fri Jul 27 2007 Karl MacMillan <kmacmill@localhost.localdomain> - 0.1.0-1
- Initial rpm version - Initial rpm version

View File

@ -1,6 +1,6 @@
Name: freeipa-admintools Name: freeipa-admintools
Version: 0.1.0 Version: 0.1.0
Release: 1%{?dist} Release: 3%{?dist}
Summary: FreeIPA authentication server Summary: FreeIPA authentication server
Group: System Environment/Base Group: System Environment/Base
@ -36,7 +36,12 @@ rm -rf %{buildroot}
%changelog %changelog
* Mon Aug 5 2007 Rob Crittenden <rcritten@redhat.com> - 0.1.0-3
- Abstracted client class to work directly or over RPC
* Wed Aug 1 2007 Rob Crittenden <rcritten@redhat.com> - 0.1.0-2
- Update tools to do kerberos
- Add User class
* Fri Jul 27 2007 Karl MacMillan <kmacmill@localhost.localdomain> - 0.1.0-1 * Fri Jul 27 2007 Karl MacMillan <kmacmill@localhost.localdomain> - 0.1.0-1
- Initial rpm version - Initial rpm version

View File

@ -21,10 +21,11 @@
import sys import sys
from optparse import OptionParser from optparse import OptionParser
import ipa import ipa
import ipa.rpcclient import ipa.ipaclient as ipaclient
import ipa.config import ipa.config
import xmlrpclib import xmlrpclib
import kerberos
def usage(): def usage():
print "ipa-adduser [-c|--gecos STRING] [-d|--directory STRING] [-f|--firstname STRING] [-l|--lastname STRING] user" print "ipa-adduser [-c|--gecos STRING] [-d|--directory STRING] [-f|--firstname STRING] [-l|--lastname STRING] user"
@ -73,10 +74,15 @@ def main():
user['loginshell'] = "/bin/bash" user['loginshell'] = "/bin/bash"
try: try:
ipa.rpcclient.add_user(user) client = ipaclient.IPAClient()
print args[0] + " successfully added" client.add_user(user)
print args[1] + " successfully added"
except xmlrpclib.Fault, f: except xmlrpclib.Fault, f:
print f.faultString print f.faultString
return 1
except kerberos.GSSError, e:
print "Could not initialize GSSAPI: %s/%s" % (e[0][0][0], e[0][1][0])
return 1
return 0 return 0

View File

@ -20,13 +20,12 @@
import sys import sys
from optparse import OptionParser from optparse import OptionParser
import ipa import ipa.ipaclient as ipaclient
import ipa.rpcclient
import ipa.config import ipa.config
import base64
import sys import sys
import xmlrpclib import xmlrpclib
import kerberos
def usage(): def usage():
print "ipa-finduser <uid>" print "ipa-finduser <uid>"
@ -48,16 +47,27 @@ def main():
usage() usage()
try: try:
ent = ipa.rpcclient.get_user(args[1]) client = ipaclient.IPAClient()
for name, value in ent.items(): ent = client.get_user(args[1])
if isinstance(value, str): attr = ent.attrList()
print name + ": " + value
print "dn: " + ent.dn
for a in attr:
value = ent.getValues(a)
if isinstance(value,str):
print a + ": " + value
else: else:
print name + ": " print a + ": "
for x in value: for l in value:
print "\t" + x print "\t" + l
except xmlrpclib.Fault, fault: except xmlrpclib.Fault, fault:
print fault.faultString print fault.faultString
return 1
except kerberos.GSSError, e:
print "Could not initialize GSSAPI: %s/%s" % (e[0][0][0], e[0][1][0])
return 1
return 0 return 0

View File

@ -1,6 +1,6 @@
Name: freeipa-python Name: freeipa-python
Version: 0.1.0 Version: 0.1.0
Release: 1%{?dist} Release: 3%{?dist}
Summary: FreeIPA authentication server Summary: FreeIPA authentication server
Group: System Environment/Base Group: System Environment/Base
@ -42,6 +42,13 @@ rm -rf %{buildroot}
%changelog %changelog
* Mon Aug 5 2007 Rob Crittenden <rcritten@redhat.com> - 0.1.0-3
- Abstracted client class to work directly or over RPC
* Wed Aug 1 2007 Rob Crittenden <rcritten@redhat.com> - 0.1.0-2
- Add User class
- Add kerberos authentication to the XML-RPC request made from tools.
* Fri Jul 27 2007 Karl MacMillan <kmacmill@localhost.localdomain> - 0.1.0-1 * Fri Jul 27 2007 Karl MacMillan <kmacmill@localhost.localdomain> - 0.1.0-1
- Initial rpm version - Initial rpm version

View File

@ -1,6 +1,6 @@
Name: freeipa-python Name: freeipa-python
Version: VERSION Version: VERSION
Release: 1%{?dist} Release: 3%{?dist}
Summary: FreeIPA authentication server Summary: FreeIPA authentication server
Group: System Environment/Base Group: System Environment/Base
@ -42,6 +42,13 @@ rm -rf %{buildroot}
%changelog %changelog
* Mon Aug 5 2007 Rob Crittenden <rcritten@redhat.com> - 0.1.0-3
- Abstracted client class to work directly or over RPC
* Wed Aug 1 2007 Rob Crittenden <rcritten@redhat.com> - 0.1.0-2
- Add User class
- Add kerberos authentication to the XML-RPC request made from tools.
* Fri Jul 27 2007 Karl MacMillan <kmacmill@localhost.localdomain> - 0.1.0-1 * Fri Jul 27 2007 Karl MacMillan <kmacmill@localhost.localdomain> - 0.1.0-1
- Initial rpm version - Initial rpm version

87
ipa-python/ipaclient.py Normal file
View File

@ -0,0 +1,87 @@
#! /usr/bin/python -E
# Authors: Rob Crittenden <rcritten@redhat.com>
#
# Copyright (C) 2007 Red Hat
# see file 'COPYING' for use and warranty information
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License as
# published by the Free Software Foundation; version 2 or later
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
#
#!/usr/bin/python
import sys
sys.path.append("/usr/share/ipa")
from ipaserver import funcs
import ipa.rpcclient as rpcclient
import user
import ipa
import config
class IPAClient:
def __init__(self,local=None):
self.local = local
ipa.config.init_config()
if local:
self.transport = funcs.IPAServer()
# client needs to call set_principal(user@REALM)
else:
self.transport = rpcclient.RPCClient()
def set_principal(self,princ):
if self.local:
self.transport.set_principal(princ)
def get_user(self,uid):
result = self.transport.get_user(uid)
return user.User(result)
def add_user(self,user):
realm = config.config.get_realm()
# FIXME: This should be dynamic and can include just about anything
# Let us add in some missing attributes
if user.get('homeDirectory') is None:
user['homeDirectory'] ='/home/%s' % user['uid']
if user.get('gecos') is None:
user['gecos'] = user['uid']
# FIXME: This can be removed once the DS plugin is installed
user['uidNumber'] ='501'
# FIXME: What is the default group for users?
user['gidNumber'] ='501'
user['krbPrincipalName'] = "%s@%s" % (user['uid'], realm)
user['cn'] = "%s %s" % (user['givenName'], user['sn'])
if user.get('gn'):
del user['gn']
result = self.transport.add_user(user)
return result
def get_all_users(self):
result = self.transport.get_all_users()
all_users = []
for (attrs) in result:
if attrs is not None:
all_users.append(user.User(attrs))
return all_users
def get_add_schema(self):
result = self.transport.get_add_schema()
return result

View File

@ -0,0 +1,55 @@
#! /usr/bin/python -E
# Authors: Rob Crittenden <rcritten@redhat.com>
#
# Copyright (C) 2007 Red Hat
# see file 'COPYING' for use and warranty information
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License as
# published by the Free Software Foundation; version 2 or later
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
#
#!/usr/bin/python
import httplib
import xmlrpclib
import kerberos
from kerberos import GSSError
class KerbTransport(xmlrpclib.Transport):
"""Handles Kerberos Negotiation authentication to an XML-RPC server."""
def get_host_info(self, host):
host, extra_headers, x509 = xmlrpclib.Transport.get_host_info(self, host)
# Set the remote host principal
h = host
hostinfo = h.split(':')
service = "HTTP@" + hostinfo[0]
try:
rc, vc = kerberos.authGSSClientInit(service);
except kerberos.GSSError, e:
raise GSSError(e)
try:
kerberos.authGSSClientStep(vc, "");
except kerberos.GSSError, e:
raise GSSError(e)
extra_headers = [
("Authorization", "negotiate %s" % kerberos.authGSSClientResponse(vc) )
]
return host, extra_headers, x509

View File

@ -20,85 +20,101 @@
#!/usr/bin/python #!/usr/bin/python
try:
import krbV
except ImportError:
pass
import xmlrpclib import xmlrpclib
import socket import socket
import config import config
from krbtransport import KerbTransport
from kerberos import GSSError
import os
import base64
import user
import ipa
# Some errors to catch # Some errors to catch
# http://cvs.fedora.redhat.com/viewcvs/ldapserver/ldap/servers/plugins/pam_passthru/README?root=dirsec&rev=1.6&view=auto # http://cvs.fedora.redhat.com/viewcvs/ldapserver/ldap/servers/plugins/pam_passthru/README?root=dirsec&rev=1.6&view=auto
def server_url(): class RPCClient:
return "http://" + config.config.get_server() + "/ipa"
def setup_server(): def __init__(self):
return xmlrpclib.ServerProxy(server_url()) ipa.config.init_config()
def get_user(username): def server_url(self):
"""Get a specific user""" return "http://" + config.config.get_server() + "/ipa"
server = setup_server()
try:
result = server.get_user(username)
myuser = result
except xmlrpclib.Fault, fault:
raise xmlrpclib.Fault(fault.faultCode, fault.faultString)
return None
except socket.error, (value, msg):
raise xmlrpclib.Fault(value, msg)
return None
return myuser def setup_server(self):
return xmlrpclib.ServerProxy(self.server_url(), KerbTransport())
def add_user(user): def convert_entry(self,ent):
"""Add a new user""" # Convert into a dict. We need to handle multi-valued attributes as well
server = setup_server() # so we'll convert those into lists.
user={}
for (k) in ent:
k = k.lower()
if user.get(k) is not None:
if isinstance(user[k],list):
user[k].append(ent[k].strip())
else:
first = user[k]
user[k] = ()
user[k].append(first)
user[k].append(ent[k].strip())
else:
user[k] = ent[k]
return user
def get_user(self,username):
"""Get a specific user"""
server = self.setup_server()
try:
result = server.get_user(username)
except xmlrpclib.Fault, fault:
raise xmlrpclib.Fault(fault.faultCode, fault.faultString)
except socket.error, (value, msg):
raise xmlrpclib.Fault(value, msg)
# FIXME: Get the realm from somewhere
realm = config.config.get_realm()
# FIXME: This should be dynamic and can include just about anything
# Let us add in some missing attributes
if user.get('homeDirectory') is None:
user['homeDirectory'] ='/home/%s' % user['uid']
if user.get('gecos') is None:
user['gecos'] = user['uid']
# FIXME: This can be removed once the DS plugin is installed
user['uidNumber'] ='501'
# FIXME: What is the default group for users?
user['gidNumber'] ='501'
user['krbPrincipalName'] = "%s@%s" % (user['uid'], realm)
user['cn'] = "%s %s" % (user['givenName'], user['sn'])
try:
result = server.add_user(user)
return result return result
except xmlrpclib.Fault, fault:
raise xmlrpclib.Fault(fault.faultCode, fault.faultString)
return None def add_user(self,user):
except socket.error, (value, msg): """Add a new user"""
raise xmlrpclib.Fault(value, msg) server = self.setup_server()
return None
def get_add_schema(): try:
"""Get the list of attributes we need to ask when adding a new result = server.add_user(user)
user. except xmlrpclib.Fault, fault:
""" raise xmlrpclib.Fault(fault.faultCode, fault.faultString)
server = setup_server() except socket.error, (value, msg):
raise xmlrpclib.Fault(value, msg)
return result
def get_add_schema(self):
"""Get the list of attributes we need to ask when adding a new
user.
"""
server = self.setup_server()
# FIXME: Hardcoded and designed for the TurboGears GUI. Do we want
# this for the CLI as well?
try:
result = server.get_add_schema()
except xmlrpclib.Fault, fault:
raise xmlrpclib.Fault(fault.faultCode, fault.faultString)
except socket.error, (value, msg):
raise xmlrpclib.Fault(value, msg)
return result
# FIXME: Hardcoded and designed for the TurboGears GUI. Do we want def get_all_users (self):
# this for the CLI as well? """Return a list containing a User object for each existing user."""
try:
result = server.get_add_schema() server = self.setup_server()
except xmlrpclib.Fault, fault: try:
raise xmlrpclib.Fault(fault, fault.faultString) result = server.get_all_users()
return None except xmlrpclib.Fault, fault:
except socket.error, (value, msg): raise xmlrpclib.Fault(fault.faultCode, fault.faultString)
raise xmlrpclib.Fault(value, msg) except socket.error, (value, msg):
return None raise xmlrpclib.Fault(value, msg)
return result return result

112
ipa-python/user.py Normal file
View File

@ -0,0 +1,112 @@
import ldap
import ldif
import re
import cStringIO
class User:
"""This class represents an IPA user. An LDAP entry consists of a DN
and a list of attributes. Each attribute consists of a name and a list of
values. For the time being I will maintain this.
In python-ldap, entries are returned as a list of 2-tuples.
Instance variables:
dn - string - the string DN of the entry
data - cidict - case insensitive dict of the attributes and values"""
def __init__(self,entrydata):
"""data is the raw data returned from the python-ldap result method,
which is a search result entry or a reference or None.
If creating a new empty entry, data is the string DN."""
if entrydata:
if isinstance(entrydata,tuple):
self.dn = entrydata[0]
self.data = ldap.cidict.cidict(entrydata[1])
elif isinstance(entrydata,str):
self.dn = entrydata
self.data = ldap.cidict.cidict()
elif isinstance(entrydata,dict):
self.dn = entrydata['dn']
del entrydata['dn']
self.data = ldap.cidict.cidict(entrydata)
else:
self.dn = ''
self.data = ldap.cidict.cidict()
def __nonzero__(self):
"""This allows us to do tests like if entry: returns false if there is no data,
true otherwise"""
return self.data != None and len(self.data) > 0
def hasAttr(self,name):
"""Return True if this entry has an attribute named name, False otherwise"""
return self.data and self.data.has_key(name)
def __getattr__(self,name):
"""If name is the name of an LDAP attribute, return the first value for that
attribute - equivalent to getValue - this allows the use of
entry.cn
instead of
entry.getValue('cn')
This also allows us to return None if an attribute is not found rather than
throwing an exception"""
return self.getValue(name)
def getValues(self,name):
"""Get the list (array) of values for the attribute named name"""
return self.data.get(name)
def getValue(self,name):
"""Get the first value for the attribute named name"""
value = self.data.get(name,[None])
if isinstance(value[0],list) or isinstance(value[0],tuple):
return value[0]
else:
return value
def setValue(self,name,*value):
"""Value passed in may be a single value, several values, or a single sequence.
For example:
ent.setValue('name', 'value')
ent.setValue('name', 'value1', 'value2', ..., 'valueN')
ent.setValue('name', ['value1', 'value2', ..., 'valueN'])
ent.setValue('name', ('value1', 'value2', ..., 'valueN'))
Since *value is a tuple, we may have to extract a list or tuple from that
tuple as in the last two examples above"""
if isinstance(value[0],list) or isinstance(value[0],tuple):
self.data[name] = value[0]
else:
self.data[name] = value
setValues = setValue
def toTupleList(self):
"""Convert the attrs and values to a list of 2-tuples. The first element
of the tuple is the attribute name. The second element is either a
single value or a list of values."""
return self.data.items()
def attrList(self):
"""Return a list of all attributes in the entry"""
return self.data.keys()
# def __str__(self):
# """Convert the Entry to its LDIF representation"""
# return self.__repr__()
#
# # the ldif class base64 encodes some attrs which I would rather see in raw form - to
# # encode specific attrs as base64, add them to the list below
# ldif.safe_string_re = re.compile('^$')
# base64_attrs = ['nsstate', 'krbprincipalkey', 'krbExtraData']
#
# def __repr__(self):
# """Convert the Entry to its LDIF representation"""
# sio = cStringIO.StringIO()
# # what's all this then? the unparse method will currently only accept
# # a list or a dict, not a class derived from them. self.data is a
# # cidict, so unparse barfs on it. I've filed a bug against python-ldap,
# # but in the meantime, we have to convert to a plain old dict for printing
# # I also don't want to see wrapping, so set the line width really high (1000)
# newdata = {}
# newdata.update(self.data)
# ldif.LDIFWriter(sio,User.base64_attrs,1000).unparse(self.dn,newdata)
# return sio.getvalue()

View File

@ -1,6 +1,6 @@
Name: freeipa-server Name: freeipa-server
Version: 0.1.0 Version: 0.1.0
Release: 1%{?dist} Release: 3%{?dist}
Summary: FreeIPA authentication server Summary: FreeIPA authentication server
Group: System Environment/Base Group: System Environment/Base
@ -10,7 +10,7 @@ Source0: %{name}-%{version}.tgz
BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-root-%(%{__id_u} -n) BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-root-%(%{__id_u} -n)
BuildArch: noarch BuildArch: noarch
Requires: python fedora-ds-base krb5-server krb5-server-ldap nss-tools openldap-clients httpd mod_python python-ldap freeipa-python Requires: python fedora-ds-base krb5-server krb5-server-ldap nss-tools openldap-clients httpd mod_python mod_auth_kerb python-ldap freeipa-python cyrus-sasl-gssapi
%define httpd_conf /etc/httpd/conf.d %define httpd_conf /etc/httpd/conf.d
@ -44,6 +44,17 @@ rm -rf %{buildroot}
%changelog %changelog
* Mon Aug 5 2007 Rob Crittenden <rcritten@redhat.com> - 0.1.0-3
- Abstracted client class to work directly or over RPC
* Wed Aug 1 2007 Rob Crittenden <rcritten@redhat.com> - 0.1.0-2
- Add mod_auth_kerb and cyrus-sasl-gssapi to Requires
- Remove references to admin server in ipa-server-setupssl
- Generate a client certificate for the XML-RPC server to connect to LDAP with
- Create a keytab for Apache
- Create an ldif with a test user
- Provide a certmap.conf for doing SSL client authentication
* Fri Jul 27 2007 Karl MacMillan <kmacmill@localhost.localdomain> - 0.1.0-1 * Fri Jul 27 2007 Karl MacMillan <kmacmill@localhost.localdomain> - 0.1.0-1
- Initial rpm version - Initial rpm version

View File

@ -1,6 +1,6 @@
Name: freeipa-server Name: freeipa-server
Version: VERSION Version: VERSION
Release: 1%{?dist} Release: 3%{?dist}
Summary: FreeIPA authentication server Summary: FreeIPA authentication server
Group: System Environment/Base Group: System Environment/Base
@ -10,7 +10,7 @@ Source0: %{name}-%{version}.tgz
BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-root-%(%{__id_u} -n) BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-root-%(%{__id_u} -n)
BuildArch: noarch BuildArch: noarch
Requires: python fedora-ds-base krb5-server krb5-server-ldap nss-tools openldap-clients httpd mod_python python-ldap freeipa-python Requires: python fedora-ds-base krb5-server krb5-server-ldap nss-tools openldap-clients httpd mod_python mod_auth_kerb python-ldap freeipa-python cyrus-sasl-gssapi
%define httpd_conf /etc/httpd/conf.d %define httpd_conf /etc/httpd/conf.d
@ -44,6 +44,17 @@ rm -rf %{buildroot}
%changelog %changelog
* Mon Aug 5 2007 Rob Crittenden <rcritten@redhat.com> - 0.1.0-3
- Abstracted client class to work directly or over RPC
* Wed Aug 1 2007 Rob Crittenden <rcritten@redhat.com> - 0.1.0-2
- Add mod_auth_kerb and cyrus-sasl-gssapi to Requires
- Remove references to admin server in ipa-server-setupssl
- Generate a client certificate for the XML-RPC server to connect to LDAP with
- Create a keytab for Apache
- Create an ldif with a test user
- Provide a certmap.conf for doing SSL client authentication
* Fri Jul 27 2007 Karl MacMillan <kmacmill@localhost.localdomain> - 0.1.0-1 * Fri Jul 27 2007 Karl MacMillan <kmacmill@localhost.localdomain> - 0.1.0-1
- Initial rpm version - Initial rpm version

View File

@ -6,7 +6,8 @@ install:
install -m 755 ipa-server-install $(SBINDIR) install -m 755 ipa-server-install $(SBINDIR)
install -m 755 ipa-server-setupssl $(SBINDIR) install -m 755 ipa-server-setupssl $(SBINDIR)
$(MAKE) -C share $@ $(MAKE) -C share $@
$(MAKE) -C test $@
clean: clean:
$(MAKE) -C share $@ $(MAKE) -C share $@
rm -f *~ *.pyc rm -f *~ *.pyc

View File

@ -119,6 +119,9 @@ def main():
# Restart apache # Restart apache
run(["/sbin/service", "httpd", "restart"]) run(["/sbin/service", "httpd", "restart"])
# Set apache to be on at boot
run(["/sbin/chkconfig", "httpd", "on"])
# Create the config file # Create the config file
fd = open("/etc/ipa/ipa.conf", "w") fd = open("/etc/ipa/ipa.conf", "w")
fd.write("[defaults]\n") fd.write("[defaults]\n")

View File

@ -1,4 +1,4 @@
#!/bin/sh #!/bin/bash
if [ "$1" ] ; then if [ "$1" ] ; then
password=$1 password=$1
@ -49,22 +49,14 @@ if [ -f $secdir/cert8.db ] ; then
needServerCert=1 needServerCert=1
fi fi
# look for admin server cert
if certutil -L -d $secdir -n "server-cert" 2> /dev/null ; then
echo "Using existing admin server-cert"
else
echo "No Admin Server Cert found - will create new one"
needASCert=1
fi
prefix="new-" prefix="new-"
prefixarg="-P $prefix" prefixarg="-P $prefix"
else else
needCA=1 needCA=1
needServerCert=1 needServerCert=1
needASCert=1
fi fi
if test -z "$needCA" -a -z "$needServerCert" -a -z "$needASCert" ; then if test -z "$needCA" -a -z "$needServerCert" ; then
echo "No certs needed - exiting" echo "No certs needed - exiting"
exit 0 exit 0
fi fi
@ -120,17 +112,17 @@ if test -n "$needServerCert" ; then
certutil -S $prefixarg -n "Server-Cert" -s "cn=$myhost,ou=Fedora Directory Server" -c "CA certificate" -t "u,u,u" -m 1001 -v 120 -d $secdir -z $secdir/noise.txt -f $secdir/pwdfile.txt certutil -S $prefixarg -n "Server-Cert" -s "cn=$myhost,ou=Fedora Directory Server" -c "CA certificate" -t "u,u,u" -m 1001 -v 120 -d $secdir -z $secdir/noise.txt -f $secdir/pwdfile.txt
fi fi
if test -n "$needASCert" ; then # 8. Generate the web service client certificate:
# Generate the admin server certificate echo -e "0\n2\n9\nn\n0\n9\nn\n" | certutil -S $prefixarg -n webservice -s "uid=webservice, CN=Web Service, OU=Fedora Directory Server" -c "CA certificate" -t u,pu,u -m 1002 -v 120 -d $secdir -z $secdir/noise.txt -f $secdir/pwdfile.txt -1 -5
certutil -S $prefixarg -n "server-cert" -s "cn=$myhost,ou=Fedora Administration Server" -c "CA certificate" -t "u,u,u" -m 1002 -v 120 -d $secdir -z $secdir/noise.txt -f $secdir/pwdfile.txt
# export the admin server certificate/private key for import into its key/cert db pk12util -d $secdir $prefixarg -o $secdir/webservice.p12 -n "webservice" -w $secdir/pwdfile.txt -k $secdir/pwdfile.txt
pk12util -d $secdir $prefixarg -o $secdir/adminserver.p12 -n server-cert -w $secdir/pwdfile.txt -k $secdir/pwdfile.txt
if test -n "$isroot" ; then openssl pkcs12 -in $secdir/webservice.p12 -clcerts -nokeys -out /usr/share/ipa/cert.pem -passin file:$secdir/pwdfile.txt
chown $uid:$gid $secdir/adminserver.p12 openssl pkcs12 -in $secdir/webservice.p12 -nocerts -nodes -out /usr/share/ipa/key.pem -passin file:$secdir/pwdfile.txt
fi
chmod 400 $secdir/adminserver.p12 cp -p $secdir/cacert.asc /usr/share/ipa
fi chown apache:apache /usr/share/ipa/cert.pem /usr/share/ipa/key.pem /usr/share/ipa/cacert.asc
chmod 600 /usr/share/ipa/cert.pem /usr/share/ipa/key.pem
# create the pin file # create the pin file
if [ ! -f $secdir/pin.txt ] ; then if [ ! -f $secdir/pin.txt ] ; then
@ -153,42 +145,6 @@ if [ -n "$prefix" ] ; then
mv $secdir/${prefix}key3.db $secdir/key3.db mv $secdir/${prefix}key3.db $secdir/key3.db
fi fi
# create the admin server key/cert db
asprefix=admin-serv-
if [ ! -f ${asprefix}cert8.db ] ; then
certutil -N -d $secdir -P $asprefix -f $secdir/pwdfile.txt
if test -n "$isroot" ; then
chown $uid:$gid $secdir/admin-serv-*.db
fi
chmod 600 $secdir/admin-serv-*.db
fi
if test -n "$needASCert" ; then
# import the admin server key/cert
pk12util -d $secdir -P $asprefix -n server-cert -i $secdir/adminserver.p12 -w $secdir/pwdfile.txt -k $secdir/pwdfile.txt
# import the CA cert to the admin server cert db
certutil -A -d $secdir -P $asprefix -n "CA certificate" -t "CT,," -a -i $secdir/cacert.asc
fi
if [ ! -f $secdir/password.conf ] ; then
# create the admin server password file
echo 'internal:'`cat $secdir/pwdfile.txt` > $secdir/password.conf
if test -n "$isroot" ; then
chown $uid:$gid $secdir/password.conf
fi
chmod 400 $secdir/password.conf
fi
# tell admin server to use the password file
if [ -f ../admin-serv/config/nss.conf ] ; then
sed -e "s@^NSSPassPhraseDialog .*@NSSPassPhraseDialog file:`pwd`/password.conf@" ../admin-serv/config/nss.conf > /tmp/nss.conf && mv /tmp/nss.conf ../admin-serv/config/nss.conf
if test -n "$isroot" ; then
chown $uid:$gid ../admin-serv/config/nss.conf
fi
chmod 400 ../admin-serv/config/nss.conf
fi
# enable SSL in the directory server # enable SSL in the directory server
ldapmodify -x -h localhost -p $ldapport -D "cn=Directory Manager" -w $password <<EOF ldapmodify -x -h localhost -p $ldapport -D "cn=Directory Manager" -w $password <<EOF

View File

@ -31,3 +31,26 @@ ou: groups
#objectClass: top #objectClass: top
#ou: computers #ou: computers
dn: ou=special,$SUFFIX
changetype: add
objectClass: organizationalUnit
objectClass: top
ou: special
dn: uid=webservice,ou=special,$SUFFIX
changetype: add
uid: webservice
objectClass: account
objectClass: top
objectClass: inetOrgPerson
objectClass: organizationalPerson
objectClass: person
cn: Web Service
sn: Service
dn: cn=admin,ou=groups,ou=default,$SUFFIX
changetype: add
description: ou=users administrators
objectClass: top
objectClass: groupofuniquenames
cn: admin

View File

@ -0,0 +1,82 @@
#
# BEGIN COPYRIGHT BLOCK
# This Program is free software; you can redistribute it and/or modify it under
# the terms of the GNU General Public License as published by the Free Software
# Foundation; version 2 of the License.
#
# This Program is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License along with
# this Program; if not, write to the Free Software Foundation, Inc., 59 Temple
# Place, Suite 330, Boston, MA 02111-1307 USA.
#
# In addition, as a special exception, Red Hat, Inc. gives You the additional
# right to link the code of this Program with code not covered under the GNU
# General Public License ("Non-GPL Code") and to distribute linked combinations
# including the two, subject to the limitations in this paragraph. Non-GPL Code
# permitted under this exception must only link to the code of this Program
# through those well defined interfaces identified in the file named EXCEPTION
# found in the source code files (the "Approved Interfaces"). The files of
# Non-GPL Code may instantiate templates or use macros or inline functions from
# the Approved Interfaces without causing the resulting work to be covered by
# the GNU General Public License. Only Red Hat, Inc. may make changes or
# additions to the list of Approved Interfaces. You must obey the GNU General
# Public License in all respects for all of the Program code and other code used
# in conjunction with the Program except the Non-GPL Code covered by this
# exception. If you modify this file, you may extend this exception to your
# version of the file, but you are not obligated to do so. If you do not wish to
# provide this exception without modification, you must delete this exception
# statement from your version and license this file solely under the GPL without
# exception.
#
#
# Copyright (C) 2001 Sun Microsystems, Inc. Used by permission.
# Copyright (C) 2005 Red Hat, Inc.
# All rights reserved.
# END COPYRIGHT BLOCK
#
#
# This file configures how a certificate is mapped to an LDAP entry. See the
# documentation for more information on this file.
#
# The format of this file is as follows:
# certmap <name> <issuerDN>
# <name>:<prop1> [<val1>]
# <name>:<prop2> [<val2>]
#
# Notes:
#
# 1. Mapping can be defined per issuer of a certificate. If mapping doesn't
# exists for a particular 'issuerDN' then the server uses the default
# mapping.
#
# 2. There must be an entry for <name>=default and issuerDN "default".
# This mapping is the default mapping.
#
# 3. '#' can be used to comment out a line.
#
# 4. DNComps & FilterComps are used to form the base DN and filter resp. for
# performing an LDAP search while mapping the cert to a user entry.
#
# 5. DNComps can be one of the following:
# commented out - take the user's DN from the cert as is
# empty - search the entire LDAP tree (DN == suffix)
# attr names - a comma separated list of attributes to form DN
#
# 6. FilterComps can be one of the following:
# commented out - set the filter to "objectclass=*"
# empty - set the filter to "objectclass=*"
# attr names - a comma separated list of attributes to form the filter
#
certmap default default
#default:DNComps
#default:FilterComps e, uid
#default:verifycert on
#default:CmapLdapAttr certSubjectDN
#default:library <path_to_shared_lib_or_dll>
#default:InitFn <Init function's name>
default:DNComps
default:FilterComps uid

View File

@ -6,3 +6,6 @@ aci: (targetattr!="userPassword || krbPrincipalKey ||sambaLMPassword || sambaNTP
aci: (targetattr="carLicense ||description ||displayName ||facsimileTelephoneNumber ||homePhone ||homePostalAddress ||initials ||jpegPhoto ||labeledURL ||mail ||mobile ||pager ||photo ||postOfficeBox ||postalAddress ||postalCode ||preferredDeliveryMethod ||preferredLanguage ||registeredAddress ||roomNumber | |secretary ||seeAlso ||st ||street ||telephoneNumber ||telexNumber ||title || userCertificate ||userPassword ||userSMIMECertificate ||x500UniqueIdentifier")(version 3.0; acl "Enable self write for common attributes"; allow (write) userdn="ldap:///self";) aci: (targetattr="carLicense ||description ||displayName ||facsimileTelephoneNumber ||homePhone ||homePostalAddress ||initials ||jpegPhoto ||labeledURL ||mail ||mobile ||pager ||photo ||postOfficeBox ||postalAddress ||postalCode ||preferredDeliveryMethod ||preferredLanguage ||registeredAddress ||roomNumber | |secretary ||seeAlso ||st ||street ||telephoneNumber ||telexNumber ||title || userCertificate ||userPassword ||userSMIMECertificate ||x500UniqueIdentifier")(version 3.0; acl "Enable self write for common attributes"; allow (write) userdn="ldap:///self";)
aci: (targetattr="krbPrincipalKey")(version 3.0; acl "KDC System Account"; allow(read, search,compare)userdn="ldap:///uid=kdc,cn=kerberos,$SUFFIX";) aci: (targetattr="krbPrincipalKey")(version 3.0; acl "KDC System Account"; allow(read, search,compare)userdn="ldap:///uid=kdc,cn=kerberos,$SUFFIX";)
aci: (targetattr="*")(version 3.0; acl "Directory Administrators can manage all entries"; allow(all)groupdn="ldap:///cn=Directory Administrators,$SUFFIX";) aci: (targetattr="*")(version 3.0; acl "Directory Administrators can manage all entries"; allow(all)groupdn="ldap:///cn=Directory Administrators,$SUFFIX";)
aci: (target="ldap:///uid=*,ou=users,ou=default,$SUFFIX")(targetattr="*")(version 3.0; acl "allowproxy-webservice"; allow (proxy) userdn="ldap:///uid=webservice,ou=special,$SUFFIX";)
aci: (target="ldap:///uid=*,ou=users,ou=default,$SUFFIX")(targetattr="*")(version 3.0; acl "admins can write entries"; allow(add,delete,write)groupdn="ldap:///cn=admin,ou=groups,ou=default,$SUFFIX";)
aci: (targetattr="userPrincipal")(version 3.0; acl "allow webservice to find users by kerberos principal name"; allow (read, search) userdn="ldap:///uid=webservice,ou=special,$SUFFIX";)

View File

@ -0,0 +1,8 @@
SHAREDIR = $(DESTDIR)/usr/share/ipa
install:
-mkdir -p $(SHAREDIR)
install -m 644 *.ldif $(SHAREDIR)
clean:
rm -f *~

View File

@ -1,5 +1,6 @@
# test, users, default, $REALM # test, users, default, $REALM
dn: uid=test,ou=users,ou=default,$SUFFIX dn: uid=test,ou=users,ou=default,$SUFFIX
changetype: add
uidNumber: 1001 uidNumber: 1001
uid: test uid: test
gecos: test gecos: test
@ -13,8 +14,17 @@ shadowInactive: -1
shadowLastChange: 13655 shadowLastChange: 13655
shadowFlag: -1 shadowFlag: -1
gidNumber: 100 gidNumber: 100
objectclass: krbPrincipalAux
objectclass: inetOrgPerson
objectClass: posixAccount objectClass: posixAccount
objectClass: shadowAccount objectClass: shadowAccount
objectClass: account objectClass: account
objectClass: top objectClass: top
cn: test cn: Test User
sn: User
krbPrincipalName: test@$REALM
dn: cn=admin,ou=groups,ou=default,$SUFFIX
changetype: modify
add: uniqueMember
uniqueMember: uid=test,ou=users,ou=default,$SUFFIX

View File

@ -88,8 +88,10 @@ class DsInstance:
self.__create_instance() self.__create_instance()
self.__add_default_schemas() self.__add_default_schemas()
self.__enable_ssl() self.__enable_ssl()
self.__certmap_conf()
self.restart() self.restart()
self.__add_default_layout() self.__add_default_layout()
self.__create_test_users()
def config_dirname(self): def config_dirname(self):
if not self.serverid: if not self.serverid:
@ -136,7 +138,7 @@ class DsInstance:
args = ["/usr/sbin/setup-ds.pl", "--silent", "--logfile", "-", "-f", inf_fd.name] args = ["/usr/sbin/setup-ds.pl", "--silent", "--logfile", "-", "-f", inf_fd.name]
logging.debug("calling setup-ds.pl") logging.debug("calling setup-ds.pl")
else: else:
args = ["/usr/sbin/ds_newinst.pl", inf_fd.name] args = ["/usr/bin/ds_newinst.pl", inf_fd.name]
logging.debug("calling ds_newinst.pl") logging.debug("calling ds_newinst.pl")
run(args) run(args)
logging.debug("completed creating ds instance") logging.debug("completed creating ds instance")
@ -166,3 +168,21 @@ class DsInstance:
"-w", self.admin_password, "-f", inf_fd.name] "-w", self.admin_password, "-f", inf_fd.name]
run(args) run(args)
logging.debug("done adding default ds layout") logging.debug("done adding default ds layout")
def __create_test_users(self):
logging.debug("create test users ldif")
txt = template_file(SHARE_DIR + "test-users-template.ldif", self.sub_dict)
user_fd = open(SHARE_DIR+"test-users.ldif", "w")
user_fd.write(txt)
user_fd.close()
logging.debug("done creating test users ldif")
def __certmap_conf(self):
logging.debug("configuring certmap.conf for ds instance")
dirname = self.config_dirname()
certmap_conf = template_file(SHARE_DIR+"certmap.conf.template", self.sub_dict)
certmap_fd = open(dirname+"certmap.conf", "w+")
certmap_fd.write(certmap_conf)
certmap_fd.close()
logging.debug("done configuring certmap.conf for ds instance")

View File

@ -1,6 +1,6 @@
#! /usr/bin/python -E #! /usr/bin/python -E
# Authors: Rich Megginson <richm@redhat.com> # Authors: Rich Megginson <richm@redhat.com>
# Rob Crittenden <rcritten2redhat.com # Rob Crittenden <rcritten@redhat.com
# #
# Copyright (C) 2007 Red Hat # Copyright (C) 2007 Red Hat
# see file 'COPYING' for use and warranty information # see file 'COPYING' for use and warranty information
@ -33,6 +33,8 @@ import ldap
import cStringIO import cStringIO
import time import time
import operator import operator
import struct
from ldap.controls import LDAPControl,DecodeControlTuples,EncodeControlTuples
from ldap.ldapobject import SimpleLDAPObject from ldap.ldapobject import SimpleLDAPObject
@ -197,31 +199,25 @@ class IPAdmin(SimpleLDAPObject):
raise raise
def __localinit__(self): def __localinit__(self):
SimpleLDAPObject.__init__(self,'ldap://%s:%d' % (self.host,self.port)) SimpleLDAPObject.__init__(self,'ldaps://%s:%d' % (self.host,self.port))
# see if binddn is a dn or a uid that we need to lookup
if self.binddn and not IPAdmin.is_a_dn(self.binddn): def __init__(self,host,port,cacert,bindcert,bindkey,proxydn=None):
self.simple_bind("","") # anon
ent = self.getEntry(IPAdmin.CFGSUFFIX, ldap.SCOPE_SUBTREE,
"(uid=%s)" % self.binddn,
['uid'])
if ent:
self.binddn = ent.dn
else:
print "Error: could not find %s under %s" % (self.binddn, IPAdmin.CFGSUFFIX)
self.simple_bind(self.binddn,self.bindpw)
# self.__initPart2()
def __init__(self,host,port,binddn,bindpw):
"""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 work is
done in __localinit__ and __initPart2 - these are separated out this way so done in __localinit__ and __initPart2 - these are separated out this way so
that we can call them from places other than instance creation e.g. when that we can call them from places other than instance creation e.g. when
using the start command, we just need to reconnect, not create a new instance""" using the start command, we just need to reconnect, not create a new instance"""
# ldap.set_option(ldap.OPT_DEBUG_LEVEL,255)
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_KEYFILE,bindkey)
self.__wrapmethods() self.__wrapmethods()
self.port = port or 389 self.port = port or 389
self.sslport = 0 self.sslport = 0
self.host = host self.host = host
self.binddn = binddn self.bindcert = bindcert
self.bindpw = bindpw self.bindkey = bindkey
self.proxydn = proxydn
# see if is local or not # see if is local or not
host1 = IPAdmin.getfqdn(host) host1 = IPAdmin.getfqdn(host)
host2 = IPAdmin.getfqdn() host2 = IPAdmin.getfqdn()
@ -237,7 +233,22 @@ class IPAdmin(SimpleLDAPObject):
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"""
res = self.search(*args) # 0x04 = Octet String
# 4|0x80 sets the length of the length at 4 bytes
# the struct() gets us the length in bytes of string s
# s is the proxy dn to send
if self.proxydn is not None:
proxydn = chr(0x04) + chr(4|0x80) + struct.pack('l', socket.htonl(len(self.proxydn))) + self.proxydn;
# Create the proxy control
sctrl=[]
sctrl.append(LDAPControl('2.16.840.1.113730.3.4.18',True,proxydn))
else:
sctrl=None
res = self.search_ext(args[0], args[1], filterstr=args[2], serverctrls=sctrl)
type, obj = self.result(res) type, obj = self.result(res)
if not obj: if not obj:
raise NoSuchEntryError("no such entry for " + str(args)) raise NoSuchEntryError("no such entry for " + str(args))
@ -246,10 +257,38 @@ class IPAdmin(SimpleLDAPObject):
else: # assume list/tuple else: # assume list/tuple
return obj[0] return obj[0]
def getList(self,*args):
"""This wraps the search function to find all users."""
res = self.search(*args)
type, obj = self.result(res)
if not obj:
raise NoSuchEntryError("no such entry for " + str(args))
all_users = []
for s in obj:
all_users.append(s)
return all_users
def addEntry(self,*args): def addEntry(self,*args):
"""This wraps the add function. It assumes that the entry is already """This wraps the add function. It assumes that the entry is already
populated with all of the desired objectclasses and attributes""" populated with all of the desired objectclasses and attributes"""
if self.proxydn is not None:
proxydn = chr(0x04) + chr(4|0x80) + struct.pack('l', socket.htonl(len(self.proxydn))) + self.proxydn;
# Create the proxy control
sctrl=[]
sctrl.append(LDAPControl('2.16.840.1.113730.3.4.18',True,proxydn))
else:
sctrl=None
# Create the proxy control
sctrl=[]
sctrl.append(LDAPControl('2.16.840.1.113730.3.4.18',True,proxydn))
try: try:
self.set_option(ldap.OPT_SERVER_CONTROLS, sctrl)
self.add_s(*args) self.add_s(*args)
except ldap.ALREADY_EXISTS: except ldap.ALREADY_EXISTS:
raise ldap.ALREADY_EXISTS raise ldap.ALREADY_EXISTS

View File

@ -28,6 +28,7 @@ from time import gmtime
import os import os
import pwd import pwd
import socket import socket
import time
from util import * from util import *
def host_to_domain(fqdn): def host_to_domain(fqdn):
@ -82,6 +83,8 @@ class KrbInstance:
self.__create_ds_keytab() self.__create_ds_keytab()
self.__create_http_keytab()
self.__create_sample_bind_zone() self.__create_sample_bind_zone()
self.start() self.start()
@ -175,3 +178,18 @@ class KrbInstance:
cfg_fd.close() cfg_fd.close()
pent = pwd.getpwnam(self.ds_user) pent = pwd.getpwnam(self.ds_user)
os.chown("/etc/sysconfig/fedora-ds", pent.pw_uid, pent.pw_gid) os.chown("/etc/sysconfig/fedora-ds", pent.pw_uid, pent.pw_gid)
def __create_http_keytab(self):
(kwrite, kread, kerr) = os.popen3("/usr/kerberos/sbin/kadmin.local")
kwrite.write("addprinc -randkey HTTP/"+self.fqdn+"@"+self.realm+"\n")
kwrite.flush()
kwrite.write("ktadd -k /etc/httpd/conf/ipa.keytab HTTP/"+self.fqdn+"@"+self.realm+"\n")
kwrite.flush()
kwrite.close()
kread.close()
kerr.close()
while not file_exists("/etc/httpd/conf/ipa.keytab"):
time.sleep(1)
pent = pwd.getpwnam("apache")
os.chown("/etc/httpd/conf/ipa.keytab", pent.pw_uid, pent.pw_gid)

View File

@ -24,152 +24,205 @@ import ldap
import ipaserver.dsinstance import ipaserver.dsinstance
import ipaserver.ipaldap import ipaserver.ipaldap
import ipaserver.util import ipaserver.util
import pdb
import string import string
from types import * from types import *
import xmlrpclib import xmlrpclib
import ipa.config import ipa.config
# FIXME, this needs to be auto-discovered class IPAServer:
host = 'localhost'
port = 389
binddn = "cn=directory manager"
bindpw = "freeipa"
ipa.config.init_config() def __init__(self):
basedn = ipaserver.util.realm_to_suffix(ipa.config.config.get_realm()) # FIXME, this needs to be auto-discovered
import sys self.host = 'localhost'
sys.stderr.write(basedn) self.port = 636
scope = ldap.SCOPE_SUBTREE self.bindcert = "/usr/share/ipa/cert.pem"
self.bindkey = "/usr/share/ipa/key.pem"
self.bindca = "/usr/share/ipa/cacert.asc"
ipa.config.init_config()
self.basedn = ipaserver.util.realm_to_suffix(ipa.config.config.get_realm())
self.scope = ldap.SCOPE_SUBTREE
self.princ = None
def get_user (username): def set_principal(self, princ):
"""Get a specific user's entry. Return as a dict of values. self.princ = princ
Multi-valued fields are represented as lists.
""" def get_dn_from_principal(self, princ):
ent="" """Given a kerberls principal get the LDAP uid"""
filter = "(krbPrincipalName=" + princ + ")"
# FIXME: Is this the filter we want or should it be more specific? try:
filter = "(uid=" + username + ")" m1 = ipaserver.ipaldap.IPAdmin(self.host,self.port,self.bindca,self.bindcert,self.bindkey)
try: ent = m1.getEntry(self.basedn, self.scope, filter, None)
m1 = ipaserver.ipaldap.IPAdmin(host,port,binddn,bindpw) except ldap.LDAPError, e:
ent = m1.getEntry(basedn, scope, filter, None) raise xmlrpclib.Fault(1, e)
except ldap.LDAPError, e: except ipaserver.ipaldap.NoSuchEntryError:
raise xmlrpclib.Fault(1, e) raise xmlrpclib.Fault(2, "No such user")
except ipaserver.ipaldap.NoSuchEntryError:
raise xmlrpclib.Fault(2, "No such user") return "dn:" + ent.dn
# Convert to LDIF def convert_entry(self, ent):
entry = str(ent)
# Convert to LDIF
# Strip off any junk entry = str(ent)
entry = entry.strip()
# Strip off any junk
# Don't need to identify binary fields and this breaks the parser so entry = entry.strip()
# remove double colons
entry = entry.replace('::', ':') # Don't need to identify binary fields and this breaks the parser so
specs = [spec.split(':') for spec in entry.split('\n')] # remove double colons
entry = entry.replace('::', ':')
# Convert into a dict. We need to handle multi-valued attributes as well specs = [spec.split(':') for spec in entry.split('\n')]
# so we'll convert those into lists.
user={} # Convert into a dict. We need to handle multi-valued attributes as well
for (k,v) in specs: # so we'll convert those into lists.
k = k.lower() user={}
if user.get(k) is not None: for (k,v) in specs:
if isinstance(user[k],list): k = k.lower()
user[k].append(v.strip()) if user.get(k) is not None:
if isinstance(user[k],list):
user[k].append(v.strip())
else:
first = user[k]
user[k] = []
user[k].append(first)
user[k].append(v.strip())
else: else:
first = user[k] user[k] = v.strip()
user[k] = []
user[k].append(first) return user
user[k].append(v.strip())
else: def get_user (self, username, opts=None):
user[k] = v.strip() """Get a specific user's entry. Return as a dict of values.
Multi-valued fields are represented as lists.
"""
ent=""
if opts:
self.set_principal(opts['remoteuser'])
if (isinstance(username, tuple)):
username = username[0]
try:
dn = self.get_dn_from_principal(self.princ)
except ldap.LDAPError, e:
raise xmlrpclib.Fault(1, e)
except ipaserver.ipaldap.NoSuchEntryError:
raise xmlrpclib.Fault(2, "No such user")
filter = "(uid=" + username + ")"
try:
m1 = ipaserver.ipaldap.IPAdmin(self.host,self.port,self.bindca,self.bindcert,self.bindkey,dn)
ent = m1.getEntry(self.basedn, self.scope, filter, None)
except ldap.LDAPError, e:
raise xmlrpclib.Fault(1, e)
except ipaserver.ipaldap.NoSuchEntryError:
raise xmlrpclib.Fault(2, "No such user")
return self.convert_entry(ent)
def add_user (self, user, opts=None):
"""Add a user in LDAP"""
if (isinstance(user, tuple)):
user = user[0]
dn="uid=%s,ou=users,ou=default,%s" % (user['uid'], self.basedn)
entry = ipaserver.ipaldap.Entry(str(dn))
return user # some required objectclasses
# return str(ent) # return as LDIF entry.setValues('objectClass', 'top', 'posixAccount', 'shadowAccount', 'account', 'person', 'inetOrgPerson', 'organizationalPerson', 'krbPrincipalAux', 'krbTicketPolicyAux')
# Fill in shadow fields
entry.setValue('shadowMin', '0')
entry.setValue('shadowMax', '99999')
entry.setValue('shadowWarning', '7')
entry.setValue('shadowExpire', '-1')
entry.setValue('shadowInactive', '-1')
entry.setValue('shadowFlag', '-1')
# FIXME: calculate shadowLastChange
# fill in our new entry with everything sent by the user
for u in user:
entry.setValues(str(u), str(user[u]))
def add_user (user): if opts:
"""Add a user in LDAP""" self.set_principal(opts['remoteuser'])
dn="uid=%s,ou=users,ou=default,%s" % (user['uid'], basedn)
entry = ipaserver.ipaldap.Entry(dn) try:
dn = self.get_dn_from_principal(self.princ)
except ldap.LDAPError, e:
raise xmlrpclib.Fault(1, e)
except ipaserver.ipaldap.NoSuchEntryError:
raise xmlrpclib.Fault(2, "No such user")
# some required objectclasses try:
entry.setValues('objectClass', 'top', 'posixAccount', 'shadowAccount', 'account', 'person', 'inetOrgPerson', 'organizationalPerson', 'krbPrincipalAux', 'krbTicketPolicyAux') m1 = ipaserver.ipaldap.IPAdmin(self.host,self.port,self.bindca,self.bindcert,self.bindkey,dn)
res = m1.addEntry(entry)
# Fill in shadow fields return res
entry.setValue('shadowMin', '0') except ldap.ALREADY_EXISTS:
entry.setValue('shadowMax', '99999') raise xmlrpclib.Fault(3, "User already exists")
entry.setValue('shadowWarning', '7') except ldap.LDAPError, e:
entry.setValue('shadowExpire', '-1') raise xmlrpclib.Fault(1, str(e))
entry.setValue('shadowInactive', '-1')
entry.setValue('shadowFlag', '-1') def get_add_schema (self):
"""Get the list of fields to be used when adding users in the GUI."""
# FIXME: calculate shadowLastChange
# FIXME: this needs to be pulled from LDAP
# fill in our new entry with everything sent by the user fields = []
for u in user:
entry.setValues(u, user[u]) field1 = {
"name": "uid" ,
try: "label": "Login:",
m1 = ipaserver.ipaldap.IPAdmin(host,port,binddn,bindpw) "type": "text",
res = m1.addEntry(entry) "validator": "text",
return res "required": "true"
except ldap.ALREADY_EXISTS: }
raise xmlrpclib.Fault(3, "User already exists") fields.append(field1)
return None
except ldap.LDAPError, e: field1 = {
raise xmlrpclib.Fault(1, str(e)) "name": "givenName" ,
return None "label": "First name:",
"type": "text",
def get_add_schema (): "validator": "string",
"""Get the list of fields to be used when adding users in the GUI.""" "required": "true"
}
# FIXME: this needs to be pulled from LDAP fields.append(field1)
fields = []
field1 = {
field1 = { "name": "sn" ,
"name": "uid" , "label": "Last name:",
"label": "Login:", "type": "text",
"type": "text", "validator": "string",
"validator": "text", "required": "true"
"required": "true" }
} fields.append(field1)
fields.append(field1)
field1 = {
field1 = { "name": "mail" ,
"name": "userPassword" , "label": "E-mail address:",
"label": "Password:", "type": "text",
"type": "password", "validator": "email",
"validator": "String", "required": "true"
"required": "true" }
} fields.append(field1)
fields.append(field1)
return fields
field1 = {
"name": "givenName" , def get_all_users (self):
"label": "First name:", """Return a list containing a User object for each
"type": "text", existing user.
"validator": "string", """
"required": "true"
} # FIXME: Is this the filter we want or should it be more specific?
fields.append(field1) filter = "(objectclass=posixAccount)"
try:
field1 = { m1 = ipaserver.ipaldap.IPAdmin(self.host,self.port,self.bindca,self.bindcert,self.bindkey)
"name": "sn" , all_users = m1.getList(self.basedn, self.scope, filter, None)
"label": "Last name:", except ldap.LDAPError, e:
"type": "text", raise xmlrpclib.Fault(1, e)
"validator": "string", except ipaserver.ipaldap.NoSuchEntryError:
"required": "true" raise xmlrpclib.Fault(2, "No such user")
}
fields.append(field1) users = []
for u in all_users:
field1 = { users.append(self.convert_entry(u))
"name": "mail" ,
"label": "E-mail address:", return users
"type": "text",
"validator": "email",
"required": "true"
}
fields.append(field1)
return fields

View File

@ -3,15 +3,15 @@
Alias /ipa "/usr/share/ipa/ipaserver/XMLRPC" Alias /ipa "/usr/share/ipa/ipaserver/XMLRPC"
<Directory "/usr/share/ipa/ipaserver"> <Directory "/usr/share/ipa/ipaserver">
# AuthType Kerberos AuthType Kerberos
# AuthName "Kerberos Login" AuthName "Kerberos Login"
# KrbMethodNegotiate on KrbMethodNegotiate on
# KrbMethodK5Passwd off KrbMethodK5Passwd off
# KrbServiceName HTTP KrbServiceName HTTP
# KrbAuthRealms GREYOAK.COM KrbAuthRealms GREYOAK.COM
# Krb5KeyTab /etc/httpd/conf/ipa.keytab Krb5KeyTab /etc/httpd/conf/ipa.keytab
# KrbSaveCredentials on KrbSaveCredentials on
# Require valid-user Require valid-user
ErrorDocument 401 /errors/unauthorized.html ErrorDocument 401 /errors/unauthorized.html
SetHandler mod_python SetHandler mod_python

View File

@ -123,11 +123,16 @@ 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): def _marshaled_dispatch(self, data, remoteuser):
"""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)
opts={}
opts['remoteuser'] = remoteuser
params = ipaserver.encode_args(params, opts)
# special case # special case
# if method == "get_user": # if method == "get_user":
# Marshaller._Marshaller__dump = xmlrpclib_dump # Marshaller._Marshaller__dump = xmlrpclib_dump
@ -239,7 +244,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()) response = self._marshaled_dispatch(req.read(), req.user)
req.content_type = "text/xml" req.content_type = "text/xml"
req.set_content_length(len(response)) req.set_content_length(len(response))
@ -267,10 +272,12 @@ def handler(req, profiling=False):
else: else:
opts = req.get_options() opts = req.get_options()
try: try:
f = funcs.IPAServer()
h = ModXMLRPCRequestHandler() h = ModXMLRPCRequestHandler()
h.register_function(funcs.get_user) h.register_function(f.get_user)
h.register_function(funcs.add_user) h.register_function(f.add_user)
h.register_function(funcs.get_add_schema) h.register_function(f.get_add_schema)
h.register_function(f.get_all_users)
h.handle_request(req) h.handle_request(req)
finally: finally:
pass pass