diff options
author | Simo Sorce <ssorce@redhat.com> | 2007-08-01 15:26:08 -0400 |
---|---|---|
committer | Simo Sorce <ssorce@redhat.com> | 2007-08-01 15:26:08 -0400 |
commit | 96d292b7351bd7db0d402e3a393386889e3d79d8 (patch) | |
tree | 1289da942a2ef8482b20891dbd58da2a47321f4c | |
parent | adc6632c9a6de5a81fd4dad7a637cb3db7894000 (diff) | |
parent | b1831b4593b3d219b79830f3012e7ff07f17b1d8 (diff) | |
download | freeipa.git-96d292b7351bd7db0d402e3a393386889e3d79d8.tar.gz freeipa.git-96d292b7351bd7db0d402e3a393386889e3d79d8.tar.xz freeipa.git-96d292b7351bd7db0d402e3a393386889e3d79d8.zip |
Merge from upstream
49 files changed, 2110 insertions, 79 deletions
diff --git a/Makefile b/Makefile new file mode 100644 index 00000000..350bf9bc --- /dev/null +++ b/Makefile @@ -0,0 +1,104 @@ +SUBDIRS=ipa-server ipa-admintools ipa-python + +PRJ_PREFIX=freeipa + +# Version numbers - this is for the entire server. After +# updating this you should run the version-update +# target. +SERV_MAJOR=0 +SERV_MINOR=1 +SERV_RELEASE=0 +SERV_VERSION=$(SERV_MAJOR).$(SERV_MINOR).$(SERV_RELEASE) +SERV_TARBALL_PREFIX=$(PRJ_PREFIX)-server-$(SERV_VERSION) +SERV_TARBALL=$(SERV_TARBALL_PREFIX).tgz + +ADMIN_MAJOR=0 +ADMIN_MINOR=1 +ADMIN_RELEASE=0 +ADMIN_VERSION=$(ADMIN_MAJOR).$(ADMIN_MINOR).$(ADMIN_RELEASE) +ADMIN_TARBALL_PREFIX=$(PRJ_PREFIX)-admintools-$(ADMIN_VERSION) +ADMIN_TARBALL=$(ADMIN_TARBALL_PREFIX).tgz + +PYTHON_MAJOR=0 +PYTHON_MINOR=1 +PYTHON_RELEASE=0 +PYTHON_VERSION=$(PYTHON_MAJOR).$(PYTHON_MINOR).$(PYTHON_RELEASE) +PYTHON_TARBALL_PREFIX=$(PRJ_PREFIX)-python-$(PYTHON_VERSION) +PYTHON_TARBALL=$(PYTHON_TARBALL_PREFIX).tgz + +all: + @for subdir in $(SUBDIRS); do \ + (cd $$subdir && $(MAKE) $@) || exit 1; \ + done + +install: + @for subdir in $(SUBDIRS); do \ + (cd $$subdir && $(MAKE) $@) || exit 1; \ + done + +clean: + @for subdir in $(SUBDIRS); do \ + (cd $$subdir && $(MAKE) $@) || exit 1; \ + done + rm -f *~ + +version-update: + sed s/VERSION/$(SERV_VERSION)/ ipa-server/freeipa-server.spec.in \ + > ipa-server/freeipa-server.spec + + sed s/VERSION/$(ADMIN_VERSION)/ ipa-admintools/freeipa-admintools.spec.in \ + > ipa-admintools/freeipa-admintools.spec + + sed s/VERSION/$(PYTHON_VERSION)/ ipa-python/freeipa-python.spec.in \ + > ipa-python/freeipa-python.spec + + +tarballs: + -mkdir -p dist + hg archive -t files dist/freeipa + + # ipa-server + mv dist/freeipa/ipa-server dist/$(SERV_TARBALL_PREFIX) + rm -f dist/$(SERV_TARBALL) + cd dist; tar cfz $(SERV_TARBALL) $(SERV_TARBALL_PREFIX) + rm -fr dist/$(SERV_TARBALL_PREFIX) + + # ipa-admintools + mv dist/freeipa/ipa-admintools dist/$(ADMIN_TARBALL_PREFIX) + rm -f dist/$(ADMIN_TARBALL) + cd dist; tar cfz $(ADMIN_TARBALL) $(ADMIN_TARBALL_PREFIX) + rm -fr dist/$(ADMIN_TARBALL_PREFIX) + + # ipa-python + mv dist/freeipa/ipa-python dist/$(PYTHON_TARBALL_PREFIX) + rm -f dist/$(PYTHON_TARBALL) + cd dist; tar cfz $(PYTHON_TARBALL) $(PYTHON_TARBALL_PREFIX) + rm -fr dist/$(PYTHON_TARBALL_PREFIX) + + # cleanup + rm -fr dist/freeipa + +rpm-ipa-server: + cp dist/$(SERV_TARBALL) ~/rpmbuild/SOURCES/. + rpmbuild -ba ipa-server/freeipa-server.spec + cp ~/rpmbuild/RPMS/noarch/$(PRJ_PREFIX)-server-$(SERV_VERSION)-*.rpm dist/. + cp ~/rpmbuild/SRPMS/$(PRJ_PREFIX)-server-$(SERV_VERSION)-*.src.rpm dist/. + +rpm-ipa-admin: + cp dist/$(ADMIN_TARBALL) ~/rpmbuild/SOURCES/. + rpmbuild -ba ipa-admintools/freeipa-admintools.spec + cp ~/rpmbuild/RPMS/noarch/$(PRJ_PREFIX)-admintools-$(ADMIN_VERSION)-*.rpm dist/. + cp ~/rpmbuild/SRPMS/$(PRJ_PREFIX)-admintools-$(ADMIN_VERSION)-*.src.rpm dist/. + +rpm-ipa-python: + cp dist/$(PYTHON_TARBALL) ~/rpmbuild/SOURCES/. + rpmbuild -ba ipa-python/freeipa-python.spec + cp ~/rpmbuild/RPMS/noarch/$(PRJ_PREFIX)-python-$(PYTHON_VERSION)-*.rpm dist/. + cp ~/rpmbuild/SRPMS/$(PRJ_PREFIX)-python-$(PYTHON_VERSION)-*.src.rpm dist/. + +rpms: rpm-ipa-server rpm-ipa-admin rpm-ipa-python + +dist: version-update tarballs rpms + +dist-clean: clean + rm -fr dist diff --git a/ipa-admintools/Makefile b/ipa-admintools/Makefile new file mode 100644 index 00000000..e0fd405a --- /dev/null +++ b/ipa-admintools/Makefile @@ -0,0 +1,10 @@ +SBINDIR = $(DESTDIR)/usr/sbin + +all: ; + +install: + install -m 755 ipa-adduser $(SBINDIR) + install -m 755 ipa-finduser $(SBINDIR) + +clean: + rm -f *~ *.pyc diff --git a/example-config/README b/ipa-admintools/README index e69de29b..e69de29b 100644 --- a/example-config/README +++ b/ipa-admintools/README diff --git a/ipa-admintools/freeipa-admintools.spec b/ipa-admintools/freeipa-admintools.spec new file mode 100755 index 00000000..bcd3d9d2 --- /dev/null +++ b/ipa-admintools/freeipa-admintools.spec @@ -0,0 +1,42 @@ +Name: freeipa-admintools +Version: 0.1.0 +Release: 1%{?dist} +Summary: FreeIPA authentication server + +Group: System Environment/Base +License: GPL +URL: http://www.freeipa.org +Source0: %{name}-%{version}.tgz +BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-root-%(%{__id_u} -n) +BuildArch: noarch + +Requires: python freeipa-python + +%description +FreeIPA is a server for identity, policy, and audit. + +%prep +%setup -q + +%install +rm -rf %{buildroot} +mkdir -p %{buildroot}%{_sbindir} + +make install DESTDIR=%{buildroot} + + +%clean +rm -rf %{buildroot} + + +%files +%defattr(-,root,root,-) +%{_sbindir}/ipa-adduser +%{_sbindir}/ipa-finduser + + +%changelog +* Fri Jul 27 2007 Karl MacMillan <kmacmill@localhost.localdomain> - 0.1.0-1 +- Initial rpm version + + diff --git a/ipa-admintools/freeipa-admintools.spec.in b/ipa-admintools/freeipa-admintools.spec.in new file mode 100755 index 00000000..bcd3d9d2 --- /dev/null +++ b/ipa-admintools/freeipa-admintools.spec.in @@ -0,0 +1,42 @@ +Name: freeipa-admintools +Version: 0.1.0 +Release: 1%{?dist} +Summary: FreeIPA authentication server + +Group: System Environment/Base +License: GPL +URL: http://www.freeipa.org +Source0: %{name}-%{version}.tgz +BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-root-%(%{__id_u} -n) +BuildArch: noarch + +Requires: python freeipa-python + +%description +FreeIPA is a server for identity, policy, and audit. + +%prep +%setup -q + +%install +rm -rf %{buildroot} +mkdir -p %{buildroot}%{_sbindir} + +make install DESTDIR=%{buildroot} + + +%clean +rm -rf %{buildroot} + + +%files +%defattr(-,root,root,-) +%{_sbindir}/ipa-adduser +%{_sbindir}/ipa-finduser + + +%changelog +* Fri Jul 27 2007 Karl MacMillan <kmacmill@localhost.localdomain> - 0.1.0-1 +- Initial rpm version + + diff --git a/ipa-admintools/ipa-adduser b/ipa-admintools/ipa-adduser new file mode 100644 index 00000000..b40fdee2 --- /dev/null +++ b/ipa-admintools/ipa-adduser @@ -0,0 +1,80 @@ +#! /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 only +# +# 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 +# + +import sys +from optparse import OptionParser +import ipa +import ipa.rpcclient +import xmlrpclib + +def usage(): + print "ipa-adduser [-c|--gecos STRING] [-d|--directory STRING] [-f|--firstname STRING] [-l|--lastname STRING] user" + sys.exit(1) + +def parse_options(): + parser = OptionParser() + parser.add_option("-c", "--gecos", dest="gecos", + help="Set the GECOS field") + parser.add_option("-d", "--directory", dest="directory", + help="Set the User's home directory") + parser.add_option("-f", "--firstname", dest="gn", + help="User's first name") + parser.add_option("-l", "--lastname", dest="sn", + help="User's last name") + parser.add_option("-s", "--shell", dest="shell", + help="Set user's login shell to shell") + parser.add_option("--usage", action="store_true", + help="Program usage") + + (options, args) = parser.parse_args() + + if not options.gn or not options.sn: + usage() + + return options, args + +def main(): + user={} + (options, args) = parse_options() + + if len(args) != 1: + usage() + + user['gn'] = options.gn + user['sn'] = options.sn + user['uid'] = args[0] + if options.gecos: + user['gecos'] = options.gecos + if options.directory: + user['homedirectory'] = options.directory + if options.shell: + user['loginshell'] = options.shell + else: + user['loginshell'] = "/bin/bash" + + try: + ipa.rpcclient.add_user(user) + print args[0] + " successfully added" + except xmlrpclib.Fault, f: + print f.faultString + + return 0 + +main() diff --git a/ipa-admintools/ipa-finduser b/ipa-admintools/ipa-finduser new file mode 100644 index 00000000..205b47ce --- /dev/null +++ b/ipa-admintools/ipa-finduser @@ -0,0 +1,60 @@ +#! /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 only +# +# 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 +# + +from optparse import OptionParser +import ipa +import ipa.rpcclient +import base64 +import sys +import xmlrpclib + +def usage(): + print "ipa-finduser <uid>" + sys.exit() + +def parse_options(): + parser = OptionParser() + + (options, args) = parser.parse_args() + + return options, args + +def main(): + user={} + (options, args) = parse_options() + + if len(args) != 1: + usage() + + try: + ent = ipa.rpcclient.get_user(args[0]) + for name, value in ent.items(): + if isinstance(value, str): + print name + ": " + value + else: + print name + ": " + for x in value: + print "\t" + x + except xmlrpclib.Fault, fault: + print fault.faultString + + return 0 + +main() diff --git a/ipa-install/Makefile b/ipa-install/Makefile deleted file mode 100644 index 7696489b..00000000 --- a/ipa-install/Makefile +++ /dev/null @@ -1,10 +0,0 @@ -all: ; - -install: - $(MAKE) -C src $@ - $(MAKE) -C share $@ - -clean: - $(MAKE) -C src $@ - $(MAKE) -C share $@ - rm -f *~
\ No newline at end of file diff --git a/ipa-install/src/ipa/__init__.py b/ipa-install/src/ipa/__init__.py deleted file mode 100644 index 8e20eb1b..00000000 --- a/ipa-install/src/ipa/__init__.py +++ /dev/null @@ -1 +0,0 @@ -__all__ = ["dsinstance", "krbinstance"] diff --git a/ipa-install/src/Makefile b/ipa-python/Makefile index f5a0f780..bc6554be 100644 --- a/ipa-install/src/Makefile +++ b/ipa-python/Makefile @@ -1,14 +1,11 @@ PYTHONLIBDIR ?= $(shell python -c "from distutils.sysconfig import *; print get_python_lib(1)") PACKAGEDIR ?= $(DESTDIR)/$(PYTHONLIBDIR)/ipa -SBINDIR = $(DESTDIR)/usr/sbin all: ; install: -mkdir -p $(PACKAGEDIR) - install -m 644 ipa/*.py $(PACKAGEDIR) - install -m 755 ipa-server-install $(SBINDIR) - install -m 755 ipa-server-setupssl $(SBINDIR) + install -m 644 *.py $(PACKAGEDIR) clean: rm -f *~ *.pyc
\ No newline at end of file diff --git a/ipa-cli/README b/ipa-python/README index e69de29b..e69de29b 100644 --- a/ipa-cli/README +++ b/ipa-python/README diff --git a/ipa-slapi-plugins/README b/ipa-python/__init__.py index e69de29b..e69de29b 100644 --- a/ipa-slapi-plugins/README +++ b/ipa-python/__init__.py diff --git a/ipa-python/freeipa-python.spec b/ipa-python/freeipa-python.spec new file mode 100755 index 00000000..61be3a5d --- /dev/null +++ b/ipa-python/freeipa-python.spec @@ -0,0 +1,45 @@ +Name: freeipa-python +Version: 0.1.0 +Release: 1%{?dist} +Summary: FreeIPA authentication server + +Group: System Environment/Base +License: GPL +URL: http://www.freeipa.org +Source0: %{name}-%{version}.tgz +BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-root-%(%{__id_u} -n) +BuildArch: noarch + +Requires: python + +%{!?python_sitelib: %define python_sitelib %(%{__python} -c "from distutils.sysconfig import get_python_lib; print get_python_lib()")} + +%define pkgpythondir %{python_sitelib}/ipa + +%description +FreeIPA is a server for identity, policy, and audit. + +%prep +%setup -q + +%install +rm -rf %{buildroot} +mkdir -p %{buildroot}%{pkgpythondir} + +make install DESTDIR=%{buildroot} + + +%clean +rm -rf %{buildroot} + + +%files +%defattr(-,root,root,-) +%{pkgpythondir}/* + + +%changelog +* Fri Jul 27 2007 Karl MacMillan <kmacmill@localhost.localdomain> - 0.1.0-1 +- Initial rpm version + + diff --git a/ipa-python/freeipa-python.spec.in b/ipa-python/freeipa-python.spec.in new file mode 100755 index 00000000..90a135b4 --- /dev/null +++ b/ipa-python/freeipa-python.spec.in @@ -0,0 +1,45 @@ +Name: freeipa-python +Version: VERSION +Release: 1%{?dist} +Summary: FreeIPA authentication server + +Group: System Environment/Base +License: GPL +URL: http://www.freeipa.org +Source0: %{name}-%{version}.tgz +BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-root-%(%{__id_u} -n) +BuildArch: noarch + +Requires: python + +%{!?python_sitelib: %define python_sitelib %(%{__python} -c "from distutils.sysconfig import get_python_lib; print get_python_lib()")} + +%define pkgpythondir %{python_sitelib}/ipa + +%description +FreeIPA is a server for identity, policy, and audit. + +%prep +%setup -q + +%install +rm -rf %{buildroot} +mkdir -p %{buildroot}%{pkgpythondir} + +make install DESTDIR=%{buildroot} + + +%clean +rm -rf %{buildroot} + + +%files +%defattr(-,root,root,-) +%{pkgpythondir}/* + + +%changelog +* Fri Jul 27 2007 Karl MacMillan <kmacmill@localhost.localdomain> - 0.1.0-1 +- Initial rpm version + + diff --git a/ipa-python/rpcclient.py b/ipa-python/rpcclient.py new file mode 100644 index 00000000..41602662 --- /dev/null +++ b/ipa-python/rpcclient.py @@ -0,0 +1,102 @@ +#! /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 + +try: + import krbV +except ImportError: + pass +import xmlrpclib +import socket +import os +import base64 + +# Some errors to catch +# http://cvs.fedora.redhat.com/viewcvs/ldapserver/ldap/servers/plugins/pam_passthru/README?root=dirsec&rev=1.6&view=auto + +# FIXME: do we want this set somewhere else? +server = xmlrpclib.ServerProxy("http://localhost:80/ipa") + +def get_user(username): + """Get a specific user""" + + 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 add_user(user): + """Add a new user""" + + # FIXME: Get the realm from somewhere + realm="GREYOAK.COM" + + # 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['gn'], user['sn']) + if user.get('gn'): + del user['gn'] + + 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(): + """Get the list of attributes we need to ask when adding a new + user. + """ + + # 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) + return None + except socket.error, (value, msg): + raise xmlrpclib.Fault(value, msg) + return None + + return result diff --git a/ipa-server/Makefile b/ipa-server/Makefile new file mode 100644 index 00000000..dd3fa71e --- /dev/null +++ b/ipa-server/Makefile @@ -0,0 +1,23 @@ +SUBDIRS=ipa-install xmlrpc-server +PYTHONDIR=$(DESTDIR)/usr/share/ipa/ipaserver + +all: + @for subdir in $(SUBDIRS); do \ + (cd $$subdir && $(MAKE) $@) || exit 1; \ + done + +install-ipaserver: + -mkdir -p $(PYTHONDIR) + install -m 644 ipaserver/*.py $(PYTHONDIR) + +install: install-ipaserver + @for subdir in $(SUBDIRS); do \ + (cd $$subdir && $(MAKE) $@) || exit 1; \ + done + +clean: + @for subdir in $(SUBDIRS); do \ + (cd $$subdir && $(MAKE) $@) || exit 1; \ + done + rm -f *~ + rm -f ipaserver/*~ diff --git a/ipa-server/freeipa-server.spec b/ipa-server/freeipa-server.spec new file mode 100755 index 00000000..4801eb7f --- /dev/null +++ b/ipa-server/freeipa-server.spec @@ -0,0 +1,50 @@ +Name: freeipa-server +Version: 0.1.0 +Release: 1%{?dist} +Summary: FreeIPA authentication server + +Group: System Environment/Base +License: GPL +URL: http://www.freeipa.org +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 + +%define httpd_conf /etc/httpd/conf.d + +%description +FreeIPA is a server for identity, policy, and audit. + +%prep +%setup -q + +%install +rm -rf %{buildroot} +mkdir -p %{buildroot}%{_sbindir} +mkdir -p %{buildroot}%{httpd_conf} + +make install DESTDIR=%{buildroot} + + +%clean +rm -rf %{buildroot} + + +%files +%defattr(-,root,root,-) +%{_sbindir}/ipa-server-install +%{_sbindir}/ipa-server-setupssl + +%dir %{_usr}/share/ipa +%{_usr}/share/ipa/* + +%{httpd_conf}/ipa.conf + + +%changelog +* Fri Jul 27 2007 Karl MacMillan <kmacmill@localhost.localdomain> - 0.1.0-1 +- Initial rpm version + + diff --git a/ipa-server/freeipa-server.spec.in b/ipa-server/freeipa-server.spec.in new file mode 100644 index 00000000..16aff06b --- /dev/null +++ b/ipa-server/freeipa-server.spec.in @@ -0,0 +1,50 @@ +Name: freeipa-server +Version: VERSION +Release: 1%{?dist} +Summary: FreeIPA authentication server + +Group: System Environment/Base +License: GPL +URL: http://www.freeipa.org +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 + +%define httpd_conf /etc/httpd/conf.d + +%description +FreeIPA is a server for identity, policy, and audit. + +%prep +%setup -q + +%install +rm -rf %{buildroot} +mkdir -p %{buildroot}%{_sbindir} +mkdir -p %{buildroot}%{httpd_conf} + +make install DESTDIR=%{buildroot} + + +%clean +rm -rf %{buildroot} + + +%files +%defattr(-,root,root,-) +%{_sbindir}/ipa-server-install +%{_sbindir}/ipa-server-setupssl + +%dir %{_usr}/share/ipa +%{_usr}/share/ipa/* + +%{httpd_conf}/ipa.conf + + +%changelog +* Fri Jul 27 2007 Karl MacMillan <kmacmill@localhost.localdomain> - 0.1.0-1 +- Initial rpm version + + diff --git a/ipa-server/ipa-install/Makefile b/ipa-server/ipa-install/Makefile new file mode 100644 index 00000000..0d495397 --- /dev/null +++ b/ipa-server/ipa-install/Makefile @@ -0,0 +1,12 @@ +SBINDIR=$(DESTDIR)/usr/sbin + +all: ; + +install: + install -m 755 ipa-server-install $(SBINDIR) + install -m 755 ipa-server-setupssl $(SBINDIR) + $(MAKE) -C share $@ + +clean: + $(MAKE) -C share $@ + rm -f *~ *.pyc
\ No newline at end of file diff --git a/ipa-server/ipa-install/README b/ipa-server/ipa-install/README new file mode 100644 index 00000000..2e1dd0d4 --- /dev/null +++ b/ipa-server/ipa-install/README @@ -0,0 +1,23 @@ + +Required packages: + +krb5-server +fedora-ds-base +openldap-clients +krb5-server-ldap +cyrus-sasl-gssapi +httpd +mod_auth_kerb + + +Installation example: + +TEMPORARY: (until fedora ds scripts are fixed) +please use the fedora-ds.init.patch under share/ to patch your init scripts before +running ipa-server-install + +cd ipa-install +make install +cd .. +/usr/sbin/ipa-server-install -u fds -r FREEIPA.ORG -p freeipa -m ipafree + diff --git a/ipa-server/ipa-install/freeipa-setup-20070713.patch b/ipa-server/ipa-install/freeipa-setup-20070713.patch new file mode 100644 index 00000000..5a36eee0 --- /dev/null +++ b/ipa-server/ipa-install/freeipa-setup-20070713.patch @@ -0,0 +1,288 @@ +diff -r 5ebd8adc48b8 ipa-install/README +--- a/ipa-install/README Mon Jul 02 15:51:04 2007 -0400 ++++ b/ipa-install/README Fri Jul 13 16:25:05 2007 -0400 +@@ -5,12 +5,17 @@ fedora-ds-base + fedora-ds-base + openldap-clients + krb5-server-ldap ++cyrus-sasl-gssapi + + + Installation example: ++ ++TEMPORARY: (until fedora ds scripts are fixed) ++please use the fedora-ds.init.patch under share/ to patch your init scripts before ++running ipa-server-install + + cd ipa-install + make install + cd .. +-/usr/sbin/ipa-server-install -r FREEIPA.ORG -a rc1.freeipa.org -p freeipa -m ipafree ++/usr/sbin/ipa-server-install -u fds -r FREEIPA.ORG -p freeipa -m ipafree + +diff -r 5ebd8adc48b8 ipa-install/share/bind.zone.db.template +--- /dev/null Thu Jan 01 00:00:00 1970 +0000 ++++ b/ipa-install/share/bind.zone.db.template Fri Jul 13 16:22:12 2007 -0400 +@@ -0,0 +1,26 @@ ++$$ORIGIN $DOMAIN. ++$$TTL 86400 ++@ IN SOA $DOMAIN. root.$DOMAIN. ( ++ 01 ; serial (d. adams) ++ 3H ; refresh ++ 15M ; retry ++ 1W ; expiry ++ 1D ) ; minimum ++ ++ IN NS $HOST ++$HOST IN A $IP ++; ++; ldap servers ++_ldap._tcp IN SRV 0 100 389 $HOST ++ ++;kerberos realm ++_kerberos IN TXT $REALM ++ ++; kerberos servers ++_kerberos._tcp IN SRV 0 100 88 $HOST ++_kerberos._udp IN SRV 0 100 88 $HOST ++_kerberos-master._tcp IN SRV 0 100 88 $HOST ++_kerberos-master._udp IN SRV 0 100 88 $HOST ++_kpasswd._tcp IN SRV 0 100 88 $HOST ++_kpasswd._udp IN SRV 0 100 88 $HOST ++ +diff -r 5ebd8adc48b8 ipa-install/share/fedora-ds.init.patch +--- /dev/null Thu Jan 01 00:00:00 1970 +0000 ++++ b/ipa-install/share/fedora-ds.init.patch Fri Jul 13 14:45:53 2007 -0400 +@@ -0,0 +1,12 @@ ++--- /etc/init.d/fedora-ds.orig 2007-07-06 18:21:30.000000000 -0400 +++++ /etc/init.d/fedora-ds 2007-05-18 19:36:24.000000000 -0400 ++@@ -10,6 +10,9 @@ ++ # datadir: /var/lib/fedora-ds/slapd-<instance name> ++ # ++ +++# Get config. +++[ -r /etc/sysconfig/fedora-ds ] && . /etc/sysconfig/fedora-ds +++ ++ # Source function library. ++ if [ -f /etc/rc.d/init.d/functions ] ; then ++ . /etc/rc.d/init.d/functions +diff -r 5ebd8adc48b8 ipa-install/share/krb5.conf.template +--- a/ipa-install/share/krb5.conf.template Mon Jul 02 15:51:04 2007 -0400 ++++ b/ipa-install/share/krb5.conf.template Fri Jul 13 11:01:36 2007 -0400 +@@ -9,6 +9,13 @@ + dns_lookup_kdc = true + ticket_lifetime = 24h + forwardable = yes ++ ++[realms] ++ $REALM = { ++ kdc = $FQDN:88 ++ admin_server = $FQDN:749 ++ default_domain = $DOMAIN ++} + + [domain_realm] + .$DOMAIN = $REALM +@@ -29,7 +36,7 @@ + ldap_servers = ldap://127.0.0.1/ + ldap_kerberos_container_dn = cn=kerberos,$SUFFIX + ldap_kdc_dn = uid=kdc,cn=kerberos,$SUFFIX +-; ldap_kadmind_dn = cn=Directory Manager ++ ldap_kadmind_dn = uid=kdc,cn=kerberos,$SUFFIX + ldap_service_password_file = /var/kerberos/krb5kdc/ldappwd + } + +diff -r 5ebd8adc48b8 ipa-install/src/ipa-server-install +--- a/ipa-install/src/ipa-server-install Mon Jul 02 15:51:04 2007 -0400 ++++ b/ipa-install/src/ipa-server-install Fri Jul 13 19:43:13 2007 -0400 +@@ -26,6 +26,7 @@ + + VERSION = "%prog .1" + ++import socket + import logging + from optparse import OptionParser + import ipa.dsinstance +@@ -37,8 +38,6 @@ def parse_options(): + help="ds user") + parser.add_option("-r", "--realm", dest="realm_name", + help="realm name") +- parser.add_option("-a", "--host-address", dest="host_name", +- help="host address (name or IP address)") + parser.add_option("-p", "--password", dest="password", + help="admin password") + parser.add_option("-m", "--master-password", dest="master_password", +@@ -46,8 +45,8 @@ def parse_options(): + + options, args = parser.parse_args() + +- if not options.realm_name or not options.host_name or not options.password: +- parser.error("error: password, realm, and host name required") ++ if not options.ds_user or not options.realm_name or not options.password or not options.master_password: ++ parser.error("error: all options are required") + + return options + +@@ -56,13 +55,35 @@ def main(): + format='%(asctime)s %(levelname)s %(message)s', + filename='ipa-install.log', + filemode='w') ++ + options = parse_options() ++ ++ # check the hostname is correctly configured, it must be as the kldap ++ # utilities just use the hostname as returned by gethostbyname to set ++ # up some of the standard entries ++ ++ host_name = socket.gethostname() ++ if len(host_name.split(".")) < 2: ++ print "Invalid hostname <"+host_name+">" ++ print "Check the /etc/hosts file and make sure to have a valid FQDN" ++ return "-Fatal Error-" ++ ++ if socket.gethostbyname(host_name) == "127.0.0.1": ++ print "The hostname resolves to the localhost address (127.0.0.1)" ++ print "Please change your /etc/hosts file or your DNS so that the" ++ print "hostname resolves to the ip address of your network interface." ++ print "The KDC service does not listen on 127.0.0.1" ++ return "-Fatal Error-" ++ ++ print "The Final KDC Host Name will be: " + host_name ++ + ds = ipa.dsinstance.DsInstance() +- ds.create_instance(options.ds_user, options.realm_name, options.host_name, options.password) ++ ds.create_instance(options.ds_user, options.realm_name, host_name, options.password) + + krb = ipa.krbinstance.KrbInstance() +- krb.create_instance(options.ds_user, options.realm_name, options.host_name, options.password, options.master_password) +- #restart ds after the krb instance have add the sasl map ++ krb.create_instance(options.ds_user, options.realm_name, host_name, options.password, options.master_password) ++ ++ #restart ds after the krb instance have add the sasl map and the ldap keytab + ds.restart() + + return 0 +diff -r 5ebd8adc48b8 ipa-install/src/ipa/krbinstance.py +--- a/ipa-install/src/ipa/krbinstance.py Mon Jul 02 15:51:04 2007 -0400 ++++ b/ipa-install/src/ipa/krbinstance.py Fri Jul 13 19:20:41 2007 -0400 +@@ -25,6 +25,9 @@ import logging + import logging + from random import Random + from time import gmtime ++import os ++import pwd ++import socket + + SHARE_DIR = "/usr/share/ipa/" + +@@ -32,6 +35,10 @@ def realm_to_suffix(realm_name): + s = realm_name.split(".") + terms = ["dc=" + x.lower() for x in s] + return ",".join(terms) ++ ++def host_to_domain(fqdn): ++ s = fqdn.split(".") ++ return ".".join(s[1:]) + + def generate_kdc_password(): + rndpwd = '' +@@ -75,8 +82,10 @@ class KrbInstance: + class KrbInstance: + def __init__(self): + self.ds_user = None +- self.realm_name = None +- self.host_name = None ++ self.fqdn = None ++ self.realm = None ++ self.domain = None ++ self.host = None + self.admin_password = None + self.master_password = None + self.suffix = None +@@ -85,12 +94,15 @@ class KrbInstance: + + def create_instance(self, ds_user, realm_name, host_name, admin_password, master_password): + self.ds_user = ds_user +- self.realm_name = realm_name.upper() +- self.host_name = host_name ++ self.fqdn = host_name ++ self.ip = socket.gethostbyname(host_name) ++ self.realm = realm_name.upper() ++ self.host = host_name.split(".")[0] ++ self.domain = host_to_domain(host_name) + self.admin_password = admin_password + self.master_password = master_password + +- self.suffix = realm_to_suffix(self.realm_name) ++ self.suffix = realm_to_suffix(self.realm) + self.kdc_password = generate_kdc_password() + self.__configure_kdc_account_password() + +@@ -99,6 +111,10 @@ class KrbInstance: + self.__configure_ldap() + + self.__create_instance() ++ ++ self.__create_ds_keytab() ++ ++ self.__create_sample_bind_zone() + + self.start() + +@@ -120,12 +136,13 @@ class KrbInstance: + pwd_fd.close() + + def __setup_sub_dict(self): +- #FIXME: can DOMAIN be different than REALM ? +- self.sub_dict = dict(FQHN=self.host_name, ++ self.sub_dict = dict(FQDN=self.fqdn, ++ IP=self.ip, + PASSWORD=self.kdc_password, + SUFFIX=self.suffix, +- DOMAIN= self.realm_name.lower(), +- REALM=self.realm_name) ++ DOMAIN=self.domain, ++ HOST=self.host, ++ REALM=self.realm) + + def __configure_ldap(self): + +@@ -153,7 +170,7 @@ class KrbInstance: + krb5_fd.close() + + #populate the directory with the realm structure +- args = ["/usr/kerberos/sbin/kdb5_ldap_util", "-D", "uid=kdc,cn=kerberos,"+self.suffix, "-w", self.kdc_password, "create", "-s", "-r", self.realm_name, "-subtrees", self.suffix, "-sscope", "sub"] ++ args = ["/usr/kerberos/sbin/kdb5_ldap_util", "-D", "uid=kdc,cn=kerberos,"+self.suffix, "-w", self.kdc_password, "create", "-s", "-P", self.master_password, "-r", self.realm, "-subtrees", self.suffix, "-sscope", "sub"] + run(args) + + # TODO: NOT called yet, need to find out how to make sure the plugin is available first +@@ -165,5 +182,28 @@ class KrbInstance: + extop_fd.close() + + #add an ACL to let the DS user read the master key +- args = ["/usr/bin/setfacl", "-m", "u:"+self.ds_user+":r", "/var/kerberos/krb5kdc/.k5."+self.realm_name] ++ args = ["/usr/bin/setfacl", "-m", "u:"+self.ds_user+":r", "/var/kerberos/krb5kdc/.k5."+self.realm] + run(args) ++ ++ def __create_sample_bind_zone(self): ++ bind_txt = template_file(SHARE_DIR + "bind.zone.db.template", self.sub_dict) ++ [bind_fd, bind_name] = tempfile.mkstemp(".db","sammple.zone.") ++ os.write(bind_fd, bind_txt) ++ os.close(bind_fd) ++ print "Sample zone file for bind has been created in "+bind_name ++ ++ def __create_ds_keytab(self): ++ (kwrite, kread, kerr) = os.popen3("/usr/kerberos/sbin/kadmin.local") ++ kwrite.write("addprinc -randkey ldap/"+self.fqdn+"@"+self.realm+"\n") ++ kwrite.flush() ++ kwrite.write("ktadd -k /etc/fedora-ds/ds.keytab ldap/"+self.fqdn+"@"+self.realm+"\n") ++ kwrite.flush() ++ kwrite.close() ++ kread.close() ++ kerr.close() ++ ++ cfg_fd = open("/etc/sysconfig/fedora-ds", "a") ++ cfg_fd.write("export KRB5_KTNAME=/etc/fedora-ds/ds.keytab\n") ++ cfg_fd.close() ++ pent = pwd.getpwnam(self.ds_user) ++ os.chown("/etc/sysconfig/fedora-ds", pent.pw_uid, pent.pw_gid) diff --git a/ipa-install/src/ipa-server-install b/ipa-server/ipa-install/ipa-server-install index cff05586..7abcafd8 100644 --- a/ipa-install/src/ipa-server-install +++ b/ipa-server/ipa-install/ipa-server-install @@ -26,11 +26,15 @@ VERSION = "%prog .1" +import sys +sys.path.append("/usr/share/ipa") + import socket import logging from optparse import OptionParser -import ipa.dsinstance -import ipa.krbinstance +import ipaserver.dsinstance +import ipaserver.krbinstance +from ipaserver.util import run def parse_options(): parser = OptionParser(version=VERSION) @@ -42,6 +46,9 @@ def parse_options(): help="admin password") parser.add_option("-m", "--master-password", dest="master_password", help="kerberos master password") + parser.add_option("-d", "--debug", dest="debug", action="store_true", + dest="debug", default=False, help="print debugging information") + parser.add_option("--hostname", dest="host_name", help="fully qualified name of server") options, args = parser.parse_args() @@ -50,19 +57,37 @@ def parse_options(): return options -def main(): +def logging_setup(options): + # Always log everything (i.e., DEBUG) to the log + # file. logging.basicConfig(level=logging.DEBUG, format='%(asctime)s %(levelname)s %(message)s', - filename='ipa-install.log', + filename='ipaserver-install.log', filemode='w') + console = logging.StreamHandler() + # If the debug option is set, also log debug messages to the console + if options.debug: + console.setLevel(logging.DEBUG) + else: + # Otherwise, log critical and error messages + console.setLevel(logging.ERROR) + formatter = logging.Formatter('%(name)-12s: %(levelname)-8s %(message)s') + console.setFormatter(formatter) + logging.getLogger('').addHandler(console) + +def main(): options = parse_options() + logging_setup(options) # check the hostname is correctly configured, it must be as the kldap # utilities just use the hostname as returned by gethostbyname to set # up some of the standard entries - host_name = socket.gethostname() + if options.host_name: + host_name = options.host_name + else: + host_name = socket.gethostname() if len(host_name.split(".")) < 2: print "Invalid hostname <"+host_name+">" print "Check the /etc/hosts file and make sure to have a valid FQDN" @@ -77,15 +102,23 @@ def main(): print "The Final KDC Host Name will be: " + host_name - ds = ipa.dsinstance.DsInstance() - ds.create_instance(options.ds_user, options.realm_name, host_name, options.password) - krb = ipa.krbinstance.KrbInstance() - krb.create_instance(options.ds_user, options.realm_name, host_name, options.password, options.master_password) + # Create a directory server instance + ds = ipaserver.dsinstance.DsInstance() + ds.create_instance(options.ds_user, options.realm_name, host_name, + options.password) - #restart ds after the krb instance have add the sasl map and the ldap keytab + # Create a kerberos instance + krb = ipaserver.krbinstance.KrbInstance() + krb.create_instance(options.ds_user, options.realm_name, host_name, + options.password, options.master_password) + + # Restart ds after the krb instance have add the sasl map ds.restart() + # Restart apache + run(["/sbin/service", "httpd", "restart"]) + return 0 main() diff --git a/ipa-install/src/ipa-server-setupssl b/ipa-server/ipa-install/ipa-server-setupssl index f7532790..f7532790 100644 --- a/ipa-install/src/ipa-server-setupssl +++ b/ipa-server/ipa-install/ipa-server-setupssl diff --git a/ipa-install/share/60kerberos.ldif b/ipa-server/ipa-install/share/60kerberos.ldif index 3431d22e..3431d22e 100644 --- a/ipa-install/share/60kerberos.ldif +++ b/ipa-server/ipa-install/share/60kerberos.ldif diff --git a/ipa-install/share/60samba.ldif b/ipa-server/ipa-install/share/60samba.ldif index d3a6d31b..d3a6d31b 100644 --- a/ipa-install/share/60samba.ldif +++ b/ipa-server/ipa-install/share/60samba.ldif diff --git a/ipa-install/share/Makefile b/ipa-server/ipa-install/share/Makefile index 380480bc..380480bc 100644 --- a/ipa-install/share/Makefile +++ b/ipa-server/ipa-install/share/Makefile diff --git a/ipa-install/share/bootstrap-template.ldif b/ipa-server/ipa-install/share/bootstrap-template.ldif index d83f715b..d83f715b 100644 --- a/ipa-install/share/bootstrap-template.ldif +++ b/ipa-server/ipa-install/share/bootstrap-template.ldif diff --git a/ipa-install/share/default-aci.ldif b/ipa-server/ipa-install/share/default-aci.ldif index 8916833c..8916833c 100644 --- a/ipa-install/share/default-aci.ldif +++ b/ipa-server/ipa-install/share/default-aci.ldif diff --git a/ipa-install/share/kdc.conf.template b/ipa-server/ipa-install/share/kdc.conf.template index 69e769e3..69e769e3 100644 --- a/ipa-install/share/kdc.conf.template +++ b/ipa-server/ipa-install/share/kdc.conf.template diff --git a/ipa-install/share/kerberos.ldif b/ipa-server/ipa-install/share/kerberos.ldif index ae4564f6..ae4564f6 100644 --- a/ipa-install/share/kerberos.ldif +++ b/ipa-server/ipa-install/share/kerberos.ldif diff --git a/ipa-server/ipa-install/share/krb5.conf.template b/ipa-server/ipa-install/share/krb5.conf.template new file mode 100644 index 00000000..23a24703 --- /dev/null +++ b/ipa-server/ipa-install/share/krb5.conf.template @@ -0,0 +1,42 @@ +[logging] + default = FILE:/var/log/krb5libs.log + kdc = FILE:/var/log/krb5kdc.log + admin_server = FILE:/var/log/kadmind.log + +[libdefaults] + default_realm = $REALM + dns_lookup_realm = true + dns_lookup_kdc = true + ticket_lifetime = 24h + forwardable = yes + +[realms] + $REALM = { + kdc = $FQDN:88 + admin_server = $FQDN:749 + default_domain = $DOMAIN +} + +[domain_realm] + .$DOMAIN = $REALM + $DOMAIN = $REALM + +[appdefaults] + pam = { + debug = false + ticket_lifetime = 36000 + renew_lifetime = 36000 + forwardable = true + krb4_convert = false + } + +[dbmodules] + $REALM = { + db_library = kldap + ldap_servers = ldap://127.0.0.1/ + ldap_kerberos_container_dn = cn=kerberos,$SUFFIX + ldap_kdc_dn = uid=kdc,cn=kerberos,$SUFFIX + ldap_kadmind_dn = uid=kdc,cn=kerberos,$SUFFIX + ldap_service_password_file = /var/kerberos/krb5kdc/ldappwd + } + diff --git a/ipa-install/test/test-users.ldif b/ipa-server/ipa-install/test/test-users.ldif index 424eedb5..424eedb5 100644 --- a/ipa-install/test/test-users.ldif +++ b/ipa-server/ipa-install/test/test-users.ldif diff --git a/ipa-slapi-plugins/ipa-pwd-extop/README b/ipa-server/ipa-slapi-plugins/README index e69de29b..e69de29b 100644 --- a/ipa-slapi-plugins/ipa-pwd-extop/README +++ b/ipa-server/ipa-slapi-plugins/README diff --git a/ipa-slapi-plugins/ipa-pwd-extop/Makefile b/ipa-server/ipa-slapi-plugins/ipa-pwd-extop/Makefile index 2a564643..2a564643 100644 --- a/ipa-slapi-plugins/ipa-pwd-extop/Makefile +++ b/ipa-server/ipa-slapi-plugins/ipa-pwd-extop/Makefile diff --git a/ipa-web/README b/ipa-server/ipa-slapi-plugins/ipa-pwd-extop/README index e69de29b..e69de29b 100644 --- a/ipa-web/README +++ b/ipa-server/ipa-slapi-plugins/ipa-pwd-extop/README diff --git a/ipa-slapi-plugins/ipa-pwd-extop/ipa_pwd_extop.c b/ipa-server/ipa-slapi-plugins/ipa-pwd-extop/ipa_pwd_extop.c index f871ee4f..f871ee4f 100644 --- a/ipa-slapi-plugins/ipa-pwd-extop/ipa_pwd_extop.c +++ b/ipa-server/ipa-slapi-plugins/ipa-pwd-extop/ipa_pwd_extop.c diff --git a/ipa-slapi-plugins/ipa-pwd-extop/plugin-conf.ldif b/ipa-server/ipa-slapi-plugins/ipa-pwd-extop/plugin-conf.ldif index 738ef7ab..738ef7ab 100644 --- a/ipa-slapi-plugins/ipa-pwd-extop/plugin-conf.ldif +++ b/ipa-server/ipa-slapi-plugins/ipa-pwd-extop/plugin-conf.ldif diff --git a/ipa-server/ipaserver/__init__.py b/ipa-server/ipaserver/__init__.py new file mode 100644 index 00000000..6d254d6a --- /dev/null +++ b/ipa-server/ipaserver/__init__.py @@ -0,0 +1,57 @@ +#! /usr/bin/python -E +# Authors: Karl MacMillan <kmacmillan@mentalrootkit.com> +# see inline +# +# 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 +# + +__all__ = ["dsinstance", "krbinstance"] + +# +# Functions common for the XML RPC client and server +# +# Authors: +# Mike McLean <mikem@redhat.com> (from koji) + +# functions for encoding/decoding optional arguments + +def encode_args(*args,**opts): + """The function encodes optional arguments as regular arguments. + + This is used to allow optional arguments in xmlrpc calls + Returns a tuple of args + """ + if opts: + opts['__starstar'] = True + args = args + (opts,) + return args + +def decode_args(*args): + """Decodes optional arguments from a flat argument list + + Complementary to encode_args + Returns a tuple (args,opts) where args is a tuple and opts is a dict + """ + opts = {} + if len(args) > 0: + last = args[-1] + if type(last) == dict and last.get('__starstar',False): + del last['__starstar'] + opts = last + args = args[:-1] + return args,opts + diff --git a/ipa-install/src/ipa/dsinstance.py b/ipa-server/ipaserver/dsinstance.py index a275bf40..775a2f2b 100644 --- a/ipa-install/src/ipa/dsinstance.py +++ b/ipa-server/ipaserver/dsinstance.py @@ -24,8 +24,13 @@ import tempfile import shutil import logging import pwd +from util import * + SHARE_DIR = "/usr/share/ipa/" +SERVER_ROOT_64 = "/usr/lib64/fedora-ds-base" +SERVER_ROOT_32 = "/usr/lib/fedora-ds-base" + def generate_serverid(): """Generate a UUID (universally unique identifier) suitable @@ -44,38 +49,17 @@ def realm_to_suffix(realm_name): terms = ["dc=" + x.lower() for x in s] return ",".join(terms) -def template_str(txt, vars): - return string.Template(txt).substitute(vars) - -def template_file(infilename, vars): - txt = open(infilename).read() - return template_str(txt, vars) - -def write_tmp_file(txt): - fd = tempfile.NamedTemporaryFile() - fd.write(txt) - fd.flush() - - return fd - -def run(args, stdin=None): - p = subprocess.Popen(args, stdout=subprocess.PIPE, stderr=subprocess.PIPE) - if stdin: - stdout,stderr = p.communicate(stdin) +def find_server_root(): + if dir_exists(SERVER_ROOT_64): + return SERVER_ROOT_64 else: - stdout,stderr = p.communicate() - logging.info(stdout) - logging.info(stderr) - - if p.returncode != 0: - raise subprocess.CalledProcessError(p.returncode, args[0]) - + return SERVER_ROOT_32 INF_TEMPLATE = """ [General] FullMachineName= $FQHN SuiteSpotUserID= $USER -ServerRoot= /usr/lib/fedora-ds-base +ServerRoot= $SERVER_ROOT [slapd] ServerPort= 389 ServerIdentifier= $SERVERID @@ -126,22 +110,39 @@ class DsInstance: def __setup_sub_dict(self): suffix = realm_to_suffix(self.realm_name) + server_root = find_server_root() self.sub_dict = dict(FQHN=self.host_name, SERVERID=self.serverid, PASSWORD=self.admin_password, SUFFIX=suffix, - REALM=self.realm_name, USER=self.ds_user) + REALM=self.realm_name, USER=self.ds_user, + SERVER_ROOT=server_root) def __create_ds_user(self): try: pwd.getpwnam(self.ds_user) + logging.debug("ds user %s exists" % self.ds_user) except KeyError: + logging.debug("adding ds user %s" % self.ds_user) args = ["/usr/sbin/useradd", "-c", "DS System User", "-d", "/var/lib/fedora-ds", "-M", "-r", "-s", "/sbin/nologin", self.ds_user] run(args) + logging.debug("done adding user") def __create_instance(self): + logging.debug("creating ds instance . . . ") inf_txt = template_str(INF_TEMPLATE, self.sub_dict) + logging.debug(inf_txt) inf_fd = write_tmp_file(inf_txt) - args = ["/usr/bin/ds_newinst.pl", inf_fd.name] + logging.debug("writing inf template") + if file_exists("/usr/sbin/setup-ds.pl"): + args = ["/usr/sbin/setup-ds.pl", "--silent", "--logfile", "-", "-f", inf_fd.name] + logging.debug("calling setup-ds.pl") + else: + args = ["/usr/sbin/ds_newinst.pl", inf_fd.name] + logging.debug("calling ds_newinst.pl") run(args) + logging.debug("completed creating ds instance") + logging.debug("restarting ds instance") + self.restart() + logging.debug("done restarting ds instance") def __add_default_schemas(self): shutil.copyfile(SHARE_DIR + "60kerberos.ldif", @@ -150,14 +151,18 @@ class DsInstance: self.schema_dirname() + "60samba.ldif") def __enable_ssl(self): + logging.debug("configuring ssl for ds instance") dirname = self.config_dirname() args = ["/usr/sbin/ipa-server-setupssl", self.admin_password, dirname, self.host_name] run(args) + logging.debug("done configuring ssl for ds instance") def __add_default_layout(self): txt = template_file(SHARE_DIR + "bootstrap-template.ldif", self.sub_dict) inf_fd = write_tmp_file(txt) + logging.debug("adding default ds layout") args = ["/usr/bin/ldapmodify", "-xv", "-D", "cn=Directory Manager", "-w", self.admin_password, "-f", inf_fd.name] run(args) + logging.debug("done adding default ds layout") diff --git a/ipa-server/ipaserver/ipaldap.py b/ipa-server/ipaserver/ipaldap.py new file mode 100644 index 00000000..f440ae4b --- /dev/null +++ b/ipa-server/ipaserver/ipaldap.py @@ -0,0 +1,395 @@ +#! /usr/bin/python -E +# Authors: Rich Megginson <richm@redhat.com> +# Rob Crittenden <rcritten2redhat.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 +# + +import sys +import os +import os.path +import popen2 +import base64 +import urllib +import urllib2 +import socket +import ldif +import re +import ldap +import cStringIO +import time +import operator + +from ldap.ldapobject import SimpleLDAPObject + +class Error(Exception): pass +class InvalidArgumentError(Error): + def __init__(self,message): self.message = message + def __repr__(self): return message +class NoSuchEntryError(Error): + def __init__(self,message): self.message = message + def __repr__(self): return message + +class Entry: + """This class represents an LDAP Entry object. An LDAP entry consists of a DN + and a list of attributes. Each attribute consists of a name and a list of + values. 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() + 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""" + return self.data.get(name,[None])[0] + + 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 __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,Entry.base64_attrs,1000).unparse(self.dn,newdata) + return sio.getvalue() + +def wrapper(f,name): + """This is the method that wraps all of the methods of the superclass. This seems + to need to be an unbound method, that's why it's outside of IPAdmin. Perhaps there + is some way to do this with the new classmethod or staticmethod of 2.4. + Basically, we replace every call to a method in SimpleLDAPObject (the superclass + of IPAdmin) with a call to inner. The f argument to wrapper is the bound method + of IPAdmin (which is inherited from the superclass). Bound means that it will implicitly + be called with the self argument, it is not in the args list. name is the name of + the method to call. If name is a method that returns entry objects (e.g. result), + we wrap the data returned by an Entry class. If name is a method that takes an entry + argument, we extract the raw data from the entry object to pass in.""" + def inner(*args, **kargs): + if name == 'result': + type, data = f(*args, **kargs) + # data is either a 2-tuple or a list of 2-tuples + # print data + if data: + if isinstance(data,tuple): + return type, Entry(data) + elif isinstance(data,list): + return type, [Entry(x) for x in data] + else: + raise TypeError, "unknown data type %s returned by result" % type(data) + else: + return type, data + elif name.startswith('add'): + # the first arg is self + # the second and third arg are the dn and the data to send + # We need to convert the Entry into the format used by + # python-ldap + ent = args[0] + if isinstance(ent,Entry): + return f(ent.dn, ent.toTupleList(), *args[2:]) + else: + return f(*args, **kargs) + else: + return f(*args, **kargs) + return inner + +class IPAdmin(SimpleLDAPObject): + CFGSUFFIX = "o=NetscapeRoot" + DEFAULT_USER_ID = "nobody" + + def __initPart2(self): + if self.binddn and len(self.binddn) and not hasattr(self,'sroot'): + try: + ent = self.getEntry('cn=config', ldap.SCOPE_BASE, '(objectclass=*)', + [ 'nsslapd-instancedir', 'nsslapd-errorlog' ]) + instdir = ent.getValue('nsslapd-instancedir') + self.sroot, self.inst = re.match(r'(.*)[\/]slapd-(\w+)$', instdir).groups() + self.errlog = ent.getValue('nsslapd-errorlog') + except (ldap.INSUFFICIENT_ACCESS, ldap.CONNECT_ERROR, NoSuchEntryError): + pass # usually means +# print "ignored exception" + except ldap.LDAPError, e: + print "caught exception ", e + 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() + + def __init__(self,host,port,binddn,bindpw): + """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""" + self.__wrapmethods() + self.port = port or 389 + self.sslport = 0 + self.host = host + self.binddn = binddn + self.bindpw = bindpw + # see if is local or not + host1 = IPAdmin.getfqdn(host) + host2 = IPAdmin.getfqdn() + self.isLocal = (host1 == host2) + self.suffixes = {} + self.__localinit__() + + def __str__(self): + return self.host + ":" + str(self.port) + + def toLDAPURL(self): + return "ldap://%s:%d/" % (self.host,self.port) + + def getEntry(self,*args): + """This wraps the search function. It is common to just get one entry""" + res = self.search(*args) + type, obj = self.result(res) + if not obj: + raise NoSuchEntryError("no such entry for " + str(args)) + elif isinstance(obj,Entry): + return obj + else: # assume list/tuple + return obj[0] + + 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""" + try: + self.add_s(*args) + except ldap.ALREADY_EXISTS: + raise ldap.ALREADY_EXISTS + except ldap.LDAPError, e: + raise e + return "Success" + + def __wrapmethods(self): + """This wraps all methods of SimpleLDAPObject, so that we can intercept + the methods that deal with entries. Instead of using a raw list of tuples + of lists of hashes of arrays as the entry object, we want to wrap entries + in an Entry class that provides some useful methods""" + for name in dir(self.__class__.__bases__[0]): + attr = getattr(self, name) + if callable(attr): + setattr(self, name, wrapper(attr, name)) + + def exportLDIF(self, file, suffix, forrepl=False, verbose=False): + cn = "export" + str(int(time.time())) + dn = "cn=%s, cn=export, cn=tasks, cn=config" % cn + entry = Entry(dn) + entry.setValues('objectclass', 'top', 'extensibleObject') + entry.setValues('cn', cn) + entry.setValues('nsFilename', file) + entry.setValues('nsIncludeSuffix', suffix) + if forrepl: + entry.setValues('nsExportReplica', 'true') + + rc = self.startTaskAndWait(entry, verbose) + + if rc: + if verbose: + print "Error: export task %s for file %s exited with %d" % (cn,file,rc) + else: + if verbose: + print "Export task %s for file %s completed successfully" % (cn,file) + return rc + + def waitForEntry(self, dn, timeout=7200, attr='', quiet=False): + scope = ldap.SCOPE_BASE + filter = "(objectclass=*)" + attrlist = [] + if attr: + filter = "(%s=*)" % attr + attrlist.append(attr) + timeout += int(time.time()) + + if isinstance(dn,Entry): + dn = dn.dn + + # wait for entry and/or attr to show up + if not quiet: + sys.stdout.write("Waiting for %s %s:%s " % (self,dn,attr)) + sys.stdout.flush() + entry = None + while not entry and int(time.time()) < timeout: + try: + entry = self.getEntry(dn, scope, filter, attrlist) + except NoSuchEntryError: pass # found entry, but no attr + except ldap.NO_SUCH_OBJECT: pass # no entry yet + except ldap.LDAPError, e: # badness + print "\nError reading entry", dn, e + break + if not entry: + if not quiet: + sys.stdout.write(".") + sys.stdout.flush() + time.sleep(1) + + if not entry and int(time.time()) > timeout: + print "\nwaitForEntry timeout for %s for %s" % (self,dn) + elif entry and not quiet: + print "\nThe waited for entry is:", entry + else: + print "\nError: could not read entry %s from %s" % (dn,self) + + return entry + + def addSchema(self, attr, val): + dn = "cn=schema" + self.modify_s(dn, [(ldap.MOD_ADD, attr, val)]) + + def addAttr(self, *args): + return self.addSchema('attributeTypes', args) + + def addObjClass(self, *args): + return self.addSchema('objectClasses', args) + + ########################### + # Static methods start here + ########################### + def normalizeDN(dn): + # not great, but will do until we use a newer version of python-ldap + # that has DN utilities + ary = ldap.explode_dn(dn.lower()) + return ",".join(ary) + normalizeDN = staticmethod(normalizeDN) + + def getfqdn(name=''): + return socket.getfqdn(name) + getfqdn = staticmethod(getfqdn) + + def getdomainname(name=''): + fqdn = IPAdmin.getfqdn(name) + index = fqdn.find('.') + if index >= 0: + return fqdn[index+1:] + else: + return fqdn + getdomainname = staticmethod(getdomainname) + + def getdefaultsuffix(name=''): + dm = IPAdmin.getdomainname(name) + if dm: + return "dc=" + dm.replace('.', ', dc=') + else: + return 'dc=localdomain' + getdefaultsuffix = staticmethod(getdefaultsuffix) + + def getnewhost(args): + """One of the arguments to createInstance is newhost. If this is specified, we need + to convert it to the fqdn. If not given, we need to figure out what the fqdn of the + local host is. This method sets newhost in args to the appropriate value and + returns True if newhost is the localhost, False otherwise""" + isLocal = False + if args.has_key('newhost'): + args['newhost'] = IPAdmin.getfqdn(args['newhost']) + myhost = IPAdmin.getfqdn() + if myhost == args['newhost']: + isLocal = True + elif args['newhost'] == 'localhost' or \ + args['newhost'] == 'localhost.localdomain': + isLocal = True + else: + isLocal = True + args['newhost'] = IPAdmin.getfqdn() + return isLocal + getnewhost = staticmethod(getnewhost) + + def is_a_dn(dn): + """Returns True if the given string is a DN, False otherwise.""" + return (dn.find("=") > 0) + is_a_dn = staticmethod(is_a_dn) diff --git a/ipa-install/src/ipa/krbinstance.py b/ipa-server/ipaserver/krbinstance.py index d33dea90..131eee35 100644 --- a/ipa-install/src/ipa/krbinstance.py +++ b/ipa-server/ipaserver/krbinstance.py @@ -49,36 +49,10 @@ def generate_kdc_password(): rndpwd += chr(r.randint(65,90)) #stricter set for testing return rndpwd -def template_str(txt, vars): - return string.Template(txt).substitute(vars) - -def template_file(infilename, vars): - txt = open(infilename).read() - return template_str(txt, vars) - -def write_tmp_file(txt): - fd = tempfile.NamedTemporaryFile() - fd.write(txt) - fd.flush() - - return fd - def ldap_mod(fd, dn, pwd): args = ["/usr/bin/ldapmodify", "-h", "127.0.0.1", "-xv", "-D", dn, "-w", pwd, "-f", fd.name] run(args) -def run(args, stdin=None): - p = subprocess.Popen(args, stdout=subprocess.PIPE, stderr=subprocess.PIPE) - if stdin: - stdout,stderr = p.communicate(stdin) - else: - stdout,stderr = p.communicate() - logging.info(stdout) - logging.info(stderr) - - if p.returncode != 0: - raise subprocess.CalledProcessError(p.returncode, args[0]) - class KrbInstance: def __init__(self): self.ds_user = None diff --git a/ipa-server/ipaserver/util.py b/ipa-server/ipaserver/util.py new file mode 100644 index 00000000..2f677dad --- /dev/null +++ b/ipa-server/ipaserver/util.py @@ -0,0 +1,80 @@ +#! /usr/bin/python -E +# Authors: Simo Sorce <ssorce@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 +# + +SHARE_DIR = "/usr/share/ipa/" + +import string +import tempfile +import logging +import subprocess +import os +import stat + +def realm_to_suffix(realm_name): + s = realm_name.split(".") + terms = ["dc=" + x.lower() for x in s] + return ",".join(terms) + + +def template_str(txt, vars): + return string.Template(txt).substitute(vars) + +def template_file(infilename, vars): + txt = open(infilename).read() + return template_str(txt, vars) + +def write_tmp_file(txt): + fd = tempfile.NamedTemporaryFile() + fd.write(txt) + fd.flush() + + return fd + +def run(args, stdin=None): + p = subprocess.Popen(args, stdout=subprocess.PIPE, stderr=subprocess.PIPE) + if stdin: + stdout,stderr = p.communicate(stdin) + else: + stdout,stderr = p.communicate() + logging.info(stdout) + logging.info(stderr) + + if p.returncode != 0: + raise subprocess.CalledProcessError(p.returncode, args[0]) + +def file_exists(filename): + try: + mode = os.stat(filename)[stat.ST_MODE] + if stat.S_ISREG(mode): + return True + else: + return False + except: + return False + +def dir_exists(filename): + try: + mode = os.stat(filename)[stat.ST_MODE] + if stat.S_ISDIR(mode): + return True + else: + return False + except: + return False diff --git a/ipa-server/xmlrpc-server/Makefile b/ipa-server/xmlrpc-server/Makefile new file mode 100644 index 00000000..10b796ea --- /dev/null +++ b/ipa-server/xmlrpc-server/Makefile @@ -0,0 +1,12 @@ +SHAREDIR = $(DESTDIR)/usr/share/ipa/ipaserver +HTTPDIR = $(DESTDIR)/etc/httpd/conf.d/ + +all: ; + +install: + -mkdir -p $(SHAREDIR) + install -m 644 *.py $(SHAREDIR) + install -m 644 ipa.conf $(HTTPDIR) + +clean: + rm -f *~ *.pyc diff --git a/ipa-web/api/README b/ipa-server/xmlrpc-server/README index e69de29b..e69de29b 100644 --- a/ipa-web/api/README +++ b/ipa-server/xmlrpc-server/README diff --git a/ipa-server/xmlrpc-server/funcs.py b/ipa-server/xmlrpc-server/funcs.py new file mode 100644 index 00000000..d6473f9a --- /dev/null +++ b/ipa-server/xmlrpc-server/funcs.py @@ -0,0 +1,170 @@ +# 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 only +# +# 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 +# + +import sys +sys.path.append("/usr/share/ipa") + +import ldap +import ipaserver.dsinstance +import ipaserver.ipaldap +import pdb +import string +from types import * +import xmlrpclib + +# FIXME, this needs to be auto-discovered +host = 'localhost' +port = 389 +binddn = "cn=directory manager" +bindpw = "freeipa" + +basedn = "dc=greyoak,dc=com" +scope = ldap.SCOPE_SUBTREE + +def get_user (username): + """Get a specific user's entry. Return as a dict of values. + Multi-valued fields are represented as lists. + """ + ent="" + + # FIXME: Is this the filter we want or should it be more specific? + filter = "(uid=" + username + ")" + try: + m1 = ipaserver.ipaldap.IPAdmin(host,port,binddn,bindpw) + ent = m1.getEntry(basedn, scope, filter, None) + except ldap.LDAPError, e: + raise xmlrpclib.Fault(1, e) + except ipaserver.ipaldap.NoSuchEntryError: + raise xmlrpclib.Fault(2, "No such user") + + # Convert to LDIF + entry = str(ent) + + # Strip off any junk + entry = entry.strip() + + # Don't need to identify binary fields and this breaks the parser so + # remove double colons + entry = entry.replace('::', ':') + specs = [spec.split(':') for spec in entry.split('\n')] + + # Convert into a dict. We need to handle multi-valued attributes as well + # so we'll convert those into lists. + user={} + for (k,v) in specs: + k = k.lower() + 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: + user[k] = v.strip() + + return user +# return str(ent) # return as LDIF + +def add_user (user): + """Add a user in LDAP""" + dn="uid=%s,ou=users,ou=default,dc=greyoak,dc=com" % user['uid'] + entry = ipaserver.ipaldap.Entry(dn) + + # some required objectclasses + 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(u, user[u]) + + try: + m1 = ipaserver.ipaldap.IPAdmin(host,port,binddn,bindpw) + 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 (): + """Get the list of fields to be used when adding users in the GUI.""" + + # FIXME: this needs to be pulled from LDAP + fields = [] + + field1 = { + "name": "uid" , + "label": "Login:", + "type": "text", + "validator": "text", + "required": "true" + } + fields.append(field1) + + field1 = { + "name": "userPassword" , + "label": "Password:", + "type": "password", + "validator": "String", + "required": "true" + } + fields.append(field1) + + field1 = { + "name": "gn" , + "label": "First name:", + "type": "text", + "validator": "string", + "required": "true" + } + fields.append(field1) + + field1 = { + "name": "sn" , + "label": "Last name:", + "type": "text", + "validator": "string", + "required": "true" + } + fields.append(field1) + + field1 = { + "name": "mail" , + "label": "E-mail address:", + "type": "text", + "validator": "email", + "required": "true" + } + fields.append(field1) + + return fields diff --git a/ipa-server/xmlrpc-server/ipa.conf b/ipa-server/xmlrpc-server/ipa.conf new file mode 100644 index 00000000..1880268c --- /dev/null +++ b/ipa-server/xmlrpc-server/ipa.conf @@ -0,0 +1,24 @@ +# LoadModule auth_kerb_module modules/mod_auth_kerb.so + +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 + ErrorDocument 401 /errors/unauthorized.html + + SetHandler mod_python + PythonHandler ipaxmlrpc + + PythonDebug Off + + # this is pointless to use since it would just reload ipaxmlrpc.py + PythonAutoReload Off +</Directory> diff --git a/ipa-server/xmlrpc-server/ipaxmlrpc.py b/ipa-server/xmlrpc-server/ipaxmlrpc.py new file mode 100644 index 00000000..ad5e3068 --- /dev/null +++ b/ipa-server/xmlrpc-server/ipaxmlrpc.py @@ -0,0 +1,277 @@ +# mod_python script + +# ipaxmlrpc - an XMLRPC interface for ipa. +# Copyright (c) 2007 Red Hat +# +# IPA is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; +# version 2.1 of the License. +# +# This software 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 +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this software; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +# +# Based on kojixmlrpc - an XMLRPC interface for koji by +# Mike McLean <mikem@redhat.com> +# +# Authors: +# Rob Crittenden <rcritten@redhat.com> + +import sys +sys.path.append("/usr/share/ipa") + + +import time +import traceback +import pprint +from xmlrpclib import Marshaller,loads,dumps,Fault +from mod_python import apache + +import ipaserver +import funcs +import string +import base64 + +# +# An override so we can base64 encode all outgoing values. +# This is set by calling: Marshaller._Marshaller__dump = xmlrpclib_dump +# +# Not currently used. +# +def xmlrpclib_escape(s, replace = string.replace): + """ + xmlrpclib only handles certain characters. Lets encode the whole + blob + """ + + return base64.encodestring(s) + +def xmlrpclib_dump(self, value, write): + """ + xmlrpclib cannot marshal instances of subclasses of built-in + types. This function overrides xmlrpclib.Marshaller.__dump so that + any value that is an instance of one of its acceptable types is + marshalled as that type. + + xmlrpclib also cannot handle invalid 7-bit control characters. See + above. + """ + + # Use our escape function + args = [self, value, write] + if isinstance(value, (str, unicode)): + args.append(xmlrpclib_escape) + + try: + # Try for an exact match first + f = self.dispatch[type(value)] + except KeyError: + # Try for an isinstance() match + for Type, f in self.dispatch.iteritems(): + if isinstance(value, Type): + f(*args) + return + raise TypeError, "cannot marshal %s objects" % type(value) + else: + f(*args) + + +class ModXMLRPCRequestHandler(object): + """Simple XML-RPC handler for mod_python environment""" + + def __init__(self): + self.funcs = {} + self.traceback = False + #introspection functions + self.register_function(self.list_api, name="_listapi") + self.register_function(self.system_listMethods, name="system.listMethods") + self.register_function(self.system_methodSignature, name="system.methodSignature") + self.register_function(self.system_methodHelp, name="system.methodHelp") + self.register_function(self.multiCall) + + def register_function(self, function, name = None): + if name is None: + name = function.__name__ + self.funcs[name] = function + + def register_module(self, instance, prefix=None): + """Register all the public functions in an instance with prefix prepended + + For example + h.register_module(exports,"pub.sys") + will register the methods of exports with names like + pub.sys.method1 + pub.sys.method2 + ...etc + """ + for name in dir(instance): + if name.startswith('_'): + continue + function = getattr(instance, name) + if not callable(function): + continue + if prefix is not None: + name = "%s.%s" %(prefix,name) + self.register_function(function, name=name) + + def register_instance(self,instance): + self.register_module(instance) + + def _marshaled_dispatch(self, data): + """Dispatches an XML-RPC method from marshalled (XML) data.""" + + params, method = loads(data) + + # special case +# if method == "get_user": +# Marshaller._Marshaller__dump = xmlrpclib_dump + + start = time.time() + # generate response + try: + response = self._dispatch(method, params) + # wrap response in a singleton tuple + response = (response,) + response = dumps(response, methodresponse=1, allow_none=1) + except Fault, fault: + self.traceback = True + response = dumps(fault) + except: + self.traceback = True + # report exception back to server + e_class, e = sys.exc_info()[:2] + faultCode = getattr(e_class,'faultCode',1) + tb_str = ''.join(traceback.format_exception(*sys.exc_info())) + faultString = tb_str + response = dumps(Fault(faultCode, faultString)) + + return response + + def _dispatch(self,method,params): + func = self.funcs.get(method,None) + if func is None: + raise Fault(1, "Invalid method: %s" % method) + params,opts = ipaserver.decode_args(*params) + + ret = func(*params,**opts) + + return ret + + def multiCall(self, calls): + """Execute a multicall. Execute each method call in the calls list, collecting + results and errors, and return those as a list.""" + results = [] + for call in calls: + try: + result = self._dispatch(call['methodName'], call['params']) + except Fault, fault: + results.append({'faultCode': fault.faultCode, 'faultString': fault.faultString}) + except: + # transform unknown exceptions into XML-RPC Faults + # don't create a reference to full traceback since this creates + # a circular reference. + exc_type, exc_value = sys.exc_info()[:2] + faultCode = getattr(exc_type, 'faultCode', 1) + faultString = ', '.join(exc_value.args) + trace = traceback.format_exception(*sys.exc_info()) + # traceback is not part of the multicall spec, but we include it for debugging purposes + results.append({'faultCode': faultCode, 'faultString': faultString, 'traceback': trace}) + else: + results.append([result]) + + return results + + def list_api(self): + funcs = [] + for name,func in self.funcs.items(): + #the keys in self.funcs determine the name of the method as seen over xmlrpc + #func.__name__ might differ (e.g. for dotted method names) + args = self._getFuncArgs(func) + funcs.append({'name': name, + 'doc': func.__doc__, + 'args': args}) + return funcs + + def _getFuncArgs(self, func): + args = [] + for x in range(0, func.func_code.co_argcount): + if x == 0 and func.func_code.co_varnames[x] == "self": + continue + if func.func_defaults and func.func_code.co_argcount - x <= len(func.func_defaults): + args.append((func.func_code.co_varnames[x], func.func_defaults[x - func.func_code.co_argcount + len(func.func_defaults)])) + else: + args.append(func.func_code.co_varnames[x]) + return args + + def system_listMethods(self): + return self.funcs.keys() + + def system_methodSignature(self, method): + #it is not possible to autogenerate this data + return 'signatures not supported' + + def system_methodHelp(self, method): + func = self.funcs.get(method) + if func is None: + return "" + arglist = [] + for arg in self._getFuncArgs(func): + if isinstance(arg,str): + arglist.append(arg) + else: + arglist.append('%s=%s' % (arg[0], arg[1])) + ret = '%s(%s)' % (method, ", ".join(arglist)) + if func.__doc__: + ret += "\ndescription: %s" % func.__doc__ + return ret + + def handle_request(self,req): + """Handle a single XML-RPC request""" + + # XMLRPC uses POST only. Reject anything else + if req.method != 'POST': + req.allow_methods(['POST'],1) + raise apache.SERVER_RETURN, apache.HTTP_METHOD_NOT_ALLOWED + + response = self._marshaled_dispatch(req.read()) + + req.content_type = "text/xml" + req.set_content_length(len(response)) + req.write(response) + + +# +# mod_python handler +# + +def handler(req, profiling=False): + if profiling: + import profile, pstats, StringIO, tempfile + global _profiling_req + _profiling_req = req + temp = tempfile.NamedTemporaryFile() + profile.run("import ipxmlrpc; ipaxmlrpc.handler(ipaxmlrpc._profiling_req, False)", temp.name) + stats = pstats.Stats(temp.name) + strstream = StringIO.StringIO() + sys.stdout = strstream + stats.sort_stats("time") + stats.print_stats() + req.write("<pre>" + strstream.getvalue() + "</pre>") + _profiling_req = None + else: + opts = req.get_options() + try: + h = ModXMLRPCRequestHandler() + h.register_function(funcs.get_user) + h.register_function(funcs.add_user) + h.register_function(funcs.get_add_schema) + h.handle_request(req) + finally: + pass + return apache.OK diff --git a/ipa-web/client/README b/ipa-web/client/README deleted file mode 100644 index e69de29b..00000000 --- a/ipa-web/client/README +++ /dev/null diff --git a/ipa-web/gui/README b/ipa-web/gui/README deleted file mode 100644 index e69de29b..00000000 --- a/ipa-web/gui/README +++ /dev/null |