mirror of
https://salsa.debian.org/freeipa-team/freeipa.git
synced 2025-02-25 18:55:28 -06:00
- 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:
parent
66ab69d0b2
commit
993f76fe60
@ -1,6 +1,6 @@
|
||||
Name: freeipa-admintools
|
||||
Version: 0.1.0
|
||||
Release: 1%{?dist}
|
||||
Release: 3%{?dist}
|
||||
Summary: FreeIPA authentication server
|
||||
|
||||
Group: System Environment/Base
|
||||
@ -36,7 +36,12 @@ rm -rf %{buildroot}
|
||||
|
||||
|
||||
%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
|
||||
- Initial rpm version
|
||||
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
Name: freeipa-admintools
|
||||
Version: 0.1.0
|
||||
Release: 1%{?dist}
|
||||
Release: 3%{?dist}
|
||||
Summary: FreeIPA authentication server
|
||||
|
||||
Group: System Environment/Base
|
||||
@ -36,7 +36,12 @@ rm -rf %{buildroot}
|
||||
|
||||
|
||||
%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
|
||||
- Initial rpm version
|
||||
|
||||
|
||||
|
@ -21,10 +21,11 @@
|
||||
import sys
|
||||
from optparse import OptionParser
|
||||
import ipa
|
||||
import ipa.rpcclient
|
||||
import ipa.ipaclient as ipaclient
|
||||
import ipa.config
|
||||
|
||||
import xmlrpclib
|
||||
import kerberos
|
||||
|
||||
def usage():
|
||||
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"
|
||||
|
||||
try:
|
||||
ipa.rpcclient.add_user(user)
|
||||
print args[0] + " successfully added"
|
||||
client = ipaclient.IPAClient()
|
||||
client.add_user(user)
|
||||
print args[1] + " successfully added"
|
||||
except xmlrpclib.Fault, f:
|
||||
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
|
||||
|
||||
|
@ -20,13 +20,12 @@
|
||||
|
||||
import sys
|
||||
from optparse import OptionParser
|
||||
import ipa
|
||||
import ipa.rpcclient
|
||||
import ipa.ipaclient as ipaclient
|
||||
import ipa.config
|
||||
|
||||
import base64
|
||||
import sys
|
||||
import xmlrpclib
|
||||
import kerberos
|
||||
|
||||
def usage():
|
||||
print "ipa-finduser <uid>"
|
||||
@ -48,16 +47,27 @@ def main():
|
||||
usage()
|
||||
|
||||
try:
|
||||
ent = ipa.rpcclient.get_user(args[1])
|
||||
for name, value in ent.items():
|
||||
if isinstance(value, str):
|
||||
print name + ": " + value
|
||||
client = ipaclient.IPAClient()
|
||||
ent = client.get_user(args[1])
|
||||
attr = ent.attrList()
|
||||
|
||||
print "dn: " + ent.dn
|
||||
|
||||
for a in attr:
|
||||
value = ent.getValues(a)
|
||||
if isinstance(value,str):
|
||||
print a + ": " + value
|
||||
else:
|
||||
print name + ": "
|
||||
for x in value:
|
||||
print "\t" + x
|
||||
print a + ": "
|
||||
for l in value:
|
||||
print "\t" + l
|
||||
|
||||
except xmlrpclib.Fault, fault:
|
||||
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
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
Name: freeipa-python
|
||||
Version: 0.1.0
|
||||
Release: 1%{?dist}
|
||||
Release: 3%{?dist}
|
||||
Summary: FreeIPA authentication server
|
||||
|
||||
Group: System Environment/Base
|
||||
@ -42,6 +42,13 @@ rm -rf %{buildroot}
|
||||
|
||||
|
||||
%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
|
||||
- Initial rpm version
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
Name: freeipa-python
|
||||
Version: VERSION
|
||||
Release: 1%{?dist}
|
||||
Release: 3%{?dist}
|
||||
Summary: FreeIPA authentication server
|
||||
|
||||
Group: System Environment/Base
|
||||
@ -42,6 +42,13 @@ rm -rf %{buildroot}
|
||||
|
||||
|
||||
%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
|
||||
- Initial rpm version
|
||||
|
||||
|
87
ipa-python/ipaclient.py
Normal file
87
ipa-python/ipaclient.py
Normal 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
|
55
ipa-python/krbtransport.py
Normal file
55
ipa-python/krbtransport.py
Normal 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
|
||||
|
@ -20,85 +20,101 @@
|
||||
|
||||
#!/usr/bin/python
|
||||
|
||||
try:
|
||||
import krbV
|
||||
except ImportError:
|
||||
pass
|
||||
import xmlrpclib
|
||||
import socket
|
||||
import config
|
||||
from krbtransport import KerbTransport
|
||||
from kerberos import GSSError
|
||||
import os
|
||||
import base64
|
||||
import user
|
||||
import ipa
|
||||
|
||||
# Some errors to catch
|
||||
# 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:
|
||||
|
||||
def __init__(self):
|
||||
ipa.config.init_config()
|
||||
|
||||
def server_url(self):
|
||||
return "http://" + config.config.get_server() + "/ipa"
|
||||
|
||||
def setup_server():
|
||||
return xmlrpclib.ServerProxy(server_url())
|
||||
def setup_server(self):
|
||||
return xmlrpclib.ServerProxy(self.server_url(), KerbTransport())
|
||||
|
||||
def get_user(username):
|
||||
def convert_entry(self,ent):
|
||||
# Convert into a dict. We need to handle multi-valued attributes as well
|
||||
# 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 = setup_server()
|
||||
server = self.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
|
||||
return result
|
||||
|
||||
def add_user(user):
|
||||
|
||||
def add_user(self,user):
|
||||
"""Add a new user"""
|
||||
server = setup_server()
|
||||
|
||||
# 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'])
|
||||
server = self.setup_server()
|
||||
|
||||
try:
|
||||
result = server.add_user(user)
|
||||
return 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
|
||||
|
||||
def get_add_schema():
|
||||
return result
|
||||
|
||||
def get_add_schema(self):
|
||||
"""Get the list of attributes we need to ask when adding a new
|
||||
user.
|
||||
"""
|
||||
server = setup_server()
|
||||
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, fault.faultString)
|
||||
return None
|
||||
raise xmlrpclib.Fault(fault.faultCode, fault.faultString)
|
||||
except socket.error, (value, msg):
|
||||
raise xmlrpclib.Fault(value, msg)
|
||||
|
||||
return result
|
||||
|
||||
def get_all_users (self):
|
||||
"""Return a list containing a User object for each existing user."""
|
||||
|
||||
server = self.setup_server()
|
||||
try:
|
||||
result = server.get_all_users()
|
||||
except xmlrpclib.Fault, fault:
|
||||
raise xmlrpclib.Fault(fault.faultCode, fault.faultString)
|
||||
except socket.error, (value, msg):
|
||||
raise xmlrpclib.Fault(value, msg)
|
||||
return None
|
||||
|
||||
return result
|
||||
|
112
ipa-python/user.py
Normal file
112
ipa-python/user.py
Normal 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()
|
@ -1,6 +1,6 @@
|
||||
Name: freeipa-server
|
||||
Version: 0.1.0
|
||||
Release: 1%{?dist}
|
||||
Release: 3%{?dist}
|
||||
Summary: FreeIPA authentication server
|
||||
|
||||
Group: System Environment/Base
|
||||
@ -10,7 +10,7 @@ Source0: %{name}-%{version}.tgz
|
||||
BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-root-%(%{__id_u} -n)
|
||||
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
|
||||
|
||||
@ -44,6 +44,17 @@ rm -rf %{buildroot}
|
||||
|
||||
|
||||
%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
|
||||
- Initial rpm version
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
Name: freeipa-server
|
||||
Version: VERSION
|
||||
Release: 1%{?dist}
|
||||
Release: 3%{?dist}
|
||||
Summary: FreeIPA authentication server
|
||||
|
||||
Group: System Environment/Base
|
||||
@ -10,7 +10,7 @@ Source0: %{name}-%{version}.tgz
|
||||
BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-root-%(%{__id_u} -n)
|
||||
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
|
||||
|
||||
@ -44,6 +44,17 @@ rm -rf %{buildroot}
|
||||
|
||||
|
||||
%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
|
||||
- Initial rpm version
|
||||
|
||||
|
@ -6,6 +6,7 @@ install:
|
||||
install -m 755 ipa-server-install $(SBINDIR)
|
||||
install -m 755 ipa-server-setupssl $(SBINDIR)
|
||||
$(MAKE) -C share $@
|
||||
$(MAKE) -C test $@
|
||||
|
||||
clean:
|
||||
$(MAKE) -C share $@
|
||||
|
@ -119,6 +119,9 @@ def main():
|
||||
# Restart apache
|
||||
run(["/sbin/service", "httpd", "restart"])
|
||||
|
||||
# Set apache to be on at boot
|
||||
run(["/sbin/chkconfig", "httpd", "on"])
|
||||
|
||||
# Create the config file
|
||||
fd = open("/etc/ipa/ipa.conf", "w")
|
||||
fd.write("[defaults]\n")
|
||||
|
@ -1,4 +1,4 @@
|
||||
#!/bin/sh
|
||||
#!/bin/bash
|
||||
|
||||
if [ "$1" ] ; then
|
||||
password=$1
|
||||
@ -49,22 +49,14 @@ if [ -f $secdir/cert8.db ] ; then
|
||||
needServerCert=1
|
||||
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-"
|
||||
prefixarg="-P $prefix"
|
||||
else
|
||||
needCA=1
|
||||
needServerCert=1
|
||||
needASCert=1
|
||||
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"
|
||||
exit 0
|
||||
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
|
||||
fi
|
||||
|
||||
if test -n "$needASCert" ; then
|
||||
# Generate the admin server certificate
|
||||
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
|
||||
# 8. Generate the web service client 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
|
||||
|
||||
# export the admin server certificate/private key for import into its key/cert db
|
||||
pk12util -d $secdir $prefixarg -o $secdir/adminserver.p12 -n server-cert -w $secdir/pwdfile.txt -k $secdir/pwdfile.txt
|
||||
if test -n "$isroot" ; then
|
||||
chown $uid:$gid $secdir/adminserver.p12
|
||||
fi
|
||||
chmod 400 $secdir/adminserver.p12
|
||||
fi
|
||||
pk12util -d $secdir $prefixarg -o $secdir/webservice.p12 -n "webservice" -w $secdir/pwdfile.txt -k $secdir/pwdfile.txt
|
||||
|
||||
openssl pkcs12 -in $secdir/webservice.p12 -clcerts -nokeys -out /usr/share/ipa/cert.pem -passin file:$secdir/pwdfile.txt
|
||||
openssl pkcs12 -in $secdir/webservice.p12 -nocerts -nodes -out /usr/share/ipa/key.pem -passin file:$secdir/pwdfile.txt
|
||||
|
||||
cp -p $secdir/cacert.asc /usr/share/ipa
|
||||
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
|
||||
if [ ! -f $secdir/pin.txt ] ; then
|
||||
@ -153,42 +145,6 @@ if [ -n "$prefix" ] ; then
|
||||
mv $secdir/${prefix}key3.db $secdir/key3.db
|
||||
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
|
||||
|
||||
ldapmodify -x -h localhost -p $ldapport -D "cn=Directory Manager" -w $password <<EOF
|
||||
|
@ -31,3 +31,26 @@ ou: groups
|
||||
#objectClass: top
|
||||
#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
|
||||
|
82
ipa-server/ipa-install/share/certmap.conf.template
Normal file
82
ipa-server/ipa-install/share/certmap.conf.template
Normal 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
|
@ -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="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: (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";)
|
||||
|
8
ipa-server/ipa-install/test/Makefile
Normal file
8
ipa-server/ipa-install/test/Makefile
Normal file
@ -0,0 +1,8 @@
|
||||
SHAREDIR = $(DESTDIR)/usr/share/ipa
|
||||
|
||||
install:
|
||||
-mkdir -p $(SHAREDIR)
|
||||
install -m 644 *.ldif $(SHAREDIR)
|
||||
|
||||
clean:
|
||||
rm -f *~
|
@ -1,5 +1,6 @@
|
||||
# test, users, default, $REALM
|
||||
dn: uid=test,ou=users,ou=default,$SUFFIX
|
||||
changetype: add
|
||||
uidNumber: 1001
|
||||
uid: test
|
||||
gecos: test
|
||||
@ -13,8 +14,17 @@ shadowInactive: -1
|
||||
shadowLastChange: 13655
|
||||
shadowFlag: -1
|
||||
gidNumber: 100
|
||||
objectclass: krbPrincipalAux
|
||||
objectclass: inetOrgPerson
|
||||
objectClass: posixAccount
|
||||
objectClass: shadowAccount
|
||||
objectClass: account
|
||||
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
|
@ -88,8 +88,10 @@ class DsInstance:
|
||||
self.__create_instance()
|
||||
self.__add_default_schemas()
|
||||
self.__enable_ssl()
|
||||
self.__certmap_conf()
|
||||
self.restart()
|
||||
self.__add_default_layout()
|
||||
self.__create_test_users()
|
||||
|
||||
def config_dirname(self):
|
||||
if not self.serverid:
|
||||
@ -136,7 +138,7 @@ class DsInstance:
|
||||
args = ["/usr/sbin/setup-ds.pl", "--silent", "--logfile", "-", "-f", inf_fd.name]
|
||||
logging.debug("calling setup-ds.pl")
|
||||
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")
|
||||
run(args)
|
||||
logging.debug("completed creating ds instance")
|
||||
@ -166,3 +168,21 @@ class DsInstance:
|
||||
"-w", self.admin_password, "-f", inf_fd.name]
|
||||
run(args)
|
||||
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")
|
||||
|
@ -1,6 +1,6 @@
|
||||
#! /usr/bin/python -E
|
||||
# Authors: Rich Megginson <richm@redhat.com>
|
||||
# Rob Crittenden <rcritten2redhat.com
|
||||
# Rob Crittenden <rcritten@redhat.com
|
||||
#
|
||||
# Copyright (C) 2007 Red Hat
|
||||
# see file 'COPYING' for use and warranty information
|
||||
@ -33,6 +33,8 @@ import ldap
|
||||
import cStringIO
|
||||
import time
|
||||
import operator
|
||||
import struct
|
||||
from ldap.controls import LDAPControl,DecodeControlTuples,EncodeControlTuples
|
||||
|
||||
from ldap.ldapobject import SimpleLDAPObject
|
||||
|
||||
@ -197,31 +199,25 @@ class IPAdmin(SimpleLDAPObject):
|
||||
raise
|
||||
|
||||
def __localinit__(self):
|
||||
SimpleLDAPObject.__init__(self,'ldap://%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):
|
||||
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()
|
||||
SimpleLDAPObject.__init__(self,'ldaps://%s:%d' % (self.host,self.port))
|
||||
|
||||
def __init__(self,host,port,binddn,bindpw):
|
||||
def __init__(self,host,port,cacert,bindcert,bindkey,proxydn=None):
|
||||
"""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
|
||||
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"""
|
||||
# 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.port = port or 389
|
||||
self.sslport = 0
|
||||
self.host = host
|
||||
self.binddn = binddn
|
||||
self.bindpw = bindpw
|
||||
self.bindcert = bindcert
|
||||
self.bindkey = bindkey
|
||||
self.proxydn = proxydn
|
||||
# see if is local or not
|
||||
host1 = IPAdmin.getfqdn(host)
|
||||
host2 = IPAdmin.getfqdn()
|
||||
@ -237,7 +233,22 @@ class IPAdmin(SimpleLDAPObject):
|
||||
|
||||
def getEntry(self,*args):
|
||||
"""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)
|
||||
if not obj:
|
||||
raise NoSuchEntryError("no such entry for " + str(args))
|
||||
@ -246,10 +257,38 @@ class IPAdmin(SimpleLDAPObject):
|
||||
else: # assume list/tuple
|
||||
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):
|
||||
"""This wraps the add function. It assumes that the entry is already
|
||||
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:
|
||||
self.set_option(ldap.OPT_SERVER_CONTROLS, sctrl)
|
||||
self.add_s(*args)
|
||||
except ldap.ALREADY_EXISTS:
|
||||
raise ldap.ALREADY_EXISTS
|
||||
|
@ -28,6 +28,7 @@ from time import gmtime
|
||||
import os
|
||||
import pwd
|
||||
import socket
|
||||
import time
|
||||
from util import *
|
||||
|
||||
def host_to_domain(fqdn):
|
||||
@ -82,6 +83,8 @@ class KrbInstance:
|
||||
|
||||
self.__create_ds_keytab()
|
||||
|
||||
self.__create_http_keytab()
|
||||
|
||||
self.__create_sample_bind_zone()
|
||||
|
||||
self.start()
|
||||
@ -175,3 +178,18 @@ class KrbInstance:
|
||||
cfg_fd.close()
|
||||
pent = pwd.getpwnam(self.ds_user)
|
||||
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)
|
||||
|
@ -24,40 +24,44 @@ import ldap
|
||||
import ipaserver.dsinstance
|
||||
import ipaserver.ipaldap
|
||||
import ipaserver.util
|
||||
import pdb
|
||||
import string
|
||||
from types import *
|
||||
import xmlrpclib
|
||||
import ipa.config
|
||||
|
||||
# FIXME, this needs to be auto-discovered
|
||||
host = 'localhost'
|
||||
port = 389
|
||||
binddn = "cn=directory manager"
|
||||
bindpw = "freeipa"
|
||||
class IPAServer:
|
||||
|
||||
ipa.config.init_config()
|
||||
basedn = ipaserver.util.realm_to_suffix(ipa.config.config.get_realm())
|
||||
import sys
|
||||
sys.stderr.write(basedn)
|
||||
scope = ldap.SCOPE_SUBTREE
|
||||
def __init__(self):
|
||||
# FIXME, this needs to be auto-discovered
|
||||
self.host = 'localhost'
|
||||
self.port = 636
|
||||
self.bindcert = "/usr/share/ipa/cert.pem"
|
||||
self.bindkey = "/usr/share/ipa/key.pem"
|
||||
self.bindca = "/usr/share/ipa/cacert.asc"
|
||||
|
||||
def get_user (username):
|
||||
"""Get a specific user's entry. Return as a dict of values.
|
||||
Multi-valued fields are represented as lists.
|
||||
"""
|
||||
ent=""
|
||||
ipa.config.init_config()
|
||||
self.basedn = ipaserver.util.realm_to_suffix(ipa.config.config.get_realm())
|
||||
self.scope = ldap.SCOPE_SUBTREE
|
||||
self.princ = None
|
||||
|
||||
# FIXME: Is this the filter we want or should it be more specific?
|
||||
filter = "(uid=" + username + ")"
|
||||
def set_principal(self, princ):
|
||||
self.princ = princ
|
||||
|
||||
def get_dn_from_principal(self, princ):
|
||||
"""Given a kerberls principal get the LDAP uid"""
|
||||
filter = "(krbPrincipalName=" + princ + ")"
|
||||
try:
|
||||
m1 = ipaserver.ipaldap.IPAdmin(host,port,binddn,bindpw)
|
||||
ent = m1.getEntry(basedn, scope, filter, None)
|
||||
m1 = ipaserver.ipaldap.IPAdmin(self.host,self.port,self.bindca,self.bindcert,self.bindkey)
|
||||
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 "dn:" + ent.dn
|
||||
|
||||
def convert_entry(self, ent):
|
||||
|
||||
# Convert to LDIF
|
||||
entry = str(ent)
|
||||
|
||||
@ -86,12 +90,41 @@ def get_user (username):
|
||||
user[k] = v.strip()
|
||||
|
||||
return user
|
||||
# return str(ent) # return as LDIF
|
||||
|
||||
def add_user (user):
|
||||
def get_user (self, username, opts=None):
|
||||
"""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"""
|
||||
dn="uid=%s,ou=users,ou=default,%s" % (user['uid'], basedn)
|
||||
entry = ipaserver.ipaldap.Entry(dn)
|
||||
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))
|
||||
|
||||
# some required objectclasses
|
||||
entry.setValues('objectClass', 'top', 'posixAccount', 'shadowAccount', 'account', 'person', 'inetOrgPerson', 'organizationalPerson', 'krbPrincipalAux', 'krbTicketPolicyAux')
|
||||
@ -108,20 +141,28 @@ def add_user (user):
|
||||
|
||||
# fill in our new entry with everything sent by the user
|
||||
for u in user:
|
||||
entry.setValues(u, user[u])
|
||||
entry.setValues(str(u), str(user[u]))
|
||||
|
||||
if opts:
|
||||
self.set_principal(opts['remoteuser'])
|
||||
|
||||
try:
|
||||
m1 = ipaserver.ipaldap.IPAdmin(host,port,binddn,bindpw)
|
||||
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")
|
||||
|
||||
try:
|
||||
m1 = ipaserver.ipaldap.IPAdmin(self.host,self.port,self.bindca,self.bindcert,self.bindkey,dn)
|
||||
res = m1.addEntry(entry)
|
||||
return res
|
||||
except ldap.ALREADY_EXISTS:
|
||||
raise xmlrpclib.Fault(3, "User already exists")
|
||||
return None
|
||||
except ldap.LDAPError, e:
|
||||
raise xmlrpclib.Fault(1, str(e))
|
||||
return None
|
||||
|
||||
def get_add_schema ():
|
||||
def get_add_schema (self):
|
||||
"""Get the list of fields to be used when adding users in the GUI."""
|
||||
|
||||
# FIXME: this needs to be pulled from LDAP
|
||||
@ -136,15 +177,6 @@ def get_add_schema ():
|
||||
}
|
||||
fields.append(field1)
|
||||
|
||||
field1 = {
|
||||
"name": "userPassword" ,
|
||||
"label": "Password:",
|
||||
"type": "password",
|
||||
"validator": "String",
|
||||
"required": "true"
|
||||
}
|
||||
fields.append(field1)
|
||||
|
||||
field1 = {
|
||||
"name": "givenName" ,
|
||||
"label": "First name:",
|
||||
@ -173,3 +205,24 @@ def get_add_schema ():
|
||||
fields.append(field1)
|
||||
|
||||
return fields
|
||||
|
||||
def get_all_users (self):
|
||||
"""Return a list containing a User object for each
|
||||
existing user.
|
||||
"""
|
||||
|
||||
# FIXME: Is this the filter we want or should it be more specific?
|
||||
filter = "(objectclass=posixAccount)"
|
||||
try:
|
||||
m1 = ipaserver.ipaldap.IPAdmin(self.host,self.port,self.bindca,self.bindcert,self.bindkey)
|
||||
all_users = m1.getList(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")
|
||||
|
||||
users = []
|
||||
for u in all_users:
|
||||
users.append(self.convert_entry(u))
|
||||
|
||||
return users
|
||||
|
@ -3,15 +3,15 @@
|
||||
Alias /ipa "/usr/share/ipa/ipaserver/XMLRPC"
|
||||
|
||||
<Directory "/usr/share/ipa/ipaserver">
|
||||
# AuthType Kerberos
|
||||
# AuthName "Kerberos Login"
|
||||
# KrbMethodNegotiate on
|
||||
# KrbMethodK5Passwd off
|
||||
# KrbServiceName HTTP
|
||||
# KrbAuthRealms GREYOAK.COM
|
||||
# Krb5KeyTab /etc/httpd/conf/ipa.keytab
|
||||
# KrbSaveCredentials on
|
||||
# Require valid-user
|
||||
AuthType Kerberos
|
||||
AuthName "Kerberos Login"
|
||||
KrbMethodNegotiate on
|
||||
KrbMethodK5Passwd off
|
||||
KrbServiceName HTTP
|
||||
KrbAuthRealms GREYOAK.COM
|
||||
Krb5KeyTab /etc/httpd/conf/ipa.keytab
|
||||
KrbSaveCredentials on
|
||||
Require valid-user
|
||||
ErrorDocument 401 /errors/unauthorized.html
|
||||
|
||||
SetHandler mod_python
|
||||
|
@ -123,11 +123,16 @@ class ModXMLRPCRequestHandler(object):
|
||||
def register_instance(self,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."""
|
||||
|
||||
params, method = loads(data)
|
||||
|
||||
opts={}
|
||||
opts['remoteuser'] = remoteuser
|
||||
|
||||
params = ipaserver.encode_args(params, opts)
|
||||
|
||||
# special case
|
||||
# if method == "get_user":
|
||||
# Marshaller._Marshaller__dump = xmlrpclib_dump
|
||||
@ -239,7 +244,7 @@ class ModXMLRPCRequestHandler(object):
|
||||
req.allow_methods(['POST'],1)
|
||||
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.set_content_length(len(response))
|
||||
@ -267,10 +272,12 @@ def handler(req, profiling=False):
|
||||
else:
|
||||
opts = req.get_options()
|
||||
try:
|
||||
f = funcs.IPAServer()
|
||||
h = ModXMLRPCRequestHandler()
|
||||
h.register_function(funcs.get_user)
|
||||
h.register_function(funcs.add_user)
|
||||
h.register_function(funcs.get_add_schema)
|
||||
h.register_function(f.get_user)
|
||||
h.register_function(f.add_user)
|
||||
h.register_function(f.get_add_schema)
|
||||
h.register_function(f.get_all_users)
|
||||
h.handle_request(req)
|
||||
finally:
|
||||
pass
|
||||
|
Loading…
Reference in New Issue
Block a user