URL: https://github.com/freeipa/freeipa/pull/397 Author: tiran Title: #397: Improve wheel building and provide ipaserver wheel for local testing Action: synchronized
To pull the PR as Git branch: git remote add ghfreeipa https://github.com/freeipa/freeipa git fetch ghfreeipa pull/397/head:pr397 git checkout pr397
From 3388a271d88e40df67ee79a69f5e10404dc1449d Mon Sep 17 00:00:00 2001 From: Christian Heimes <chei...@redhat.com> Date: Tue, 17 Jan 2017 08:49:54 +0100 Subject: [PATCH 1/3] Conditionally import pyhbac The pyhbac module is part of SSSD. It's not available as stand-alone PyPI package. It would take a lot of effort to package it because the code is deeply tight into SSSD. Let's follow the example of other SSSD Python packages and make the import of pyhbac conditionally. It's only necessary for caacl and hbactest plugins. I renamed convert_to_ipa_rule() to _convert_to_ipa_rule() because it does not check for presence of pyhbac package itself. The check is performed earlier in execute(). The prefix indicates that it is an internal function and developers have to think twice before using it in another place. This makes it much easier to install ipaserver with instrumented build of Python with a different ABI or in isolated virtual envs to profile and debug the server. Signed-off-by: Christian Heimes <chei...@redhat.com> --- ipaserver/plugins/caacl.py | 86 ----------------------------------------- ipaserver/plugins/cert.py | 90 ++++++++++++++++++++++++++++++++++++++++++- ipaserver/plugins/hbactest.py | 19 +++++++-- 3 files changed, 105 insertions(+), 90 deletions(-) diff --git a/ipaserver/plugins/caacl.py b/ipaserver/plugins/caacl.py index ff1178a..43a397d 100644 --- a/ipaserver/plugins/caacl.py +++ b/ipaserver/plugins/caacl.py @@ -2,12 +2,10 @@ # Copyright (C) 2015 FreeIPA Contributors see COPYING for license # -import pyhbac import six from ipalib import api, errors, output from ipalib import Bool, Str, StrEnum -from ipalib.constants import IPA_CA_CN from ipalib.plugable import Registry from .baseldap import ( LDAPObject, LDAPSearch, LDAPCreate, LDAPDelete, LDAPQuery, @@ -80,90 +78,6 @@ register = Registry() -def _acl_make_request(principal_type, principal, ca_id, profile_id): - """Construct HBAC request for the given principal, CA and profile""" - - req = pyhbac.HbacRequest() - req.targethost.name = ca_id - req.service.name = profile_id - if principal_type == 'user': - req.user.name = principal.username - elif principal_type == 'host': - req.user.name = principal.hostname - elif principal_type == 'service': - req.user.name = unicode(principal) - groups = [] - if principal_type == 'user': - user_obj = api.Command.user_show(principal.username)['result'] - groups = user_obj.get('memberof_group', []) - groups += user_obj.get('memberofindirect_group', []) - elif principal_type == 'host': - host_obj = api.Command.host_show(principal.hostname)['result'] - groups = host_obj.get('memberof_hostgroup', []) - groups += host_obj.get('memberofindirect_hostgroup', []) - req.user.groups = sorted(set(groups)) - return req - - -def _acl_make_rule(principal_type, obj): - """Turn CA ACL object into HBAC rule. - - ``principal_type`` - String in {'user', 'host', 'service'} - """ - rule = pyhbac.HbacRule(obj['cn'][0]) - rule.enabled = obj['ipaenabledflag'][0] - rule.srchosts.category = {pyhbac.HBAC_CATEGORY_ALL} - - # add CA(s) - if 'ipacacategory' in obj and obj['ipacacategory'][0].lower() == 'all': - rule.targethosts.category = {pyhbac.HBAC_CATEGORY_ALL} - else: - # For compatibility with pre-lightweight-CAs CA ACLs, - # no CA members implies the host authority (only) - rule.targethosts.names = obj.get('ipamemberca_ca', [IPA_CA_CN]) - - # add profiles - if ('ipacertprofilecategory' in obj - and obj['ipacertprofilecategory'][0].lower() == 'all'): - rule.services.category = {pyhbac.HBAC_CATEGORY_ALL} - else: - attr = 'ipamembercertprofile_certprofile' - rule.services.names = obj.get(attr, []) - - # add principals and principal's groups - category_attr = '{}category'.format(principal_type) - if category_attr in obj and obj[category_attr][0].lower() == 'all': - rule.users.category = {pyhbac.HBAC_CATEGORY_ALL} - else: - if principal_type == 'user': - rule.users.names = obj.get('memberuser_user', []) - rule.users.groups = obj.get('memberuser_group', []) - elif principal_type == 'host': - rule.users.names = obj.get('memberhost_host', []) - rule.users.groups = obj.get('memberhost_hostgroup', []) - elif principal_type == 'service': - rule.users.names = [ - unicode(principal) - for principal in obj.get('memberservice_service', []) - ] - - return rule - - -def acl_evaluate(principal, ca_id, profile_id): - if principal.is_user: - principal_type = 'user' - elif principal.is_host: - principal_type = 'host' - else: - principal_type = 'service' - req = _acl_make_request(principal_type, principal, ca_id, profile_id) - acls = api.Command.caacl_find(no_members=False)['result'] - rules = [_acl_make_rule(principal_type, obj) for obj in acls] - return req.evaluate(rules) == pyhbac.HBAC_EVAL_ALLOW - - @register() class caacl(LDAPObject): """ diff --git a/ipaserver/plugins/cert.py b/ipaserver/plugins/cert.py index 9f90107..f276b0e 100644 --- a/ipaserver/plugins/cert.py +++ b/ipaserver/plugins/cert.py @@ -43,7 +43,6 @@ from .virtual import VirtualCommand from .baseldap import pkey_to_value from .certprofile import validate_profile_id -from .caacl import acl_evaluate from ipalib.text import _ from ipalib.request import context from ipalib import output @@ -52,6 +51,11 @@ from ipapython.ipa_log_manager import root_logger from ipaserver.plugins.service import normalize_principal, validate_realm +try: + import pyhbac +except ImportError: + raise errors.SkipPluginModule(reason=_('pyhbac is not installed.')) + if six.PY3: unicode = str @@ -158,6 +162,90 @@ PKIDATE_FORMAT = '%Y-%m-%d' +def _acl_make_request(principal_type, principal, ca_id, profile_id): + """Construct HBAC request for the given principal, CA and profile""" + + req = pyhbac.HbacRequest() + req.targethost.name = ca_id + req.service.name = profile_id + if principal_type == 'user': + req.user.name = principal.username + elif principal_type == 'host': + req.user.name = principal.hostname + elif principal_type == 'service': + req.user.name = unicode(principal) + groups = [] + if principal_type == 'user': + user_obj = api.Command.user_show(principal.username)['result'] + groups = user_obj.get('memberof_group', []) + groups += user_obj.get('memberofindirect_group', []) + elif principal_type == 'host': + host_obj = api.Command.host_show(principal.hostname)['result'] + groups = host_obj.get('memberof_hostgroup', []) + groups += host_obj.get('memberofindirect_hostgroup', []) + req.user.groups = sorted(set(groups)) + return req + + +def _acl_make_rule(principal_type, obj): + """Turn CA ACL object into HBAC rule. + + ``principal_type`` + String in {'user', 'host', 'service'} + """ + rule = pyhbac.HbacRule(obj['cn'][0]) + rule.enabled = obj['ipaenabledflag'][0] + rule.srchosts.category = {pyhbac.HBAC_CATEGORY_ALL} + + # add CA(s) + if 'ipacacategory' in obj and obj['ipacacategory'][0].lower() == 'all': + rule.targethosts.category = {pyhbac.HBAC_CATEGORY_ALL} + else: + # For compatibility with pre-lightweight-CAs CA ACLs, + # no CA members implies the host authority (only) + rule.targethosts.names = obj.get('ipamemberca_ca', [IPA_CA_CN]) + + # add profiles + if ('ipacertprofilecategory' in obj + and obj['ipacertprofilecategory'][0].lower() == 'all'): + rule.services.category = {pyhbac.HBAC_CATEGORY_ALL} + else: + attr = 'ipamembercertprofile_certprofile' + rule.services.names = obj.get(attr, []) + + # add principals and principal's groups + category_attr = '{}category'.format(principal_type) + if category_attr in obj and obj[category_attr][0].lower() == 'all': + rule.users.category = {pyhbac.HBAC_CATEGORY_ALL} + else: + if principal_type == 'user': + rule.users.names = obj.get('memberuser_user', []) + rule.users.groups = obj.get('memberuser_group', []) + elif principal_type == 'host': + rule.users.names = obj.get('memberhost_host', []) + rule.users.groups = obj.get('memberhost_hostgroup', []) + elif principal_type == 'service': + rule.users.names = [ + unicode(principal) + for principal in obj.get('memberservice_service', []) + ] + + return rule + + +def acl_evaluate(principal, ca_id, profile_id): + if principal.is_user: + principal_type = 'user' + elif principal.is_host: + principal_type = 'host' + else: + principal_type = 'service' + req = _acl_make_request(principal_type, principal, ca_id, profile_id) + acls = api.Command.caacl_find(no_members=False)['result'] + rules = [_acl_make_rule(principal_type, obj) for obj in acls] + return req.evaluate(rules) == pyhbac.HBAC_EVAL_ALLOW + + def normalize_pkidate(value): return datetime.datetime.strptime(value, PKIDATE_FORMAT) diff --git a/ipaserver/plugins/hbactest.py b/ipaserver/plugins/hbactest.py index 626e894..e156568 100644 --- a/ipaserver/plugins/hbactest.py +++ b/ipaserver/plugins/hbactest.py @@ -29,9 +29,14 @@ except ImportError: _dcerpc_bindings_installed = False -import pyhbac import six +try: + import pyhbac +except ImportError: + pyhbac = None + + if six.PY3: unicode = str @@ -210,7 +215,7 @@ register = Registry() -def convert_to_ipa_rule(rule): +def _convert_to_ipa_rule(rule): # convert a dict with a rule to an pyhbac rule ipa_rule = pyhbac.HbacRule(rule['cn'][0]) ipa_rule.enabled = rule['ipaenabledflag'][0] @@ -309,6 +314,14 @@ def canonicalize(self, host): return host def execute(self, *args, **options): + if pyhbac is None: + raise errors.ValidationError( + name=_('missing pyhbac'), + error=_( + 'pyhbac is not available on the server.' + ) + ) + # First receive all needed information: # 1. HBAC rules (whether enabled or disabled) # 2. Required options are (user, target host, service) @@ -356,7 +369,7 @@ def execute(self, *args, **options): # --disabled will import all disabled rules # --rules will implicitly add the rules from a rule list for rule in hbacset: - ipa_rule = convert_to_ipa_rule(rule) + ipa_rule = _convert_to_ipa_rule(rule) if ipa_rule.name in testrules: ipa_rule.enabled = True rules.append(ipa_rule) From 2ab29d0036f25b73ebdf42f592226df1aa83812a Mon Sep 17 00:00:00 2001 From: Christian Heimes <chei...@redhat.com> Date: Tue, 17 Jan 2017 08:57:33 +0100 Subject: [PATCH 2/3] Add extra_requires for additional dependencies ipaserver did not have extra_requires to state additional dependencies. Signed-off-by: Christian Heimes <chei...@redhat.com> --- ipaserver/setup.py | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/ipaserver/setup.py b/ipaserver/setup.py index d3c735c..beef1cb 100755 --- a/ipaserver/setup.py +++ b/ipaserver/setup.py @@ -59,12 +59,6 @@ "pyasn1", "pyldap", "six", - # not available on PyPI - # "python-libipa_hbac", - # "python-sss", - # "python-sss-murmur", - # "python-SSSDConfig", - # "samba-python", ], entry_points={ 'custodia.authorizers': [ @@ -74,4 +68,12 @@ 'IPASecStore = ipaserver.secrets.store:IPASecStore', ], }, + extras_require={ + # These packages are currently not available on PyPI. + "caacl": ["pyhbac"], + "dcerpc": ["samba", "pysss", "pysss_nss_idmap"], + "hbactest": ["pyhbac"], + "install": ["SSSDConfig"], + "trust": ["pysss_murmur", "pysss_nss_idmap"], + } ) From 5ef583999c4f3d649cbf675b2f049faa855cf275 Mon Sep 17 00:00:00 2001 From: Christian Heimes <chei...@redhat.com> Date: Tue, 17 Jan 2017 12:16:25 +0100 Subject: [PATCH 3/3] Add an option to build ipaserver wheels To create a wheel bundle with ipaserver and its dependencies: make wheel_bundle IPA_SERVER_WHEELS=1 To include additional dependencies: make wheel_bundle IPA_EXTRA_WHEELS=ipatests[webui] Signed-off-by: Christian Heimes <chei...@redhat.com> --- Makefile.am | 19 +++++++++++++++++-- configure.ac | 6 ++++++ ipaserver/plugins/hbactest.py | 10 +--------- ipaserver/setup.py | 1 - ipasetup.py.in | 3 ++- 5 files changed, 26 insertions(+), 13 deletions(-) diff --git a/Makefile.am b/Makefile.am index df4e05a..5946e71 100644 --- a/Makefile.am +++ b/Makefile.am @@ -221,6 +221,14 @@ endif # WITH_JSLINT WHEELDISTDIR = $(top_builddir)/dist/wheels WHEELBUNDLEDIR = $(top_builddir)/dist/bundle +@MK_IFEQ@ ($(IPA_SERVER_WHEELS),1) + IPA_WHEEL_PACKAGES @MK_ASSIGN@ $(IPACLIENT_SUBDIRS) ipaplatform ipaserver + IPA_OMIT_INSTALL @MK_ASSIGN@ 0 +@MK_ELSE@ + IPA_WHEEL_PACKAGES @MK_ASSIGN@ $(IPACLIENT_SUBDIRS) + IPA_OMIT_INSTALL @MK_ASSIGN@ 1 +@MK_ENDIF@ + $(WHEELDISTDIR): mkdir -p $(WHEELDISTDIR) @@ -228,12 +236,19 @@ $(WHEELBUNDLEDIR): mkdir -p $(WHEELBUNDLEDIR) bdist_wheel: $(WHEELDISTDIR) - for dir in $(IPACLIENT_SUBDIRS); do \ + rm -f $(foreach item,$(IPA_WHEEL_PACKAGES) ipatests,$(WHEELDISTDIR)/$(item)-*.whl) + export IPA_OMIT_INSTALL=$(IPA_OMIT_INSTALL); \ + for dir in $(IPA_WHEEL_PACKAGES) ipatests; do \ $(MAKE) $(AM_MAKEFLAGS) -C $${dir} $@ || exit 1; \ done wheel_bundle: $(WHEELBUNDLEDIR) bdist_wheel - $(PYTHON) -m pip wheel --wheel-dir $(WHEELBUNDLEDIR) $(WHEELDISTDIR)/*.whl + rm -f $(foreach item,$(IPA_WHEEL_PACKAGES) ipatests,$(WHEELBUNDLEDIR)/$(item)-*.whl) + $(PYTHON) -m pip wheel \ + --find-links $(WHEELDISTDIR) \ + --find-links $(WHEELBUNDLEDIR) \ + --wheel-dir $(WHEELBUNDLEDIR) \ + $(IPA_WHEEL_PACKAGES) $(IPA_EXTRA_WHEELS) wheel_placeholder: $(WHEELDISTDIR) for dir in $(IPA_PLACEHOLDERS); do \ diff --git a/configure.ac b/configure.ac index 2d84426..34df607 100644 --- a/configure.ac +++ b/configure.ac @@ -341,6 +341,12 @@ AC_SUBST([GIT_VERSION], [IPA_GIT_VERSION]) # used by Makefile.am for files depending on templates AC_SUBST([CONFIG_STATUS]) +# workaround for syntax clash between make and automake +AC_SUBST([MK_IFEQ], [ifeq]) +AC_SUBST([MK_ELSE], [else]) +AC_SUBST([MK_ENDIF], [endif]) +AC_SUBST([MK_ASSIGN], [=]) + dnl --------------------------------------------------------------------------- dnl Finish dnl --------------------------------------------------------------------------- diff --git a/ipaserver/plugins/hbactest.py b/ipaserver/plugins/hbactest.py index e156568..9c39b9a 100644 --- a/ipaserver/plugins/hbactest.py +++ b/ipaserver/plugins/hbactest.py @@ -34,7 +34,7 @@ try: import pyhbac except ImportError: - pyhbac = None + raise errors.SkipPluginModule(reason=_('pyhbac is not installed.')) if six.PY3: @@ -314,14 +314,6 @@ def canonicalize(self, host): return host def execute(self, *args, **options): - if pyhbac is None: - raise errors.ValidationError( - name=_('missing pyhbac'), - error=_( - 'pyhbac is not available on the server.' - ) - ) - # First receive all needed information: # 1. HBAC rules (whether enabled or disabled) # 2. Required options are (user, target host, service) diff --git a/ipaserver/setup.py b/ipaserver/setup.py index beef1cb..94fb5de 100755 --- a/ipaserver/setup.py +++ b/ipaserver/setup.py @@ -70,7 +70,6 @@ }, extras_require={ # These packages are currently not available on PyPI. - "caacl": ["pyhbac"], "dcerpc": ["samba", "pysss", "pysss_nss_idmap"], "hbactest": ["pyhbac"], "install": ["SSSDConfig"], diff --git a/ipasetup.py.in b/ipasetup.py.in index 915f0ed..8564a72 100644 --- a/ipasetup.py.in +++ b/ipasetup.py.in @@ -29,7 +29,8 @@ class build_py(setuptools_build_py): def finalize_options(self): setuptools_build_py.finalize_options(self) - if 'bdist_wheel' in self.distribution.commands: + omit = os.environ.get('IPA_OMIT_INSTALL', '0') + if omit == '1': distname = self.distribution.metadata.name self.skip_package = '{}.install'.format(distname) log.warn("bdist_wheel: Ignore package: %s",
-- Manage your subscription for the Freeipa-devel mailing list: https://www.redhat.com/mailman/listinfo/freeipa-devel Contribute to FreeIPA: http://www.freeipa.org/page/Contribute/Code