summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAdam Young <ayoung@redhat.com>2010-07-15 12:21:02 -0400
committerAdam Young <ayoung@redhat.com>2010-07-15 12:21:02 -0400
commitc62f8e9fdef1214cced53eb4e1267d0c1cdc1325 (patch)
tree5eabd770aa13b700b3de6eae0528c16ed5c47604
parent28a5ef6616a36590c3acec16a233dc587c782a8b (diff)
parent57a9001f8d12a5caec01c628089a4624499b76bc (diff)
downloadfreeipa-c62f8e9fdef1214cced53eb4e1267d0c1cdc1325.tar.gz
freeipa-c62f8e9fdef1214cced53eb4e1267d0c1cdc1325.tar.xz
freeipa-c62f8e9fdef1214cced53eb4e1267d0c1cdc1325.zip
Merged wsgi, based on pavels changes.
-rw-r--r--install/share/nis.uldif12
-rwxr-xr-xinstall/tools/ipa-compat-manage60
-rwxr-xr-xinstall/tools/ipa-nis-manage94
-rw-r--r--ipalib/constants.py1
-rw-r--r--ipalib/frontend.py2
-rw-r--r--ipalib/plugable.py5
-rw-r--r--ipalib/plugins/baseldap.py16
-rw-r--r--ipalib/plugins/cert.py165
-rw-r--r--ipalib/plugins/group.py1
-rw-r--r--ipalib/plugins/host.py56
-rw-r--r--ipalib/plugins/migration.py5
-rw-r--r--ipalib/plugins/netgroup.py72
-rw-r--r--ipalib/plugins/service.py109
-rw-r--r--ipalib/plugins/user.py1
-rw-r--r--ipalib/x509.py289
-rw-r--r--ipapython/nsslib.py1
-rw-r--r--ipaserver/plugins/ldap2.py46
-rw-r--r--ipaserver/plugins/selfsign.py7
-rwxr-xr-xlite-server.py1
-rw-r--r--tests/test_xmlrpc/test_cert.py34
-rw-r--r--tests/test_xmlrpc/test_host_plugin.py3
-rw-r--r--tests/test_xmlrpc/test_netgroup_plugin.py146
-rw-r--r--tests/test_xmlrpc/test_service_plugin.py1
-rw-r--r--tests/test_xmlrpc/xmlrpc_test.py2
24 files changed, 634 insertions, 495 deletions
diff --git a/install/share/nis.uldif b/install/share/nis.uldif
index 86f521b61..5860d23af 100644
--- a/install/share/nis.uldif
+++ b/install/share/nis.uldif
@@ -65,12 +65,22 @@ default:nis-map: netid.byname
default:nis-base: cn=users, cn=accounts, $SUFFIX
default:nis-secure: no
+# Note that the escapes in this entry can be quite confusing. The trick
+# is that each level of nesting requires (2^n) - 1 escapes. So the
+# first level is \", the second is \\\", the third is \\\\\\\", etc.
+# (1, 3, 7, 15, more than that and you'll go insane)
+
+# Note that this configuration mirrors the Schema Compat configuration for
+# triples.
dn: nis-domain=$DOMAIN+nis-map=netgroup, cn=NIS Server, cn=plugins, cn=config
default:objectclass: top
default:objectclass: extensibleObject
default:nis-domain: $DOMAIN
default:nis-map: netgroup
-default:nis-base: cn=ng, cn=compat, $SUFFIX
+default:nis-base: cn=ng, cn=alt, $SUFFIX
+default:nis-filter: (objectClass=ipanisNetgroup)
+default:nis-key-format: %{cn}
+default:nis-value-format: %merge(" ","%{memberNisNetgroup}","(%link(\"%collect(\\\"%{externalHost}\\\",\\\"%deref(\\\\\\\"memberHost\\\\\\\",\\\\\\\"fqdn\\\\\\\")\\\",\\\"%deref_r(\\\\\\\"member\\\\\\\",\\\\\\\"fqdn\\\\\\\")\\\",\\\"%deref_r(\\\\\\\"memberHost\\\\\\\",\\\\\\\"member\\\\\\\",\\\\\\\"fqdn\\\\\\\")\\\")\",\"-\",\",\",\"%collect(\\\"%deref(\\\\\\\"memberUser\\\\\\\",\\\\\\\"uid\\\\\\\")\\\",\\\"%deref_r(\\\\\\\"member\\\\\\\",\\\\\\\"uid\\\\\\\")\\\",\\\"%deref_r(\\\\\\\"memberUser\\\\\\\",\\\\\\\"member\\\\\\\",\\\\\\\"uid\\\\\\\")\\\")\",\"-\"),%{nisDomainName:-})")
default:nis-secure: no
dn: cn=ng,cn=Schema Compatibility,cn=plugins,cn=config
diff --git a/install/tools/ipa-compat-manage b/install/tools/ipa-compat-manage
index b22ce77f9..3128ed718 100755
--- a/install/tools/ipa-compat-manage
+++ b/install/tools/ipa-compat-manage
@@ -22,18 +22,12 @@
import sys
try:
from optparse import OptionParser
- from ipapython import entity, ipautil, config
+ from ipapython import ipautil, config
from ipaserver.install import installutils
- from ipaserver.install.ldapupdate import LDAPUpdate, BadSyntax, UPDATES_DIR
+ from ipaserver.install.ldapupdate import LDAPUpdate, BadSyntax
from ipaserver.plugins.ldap2 import ldap2
- from ipalib import errors
+ from ipalib import api, errors
import logging
- import re
- import krbV
- import platform
- import shlex
- import time
- import random
except ImportError:
print >> sys.stderr, """\
There was a problem importing one of the required Python modules. The
@@ -43,6 +37,8 @@ error was:
""" % sys.exc_value
sys.exit(1)
+netgroup_compat_dn = "cn=ng,cn=Schema Compatibility,cn=plugins,cn=config"
+
def parse_options():
usage = "%prog [options] <enable|disable>\n"
usage += "%prog [options]\n"
@@ -71,7 +67,7 @@ def get_dirman_password():
def main():
retval = 0
loglevel = logging.ERROR
- files=['/usr/share/ipa/schema_compat.uldif']
+ files = ['/usr/share/ipa/schema_compat.uldif']
options, args = parse_options()
if options.debug:
@@ -94,6 +90,9 @@ def main():
else:
dirman_password = get_dirman_password()
+ api.bootstrap(context='cli', debug=options.debug)
+ api.finalize()
+
conn = None
try:
ldapuri = 'ldap://%s' % installutils.get_fqdn()
@@ -102,42 +101,57 @@ def main():
conn.connect(
bind_dn='cn=directory manager', bind_pw=dirman_password
)
- except errors.LDAPError, e:
+ except errors.LDAPError, lde:
print "An error occurred while connecting to the server."
- print e
+ print lde
return 1
if args[0] == "enable":
try:
- conn.get_entry('cn=Schema Compatibility,cn=plugins,cn=config')
+ conn.get_entry('cn=Schema Compatibility,cn=plugins,cn=config', normalize=False)
print "Plugin already Enabled"
retval = 2
except errors.NotFound:
print "Enabling plugin"
- except errors.LDAPError, e:
+ except errors.LDAPError, lde:
print "An error occurred while talking to the server."
- print e
+ print lde
retval = 1
if retval == 0:
ld = LDAPUpdate(dm_password=dirman_password, sub_dict={})
- retval = ld.update(files)
- if retval == 0:
+ rv = ld.update(files)
+ if rv:
print "This setting will not take effect until you restart Directory Server."
+ else:
+ print "Updating Directory Server failed."
+ retval = 1
elif args[0] == "disable":
- # Make a quick hack foir now, directly delete the entries by name,
+ # We can't disable schema compat if the NIS plugin is enabled
+ try:
+ conn.get_entry(netgroup_compat_dn, normalize=False)
+ print "The NIS plugin is configured, cannot disable compatibility."
+ print "Run 'ipa-nis-manage disable' first."
+ return 2
+ except errors.NotFound:
+ pass
+ # Make a quick hack for now, directly delete the entries by name,
# In future we should add delete capabilites to LDAPUpdate
try:
- conn.delete_entry('cn=groups,cn=Schema Compatibility,cn=plugins,cn=config')
- conn.delete_entry('cn=users,cn=Schema Compatibility,cn=plugins,cn=config')
- conn.delete_entry('cn=Schema Compatibility,cn=plugins,cn=config')
+ conn.delete_entry('cn=groups,cn=Schema Compatibility,cn=plugins,cn=config', normalize=False)
+ conn.delete_entry('cn=users,cn=Schema Compatibility,cn=plugins,cn=config', normalize=False)
+ conn.delete_entry('cn=Schema Compatibility,cn=plugins,cn=config', normalize=False)
except errors.NotFound:
print "Plugin is already disabled"
retval = 2
- except errors.LDAPError, e:
+ except errors.DatabaseError, dbe:
+ print "An error occurred while talking to the server."
+ print lde
+ retval = 1
+ except errors.LDAPError, lde:
print "An error occurred while talking to the server."
- print e
+ print lde
retval = 1
else:
diff --git a/install/tools/ipa-nis-manage b/install/tools/ipa-nis-manage
index 22cfd432e..706b0e630 100755
--- a/install/tools/ipa-nis-manage
+++ b/install/tools/ipa-nis-manage
@@ -22,11 +22,11 @@
import sys
try:
from optparse import OptionParser
- from ipapython import entity, ipautil, config
+ from ipapython import ipautil, config
from ipaserver.install import installutils
- from ipaserver.install.ldapupdate import LDAPUpdate, BadSyntax, UPDATES_DIR
+ from ipaserver.install.ldapupdate import LDAPUpdate, BadSyntax
from ipaserver.plugins.ldap2 import ldap2
- from ipalib import errors
+ from ipalib import api, errors
import logging
except ImportError:
print >> sys.stderr, """\
@@ -38,6 +38,7 @@ error was:
sys.exit(1)
nis_config_dn = "cn=NIS Server, cn=plugins, cn=config"
+compat_dn = "cn=Schema Compatibility,cn=plugins,cn=config"
def parse_options():
usage = "%prog [options] <enable|disable>\n"
@@ -64,10 +65,14 @@ def get_dirman_password():
return password
-def get_nis_config(conn):
+def get_entry(dn, conn):
+ """
+ Return the entry for the given DN. If the entry is not found return
+ None.
+ """
entry = None
try:
- (dn, entry) = conn.get_entry(nis_config_dn)
+ (dn, entry) = conn.get_entry(dn, normalize=False)
except errors.NotFound:
pass
return entry
@@ -75,7 +80,7 @@ def get_nis_config(conn):
def main():
retval = 0
loglevel = logging.ERROR
- files=['/usr/share/ipa/nis.uldif']
+ files = ['/usr/share/ipa/nis.uldif']
servicemsg = ""
options, args = parse_options()
@@ -99,6 +104,9 @@ def main():
else:
dirman_password = get_dirman_password()
+ api.bootstrap(context='cli', debug=options.debug)
+ api.finalize()
+
conn = None
try:
ldapuri = 'ldap://%s' % installutils.get_fqdn()
@@ -107,62 +115,86 @@ def main():
conn.connect(
bind_dn='cn=directory manager', bind_pw=dirman_password
)
- except errors.LDAPError, e:
+ except errors.LDAPError, lde:
print "An error occurred while connecting to the server."
- print e
+ print lde
return 1
if args[0] == "enable":
+ compat = get_entry(compat_dn, conn)
+ if compat is None:
+ print "The compat plugin needs to be enabled: ipa-compat-manage enable"
+ return 1
entry = None
try:
- entry = get_nis_config(conn)
- except errors.LDAPError, e:
+ entry = get_entry(nis_config_dn, conn)
+ except errors.LDAPError, lde:
print "An error occurred while talking to the server."
- print e
+ print lde
retval = 1
# Enable either the portmap or rpcbind service
try:
ipautil.run(["/sbin/chkconfig", "portmap", "on"])
servicemsg = "portmap"
- except ipautil.CalledProcessError, e:
- if e.returncode == 1:
+ except ipautil.CalledProcessError, cpe:
+ if cpe.returncode == 1:
try:
ipautil.run(["/sbin/chkconfig", "rpcbind", "on"])
servicemsg = "rpcbind"
- except ipautil.CalledProcessError, e:
+ except ipautil.CalledProcessError, cpe:
print "Unable to enable either portmap or rpcbind"
retval = 3
- if entry is None:
+ # The cn=config entry for the plugin may already exist but it
+ # could be turned off, handle both cases.
+ if (entry is None or
+ entry.get('nsslapd-pluginenabled', [''])[0].lower() == 'off'):
+ # Already configured, just enable the plugin
print "Enabling plugin"
-
- if entry is None:
- # Load the plugin configuration
- ld = LDAPUpdate(dm_password=dirman_password, sub_dict={})
- retval = ld.update(files)
+ ld = LDAPUpdate(dm_password=dirman_password, sub_dict={})
+ if ld.update(files) != True:
+ retval = 1
+ mod = {'nsslapd-pluginenabled': 'on'}
+ try:
+ conn.update_entry(nis_config_dn, mod, normalize=False)
+ except errors.EmptyModlist:
+ # plugin is already enabled, silently continue
+ pass
else:
- if entry.get('nsslapd-pluginenabled', '').lower() == 'off':
- # Already configured, just enable the plugin
- print "Enabling plugin"
- mod = {'nsslapd-pluginenabled': 'on'}
- conn.update_entry(nis_config_dn, mod)
- else:
- print "Plugin already Enabled"
- retval = 2
+ print "Plugin already Enabled"
+ retval = 2
elif args[0] == "disable":
try:
mod = {'nsslapd-pluginenabled': 'off'}
- conn.update_entry(nis_config_dn, mod)
+ conn.update_entry(nis_config_dn, mod, normalize=False)
except errors.NotFound:
print "Plugin is already disabled"
retval = 2
- except errors.LDAPError, e:
+ except errors.EmptyModlist:
+ print "Plugin is already disabled"
+ retval = 2
+ except errors.LDAPError, lde:
print "An error occurred while talking to the server."
- print e
+ print lde
retval = 1
+ # delete the netgroups compat area.
+ try:
+ conn.delete_entry('cn=ng,cn=Schema Compatibility,cn=plugins,cn=config', normalize=False)
+ except errors.NotFound:
+ pass
+ except errors.DatabaseError, dbe:
+ print "An error occurred while talking to the server."
+ print lde
+ retval = 1
+ except errors.LDAPError, lde:
+ print "An error occurred while talking to the server."
+ print lde
+ retval = 1
+
+
else:
retval = 1
diff --git a/ipalib/constants.py b/ipalib/constants.py
index 4008b0721..6b0ab5746 100644
--- a/ipalib/constants.py
+++ b/ipalib/constants.py
@@ -121,6 +121,7 @@ DEFAULT_CONFIG = (
# Debugging:
('verbose', 0),
('debug', False),
+ ('startup_traceback', False),
('mode', 'production'),
# CA plugin:
diff --git a/ipalib/frontend.py b/ipalib/frontend.py
index c270981f6..d320f02e0 100644
--- a/ipalib/frontend.py
+++ b/ipalib/frontend.py
@@ -1165,6 +1165,8 @@ class Method(Attribute, Command):
if 'no_output' in param.flags:
continue
yield param
+ for param in self._get_param_iterable('output_params', verb='has'):
+ yield param
class Property(Attribute):
diff --git a/ipalib/plugable.py b/ipalib/plugable.py
index 397004eb1..fd5f31a76 100644
--- a/ipalib/plugable.py
+++ b/ipalib/plugable.py
@@ -536,8 +536,9 @@ class API(DictProxy):
'skipping plugin module %s: %s', fullname, e.reason
)
except StandardError, e:
- import traceback
- self.log.error('could not load plugin module %r\n%s', pyfile, traceback.format_exc())
+ if self.env.startup_traceback:
+ import traceback
+ self.log.error('could not load plugin module %r\n%s', pyfile, traceback.format_exc())
raise e
def finalize(self):
diff --git a/ipalib/plugins/baseldap.py b/ipalib/plugins/baseldap.py
index 92b42d4ee..d492ebde9 100644
--- a/ipalib/plugins/baseldap.py
+++ b/ipalib/plugins/baseldap.py
@@ -70,6 +70,8 @@ class LDAPObject(Object):
object_name_plural = 'entries'
object_class = []
object_class_config = None
+ search_attributes = []
+ search_attributes_config = None
default_attributes = []
hidden_attributes = ['objectclass', 'aci']
uuid_attribute = ''
@@ -834,11 +836,23 @@ class LDAPSearch(CallbackInterface, crud.Search):
set(self.obj.default_attributes + search_kw.keys())
)
+ if self.obj.search_attributes:
+ search_attrs = self.obj.search_attributes
+ else:
+ search_attrs = self.obj.default_attributes
+ if self.obj.search_attributes_config:
+ config = ldap.get_ipa_config()[1]
+ config_attrs = config.get(
+ self.obj.search_attributes_config, [])
+ if len(config_attrs) == 1 and (
+ isinstance(config_attrs[0], basestring)):
+ search_attrs = config_attrs[0].split(',')
+
search_kw['objectclass'] = self.obj.object_class
attr_filter = ldap.make_filter(search_kw, rules=ldap.MATCH_ALL)
search_kw = {}
- for a in self.obj.default_attributes:
+ for a in search_attrs:
search_kw[a] = term
term_filter = ldap.make_filter(search_kw, exact=False)
diff --git a/ipalib/plugins/cert.py b/ipalib/plugins/cert.py
index 17e4c46b0..1de4ac64e 100644
--- a/ipalib/plugins/cert.py
+++ b/ipalib/plugins/cert.py
@@ -44,7 +44,7 @@ EXAMPLES:
ipa cert-request --add --principal=HTTP/lion.example.com example.csr
Retrieve an existing certificate:
- ipa cert-request 1032
+ ipa cert-show 1032
Revoke a certificate (see RFC 5280 for reason details):
ipa cert-revoke --revocation-reason=6 1032
@@ -75,53 +75,8 @@ import traceback
from ipalib.text import _
from ipalib.request import context
from ipalib.output import Output
-
-def get_serial(certificate):
- """
- Given a certificate, return the serial number in that cert
- as a Python long object.
-
- In theory there should be only one cert per object so even if we get
- passed in a list/tuple only return the first one.
- """
- if type(certificate) in (list, tuple):
- certificate = certificate[0]
- try:
- certificate = base64.b64decode(certificate)
- except Exception, e:
- pass
- try:
-
- serial = x509.get_serial_number(certificate, x509.DER)
- except PyAsn1Error:
- raise errors.CertificateOperationError(error=_('Unable to decode certificate in entry'))
-
- return serial
-
-def get_subject(certificate):
- """
- Given a certificate, return the subject
-
- In theory there should be only one cert per object so even if we get
- passed in a list/tuple only return the first one.
- """
- if type(certificate) in (list, tuple):
- certificate = certificate[0]
- try:
- certificate = base64.b64decode(certificate)
- except Exception, e:
- pass
- try:
- sub = list(x509.get_subject_components(certificate, type=x509.DER))
- sub.reverse()
- except PyAsn1Error:
- raise errors.CertificateOperationError(error=_('Unable to decode certificate in entry'))
-
- subject = ""
- for s in sub:
- subject = subject + "%s=%s," % (s[0], s[1])
-
- return subject[:-1]
+from ipalib.plugins.service import validate_principal
+import nss.nss as nss
def get_csr_hostname(csr):
"""
@@ -192,6 +147,20 @@ def normalize_csr(csr):
return csr
+def get_host_from_principal(principal):
+ """
+ Given a principal with or without a realm return the
+ host portion.
+ """
+ validate_principal(None, principal)
+ realm = principal.find('@')
+ slash = principal.find('/')
+ if realm == -1:
+ realm = len(principal)
+ hostname = principal[slash+1:realm]
+
+ return hostname
+
class cert_request(VirtualCommand):
"""
Submit a certificate signing request.
@@ -219,6 +188,9 @@ class cert_request(VirtualCommand):
default=False,
autofill=True
),
+ )
+
+ has_output_params = (
Str('certificate?',
label=_('Certificate'),
flags=['no_create', 'no_update', 'no_search'],
@@ -227,6 +199,26 @@ class cert_request(VirtualCommand):
label=_('Subject'),
flags=['no_create', 'no_update', 'no_search'],
),
+ Str('issuer?',
+ label=_('Issuer'),
+ flags=['no_create', 'no_update', 'no_search'],
+ ),
+ Str('valid_not_before?',
+ label=_('Not Before'),
+ flags=['no_create', 'no_update', 'no_search'],
+ ),
+ Str('valid_not_after?',
+ label=_('Not After'),
+ flags=['no_create', 'no_update', 'no_search'],
+ ),
+ Str('md5_fingerprint?',
+ label=_('Fingerprint (MD5)'),
+ flags=['no_create', 'no_update', 'no_search'],
+ ),
+ Str('sha1_fingerprint?',
+ label=_('Fingerprint (SHA1)'),
+ flags=['no_create', 'no_update', 'no_search'],
+ ),
Str('serial_number?',
label=_('Serial number'),
flags=['no_create', 'no_update', 'no_search'],
@@ -281,11 +273,7 @@ class cert_request(VirtualCommand):
service = api.Command['service_show'](principal, all=True, raw=True)['result']
dn = service['dn']
else:
- realm = principal.find('@')
- if realm == -1:
- realm = len(principal)
- hostname = principal[5:realm]
-
+ hostname = get_host_from_principal(principal)
service = api.Command['host_show'](hostname, all=True, raw=True)['result']
dn = service['dn']
except errors.NotFound, e:
@@ -319,12 +307,12 @@ class cert_request(VirtualCommand):
raise errors.ACIError(info="Insufficient privilege to create a certificate with subject alt name '%s'." % name)
if 'usercertificate' in service:
- serial = get_serial(base64.b64encode(service['usercertificate'][0]))
+ serial = x509.get_serial_number(service['usercertificate'][0], datatype=x509.DER)
# revoke the certificate and remove it from the service
# entry before proceeding. First we retrieve the certificate to
# see if it is already revoked, if not then we revoke it.
try:
- result = api.Command['cert_get'](unicode(serial))['result']
+ result = api.Command['cert_show'](unicode(serial))['result']
if 'revocation_reason' not in result:
try:
api.Command['cert_revoke'](unicode(serial), revocation_reason=4)
@@ -334,10 +322,20 @@ class cert_request(VirtualCommand):
except errors.NotImplementedError:
# some CA's might not implement get
pass
- api.Command['service_mod'](principal, usercertificate=None)
+ if not principal.startswith('host/'):
+ api.Command['service_mod'](principal, usercertificate=None)
+ else:
+ hostname = get_host_from_principal(principal)
+ api.Command['host_mod'](hostname, usercertificate=None)
# Request the certificate
result = self.Backend.ra.request_certificate(csr, **kw)
+ cert = x509.load_certificate(result['certificate'])
+ result['issuer'] = unicode(cert.issuer)
+ result['valid_not_before'] = unicode(cert.valid_not_before_str)
+ result['valid_not_after'] = unicode(cert.valid_not_after_str)
+ result['md5_fingerprint'] = unicode(nss.data_to_hex(nss.md5_digest(cert.der_data), 64)[0])
+ result['sha1_fingerprint'] = unicode(nss.data_to_hex(nss.sha1_digest(cert.der_data), 64)[0])
# Success? Then add it to the service entry.
if 'certificate' in result:
@@ -345,10 +343,7 @@ class cert_request(VirtualCommand):
skw = {"usercertificate": str(result.get('certificate'))}
api.Command['service_mod'](principal, **skw)
else:
- realm = principal.find('@')
- if realm == -1:
- realm = len(principal)
- hostname = principal[5:realm]
+ hostname = get_host_from_principal(principal)
skw = {"usercertificate": str(result.get('certificate'))}
api.Command['host_mod'](hostname, **skw)
@@ -370,10 +365,9 @@ class cert_status(VirtualCommand):
flags=['no_create', 'no_update', 'no_search'],
),
)
- takes_options = (
- Str('cert_request_status?',
+ has_output_params = (
+ Str('cert_request_status',
label=_('Request status'),
- flags=['no_create', 'no_update', 'no_search'],
),
)
operation = "certificate status"
@@ -393,25 +387,37 @@ _serial_number = Str('serial_number',
doc=_('Serial number in decimal or if prefixed with 0x in hexadecimal'),
)
-class cert_get(VirtualCommand):
+class cert_show(VirtualCommand):
"""
Retrieve an existing certificate.
"""
takes_args = _serial_number
- takes_options = (
- Str('certificate?',
+ has_output_params = (
+ Str('certificate',
label=_('Certificate'),
- flags=['no_create', 'no_update', 'no_search'],
),
- Str('subject?',
+ Str('subject',
label=_('Subject'),
- flags=['no_create', 'no_update', 'no_search'],
+ ),
+ Str('issuer',
+ label=_('Issuer'),
+ ),
+ Str('valid_not_before',
+ label=_('Not Before'),
+ ),
+ Str('valid_not_after',
+ label=_('Not After'),
+ ),
+ Str('md5_fingerprint',
+ label=_('Fingerprint (MD5)'),
+ ),
+ Str('sha1_fingerprint',
+ label=_('Fingerprint (SHA1)'),
),
Str('revocation_reason?',
label=_('Revocation reason'),
- flags=['no_create', 'no_update', 'no_search'],
),
)
@@ -420,10 +426,16 @@ class cert_get(VirtualCommand):
def execute(self, serial_number):
self.check_access()
result=self.Backend.ra.get_certificate(serial_number)
- result['subject'] = get_subject(result['certificate'])
+ cert = x509.load_certificate(result['certificate'])
+ result['subject'] = unicode(cert.subject)
+ result['issuer'] = unicode(cert.issuer)
+ result['valid_not_before'] = unicode(cert.valid_not_before_str)
+ result['valid_not_after'] = unicode(cert.valid_not_after_str)
+ result['md5_fingerprint'] = unicode(nss.data_to_hex(nss.md5_digest(cert.der_data), 64)[0])
+ result['sha1_fingerprint'] = unicode(nss.data_to_hex(nss.sha1_digest(cert.der_data), 64)[0])
return dict(result=result)
-api.register(cert_get)
+api.register(cert_show)
class cert_revoke(VirtualCommand):
@@ -433,10 +445,9 @@ class cert_revoke(VirtualCommand):
takes_args = _serial_number
- takes_options = (
- Flag('revoked?',
+ has_output_params = (
+ Flag('revoked',
label=_('Revoked'),
- flags=['no_create', 'no_update', 'no_search'],
),
)
operation = "revoke certificate"
@@ -468,14 +479,12 @@ class cert_remove_hold(VirtualCommand):
takes_args = _serial_number
- takes_options = (
+ has_output_params = (
Flag('unrevoked?',
label=_('Unrevoked'),
- flags=['no_create', 'no_update', 'no_search'],
),
Str('error_string?',
label=_('Error'),
- flags=['no_create', 'no_update', 'no_search'],
),
)
operation = "certificate remove hold"
diff --git a/ipalib/plugins/group.py b/ipalib/plugins/group.py
index 9da4fe569..2558c38ab 100644
--- a/ipalib/plugins/group.py
+++ b/ipalib/plugins/group.py
@@ -75,6 +75,7 @@ class group(LDAPObject):
object_name_plural = 'groups'
object_class = ['ipausergroup']
object_class_config = 'ipagroupobjectclasses'
+ search_attributes_config = 'ipagroupsearchfields'
default_attributes = [
'cn', 'description', 'gidnumber', 'member', 'memberof'
]
diff --git a/ipalib/plugins/host.py b/ipalib/plugins/host.py
index 82ef16457..b42cbbcb7 100644
--- a/ipalib/plugins/host.py
+++ b/ipalib/plugins/host.py
@@ -57,6 +57,9 @@ EXAMPLES:
Update information about a host
ipa host-mod --os='Fedora 12' test.example.com
+
+ Disable the host kerberos key
+ ipa host-disable test.example.com
"""
import platform
@@ -68,8 +71,8 @@ from ipalib import Str, Flag, Bytes
from ipalib.plugins.baseldap import *
from ipalib.plugins.service import split_principal
from ipalib.plugins.service import validate_certificate
-from ipalib.plugins.service import get_serial
from ipalib import _, ngettext
+from ipalib import x509
import base64
@@ -91,9 +94,14 @@ class host(LDAPObject):
object_name_plural = 'hosts'
object_class = ['ipaobject', 'nshost', 'ipahost', 'pkiuser', 'ipaservice']
# object_class_config = 'ipahostobjectclasses'
+ search_attributes = [
+ 'fqdn', 'description', 'l', 'nshostlocation', 'krbprincipalname',
+ 'nshardwareplatform', 'nsosversion',
+ ]
default_attributes = [
'fqdn', 'description', 'l', 'nshostlocation', 'krbprincipalname',
'nshardwareplatform', 'nsosversion', 'usercertificate', 'memberof',
+ 'krblastpwdchange',
]
uuid_attribute = 'ipauniqueid'
attribute_members = {
@@ -283,10 +291,10 @@ class host_mod(LDAPUpdate):
if 'usercertificate' in entry_attrs_old:
# FIXME: what to do here? do we revoke the old cert?
fmt = 'entry already has a certificate, serial number: %s' % (
- get_serial(entry_attrs_old['usercertificate'])
+ x509.get_serial_number(entry_attrs_old['usercertificate'][0], x509.DER)
)
raise errors.GenericError(format=fmt)
- # FIXME: should be in normalizer; see service_add
+ # FIXME: decoding should be in normalizer; see service_add
entry_attrs['usercertificate'] = base64.b64decode(cert)
return dn
@@ -316,5 +324,47 @@ class host_show(LDAPRetrieve):
"""
Display host.
"""
+ has_output_params = (
+ Flag('has_keytab',
+ label=_('Keytab'),
+ )
+ )
+
+ def post_callback(self, ldap, dn, entry_attrs, *keys, **options):
+ if 'krblastpwdchange' in entry_attrs:
+ entry_attrs['has_keytab'] = True
+ if not options.get('all', False):
+ del entry_attrs['krblastpwdchange']
+ else:
+ entry_attrs['has_keytab'] = False
+
+ return dn
api.register(host_show)
+
+
+class host_disable(LDAPQuery):
+ """
+ Disable the kerberos key of this host.
+ """
+ has_output = output.standard_value
+ msg_summary = _('Removed kerberos key from "%(value)s"')
+
+ def execute(self, *keys, **options):
+ ldap = self.obj.backend
+
+ dn = self.obj.get_dn(*keys, **options)
+ (dn, entry_attrs) = ldap.get_entry(dn, ['krblastpwdchange'])
+
+ if 'krblastpwdchange' not in entry_attrs:
+ error_msg = _('Host principal has no kerberos key')
+ raise errors.NotFound(reason=error_msg)
+
+ ldap.remove_principal_key(dn)
+
+ return dict(
+ result=True,
+ value=keys[0],
+ )
+
+api.register(host_disable)
diff --git a/ipalib/plugins/migration.py b/ipalib/plugins/migration.py
index 55a21572c..a2773efb8 100644
--- a/ipalib/plugins/migration.py
+++ b/ipalib/plugins/migration.py
@@ -31,7 +31,10 @@ from ipalib import api, errors, output, uuid
from ipalib import Command, List, Password, Str
from ipalib.cli import to_cli
if api.env.in_server and api.env.context in ['lite', 'server']:
- from ipaserver.plugins.ldap2 import ldap2
+ try:
+ from ipaserver.plugins.ldap2 import ldap2
+ except StandardError, e:
+ raise e
from ipalib import _
from ipalib.text import Gettext # FIXME: remove once the other Gettext FIXME is removed
diff --git a/ipalib/plugins/netgroup.py b/ipalib/plugins/netgroup.py
index ad97c7226..755c5f4da 100644
--- a/ipalib/plugins/netgroup.py
+++ b/ipalib/plugins/netgroup.py
@@ -46,6 +46,23 @@ from ipalib.plugins.baseldap import *
from ipalib import _, ngettext
+output_params = (
+ Str('memberuser_user?',
+ label='Member User',
+ ),
+ Str('memberuser_group?',
+ label='Member Group',
+ ),
+ Str('memberhost_host?',
+ label=_('Member Host'),
+ ),
+ Str('memberhost_hostgroup?',
+ label='Member Hostgroup',
+ ),
+ Str('externalhost?',
+ label=_('External host'),
+ ),
+ )
class netgroup(LDAPObject):
"""
Netgroup object.
@@ -55,13 +72,14 @@ class netgroup(LDAPObject):
object_name_plural = 'netgroups'
object_class = ['ipaobject', 'ipaassociation', 'ipanisnetgroup']
default_attributes = [
- 'cn', 'description', 'member', 'memberof', 'externalhost',
- 'nisdomainname',
+ 'cn', 'description', 'memberof', 'externalhost',
+ 'nisdomainname', 'memberuser', 'memberhost',
]
uuid_attribute = 'ipauniqueid'
attribute_members = {
- 'member': ['user', 'group', 'host', 'hostgroup', 'netgroup'],
'memberof': ['netgroup'],
+ 'memberuser': ['user', 'group'],
+ 'memberhost': ['host', 'hostgroup'],
}
label = _('Net Groups')
@@ -88,26 +106,6 @@ class netgroup(LDAPObject):
doc=_('IPA unique ID'),
flags=['no_create', 'no_update'],
),
- Str('member_user?',
- label='Member User',
- flags=['no_create', 'no_update', 'no_search'],
- ),
- Str('member_group?',
- label='Member Group',
- flags=['no_create', 'no_update', 'no_search'],
- ),
- Str('member_host?',
- label=_('Member host'),
- flags=['no_create', 'no_update', 'no_search'],
- ),
- Str('member_hostgroup?',
- label='Member Hostgroup',
- flags=['no_create', 'no_update', 'no_search'],
- ),
- Str('externalhost?',
- label=_('External host'),
- flags=['no_create', 'no_update', 'no_search'],
- ),
)
def get_dn(self, *keys, **kwargs):
@@ -135,6 +133,7 @@ class netgroup_add(LDAPCreate):
"""
Create new netgroup.
"""
+ has_output_params = output_params
def pre_callback(self, ldap, dn, entry_attrs, attrs_list, *keys, **options):
if not dn.startswith('cn='):
msg = 'netgroup with name "%s" already exists' % keys[-1]
@@ -160,6 +159,7 @@ class netgroup_mod(LDAPUpdate):
"""
Modify netgroup.
"""
+ has_output_params = output_params
api.register(netgroup_mod)
@@ -168,6 +168,7 @@ class netgroup_find(LDAPSearch):
"""
Search the groups.
"""
+ has_output_params = output_params
api.register(netgroup_find)
@@ -176,6 +177,7 @@ class netgroup_show(LDAPRetrieve):
"""
Display netgroup.
"""
+ has_output_params = output_params
api.register(netgroup_show)
@@ -184,14 +186,18 @@ class netgroup_add_member(LDAPAddMember):
"""
Add members to netgroup.
"""
+ has_output_params = output_params
+ member_attributes = ['memberuser', 'memberhost']
def post_callback(self, ldap, completed, failed, dn, entry_attrs, *keys, **options):
- if 'member' in failed and 'host' in failed['member']:
+ completed_external = 0
+ # Sift through the host failures. We assume that these are all
+ # hosts that aren't stored in IPA, aka external hosts.
+ if 'memberhost' in failed and 'host' in failed['memberhost']:
(dn, entry_attrs_) = ldap.get_entry(dn, ['externalhost'])
- members = entry_attrs.get('member', [])
+ members = entry_attrs.get('memberhost', [])
external_hosts = entry_attrs_.get('externalhost', [])
failed_hosts = []
- completed_external = 0
- for host in failed['member']['host']:
+ for host in failed['memberhost']['host']:
host = host.lower()
host_dn = self.api.Object['host'].get_dn(host)
if host not in external_hosts and host_dn not in members:
@@ -204,7 +210,7 @@ class netgroup_add_member(LDAPAddMember):
ldap.update_entry(dn, {'externalhost': external_hosts})
except errors.EmptyModlist:
pass
- failed['member']['host'] = failed_hosts
+ failed['memberhost']['host'] = failed_hosts
entry_attrs['externalhost'] = external_hosts
return (completed + completed_external, dn)
@@ -216,13 +222,17 @@ class netgroup_remove_member(LDAPRemoveMember):
"""
Remove members from netgroup.
"""
+ has_output_params = output_params
+ member_attributes = ['memberuser', 'memberhost']
def post_callback(self, ldap, completed, failed, dn, entry_attrs, *keys, **options):
- if 'member' in failed and 'host' in failed['member']:
+ # Run through the host failures and gracefully remove any defined as
+ # as an externalhost.
+ if 'memberhost' in failed and 'host' in failed['memberhost']:
(dn, entry_attrs) = ldap.get_entry(dn, ['externalhost'])
external_hosts = entry_attrs.get('externalhost', [])
failed_hosts = []
completed_external = 0
- for host in failed['member']['host']:
+ for host in failed['memberhost']['host']:
host = host.lower()
if host in external_hosts:
external_hosts.remove(host)
@@ -234,7 +244,7 @@ class netgroup_remove_member(LDAPRemoveMember):
ldap.update_entry(dn, {'externalhost': external_hosts})
except errors.EmptyModlist:
pass
- failed['member']['host'] = failed_hosts
+ failed['memberhost']['host'] = failed_hosts
entry_attrs['externalhost'] = external_hosts
return (completed + completed_external, dn)
diff --git a/ipalib/plugins/service.py b/ipalib/plugins/service.py
index 3484e29a4..37de3df42 100644
--- a/ipalib/plugins/service.py
+++ b/ipalib/plugins/service.py
@@ -53,6 +53,10 @@ EXAMPLES:
Find all HTTP services:
ipa service-find HTTP
+
+ Disable a service kerberos key:
+ ipa service-disable HTTP/web.example.com
+
"""
import base64
@@ -60,26 +64,9 @@ from ipalib import api, errors
from ipalib import Str, Flag, Bytes
from ipalib.plugins.baseldap import *
from ipalib import x509
-from pyasn1.error import PyAsn1Error
from ipalib import _, ngettext
-def get_serial(certificate):
- """
- Given a certificate, return the serial number in that
- cert as a Python long object.
- """
- if type(certificate) in (list, tuple):
- certificate = certificate[0]
-
- try:
- serial = x509.get_serial_number(certificate, type=x509.DER)
- except PyAsn1Error, e:
- raise errors.GenericError(
- format='Unable to decode certificate in entry: %s' % e
- )
- return serial
-
def split_principal(principal):
service = hostname = realm = None
@@ -140,7 +127,8 @@ class service(LDAPObject):
'krbprincipal', 'krbprincipalaux', 'krbticketpolicyaux', 'ipaobject',
'ipaservice', 'pkiuser'
]
- default_attributes = ['krbprincipalname', 'usercertificate', 'managedby']
+ search_attributes = ['krbprincipalname', 'managedby']
+ default_attributes = ['krbprincipalname', 'usercertificate', 'managedby', 'krblastpwdchange']
uuid_attribute = 'ipauniqueid'
attribute_members = {
'managedby': ['host'],
@@ -156,11 +144,6 @@ class service(LDAPObject):
primary_key=True,
normalizer=lambda value: normalize_principal(value),
),
- Bytes('usercertificate?', validate_certificate,
- cli_name='certificate',
- label=_('Certificate'),
- doc=_('Base-64 encoded server certificate'),
- ),
)
api.register(service)
@@ -176,6 +159,11 @@ class service_add(LDAPCreate):
Flag('force',
doc=_('force principal name even if not in DNS'),
),
+ Bytes('usercertificate?', validate_certificate,
+ cli_name='certificate',
+ label=_('Certificate'),
+ doc=_('Base-64 encoded server certificate'),
+ ),
)
def pre_callback(self, ldap, dn, entry_attrs, attrs_list, *keys, **options):
(service, hostname, realm) = split_principal(keys[-1])
@@ -189,6 +177,7 @@ class service_add(LDAPCreate):
cert = entry_attrs.get('usercertificate')
if cert:
+ cert = cert[0]
# FIXME: should be in a normalizer: need to fix normalizers
# to work on non-unicode data
entry_attrs['usercertificate'] = base64.b64decode(cert)
@@ -224,9 +213,10 @@ class service_del(LDAPDelete):
(dn, entry_attrs) = ldap.get_entry(dn, ['usercertificate'])
cert = entry_attrs.get('usercertificate')
if cert:
- serial = unicode(get_serial(cert))
+ cert = cert[0]
+ serial = unicode(x509.get_serial_number(cert, x509.DER))
try:
- result = api.Command['cert_get'](unicode(serial))['result']
+ result = api.Command['cert_show'](unicode(serial))['result']
if 'revocation_reason' not in result:
try:
api.Command['cert_revoke'](unicode(serial), revocation_reason=4)
@@ -245,15 +235,24 @@ class service_mod(LDAPUpdate):
"""
Modify service.
"""
+ takes_options = LDAPUpdate.takes_options + (
+ Bytes('usercertificate?', validate_certificate,
+ cli_name='certificate',
+ label=_('Certificate'),
+ doc=_('Base-64 encoded server certificate'),
+ ),
+ )
+
member_attributes = ['managedby']
+
def pre_callback(self, ldap, dn, entry_attrs, *keys, **options):
- cert = entry_attrs.get('usercertificate')
+ cert = options.get('usercertificate')
if cert:
(dn, entry_attrs_old) = ldap.get_entry(dn, ['usercertificate'])
if 'usercertificate' in entry_attrs_old:
# FIXME: what to do here? do we revoke the old cert?
fmt = 'entry already has a certificate, serial number: %s' % (
- get_serial(entry_attrs_old['usercertificate'])
+ x509.get_serial_number(entry_attrs_old['usercertificate'][0], x509.DER)
)
raise errors.GenericError(format=fmt)
# FIXME: should be in normalizer; see service_add
@@ -268,6 +267,13 @@ class service_find(LDAPSearch):
Search for services.
"""
member_attributes = ['managedby']
+ takes_options = LDAPSearch.takes_options + (
+ Bytes('usercertificate?', validate_certificate,
+ cli_name='certificate',
+ label=_('Certificate'),
+ doc=_('Base-64 encoded server certificate'),
+ ),
+ )
def pre_callback(self, ldap, filter, attrs_list, base_dn, *args, **options):
# lisp style!
custom_filter = '(&(objectclass=ipaService)' \
@@ -289,6 +295,28 @@ class service_show(LDAPRetrieve):
Display service.
"""
member_attributes = ['managedby']
+ takes_options = LDAPRetrieve.takes_options + (
+ Bytes('usercertificate?', validate_certificate,
+ cli_name='certificate',
+ label=_('Certificate'),
+ doc=_('Base-64 encoded server certificate'),
+ ),
+ )
+ has_output_params = (
+ Flag('has_keytab',
+ label=_('Keytab'),
+ )
+ )
+
+ def post_callback(self, ldap, dn, entry_attrs, *keys, **options):
+ if 'krblastpwdchange' in entry_attrs:
+ entry_attrs['has_keytab'] = True
+ if not options.get('all', False):
+ del entry_attrs['krblastpwdchange']
+ else:
+ entry_attrs['has_keytab'] = False
+
+ return dn
api.register(service_show)
@@ -308,3 +336,30 @@ class service_remove_host(LDAPRemoveMember):
member_attributes = ['managedby']
api.register(service_remove_host)
+
+
+class service_disable(LDAPQuery):
+ """
+ Disable the kerberos key of this service.
+ """
+ has_output = output.standard_value
+ msg_summary = _('Removed kerberos key from "%(value)s"')
+
+ def execute(self, *keys, **options):
+ ldap = self.obj.backend
+
+ dn = self.obj.get_dn(*keys, **options)
+ (dn, entry_attrs) = ldap.get_entry(dn, ['krblastpwdchange'])
+
+ if 'krblastpwdchange' not in entry_attrs:
+ error_msg = _('Service principal has no kerberos key')
+ raise errors.NotFound(reason=error_msg)
+
+ ldap.remove_principal_key(dn)
+
+ return dict(
+ result=True,
+ value=keys[0],
+ )
+
+api.register(service_disable)
diff --git a/ipalib/plugins/user.py b/ipalib/plugins/user.py
index 610d85a95..de5ff2d27 100644
--- a/ipalib/plugins/user.py
+++ b/ipalib/plugins/user.py
@@ -58,6 +58,7 @@ class user(LDAPObject):
object_name_plural = 'users'
object_class = ['posixaccount']
object_class_config = 'ipauserobjectclasses'
+ search_attributes_config = 'ipausersearchfields'
default_attributes = [
'uid', 'givenname', 'sn', 'homedirectory', 'loginshell', 'ou',
'telephonenumber', 'title', 'memberof',
diff --git a/ipalib/x509.py b/ipalib/x509.py
index 3c38a354e..4637e9f27 100644
--- a/ipalib/x509.py
+++ b/ipalib/x509.py
@@ -1,199 +1,32 @@
-"""
-Imported from pyasn1 project:
-
-Copyright (c) 2005-2009 Ilya Etingof <ilya@glas.net>, all rights reserved.
-
-THIS SOFTWARE IS NOT FAULT TOLERANT AND SHOULD NOT BE USED IN ANY SITUATION
-ENDANGERING HUMAN LIFE OR PROPERTY.
-
-Redistribution and use in source and binary forms, with or without
-modification, are permitted provided that the following conditions are met:
-
- * Redistributions of source code must retain the above copyright notice,
- this list of conditions and the following disclaimer.
-
- * Redistributions in binary form must reproduce the above copyright notice,
- this list of conditions and the following disclaimer in the documentation
- and/or other materials provided with the distribution.
-
- * The name of the authors may not be used to endorse or promote products
- derived from this software without specific prior written permission.
-
-THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS''
-AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
-ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR
-ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
-(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
-LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
-ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
-THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-"""
-
-"""
-Enhancements released under IPA GPLv2 only license
-"""
-
-# Read ASN.1/PEM X.509 certificates on stdin, parse each into plain text,
-# then build substrate from it
-import sys, string, base64
-from pyasn1.type import tag,namedtype,namedval,univ,constraint,char,useful
-from pyasn1.codec.der import decoder, encoder
-from pyasn1 import error
-
-# Would be autogenerated from ASN.1 source by a ASN.1 parser
-# X.509 spec (rfc2459)
+# Authors:
+# Rob Crittenden <rcritten@redhat.com>
+#
+# Copyright (C) 2010 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 os
+import sys
+import base64
+import nss.nss as nss
+from ipapython import ipautil
+from ipalib import api
PEM = 0
DER = 1
-# Common OIDs found in a subject
-oidtable = { "2.5.4.3": "CN",
- "2.5.4.6": "C",
- "2.5.4.7": "L",
- "2.5.4.8": "ST",
- "2.5.4.10": "O",
- "2.5.4.11": "OU",
- "1.2.840.113549.1.9.1": "E",
- "0.9.2342.19200300.100.1.25": "DC",
- }
-
-MAX = 64 # XXX ?
-
-class DirectoryString(univ.Choice):
- componentType = namedtype.NamedTypes(
- namedtype.NamedType('teletexString', char.TeletexString().subtype(subtypeSpec=constraint.ValueSizeConstraint(1, MAX))),
- namedtype.NamedType('printableString', char.PrintableString().subtype(subtypeSpec=constraint.ValueSizeConstraint(1, MAX))),
- namedtype.NamedType('universalString', char.UniversalString().subtype(subtypeSpec=constraint.ValueSizeConstraint(1, MAX))),
- namedtype.NamedType('utf8String', char.UTF8String().subtype(subtypeSpec=constraint.ValueSizeConstraint(1, MAX))),
- namedtype.NamedType('bmpString', char.BMPString().subtype(subtypeSpec=constraint.ValueSizeConstraint(1, MAX))),
- namedtype.NamedType('ia5String', char.IA5String().subtype(subtypeSpec=constraint.ValueSizeConstraint(1, MAX))) # hm, this should not be here!? XXX
- )
-
-class AttributeValue(DirectoryString): pass
-
-class AttributeType(univ.ObjectIdentifier): pass
-
-class AttributeTypeAndValue(univ.Sequence):
- componentType = namedtype.NamedTypes(
- namedtype.NamedType('type', AttributeType()),
- namedtype.NamedType('value', AttributeValue())
- )
-
-class RelativeDistinguishedName(univ.SetOf):
- componentType = AttributeTypeAndValue()
-
-class RDNSequence(univ.SequenceOf):
- componentType = RelativeDistinguishedName()
-
-class Name(univ.Choice):
- componentType = namedtype.NamedTypes(
- namedtype.NamedType('', RDNSequence())
- )
-
- def get_components(self):
- components = self.getComponentByPosition(0)
- complist = []
- for idx in range(len(components)):
- attrandvalue = components[idx].getComponentByPosition(0)
- oid = attrandvalue.getComponentByPosition(0)
- # FIXME, should handle any string type
- value = attrandvalue.getComponentByPosition(1).getComponentByType(char.PrintableString.tagSet)
- if value is None:
- value = attrandvalue.getComponentByPosition(1).getComponentByType(char.UTF8String.tagSet)
- if value is None:
- value = attrandvalue.getComponentByPosition(1).getComponentByType(char.IA5String.tagSet)
- vout = value.prettyOut(value).decode('utf-8')
- oidout = oid.prettyOut(oid).decode('utf-8')
- c = ((oidtable.get(oidout, oidout), vout))
- complist.append(c)
-
- return tuple(complist)
-
-class AlgorithmIdentifier(univ.Sequence):
- componentType = namedtype.NamedTypes(
- namedtype.NamedType('algorithm', univ.ObjectIdentifier()),
- namedtype.OptionalNamedType('parameters', univ.Null())
- # XXX syntax screwed?
-# namedtype.OptionalNamedType('parameters', univ.ObjectIdentifier())
- )
-
-class Extension(univ.Sequence):
- componentType = namedtype.NamedTypes(
- namedtype.NamedType('extnID', univ.ObjectIdentifier()),
- namedtype.DefaultedNamedType('critical', univ.Boolean('False')),
- namedtype.NamedType('extnValue', univ.OctetString())
- )
-
-class Extensions(univ.SequenceOf):
- componentType = Extension()
- sizeSpec = univ.SequenceOf.sizeSpec + constraint.ValueSizeConstraint(1, MAX)
-
-class SubjectPublicKeyInfo(univ.Sequence):
- componentType = namedtype.NamedTypes(
- namedtype.NamedType('algorithm', AlgorithmIdentifier()),
- namedtype.NamedType('subjectPublicKey', univ.BitString())
- )
-
-class UniqueIdentifier(univ.BitString): pass
-
-class Time(univ.Choice):
- componentType = namedtype.NamedTypes(
- namedtype.NamedType('utcTime', useful.UTCTime()),
- namedtype.NamedType('generalTime', useful.GeneralizedTime())
- )
-
-class Validity(univ.Sequence):
- componentType = namedtype.NamedTypes(
- namedtype.NamedType('notBefore', Time()),
- namedtype.NamedType('notAfter', Time())
- )
-
-class CertificateSerialNumber(univ.Integer): pass
-
-class Version(univ.Integer):
- namedValues = namedval.NamedValues(
- ('v1', 0), ('v2', 1), ('v3', 2)
- )
-
-class TBSCertificate(univ.Sequence):
- componentType = namedtype.NamedTypes(
- namedtype.DefaultedNamedType('version', Version('v1', tagSet=Version.tagSet.tagExplicitly(tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 0)))),
- namedtype.NamedType('serialNumber', CertificateSerialNumber()),
- namedtype.NamedType('signature', AlgorithmIdentifier()),
- namedtype.NamedType('issuer', Name()),
- namedtype.NamedType('validity', Validity()),
- namedtype.NamedType('subject', Name()),
- namedtype.NamedType('subjectPublicKeyInfo', SubjectPublicKeyInfo()),
- namedtype.OptionalNamedType('issuerUniqueID', UniqueIdentifier().subtype(implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 1))),
- namedtype.OptionalNamedType('subjectUniqueID', UniqueIdentifier().subtype(implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 2))),
- namedtype.OptionalNamedType('extensions', Extensions().subtype(explicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 3)))
- )
-
-class Certificate(univ.Sequence):
- componentType = namedtype.NamedTypes(
- namedtype.NamedType('tbsCertificate', TBSCertificate()),
- namedtype.NamedType('signatureAlgorithm', AlgorithmIdentifier()),
- namedtype.NamedType('signatureValue', univ.BitString())
- )
-
- def get_version(self):
- info = self.getComponentByName('tbsCertificate')
- version = info.getComponentByName('version')
- return version._value
-
- def get_subject(self):
- info = self.getComponentByName('tbsCertificate')
- return info.getComponentByName('subject')
-
- def get_serial_number(self):
- 'return the serial number as a Python long object'
- info = self.getComponentByName('tbsCertificate')
- return long(info.getComponentByName('serialNumber'))
-
-# end of ASN.1 data structures
-
def strip_header(pem):
"""
Remove the header and footer from a certificate.
@@ -205,69 +38,53 @@ def strip_header(pem):
return pem
-
-def load_certificate(data, type=PEM):
+def load_certificate(data, datatype=PEM, dbdir=None):
"""
Given a base64-encoded certificate, with or without the
header/footer, return a request object.
+
+ Returns a nss.Certificate type
"""
- if (type == PEM):
+ if type(data) in (tuple, list):
+ data = data[0]
+
+ if (datatype == PEM):
data = strip_header(data)
data = base64.b64decode(data)
- return decoder.decode(data, asn1Spec=Certificate())[0]
+ if dbdir is None:
+ if api.env.in_tree:
+ dbdir = api.env.dot_ipa + os.sep + 'alias'
+ else:
+ dbdir = "/etc/httpd/alias"
+
+ nss.nss_init(dbdir)
+ return nss.Certificate(buffer(data))
-def get_subject_components(certificate, type=PEM):
+def get_subject(certificate, datatype=PEM):
"""
Load an X509.3 certificate and get the subject.
-
- Return a tuple of a certificate subject.
- (('CN', u'www.example.com'), ('O', u'IPA'))
"""
- x509cert = load_certificate(certificate, type)
- return x509cert.get_subject().get_components()
+ cert = load_certificate(certificate, datatype)
+ return cert.subject
-def get_serial_number(certificate, type=PEM):
+def get_serial_number(certificate, datatype=PEM):
"""
- Return the serial number of a certificate as a Python long object.
+ Return the decimal value of the serial number.
"""
- x509cert = load_certificate(certificate, type)
- return x509cert.get_serial_number()
+ cert = load_certificate(certificate, datatype)
+ return cert.serial_number
if __name__ == '__main__':
- certType = Certificate()
-
- # Read PEM certs from stdin and print them out in plain text
-
- stSpam, stHam, stDump = 0, 1, 2
- state = stSpam
- certCnt = 0
- for certLine in sys.stdin.readlines():
- certLine = string.strip(certLine)
- if state == stSpam:
- if state == stSpam:
- if certLine == '-----BEGIN CERTIFICATE-----':
- certLines = []
- state = stHam
- continue
- if state == stHam:
- if certLine == '-----END CERTIFICATE-----':
- state = stDump
- else:
- certLines.append(certLine)
- if state == stDump:
- substrate = ''
- for certLine in certLines:
- substrate = substrate + base64.b64decode(certLine)
+ nss.nss_init_nodb()
- cert = decoder.decode(substrate, asn1Spec=certType)[0]
- print cert.prettyPrint()
+ # Read PEM certs from stdin and print out its components
- assert encoder.encode(cert) == substrate, 'cert recode fails'
+ certlines = sys.stdin.readlines()
+ cert = ''.join(certlines)
- certCnt = certCnt + 1
- state = stSpam
+ cert = load_certificate(cert)
- print '*** %s PEM cert(s) de/serialized' % certCnt
+ print cert
diff --git a/ipapython/nsslib.py b/ipapython/nsslib.py
index 1710a7d74..02bff00a8 100644
--- a/ipapython/nsslib.py
+++ b/ipapython/nsslib.py
@@ -188,7 +188,6 @@ class NSPRConnection(httplib.HTTPConnection):
httplib.HTTPConnection.__init__(self, host, port, strict)
logging.debug('%s init %s', self.__class__.__name__, host)
- nss.nss_init_nodb()
self.sock = io.Socket()
def connect(self):
diff --git a/ipaserver/plugins/ldap2.py b/ipaserver/plugins/ldap2.py
index 987203caa..3c536e241 100644
--- a/ipaserver/plugins/ldap2.py
+++ b/ipaserver/plugins/ldap2.py
@@ -124,17 +124,20 @@ def global_init(url):
try:
if api.env.context == 'server':
- # Create a new credentials cache for this Apache process
- tmpdir = tempfile.mkdtemp(prefix = "tmp-")
- ccache_file = 'FILE:%s/ccache' % tmpdir
- krbcontext = krbV.default_context()
- principal = str('HTTP/%s@%s' % (api.env.host, api.env.realm))
- keytab = krbV.Keytab(name='/etc/httpd/conf/ipa.keytab', context=krbcontext)
- principal = krbV.Principal(name=principal, context=krbcontext)
- os.environ['KRB5CCNAME'] = ccache_file
- ccache = krbV.CCache(name=ccache_file, context=krbcontext, primary_principal=principal)
- ccache.init(principal)
- ccache.init_creds_keytab(keytab=keytab, principal=principal)
+ try:
+ # Create a new credentials cache for this Apache process
+ tmpdir = tempfile.mkdtemp(prefix = "tmp-")
+ ccache_file = 'FILE:%s/ccache' % tmpdir
+ krbcontext = krbV.default_context()
+ principal = str('HTTP/%s@%s' % (api.env.host, api.env.realm))
+ keytab = krbV.Keytab(name='/etc/httpd/conf/ipa.keytab', context=krbcontext)
+ principal = krbV.Principal(name=principal, context=krbcontext)
+ os.environ['KRB5CCNAME'] = ccache_file
+ ccache = krbV.CCache(name=ccache_file, context=krbcontext, primary_principal=principal)
+ ccache.init(principal)
+ ccache.init_creds_keytab(keytab=keytab, principal=principal)
+ except krbV.Krb5Error, e:
+ raise StandardError('Unable to retrieve LDAP schema. Error initializing principal %s in %s: %s' % (principal.name, '/etc/httpd/conf/ipa.keytab', str(e)))
conn = _ldap.initialize(url)
conn.sasl_interactive_bind_s('', SASL_AUTH)
@@ -155,8 +158,9 @@ def global_init(url):
except _ldap.SERVER_DOWN:
return (None, upg)
except _ldap.LDAPError, e:
- # TODO: raise a more appropriate exception
- _handle_errors(e, **{})
+ desc = e.args[0]['desc'].strip()
+ info = e.args[0].get('info', '').strip()
+ raise StandardError('Unable to retrieve LDAP schema: %s: %s' % (desc, info))
except IndexError:
# no 'cn=schema' entry in LDAP? some servers use 'cn=subschema'
# TODO: DS uses 'cn=schema', support for other server?
@@ -821,6 +825,22 @@ class ldap2(CrudBackend, Encoder):
"""Mark entry inactive."""
self.set_entry_active(dn, False)
+ def remove_principal_key(self, dn):
+ """Remove a kerberos principal key."""
+
+ dn = self.normalize_dn(dn)
+
+ # We need to do this directly using the LDAP library because we
+ # don't have read access to krbprincipalkey so we need to delete
+ # it in the blind.
+ mod = [(_ldap.MOD_REPLACE, 'krbprincipalkey', None),
+ (_ldap.MOD_REPLACE, 'krblastpwdchange', None)]
+
+ try:
+ self.conn.modify_s(dn, mod)
+ except _ldap.LDAPError, e:
+ self._handle_errors(e, **{})
+
# CrudBackend methods
def _get_normalized_entry_for_crud(self, dn, attrs_list=None):
diff --git a/ipaserver/plugins/selfsign.py b/ipaserver/plugins/selfsign.py
index 67b8efef9..39d1c539f 100644
--- a/ipaserver/plugins/selfsign.py
+++ b/ipaserver/plugins/selfsign.py
@@ -208,12 +208,7 @@ class ra(rabase.rabase):
try:
# Grab the subject, reverse it, combine it and return it
- sub = list(x509.get_subject_components(cert))
- sub.reverse()
- subject = ""
- for s in sub:
- subject = subject + "%s=%s," % (s[0], s[1])
- subject = subject[:-1]
+ subject = x509.get_subject(cert)
serial = x509.get_serial_number(cert)
except error.PyAsn1Error, e:
diff --git a/lite-server.py b/lite-server.py
index ba7cfe3d3..22ff720f9 100755
--- a/lite-server.py
+++ b/lite-server.py
@@ -72,6 +72,7 @@ if __name__ == '__main__':
)
api.env.in_server = True
+ api.env.startup_traceback = True
(options, args) = api.bootstrap_with_global_options(parser, context='lite')
api.env._merge(
lite_port=options.port,
diff --git a/tests/test_xmlrpc/test_cert.py b/tests/test_xmlrpc/test_cert.py
index fd3db5939..a99848152 100644
--- a/tests/test_xmlrpc/test_cert.py
+++ b/tests/test_xmlrpc/test_cert.py
@@ -29,6 +29,10 @@ from ipalib import errors
import tempfile
from ipapython import ipautil
import nose
+import base64
+
+# So we can save the cert from issuance and compare it later
+cert = None
# Test setup
#
@@ -110,14 +114,40 @@ class test_cert(XMLRPC_test):
Test the `xmlrpc.cert_request` method with --add.
"""
# Our host should exist from previous test
+ global cert
csr = unicode(self.generateCSR(self.subject))
res = api.Command['cert_request'](csr, principal=self.service_princ, add=True)['result']
assert res['subject'] == self.subject
+ # save the cert for the service_show/find tests
+ cert = res['certificate']
+
+ def test_3_service_show(self):
+ """
+ Verify that service-show has the right certificate.
+ """
+ global cert
+
+ res = api.Command['service_show'](self.service_princ)['result']
+ assert base64.b64encode(res['usercertificate'][0]) == cert
+ def test_4_service_find(self):
+ """
+ Verify that service-find has the right certificate.
+ """
+ global cert
+
+ # Assume there is only one service
+ res = api.Command['service_find'](self.service_princ)['result']
+ assert base64.b64encode(res[0]['usercertificate'][0]) == cert
- def test_3_cleanup(self):
+ def test_5_cleanup(self):
+ """
+ Clean up cert test data
+ """
# Now clean things up
api.Command['host_del'](self.host_fqdn)
- assert(True)
+ # Verify that the service is gone
+ res = api.Command['service_find'](self.service_princ)
+ assert res['count'] == 0
diff --git a/tests/test_xmlrpc/test_host_plugin.py b/tests/test_xmlrpc/test_host_plugin.py
index 36e920b09..7ae068c36 100644
--- a/tests/test_xmlrpc/test_host_plugin.py
+++ b/tests/test_xmlrpc/test_host_plugin.py
@@ -112,6 +112,7 @@ class test_host(Declarative):
description=[u'Test host 1'],
l=[u'Undisclosed location 1'],
krbprincipalname=[u'host/%s@%s' % (fqdn1, api.env.realm)],
+ has_keytab=False
),
),
),
@@ -138,6 +139,7 @@ class test_host(Declarative):
objectclass=objectclasses.host,
managedby=[dn1],
ipauniqueid=[fuzzy_uuid],
+ has_keytab=False
),
),
),
@@ -220,6 +222,7 @@ class test_host(Declarative):
description=[u'Updated host 1'],
l=[u'Undisclosed location 1'],
krbprincipalname=[u'host/%s@%s' % (fqdn1, api.env.realm)],
+ has_keytab=False
),
),
),
diff --git a/tests/test_xmlrpc/test_netgroup_plugin.py b/tests/test_xmlrpc/test_netgroup_plugin.py
index 3e98f2654..3363883ae 100644
--- a/tests/test_xmlrpc/test_netgroup_plugin.py
+++ b/tests/test_xmlrpc/test_netgroup_plugin.py
@@ -22,10 +22,18 @@ Test the `ipalib/plugins/netgroup.py` module.
"""
import sys
+import nose
+import krbV
from xmlrpc_test import XMLRPC_test, assert_attr_equal, assert_is_member
from ipalib import api
from ipalib import errors
+from ipaserver.plugins.ldap2 import ldap2
+# Global so we can save the value between tests
+netgroup_dn = None
+
+# See if our LDAP server is up and we can talk to it over GSSAPI
+ccache = krbV.default_context().default_ccache().name
class test_netgroup(XMLRPC_test):
"""
@@ -40,7 +48,7 @@ class test_netgroup(XMLRPC_test):
host_localityname = u'Undisclosed location'
host_kw = {'fqdn': host_fqdn, 'description': host_description, 'localityname': host_localityname, 'raw': True}
- hg_cn = u'ng1'
+ hg_cn = u'hg1'
hg_description = u'Netgroup'
hg_kw = {'cn': hg_cn, 'description': hg_description, 'raw': True}
@@ -50,6 +58,13 @@ class test_netgroup(XMLRPC_test):
user_home = u'/home/%s' % user_uid
user_kw = {'givenname': user_givenname,'sn': user_sn,'uid': user_uid,'homedirectory': user_home, 'raw': True}
+ # user2 is a member of testgroup
+ user2_uid = u'pexample'
+ user2_givenname = u'Pete'
+ user2_sn = u'Example'
+ user2_home = u'/home/%s' % user2_uid
+ user2_kw = {'givenname': user2_givenname,'sn': user2_sn,'uid': user2_uid,'homedirectory': user2_home, 'raw': True}
+
group_cn = u'testgroup'
group_description = u'This is a test'
group_kw = {'description': group_description,'cn': group_cn}
@@ -81,11 +96,22 @@ class test_netgroup(XMLRPC_test):
assert_attr_equal(entry, 'givenname', self.user_givenname)
assert_attr_equal(entry, 'uid', self.user_uid)
+ # Add our second user
+ entry = api.Command['user_add'](**self.user2_kw)['result']
+ assert_attr_equal(entry, 'givenname', self.user2_givenname)
+ assert_attr_equal(entry, 'uid', self.user2_uid)
+
# Add a group
entry = api.Command['group_add'](**self.group_kw)['result']
assert_attr_equal(entry, 'description', self.group_description)
assert_attr_equal(entry, 'cn', self.group_cn)
+ # Add a user to the group
+ kw = {'raw': True}
+ kw['user'] = self.user2_uid
+ res = api.Command['group_add_member'](self.group_cn, **kw)
+ assert res['completed'] == 1
+
def test_3_netgroup_add_member(self):
"""
Test the `xmlrpc.netgroup_add_member` method.
@@ -93,25 +119,25 @@ class test_netgroup(XMLRPC_test):
kw = {'raw': True}
kw['host'] = self.host_fqdn
entry = api.Command['netgroup_add_member'](self.ng_cn, **kw)['result']
- assert_is_member(entry, 'fqdn=%s' % self.host_fqdn)
+ assert_is_member(entry, 'fqdn=%s' % self.host_fqdn, 'memberhost')
kw = {'raw': True}
kw['hostgroup'] = self.hg_cn
ret = api.Command['netgroup_add_member'](self.ng_cn, **kw)
assert ret['completed'] == 1
- assert_is_member(ret['result'], 'cn=%s' % self.hg_cn)
+ assert_is_member(ret['result'], 'cn=%s' % self.hg_cn, 'memberhost')
kw = {'raw': True}
kw['user'] = self.user_uid
ret = api.Command['netgroup_add_member'](self.ng_cn, **kw)
assert ret['completed'] == 1
- assert_is_member(ret['result'], 'uid=%s' % self.user_uid)
+ assert_is_member(ret['result'], 'uid=%s' % self.user_uid, 'memberuser')
kw = {'raw': True}
kw['group'] = self.group_cn
ret = api.Command['netgroup_add_member'](self.ng_cn, **kw)
assert ret['completed'] == 1
- assert_is_member(ret['result'], 'cn=%s' % self.group_cn)
+ assert_is_member(ret['result'], 'cn=%s' % self.group_cn, 'memberuser')
def test_4_netgroup_add_member(self):
"""
@@ -122,36 +148,36 @@ class test_netgroup(XMLRPC_test):
ret = api.Command['netgroup_add_member'](self.ng_cn, **kw)
assert ret['completed'] == 0
failed = ret['failed']
- assert 'member' in failed
- assert 'host' in failed['member']
- assert self.host_fqdn in failed['member']['host']
+ assert 'memberhost' in failed
+ assert 'host' in failed['memberhost']
+ assert self.host_fqdn in failed['memberhost']['host']
kw = {'raw': True}
kw['hostgroup'] = self.hg_cn
ret = api.Command['netgroup_add_member'](self.ng_cn, **kw)
assert ret['completed'] == 0
failed = ret['failed']
- assert 'member' in failed
- assert 'hostgroup' in failed['member']
- assert self.hg_cn in failed['member']['hostgroup']
+ assert 'memberhost' in failed
+ assert 'hostgroup' in failed['memberhost']
+ assert self.hg_cn in failed['memberhost']['hostgroup']
kw = {'raw': True}
kw['user'] = self.user_uid
ret = api.Command['netgroup_add_member'](self.ng_cn, **kw)
assert ret['completed'] == 0
failed = ret['failed']
- assert 'member' in failed
- assert 'user' in failed['member']
- assert self.user_uid in failed['member']['user']
+ assert 'memberuser' in failed
+ assert 'user' in failed['memberuser']
+ assert self.user_uid in failed['memberuser']['user']
kw = {'raw': True}
kw['group'] = self.group_cn
ret = api.Command['netgroup_add_member'](self.ng_cn, **kw)
assert ret['completed'] == 0
failed = ret['failed']
- assert 'member' in failed
- assert 'group' in failed['member']
- assert self.group_cn in failed['member']['group']
+ assert 'memberuser' in failed
+ assert 'group' in failed['memberuser']
+ assert self.group_cn in failed['memberuser']['group']
def test_5_netgroup_add_member(self):
"""
@@ -166,28 +192,69 @@ class test_netgroup(XMLRPC_test):
def test_6_netgroup_show(self):
"""
- Test the `xmlrpc.netgroup_show` method.
+ Test the `xmlrpc.netgroup_show` method with --all.
"""
entry = api.Command['netgroup_show'](self.ng_cn, all=True, raw=True)['result']
assert_attr_equal(entry, 'description', self.ng_description)
assert_attr_equal(entry, 'cn', self.ng_cn)
- assert_is_member(entry, 'fqdn=%s' % self.host_fqdn)
- assert_is_member(entry, 'cn=%s' % self.hg_cn)
- assert_is_member(entry, 'uid=%s' % self.user_uid)
- assert_is_member(entry, 'cn=%s' % self.group_cn)
+ assert_is_member(entry, 'fqdn=%s' % self.host_fqdn, 'memberhost')
+ assert_is_member(entry, 'cn=%s' % self.hg_cn, 'memberhost')
+ assert_is_member(entry, 'uid=%s' % self.user_uid, 'memberuser')
+ assert_is_member(entry, 'cn=%s' % self.group_cn, 'memberuser')
assert_attr_equal(entry, 'objectclass', 'ipaobject')
+ assert_attr_equal(entry, 'objectclass', 'ipanisnetgroup')
+ assert_attr_equal(entry, 'objectclass', 'ipaassociation')
+
+ def test_6a_netgroup_show(self):
+ """
+ Test the `xmlrpc.netgroup_show` method.
+ """
+ global netgroup_dn
+ entry = api.Command['netgroup_show'](self.ng_cn, all=False, raw=True)['result']
+ assert_attr_equal(entry, 'description', self.ng_description)
+ assert_attr_equal(entry, 'cn', self.ng_cn)
+ assert_is_member(entry, 'fqdn=%s' % self.host_fqdn, 'memberhost')
+ assert_is_member(entry, 'cn=%s' % self.hg_cn, 'memberhost')
+ assert_is_member(entry, 'uid=%s' % self.user_uid, 'memberuser')
+ assert_is_member(entry, 'cn=%s' % self.group_cn, 'memberuser')
+ netgroup_dn = entry['dn']
+
+ def test_6b_netgroup_show(self):
+ """
+ Confirm the underlying triples
+ """
+ # Do an LDAP query to the compat area and verify that the entry
+ # is correct
+ conn = ldap2(shared_instance=False, ldap_uri=api.env.ldap_uri, base_dn=api.env.basedn)
+ conn.connect(ccache=ccache)
+ try:
+ entries = conn.find_entries('cn=%s' % self.ng_cn,
+ base_dn='cn=ng,cn=compat,%s' % api.env.basedn)
+ except errors.NotFound:
+ raise nose.SkipTest('compat and nis are not enabled, skipping test')
+ finally:
+ conn.disconnect()
+ triples = entries[0][0][1]['nisnetgrouptriple']
+
+ # This may not prove to be reliable since order is not guaranteed
+ # and even which user gets into which triple can be random.
+ assert '(nosuchhost,jexample,example.com)' in triples
+ assert '(ipatesthost.%s,pexample,example.com)' % api.env.domain in triples
def test_7_netgroup_find(self):
"""
- Test the `xmlrpc.hostgroup_find` method.
+ Test the `xmlrpc.netgroup_find` method.
"""
- entries = api.Command.netgroup_find(self.ng_cn, raw=True)['result']
+ result = api.Command.netgroup_find(self.ng_cn, raw=True)
+ entries = result['result']
+
+ assert(result['count'] == 1)
assert_attr_equal(entries[0], 'description', self.ng_description)
assert_attr_equal(entries[0], 'cn', self.ng_cn)
def test_8_netgroup_mod(self):
"""
- Test the `xmlrpc.hostgroup_mod` method.
+ Test the `xmlrpc.netgroup_mod` method.
"""
newdesc = u'Updated host group'
modkw = {'cn': self.ng_cn, 'description': newdesc, 'raw': True}
@@ -201,7 +268,7 @@ class test_netgroup(XMLRPC_test):
def test_9_netgroup_remove_member(self):
"""
- Test the `xmlrpc.hostgroup_remove_member` method.
+ Test the `xmlrpc.netgroup_remove_member` method.
"""
kw = {'raw': True}
kw['host'] = self.host_fqdn
@@ -232,18 +299,18 @@ class test_netgroup(XMLRPC_test):
ret = api.Command['netgroup_remove_member'](self.ng_cn, **kw)
assert ret['completed'] == 0
failed = ret['failed']
- assert 'member' in failed
- assert 'host' in failed['member']
- assert self.host_fqdn in failed['member']['host']
+ assert 'memberhost' in failed
+ assert 'host' in failed['memberhost']
+ assert self.host_fqdn in failed['memberhost']['host']
kw = {'raw': True}
kw['hostgroup'] = self.hg_cn
ret = api.Command['netgroup_remove_member'](self.ng_cn, **kw)
assert ret['completed'] == 0
failed = ret['failed']
- assert 'member' in failed
- assert 'hostgroup' in failed['member']
- assert self.hg_cn in failed['member']['hostgroup']
+ assert 'memberhost' in failed
+ assert 'hostgroup' in failed['memberhost']
+ assert self.hg_cn in failed['memberhost']['hostgroup']
kw = {'raw': True}
kw['user'] = self.user_uid
@@ -251,18 +318,18 @@ class test_netgroup(XMLRPC_test):
ret = api.Command['netgroup_remove_member'](self.ng_cn, **kw)
assert ret['completed'] == 0
failed = ret['failed']
- assert 'member' in failed
- assert 'user' in failed['member']
- assert self.user_uid in failed['member']['user']
+ assert 'memberuser' in failed
+ assert 'user' in failed['memberuser']
+ assert self.user_uid in failed['memberuser']['user']
kw = {'raw': True}
kw['group'] = self.group_cn
ret = api.Command['netgroup_remove_member'](self.ng_cn, **kw)
assert ret['completed'] == 0
failed = ret['failed']
- assert 'member' in failed
- assert 'group' in failed['member']
- assert self.group_cn in failed['member']['group']
+ assert 'memberuser' in failed
+ assert 'group' in failed['memberuser']
+ assert self.group_cn in failed['memberuser']['group']
def test_b_netgroup_del(self):
"""
@@ -304,8 +371,9 @@ class test_netgroup(XMLRPC_test):
else:
assert False
- # Remove the user
+ # Remove the users
assert api.Command['user_del'](self.user_uid)['result'] is True
+ assert api.Command['user_del'](self.user2_uid)['result'] is True
# Verify that it is gone
try:
diff --git a/tests/test_xmlrpc/test_service_plugin.py b/tests/test_xmlrpc/test_service_plugin.py
index 432a86b0e..299c64fc1 100644
--- a/tests/test_xmlrpc/test_service_plugin.py
+++ b/tests/test_xmlrpc/test_service_plugin.py
@@ -93,6 +93,7 @@ class test_service(XMLRPC_test):
"""
entry = api.Command['service_show'](self.principal)['result']
assert_attr_equal(entry, 'krbprincipalname', self.principal)
+ assert(entry['has_keytab'] == False)
def test_6_service_find(self):
"""
diff --git a/tests/test_xmlrpc/xmlrpc_test.py b/tests/test_xmlrpc/xmlrpc_test.py
index 61fca50aa..1966edf93 100644
--- a/tests/test_xmlrpc/xmlrpc_test.py
+++ b/tests/test_xmlrpc/xmlrpc_test.py
@@ -46,6 +46,8 @@ try:
res = api.Command['user_show'](u'notfound')
except errors.NetworkError:
server_available = False
+except IOError:
+ server_available = False
except errors.NotFound:
server_available = True