From 77ae4da70632e17b6be09e9ad71fc353b3bad96e Mon Sep 17 00:00:00 2001 From: Martin Kosek Date: Wed, 19 Jun 2013 09:48:29 +0200 Subject: Remove entitlement support Entitlements code was not tested nor supported upstream since version 3.0. Remove the associated code. https://fedorahosted.org/freeipa/ticket/3739 --- ipalib/constants.py | 1 - ipalib/plugins/entitle.py | 750 --------------------------------------------- ipalib/plugins/internal.py | 21 -- 3 files changed, 772 deletions(-) delete mode 100644 ipalib/plugins/entitle.py (limited to 'ipalib') diff --git a/ipalib/constants.py b/ipalib/constants.py index de084578..79885a33 100644 --- a/ipalib/constants.py +++ b/ipalib/constants.py @@ -98,7 +98,6 @@ DEFAULT_CONFIG = ( ('container_sudorule', DN(('cn', 'sudorules'), ('cn', 'sudo'))), ('container_sudocmd', DN(('cn', 'sudocmds'), ('cn', 'sudo'))), ('container_sudocmdgroup', DN(('cn', 'sudocmdgroups'), ('cn', 'sudo'))), - ('container_entitlements', DN(('cn', 'entitlements'), ('cn', 'etc'))), ('container_automember', DN(('cn', 'automember'), ('cn', 'etc'))), ('container_selinux', DN(('cn', 'usermap'), ('cn', 'selinux'))), ('container_s4u2proxy', DN(('cn', 's4u2proxy'), ('cn', 'etc'))), diff --git a/ipalib/plugins/entitle.py b/ipalib/plugins/entitle.py deleted file mode 100644 index 05779a9f..00000000 --- a/ipalib/plugins/entitle.py +++ /dev/null @@ -1,750 +0,0 @@ -# Authors: -# Rob Crittenden -# -# 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, either version 3 of the License, or -# (at your option) any later version. -# -# 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, see . - -from ipalib import api, SkipPluginModule -try: - from rhsm.connection import * - from rhsm.certificate import EntitlementCertificate - import M2Crypto - if api.env.in_server and api.env.context in ['lite', 'server']: - from ipaserver.install.certs import NSS_DIR -except ImportError, e: - if not api.env.validate_api: - raise SkipPluginModule(reason=str(e)) - -import os -from ipalib import api, errors -from ipalib import Flag, Int, Str, Password, File -from ipalib.plugins.baseldap import * -from ipalib.plugins.virtual import * -from ipalib import _, ngettext -from ipalib.output import Output, standard_list_of_entries -from ipalib.request import context -from ipapython import ipautil -import tempfile -import shutil -import socket -import base64 -from OpenSSL import crypto -from ipapython.ipautil import run -from ipalib.request import context -from ipalib.plugins.service import validate_certificate -from ipalib import x509 - -import locale - -__doc__ = _(""" -Entitlements - -Manage entitlements for client machines - -Entitlements can be managed either by registering with an entitlement -server with a username and password or by manually importing entitlement -certificates. An entitlement certificate contains embedded information -such as the product being entitled, the quantity and the validity dates. - -An entitlement server manages the number of client entitlements available. -To mark these entitlements as used by the IPA server you provide a quantity -and they are marked as consumed on the entitlement server. - - Register with an entitlement server: - ipa entitle-register consumer - - Import an entitlement certificate: - ipa entitle-import /home/user/ipaclient.pem - - Display current entitlements: - ipa entitle-status - - Retrieve details on entitlement certificates: - ipa entitle-get - - Consume some entitlements from the entitlement server: - ipa entitle-consume 50 - -The registration ID is a Unique Identifier (UUID). This ID will be -IMPORTED if you have used entitle-import. - -Changes to /etc/rhsm/rhsm.conf require a restart of the httpd service. -""") - -def read_file(filename): - fp = open(filename, 'r') - data = fp.readlines() - fp.close() - data = ''.join(data) - return data - -def write_file(filename, pem): - cert_file = open(filename, 'w') - cert_file.write(pem) - cert_file.close() - -def read_pkcs12_pin(): - pwdfile = '%s/pwdfile.txt' % NSS_DIR - fp = open(pwdfile, 'r') - pwd = fp.read() - fp.close() - return pwd - -def get_pool(ldap): - """ - Get our entitlement pool. Assume there is only one pool. - """ - db = None - try: - (db, uuid, certfile, keyfile) = get_uuid(ldap) - if db is None: - # db is None means manual registration - return (None, uuid) - - cp = UEPConnection(handler='/candlepin', cert_file=certfile, key_file=keyfile) - - pools = cp.getPoolsList(uuid) - poolid = pools[0]['id'] - - pool = cp.getPool(poolid) - finally: - if db: - shutil.rmtree(db, ignore_errors=True) - - return (pool, uuid) - -def get_uuid(ldap): - """ - Retrieve our UUID, certificate and key from LDAP. - - Except on error the caller is responsible for removing temporary files - """ - db = None - try: - db = tempfile.mkdtemp(prefix = "tmp-") - registrations = api.Command['entitle_find'](all=True) - if registrations['count'] == 0: - shutil.rmtree(db, ignore_errors=True) - raise errors.NotRegisteredError() - result = registrations['result'][0] - uuid = str(result['ipaentitlementid'][0]) - - entry_attrs = dict(ipaentitlementid=uuid) - dn = ldap.make_dn( - entry_attrs, 'ipaentitlementid', - DN(api.env.container_entitlements, api.env.basedn) - ) - if not ldap.can_read(dn, 'userpkcs12'): - raise errors.ACIError( - info=_('not allowed to perform this command')) - - if not 'userpkcs12' in result: - return (None, uuid, None, None) - data = result['userpkcs12'][0] - pkcs12 = crypto.load_pkcs12(data, read_pkcs12_pin()) - cert = pkcs12.get_certificate() - key = pkcs12.get_privatekey() - write_file(db + '/cert.pem', - crypto.dump_certificate(crypto.FILETYPE_PEM, cert)) - write_file(db + '/key.pem', - crypto.dump_privatekey(crypto.FILETYPE_PEM, key)) - except Exception, e: - if db is not None: - shutil.rmtree(db, ignore_errors=True) - raise e - - return (db, uuid, db + '/cert.pem', db + '/key.pem') - -output_params = ( - Str('ipaentitlementid?', - label='UUID', - ), - Str('usercertificate', - label=_('Certificate'), - ), -) - -class entitle(LDAPObject): - """ - Entitlement object - """ - container_dn = api.env.container_entitlements - object_name = _('entitlement') - object_name_plural = _('entitlements') - object_class = ['ipaobject', 'ipaentitlement'] - search_attributes = ['usercertificate'] - default_attributes = ['ipaentitlement'] - uuid_attribute = 'ipaentitlementid' - - label = _('Entitlements') - label_singular = _('Entitlement') - - """ - def get_dn(self, *keys, **kwargs): - try: - (dn, entry_attrs) = self.backend.find_entry_by_attr( - self.primary_key.name, keys[-1], self.object_class, [''], - DN(self.container_dn, api.env.basedn) - ) - except errors.NotFound: - dn = super(entitle, self).get_dn(*keys, **kwargs) - return dn - """ - -api.register(entitle) - -class entitle_status(VirtualCommand): - __doc__ = _('Display current entitlements.') - - operation="show entitlement" - - has_output_params = ( - Str('uuid', - label=_('UUID'), - ), - Str('product', - label=_('Product'), - ), - Int('quantity', - label=_('Quantity'), - ), - Int('consumed', - label=_('Consumed'), - ), - ) - - has_output = ( - Output('result', - type=dict, - doc=_('Dictionary mapping variable name to value'), - ), - ) - - def execute(self, *keys, **kw): - ldap = self.api.Backend.ldap2 - - os.environ['LANG'] = 'en_US' - locale.setlocale(locale.LC_ALL, '') - - (pool, uuid) = get_pool(ldap) - - if pool is None: - # This assumes there is only 1 product - quantity = 0 - product = '' - registrations = api.Command['entitle_find'](all=True)['result'][0] - if u'usercertificate' in registrations: - certs = registrations['usercertificate'] - for cert in certs: - cert = x509.make_pem(base64.b64encode(cert)) - try: - pc = EntitlementCertificate(cert) - o = pc.getOrder() - if o.getQuantityUsed(): - quantity = quantity + int(o.getQuantityUsed()) - product = o.getName() - except M2Crypto.X509.X509Error, e: - self.error('Invalid entitlement certificate, skipping.') - pool = dict(productId=product, quantity=quantity, - consumed=quantity, uuid=unicode(uuid)) - - result={'product': unicode(pool['productId']), - 'quantity': pool['quantity'], - 'consumed': pool['consumed'], - 'uuid': unicode(uuid), - } - - return dict( - result=result - ) - -api.register(entitle_status) - - -class entitle_consume(LDAPUpdate): - __doc__ = _('Consume an entitlement.') - - operation="consume entitlement" - - msg_summary = _('Consumed %(value)s entitlement(s).') - - takes_args = ( - Int('quantity', - label=_('Quantity'), - minvalue=1, - ), - ) - - # We don't want rights or add/setattr - takes_options = ( - # LDAPUpdate requires at least one option so autofill one - # This isn't otherwise used. - Int('hidden', - label=_('Quantity'), - minvalue=1, - autofill=True, - default=1, - flags=['no_option', 'no_output'] - ), - ) - - has_output_params = output_params + ( - Str('product', - label=_('Product'), - ), - Int('consumed', - label=_('Consumed'), - ), - ) - - def execute(self, *keys, **options): - """ - Override this so we can set value to the number of entitlements - consumed. - """ - result = super(entitle_consume, self).execute(*keys, **options) - result['value'] = unicode(keys[-1]) - return result - - def pre_callback(self, ldap, dn, entry_attrs, attrs_list, *keys, **options): - assert isinstance(dn, DN) - quantity = keys[-1] - - os.environ['LANG'] = 'en_US' - locale.setlocale(locale.LC_ALL, '') - - (db, uuid, certfile, keyfile) = get_uuid(ldap) - entry_attrs['ipaentitlementid'] = uuid - dn = ldap.make_dn( - entry_attrs, self.obj.uuid_attribute, - DN(self.obj.container_dn, api.env.basedn) - ) - if db is None: - raise errors.NotRegisteredError() - try: - (pool, uuid) = get_pool(ldap) - - result=api.Command['entitle_status']()['result'] - available = result['quantity'] - result['consumed'] - - if quantity > available: - raise errors.ValidationError( - name='quantity', - error=_('There are only %d entitlements left') % available) - - try: - cp = UEPConnection(handler='/candlepin', cert_file=certfile, key_file=keyfile) - cp.bindByEntitlementPool(uuid, pool['id'], quantity=quantity) - except RestlibException, e: - raise errors.ACIError(info=e.msg) - results = cp.getCertificates(uuid) - usercertificate = [] - for cert in results: - usercertificate.append(x509.normalize_certificate(cert['cert'])) - entry_attrs['usercertificate'] = usercertificate - entry_attrs['ipaentitlementid'] = uuid - finally: - if db: - shutil.rmtree(db, ignore_errors=True) - - return dn - - def post_callback(self, ldap, dn, entry_attrs, *keys, **options): - """ - Returning the certificates isn't very interesting. Return the - status of entitlements instead. - """ - assert isinstance(dn, DN) - if 'usercertificate' in entry_attrs: - del entry_attrs['usercertificate'] - if 'userpkcs12' in entry_attrs: - del entry_attrs['userpkcs12'] - result = api.Command['entitle_status']() - for attr in result['result']: - entry_attrs[attr] = result['result'][attr] - - return dn - -api.register(entitle_consume) - - -class entitle_get(VirtualCommand): - __doc__ = _('Retrieve the entitlement certs.') - - operation="retrieve entitlement" - - has_output_params = ( - Str('product', - label=_('Product'), - ), - Int('quantity', - label=_('Quantity'), - ), - Str('start', - label=_('Start'), - ), - Str('end', - label=_('End'), - ), - Str('serial', - label=_('Serial Number'), - ), - ) - - has_output = output.standard_list_of_entries - - def execute(self, *keys, **kw): - ldap = self.api.Backend.ldap2 - - os.environ['LANG'] = 'en_US' - locale.setlocale(locale.LC_ALL, '') - - (db, uuid, certfile, keyfile) = get_uuid(ldap) - if db is None: - quantity = 0 - product = '' - registrations = api.Command['entitle_find'](all=True)['result'][0] - certs = [] - if u'usercertificate' in registrations: - # make it look like a UEP cert - for cert in registrations['usercertificate']: - certs.append(dict(cert = x509.make_pem(base64.b64encode(cert)))) - else: - try: - cp = UEPConnection(handler='/candlepin', cert_file=certfile, key_file=keyfile) - certs = cp.getCertificates(uuid) - finally: - if db: - shutil.rmtree(db, ignore_errors=True) - - entries = [] - for c in certs: - try: - pc = EntitlementCertificate(c['cert']) - except M2Crypto.X509.X509Error: - raise errors.CertificateFormatError(error=_('Not an entitlement certificate')) - order = pc.getOrder() - quantity = 0 - if order.getQuantityUsed(): - quantity = order.getQuantityUsed() - result={'product': unicode(order.getName()), - 'quantity': int(order.getQuantityUsed()), - 'start': unicode(order.getStart()), - 'end': unicode(order.getEnd()), - 'serial': unicode(pc.serialNumber()), - 'certificate': unicode(c['cert']), - } - entries.append(result) - del pc - del order - - return dict( - result=entries, - count=len(entries), - truncated=False, - ) - -api.register(entitle_get) - -class entitle_find(LDAPSearch): - __doc__ = _('Search for entitlement accounts.') - - has_output_params = output_params - INTERNAL = True - - def post_callback(self, ldap, entries, truncated, *args, **options): - if len(entries) == 0: - raise errors.NotRegisteredError() - return truncated - -api.register(entitle_find) - -class entitle_register(LDAPCreate): - __doc__ = _('Register to the entitlement system.') - - operation="register entitlement" - - msg_summary = _('Registered to entitlement server.') - - takes_args = ( - Str('username', - label=_('Username'), - ), - ) - - takes_options = LDAPCreate.takes_options + ( - Str('ipaentitlementid?', - label='UUID', - doc=_('Enrollment UUID (not implemented)'), - flags=['no_create', 'no_update'], - ), - Password('password', - label=_('Password'), - doc=_('Registration password'), - confirm=False, - ), - ) - - """ - has_output_params = ( - ) - - has_output = ( - Output('result', - type=dict, - doc=_('Dictionary mapping variable name to value'), - ), - ) - """ - - def pre_callback(self, ldap, dn, entry_attrs, attrs_list, *keys, **options): - dn = DN(self.obj.container_dn, self.api.env.basedn) - if not ldap.can_add(dn): - raise errors.ACIError(info=_('No permission to register')) - os.environ['LANG'] = 'en_US' - locale.setlocale(locale.LC_ALL, '') - - if 'ipaentitlementid' in options: - raise errors.ValidationError(name='ipaentitlementid', - error=_('Registering to specific UUID is not supported yet.')) - - try: - registrations = api.Command['entitle_find']() - raise errors.AlreadyRegisteredError() - except errors.NotRegisteredError: - pass - try: - admin_cp = UEPConnection(handler='/candlepin', username=keys[-1], password=options.get('password')) - result = admin_cp.registerConsumer(name=api.env.realm, type="domain") - uuid = result['uuid'] - db = None - try: - # Create a PKCS#12 file to store the private key and - # certificate in LDAP. Encrypt using the Apache cert - # database password. - db = tempfile.mkdtemp(prefix = "tmp-") - write_file(db + '/in.cert', result['idCert']['cert']) - write_file(db + '/in.key', result['idCert']['key']) - args = ['/usr/bin/openssl', 'pkcs12', - '-export', - '-in', db + '/in.cert', - '-inkey', db + '/in.key', - '-out', db + '/out.p12', - '-name', 'candlepin', - '-passout', 'pass:%s' % read_pkcs12_pin() - ] - - (stdout, stderr, rc) = run(args, raiseonerr=False) - pkcs12 = read_file(db + '/out.p12') - - entry_attrs['ipaentitlementid'] = uuid - entry_attrs['userpkcs12'] = pkcs12 - finally: - if db is not None: - shutil.rmtree(db, ignore_errors=True) - except RestlibException, e: - if e.code == 401: - raise errors.ACIError(info=e.msg) - else: - raise e - except socket.gaierror: - raise errors.ACIError(info=e.args[1]) - - dn = ldap.make_dn( - entry_attrs, self.obj.uuid_attribute, - DN(self.obj.container_dn, api.env.basedn) - ) - return dn - -api.register(entitle_register) - - -class entitle_import(LDAPUpdate): - __doc__ = _('Import an entitlement certificate.') - - has_output_params = ( - Str('product', - label=_('Product'), - ), - Int('quantity', - label=_('Quantity'), - ), - Int('consumed', - label=_('Consumed'), - ), - ) - - has_output = ( - Output('result', - type=dict, - doc=_('Dictionary mapping variable name to value'), - ), - ) - - takes_args = ( - File('usercertificate*', validate_certificate, - cli_name='certificate_file', - ), - ) - - # any update requires at least 1 option to be set so force an invisible - # one here by setting the uuid. - takes_options = LDAPCreate.takes_options + ( - Str('uuid?', - label=_('UUID'), - doc=_('Enrollment UUID'), - flags=['no_create', 'no_update'], - autofill=True, - default=u'IMPORTED', - ), - ) - - def pre_callback(self, ldap, dn, entry_attrs, attrs_list, *keys, **options): - assert isinstance(dn, DN) - try: - (db, uuid, certfile, keyfile) = get_uuid(ldap) - if db is not None: - raise errors.AlreadyRegisteredError() - except errors.NotRegisteredError: - pass - - try: - entry_attrs['ipaentitlementid'] = unicode('IMPORTED') - newcert = x509.normalize_certificate(keys[-1][0]) - cert = x509.make_pem(base64.b64encode(newcert)) - try: - pc = EntitlementCertificate(cert) - o = pc.getOrder() - if o is None: - raise errors.CertificateFormatError(error=_('Not an entitlement certificate')) - except M2Crypto.X509.X509Error: - raise errors.CertificateFormatError(error=_('Not an entitlement certificate')) - dn = DN(('ipaentitlementid', entry_attrs['ipaentitlementid']), dn) - (dn, current_attrs) = ldap.get_entry(dn, ['*']) - entry_attrs['usercertificate'] = current_attrs['usercertificate'] - entry_attrs['usercertificate'].append(newcert) - except errors.NotFound: - # First import, create the entry - entry_attrs['ipaentitlementid'] = unicode('IMPORTED') - entry_attrs['objectclass'] = self.obj.object_class - entry_attrs['usercertificate'] = x509.normalize_certificate(keys[-1][0]) - ldap.add_entry(dn, entry_attrs) - setattr(context, 'entitle_import', True) - - return dn - - def exc_callback(self, keys, options, exc, call_func, *call_args, **call_kwargs): - """ - If we are adding the first entry there are no updates so EmptyModlist - will get thrown. Ignore it. - """ - if call_func.func_name == 'update_entry': - if isinstance(exc, errors.EmptyModlist): - if not getattr(context, 'entitle_import', False): - raise exc - return (call_args, {}) - raise exc - - def execute(self, *keys, **options): - super(entitle_import, self).execute(*keys, **options) - - return dict( - result=api.Command['entitle_status']()['result'] - ) - -api.register(entitle_import) - -class entitle_sync(LDAPUpdate): - __doc__ = _('Re-sync the local entitlement cache with the entitlement server.') - - operation="sync entitlement" - - msg_summary = _('Entitlement(s) synchronized.') - - # We don't want rights or add/setattr - takes_options = ( - # LDAPUpdate requires at least one option so autofill one - # This isn't otherwise used. - Int('hidden', - label=_('Quantity'), - minvalue=1, - autofill=True, - default=1, - flags=['no_option', 'no_output'] - ), - ) - - has_output_params = output_params + ( - Str('product', - label=_('Product'), - ), - Int('consumed', - label=_('Consumed'), - ), - ) - - def pre_callback(self, ldap, dn, entry_attrs, attrs_list, *keys, **options): - assert isinstance(dn, DN) - os.environ['LANG'] = 'en_US' - locale.setlocale(locale.LC_ALL, '') - - (db, uuid, certfile, keyfile) = get_uuid(ldap) - if db is None: - raise errors.NotRegisteredError() - try: - (pool, uuid) = get_pool(ldap) - - cp = UEPConnection(handler='/candlepin', cert_file=certfile, key_file=keyfile) - results = cp.getCertificates(uuid) - usercertificate = [] - for cert in results: - usercertificate.append(x509.normalize_certificate(cert['cert'])) - entry_attrs['usercertificate'] = usercertificate - entry_attrs['ipaentitlementid'] = uuid - finally: - if db: - shutil.rmtree(db, ignore_errors=True) - - dn = ldap.make_dn( - entry_attrs, self.obj.uuid_attribute, - DN(self.obj.container_dn, api.env.basedn) - ) - return dn - - def post_callback(self, ldap, dn, entry_attrs, *keys, **options): - """ - Returning the certificates isn't very interesting. Return the - status of entitlements instead. - """ - assert isinstance(dn, DN) - if 'usercertificate' in entry_attrs: - del entry_attrs['usercertificate'] - if 'userpkcs12' in entry_attrs: - del entry_attrs['userpkcs12'] - result = api.Command['entitle_status']() - for attr in result['result']: - entry_attrs[attr] = result['result'][attr] - - return dn - - def exc_callback(self, keys, options, exc, call_func, *call_args, **call_kwargs): - if call_func.func_name == 'update_entry': - if isinstance(exc, errors.EmptyModlist): - # If there is nothing to change we are already synchronized. - return - raise exc - -api.register(entitle_sync) diff --git a/ipalib/plugins/internal.py b/ipalib/plugins/internal.py index ca631c1e..9bbc0c9f 100644 --- a/ipalib/plugins/internal.py +++ b/ipalib/plugins/internal.py @@ -410,27 +410,6 @@ class i18n_messages(Command): "add_permission":_("Add Permission"), "remove_permission": _("Remove Permission"), }, - "entitle": { - "account": _("Account"), - "certificate": _("Certificate"), - "certificates": _("Certificates"), - "consume": _("Consume"), - "consume_entitlement": _("Consume Entitlement"), - "consumed": _("Consumed"), - "download": _("Download"), - "download_certificate": _("Download Certificate"), - "end": _("End"), - "import_button": _("Import"), - "import_certificate": _("Import Certificate"), - "import_message": _("Enter the Base64-encoded entitlement certificate below:"), - "loading": _("Loading..."), - "no_certificate": _("No Certificate."), - "product": _("Product"), - "register": _("Register"), - "registration": _("Registration"), - "start": _("Start"), - "status": _("Status"), - }, "group": { "details": _("Group Settings"), "external": _("External"), -- cgit