From william at firstyear.id.au Sun Feb 1 05:38:45 2015 From: william at firstyear.id.au (William) Date: Sun, 01 Feb 2015 16:08:45 +1030 Subject: [Freeipa-devel] Build issues with IPA Server 4.1.99.201502010530GIT782ad36 Message-ID: <1422769125.4790.6.camel@firstyear.id.au> Running make in freeipa master: IPA Server 4.1.99.201502010530GIT782ad36 ======================== prefix: /usr exec_prefix: ${prefix} libdir: /usr/lib bindir: ${exec_prefix}/bin sbindir: ${exec_prefix}/sbin sysconfdir: /etc localstatedir: /var datadir: ${datarootdir} krb5rundir: /var/run/krb5kdc systemdsystemunitdir: /usr/lib/systemd/system source code location: . compiler: gcc -std=gnu99 cflags: -g -O2 -Wall -Wextra -Wformat-security -Wno-unused-parameter -Wno-sign-compare -Wno-missing-field-initializers LDAP libs: -llber -lldap_r KRB5 libs: -lkrb5 -lk5crypto -lcom_err KRAD libs: -lkrad OpenSSL libs: -lcrypto Maintainer mode: no cd install; if [ ! -e Makefile ]; then ../autogen.sh --prefix=/usr --sysconfdir=/etc --localstatedir=/var --libdir=/usr/lib; fi ./make-lint ************* Module ipaserver.plugins.dogtag ipaserver/plugins/dogtag.py:1909: [E1123(unexpected-keyword-arg), kra.get_client] Unexpected keyword argument 'password_file' in constructor call) ipaserver/plugins/dogtag.py:1909: [E1120(no-value-for-parameter), kra.get_client] No value for argument 'certdb_password' in constructor call) ************* Module ipatests.test_integration.config ipatests/test_integration/config.py:44: [E1002(super-on-old-class), Config.__init__] Use of super on an old style class) ipatests/test_integration/config.py:72: [E1101(no-member), Config.ad_domains] Instance of 'Config' has no 'domains' member) ipatests/test_integration/config.py:75: [E1101(no-member), Config.get_all_hosts] Instance of 'Config' has no 'domains' member) ipatests/test_integration/config.py:79: [E1002(super-on-old-class), Config.to_dict] Use of super on an old style class) ipatests/test_integration/config.py:130: [E1101(no-member), Domain.master] Instance of 'Domain' has no 'host_by_role' member) ipatests/test_integration/config.py:134: [E1101(no-member), Domain.masters] Instance of 'Domain' has no 'hosts_by_role' member) ipatests/test_integration/config.py:138: [E1101(no-member), Domain.replicas] Instance of 'Domain' has no 'hosts_by_role' member) ipatests/test_integration/config.py:142: [E1101(no-member), Domain.clients] Instance of 'Domain' has no 'hosts_by_role' member) ipatests/test_integration/config.py:146: [E1101(no-member), Domain.ads] Instance of 'Domain' has no 'hosts_by_role' member) ipatests/test_integration/config.py:150: [E1101(no-member), Domain.other_hosts] Instance of 'Domain' has no 'hosts_by_role' member) ************* Module ipatests.test_integration.env_config ipatests/test_integration/env_config.py:113: [E1101(no-member), config_from_env] Class 'Config' has no 'from_dict' member) ipatests/test_integration/env_config.py:118: [E1101(no-member), config_from_env] Class 'Config' has no 'from_dict' member) ipatests/test_integration/env_config.py:140: [E1101(no-member), config_from_env] Instance of 'Config' has no 'domains' member) ipatests/test_integration/env_config.py:143: [E1101(no-member), config_from_env] Instance of 'Config' has no 'domains' member) ************* Module ipatests.test_integration.host ipatests/test_integration/host.py:46: [E1101(no-member), Host.ldap_connect] Instance of 'Host' has no 'log' member) ipatests/test_integration/host.py:46: [E1101(no-member), Host.ldap_connect] Instance of 'Host' has no 'external_hostname' member) ipatests/test_integration/host.py:47: [E1101(no-member), Host.ldap_connect] Instance of 'Host' has no 'external_hostname' member) ipatests/test_integration/host.py:48: [E1101(no-member), Host.ldap_connect] Instance of 'Host' has no 'config' member) ipatests/test_integration/host.py:49: [E1101(no-member), Host.ldap_connect] Instance of 'Host' has no 'log' member) ipatests/test_integration/host.py:50: [E1101(no-member), Host.ldap_connect] Instance of 'Host' has no 'config' member) ************* Module ipatests.test_integration.test_testconfig ipatests/test_integration/test_testconfig.py:107: [E1101(no-member), CheckConfig.test_dict_to_env] Class 'Config' has no 'from_dict' member) ipatests/test_integration/test_testconfig.py:112: [E1101(no-member), CheckConfig.test_dict_to_dict] Class 'Config' has no 'from_dict' member) ipatests/test_integration/test_testconfig.py:122: [E1101(no-member), CheckConfig.test_dict_roundtrip] Class 'Config' has no 'from_dict' member) =============================================================================== Errors were found during the static code check. If you are certain that any of the reported errors are false positives, please mark them in the source code according to the pylint documentation. =============================================================================== Makefile:119: recipe for target 'lint' failed make: *** [lint] Error 2 Checking if I have all the needed dependencies: with the command: sudo yum install rpm-build `grep "^BuildRequires" freeipa.spec.in | awk '{ print $2 }' | grep -v "^/"` No package python-pytest-multihost available. No package python-pytest-sourceorder available. Where do you get these two packages? Are they the cause of my build failure? If not, what is the cause of these build failures? On a more general note, I have attempted to build freeipa on and off every few weeks for the last few months and regularly hit lint errors like this. I have fresh git checkouts on f21. What are the other developers doing to make their environment actually work? I can't seem to make it possible to have a stable, building working freeipa dev environment ... From mbasti at redhat.com Sun Feb 1 17:26:35 2015 From: mbasti at redhat.com (Martin Basti) Date: Sun, 01 Feb 2015 18:26:35 +0100 Subject: [Freeipa-devel] Build issues with IPA Server 4.1.99.201502010530GIT782ad36 In-Reply-To: <1422769125.4790.6.camel@firstyear.id.au> References: <1422769125.4790.6.camel@firstyear.id.au> Message-ID: <54CE61CB.4060807@redhat.com> On 01/02/15 06:38, William wrote: > Running make in freeipa master: > > IPA Server 4.1.99.201502010530GIT782ad36 > ======================== > > prefix: /usr > exec_prefix: ${prefix} > libdir: /usr/lib > bindir: ${exec_prefix}/bin > sbindir: ${exec_prefix}/sbin > sysconfdir: /etc > localstatedir: /var > datadir: ${datarootdir} > krb5rundir: /var/run/krb5kdc > systemdsystemunitdir: /usr/lib/systemd/system > source code location: . > compiler: gcc -std=gnu99 > cflags: -g -O2 -Wall -Wextra -Wformat-security > -Wno-unused-parameter -Wno-sign-compare -Wno-missing-field-initializers > LDAP libs: -llber -lldap_r > KRB5 libs: -lkrb5 -lk5crypto -lcom_err > KRAD libs: -lkrad > OpenSSL libs: -lcrypto > Maintainer mode: no > > cd install; if [ ! -e Makefile ]; then ../autogen.sh --prefix=/usr > --sysconfdir=/etc --localstatedir=/var --libdir=/usr/lib; fi > ./make-lint > ************* Module ipaserver.plugins.dogtag > ipaserver/plugins/dogtag.py:1909: [E1123(unexpected-keyword-arg), > kra.get_client] Unexpected keyword argument 'password_file' in > constructor call) > ipaserver/plugins/dogtag.py:1909: [E1120(no-value-for-parameter), > kra.get_client] No value for argument 'certdb_password' in constructor > call) > ************* Module ipatests.test_integration.config > ipatests/test_integration/config.py:44: [E1002(super-on-old-class), > Config.__init__] Use of super on an old style class) > ipatests/test_integration/config.py:72: [E1101(no-member), > Config.ad_domains] Instance of 'Config' has no 'domains' member) > ipatests/test_integration/config.py:75: [E1101(no-member), > Config.get_all_hosts] Instance of 'Config' has no 'domains' member) > ipatests/test_integration/config.py:79: [E1002(super-on-old-class), > Config.to_dict] Use of super on an old style class) > ipatests/test_integration/config.py:130: [E1101(no-member), > Domain.master] Instance of 'Domain' has no 'host_by_role' member) > ipatests/test_integration/config.py:134: [E1101(no-member), > Domain.masters] Instance of 'Domain' has no 'hosts_by_role' member) > ipatests/test_integration/config.py:138: [E1101(no-member), > Domain.replicas] Instance of 'Domain' has no 'hosts_by_role' member) > ipatests/test_integration/config.py:142: [E1101(no-member), > Domain.clients] Instance of 'Domain' has no 'hosts_by_role' member) > ipatests/test_integration/config.py:146: [E1101(no-member), Domain.ads] > Instance of 'Domain' has no 'hosts_by_role' member) > ipatests/test_integration/config.py:150: [E1101(no-member), > Domain.other_hosts] Instance of 'Domain' has no 'hosts_by_role' member) > ************* Module ipatests.test_integration.env_config > ipatests/test_integration/env_config.py:113: [E1101(no-member), > config_from_env] Class 'Config' has no 'from_dict' member) > ipatests/test_integration/env_config.py:118: [E1101(no-member), > config_from_env] Class 'Config' has no 'from_dict' member) > ipatests/test_integration/env_config.py:140: [E1101(no-member), > config_from_env] Instance of 'Config' has no 'domains' member) > ipatests/test_integration/env_config.py:143: [E1101(no-member), > config_from_env] Instance of 'Config' has no 'domains' member) > ************* Module ipatests.test_integration.host > ipatests/test_integration/host.py:46: [E1101(no-member), > Host.ldap_connect] Instance of 'Host' has no 'log' member) > ipatests/test_integration/host.py:46: [E1101(no-member), > Host.ldap_connect] Instance of 'Host' has no 'external_hostname' member) > ipatests/test_integration/host.py:47: [E1101(no-member), > Host.ldap_connect] Instance of 'Host' has no 'external_hostname' member) > ipatests/test_integration/host.py:48: [E1101(no-member), > Host.ldap_connect] Instance of 'Host' has no 'config' member) > ipatests/test_integration/host.py:49: [E1101(no-member), > Host.ldap_connect] Instance of 'Host' has no 'log' member) > ipatests/test_integration/host.py:50: [E1101(no-member), > Host.ldap_connect] Instance of 'Host' has no 'config' member) > ************* Module ipatests.test_integration.test_testconfig > ipatests/test_integration/test_testconfig.py:107: [E1101(no-member), > CheckConfig.test_dict_to_env] Class 'Config' has no 'from_dict' member) > ipatests/test_integration/test_testconfig.py:112: [E1101(no-member), > CheckConfig.test_dict_to_dict] Class 'Config' has no 'from_dict' member) > ipatests/test_integration/test_testconfig.py:122: [E1101(no-member), > CheckConfig.test_dict_roundtrip] Class 'Config' has no 'from_dict' > member) > > =============================================================================== > Errors were found during the static code check. > > If you are certain that any of the reported errors are false positives, > please > mark them in the source code according to the pylint documentation. > =============================================================================== > > Makefile:119: recipe for target 'lint' failed > make: *** [lint] Error 2 > > > > Checking if I have all the needed dependencies: with the command: > > sudo yum install rpm-build `grep "^BuildRequires" freeipa.spec.in | awk > '{ print $2 }' | grep -v "^/"` > > > No package python-pytest-multihost available. > No package python-pytest-sourceorder available. > > Where do you get these two packages? Are they the cause of my build > failure? If not, what is the cause of these build failures? > > On a more general note, I have attempted to build freeipa on and off > every few weeks for the last few months and regularly hit lint errors > like this. I have fresh git checkouts on f21. What are the other > developers doing to make their environment actually work? I can't seem > to make it possible to have a stable, building working freeipa dev > environment ... > > > _______________________________________________ > Freeipa-devel mailing list > Freeipa-devel at redhat.com > https://www.redhat.com/mailman/listinfo/freeipa-devel Hello Wiliam, you may need to enable copr repo mkosek/freeipa-master to have proper dependencies for git master branch. HTH -- Martin Basti From william at firstyear.id.au Sun Feb 1 22:31:22 2015 From: william at firstyear.id.au (William) Date: Mon, 02 Feb 2015 09:01:22 +1030 Subject: [Freeipa-devel] Build issues with IPA Server 4.1.99.201502010530GIT782ad36 In-Reply-To: <54CE61CB.4060807@redhat.com> References: <1422769125.4790.6.camel@firstyear.id.au> <54CE61CB.4060807@redhat.com> Message-ID: <1422829882.3928.3.camel@firstyear.id.au> > > Checking if I have all the needed dependencies: with the command: > > > > sudo yum install rpm-build `grep "^BuildRequires" freeipa.spec.in | awk > > '{ print $2 }' | grep -v "^/"` > > > > > > No package python-pytest-multihost available. > > No package python-pytest-sourceorder available. > > you may need to enable copr repo mkosek/freeipa-master to have proper > dependencies for git master branch. > Yes it does. However, the copr seems to more generally not play with multilib properly. Resolving Dependencies --> Running transaction check ---> Package openssl-devel.x86_64 1:1.0.1k-1.fc21 will be updated ---> Package openssl-devel.x86_64 1:1.0.1k-1pspacek20150126T1356.fc21 will be an update --> Processing Dependency: openssl-libs(x86-64) = 1:1.0.1k-1pspacek20150126T1356.fc21 for package: 1:openssl-devel-1.0.1k-1pspacek20150126T1356.fc21.x86_64 ---> Package pki-base.noarch 0:10.2.0-5.fc21 will be updated ---> Package pki-base.noarch 0:10.2.1-0.1.fc21 will be an update ---> Package python-pytest-multihost.noarch 0:0.5-1.fc21 will be installed --> Processing Dependency: pytest >= 2.4.0 for package: python-pytest-multihost-0.5-1.fc21.noarch --> Processing Dependency: PyYAML for package: python-pytest-multihost-0.5-1.fc21.noarch ---> Package python-pytest-sourceorder.noarch 0:0.1-1.fc21 will be installed --> Running transaction check ---> Package PyYAML.x86_64 0:3.11-6.fc21 will be installed ---> Package openssl-libs.x86_64 1:1.0.1k-1.fc21 will be updated --> Processing Dependency: openssl-libs(x86-64) = 1:1.0.1k-1.fc21 for package: 1:openssl-1.0.1k-1.fc21.x86_64 ---> Package openssl-libs.x86_64 1:1.0.1k-1pspacek20150126T1356.fc21 will be an update ---> Package pytest.noarch 0:2.6.4-1.fc21 will be installed --> Processing Dependency: python-py >= 1.4.26 for package: pytest-2.6.4-1.fc21.noarch --> Running transaction check ---> Package openssl.x86_64 1:1.0.1k-1.fc21 will be updated ---> Package openssl.x86_64 1:1.0.1k-1pspacek20150126T1356.fc21 will be an update ---> Package python-py.noarch 0:1.4.26-2.fc21 will be installed --> Finished Dependency Resolution It looks like it doesn't provide i686 and x86_64 properly. I assume this is because the i686 packages in copr actually need to be in the x86_64 repo also .... A replacement of the copr repo file to be: [mkosek-freeipa-master-x86_64] name=Copr repo for freeipa-master owned by mkosek baseurl=https://copr-be.cloud.fedoraproject.org/results/mkosek/freeipa-master/fedora-$releasever-x86_64/ skip_if_unavailable=True gpgcheck=0 gpgkey=https://copr-be.cloud.fedoraproject.org/results/mkosek/freeipa-master/fedora-$releasever-x86_64/pubkey.gpg enabled=1 [mkosek-freeipa-master-x86] name=Copr repo for freeipa-master owned by mkosek baseurl=https://copr-be.cloud.fedoraproject.org/results/mkosek/freeipa-master/fedora-$releasever-i386/ skip_if_unavailable=True gpgcheck=0 gpgkey=https://copr-be.cloud.fedoraproject.org/results/mkosek/freeipa-master/fedora-$releasever-i386/pubkey.gpg enabled=1 Seems to have done the trick. Now I can successfully build freeipa once more it would seem. Thanks for your help. Sincerely, William From lslebodn at redhat.com Mon Feb 2 08:25:53 2015 From: lslebodn at redhat.com (Lukas Slebodnik) Date: Mon, 2 Feb 2015 09:25:53 +0100 Subject: [Freeipa-devel] Build issues with IPA Server 4.1.99.201502010530GIT782ad36 In-Reply-To: <54CE61CB.4060807@redhat.com> References: <1422769125.4790.6.camel@firstyear.id.au> <54CE61CB.4060807@redhat.com> Message-ID: <20150202082553.GB21615@mail.corp.redhat.com> On (01/02/15 18:26), Martin Basti wrote: >On 01/02/15 06:38, William wrote: >>Running make in freeipa master: >> >> IPA Server 4.1.99.201502010530GIT782ad36 >> ======================== >> >> prefix: /usr >> exec_prefix: ${prefix} >> libdir: /usr/lib >> bindir: ${exec_prefix}/bin >> sbindir: ${exec_prefix}/sbin >> sysconfdir: /etc >> localstatedir: /var >> datadir: ${datarootdir} >> krb5rundir: /var/run/krb5kdc >> systemdsystemunitdir: /usr/lib/systemd/system >> source code location: . >> compiler: gcc -std=gnu99 >> cflags: -g -O2 -Wall -Wextra -Wformat-security >>-Wno-unused-parameter -Wno-sign-compare -Wno-missing-field-initializers >> LDAP libs: -llber -lldap_r >> KRB5 libs: -lkrb5 -lk5crypto -lcom_err >> KRAD libs: -lkrad >> OpenSSL libs: -lcrypto >> Maintainer mode: no >> >>cd install; if [ ! -e Makefile ]; then ../autogen.sh --prefix=/usr >>--sysconfdir=/etc --localstatedir=/var --libdir=/usr/lib; fi >>./make-lint >>************* Module ipaserver.plugins.dogtag >>ipaserver/plugins/dogtag.py:1909: [E1123(unexpected-keyword-arg), >>kra.get_client] Unexpected keyword argument 'password_file' in >>constructor call) >>ipaserver/plugins/dogtag.py:1909: [E1120(no-value-for-parameter), >>kra.get_client] No value for argument 'certdb_password' in constructor >>call) >>************* Module ipatests.test_integration.config >>ipatests/test_integration/config.py:44: [E1002(super-on-old-class), >>Config.__init__] Use of super on an old style class) >>ipatests/test_integration/config.py:72: [E1101(no-member), >>Config.ad_domains] Instance of 'Config' has no 'domains' member) >>ipatests/test_integration/config.py:75: [E1101(no-member), >>Config.get_all_hosts] Instance of 'Config' has no 'domains' member) >>ipatests/test_integration/config.py:79: [E1002(super-on-old-class), >>Config.to_dict] Use of super on an old style class) >>ipatests/test_integration/config.py:130: [E1101(no-member), >>Domain.master] Instance of 'Domain' has no 'host_by_role' member) >>ipatests/test_integration/config.py:134: [E1101(no-member), >>Domain.masters] Instance of 'Domain' has no 'hosts_by_role' member) >>ipatests/test_integration/config.py:138: [E1101(no-member), >>Domain.replicas] Instance of 'Domain' has no 'hosts_by_role' member) >>ipatests/test_integration/config.py:142: [E1101(no-member), >>Domain.clients] Instance of 'Domain' has no 'hosts_by_role' member) >>ipatests/test_integration/config.py:146: [E1101(no-member), Domain.ads] >>Instance of 'Domain' has no 'hosts_by_role' member) >>ipatests/test_integration/config.py:150: [E1101(no-member), >>Domain.other_hosts] Instance of 'Domain' has no 'hosts_by_role' member) >>************* Module ipatests.test_integration.env_config >>ipatests/test_integration/env_config.py:113: [E1101(no-member), >>config_from_env] Class 'Config' has no 'from_dict' member) >>ipatests/test_integration/env_config.py:118: [E1101(no-member), >>config_from_env] Class 'Config' has no 'from_dict' member) >>ipatests/test_integration/env_config.py:140: [E1101(no-member), >>config_from_env] Instance of 'Config' has no 'domains' member) >>ipatests/test_integration/env_config.py:143: [E1101(no-member), >>config_from_env] Instance of 'Config' has no 'domains' member) >>************* Module ipatests.test_integration.host >>ipatests/test_integration/host.py:46: [E1101(no-member), >>Host.ldap_connect] Instance of 'Host' has no 'log' member) >>ipatests/test_integration/host.py:46: [E1101(no-member), >>Host.ldap_connect] Instance of 'Host' has no 'external_hostname' member) >>ipatests/test_integration/host.py:47: [E1101(no-member), >>Host.ldap_connect] Instance of 'Host' has no 'external_hostname' member) >>ipatests/test_integration/host.py:48: [E1101(no-member), >>Host.ldap_connect] Instance of 'Host' has no 'config' member) >>ipatests/test_integration/host.py:49: [E1101(no-member), >>Host.ldap_connect] Instance of 'Host' has no 'log' member) >>ipatests/test_integration/host.py:50: [E1101(no-member), >>Host.ldap_connect] Instance of 'Host' has no 'config' member) >>************* Module ipatests.test_integration.test_testconfig >>ipatests/test_integration/test_testconfig.py:107: [E1101(no-member), >>CheckConfig.test_dict_to_env] Class 'Config' has no 'from_dict' member) >>ipatests/test_integration/test_testconfig.py:112: [E1101(no-member), >>CheckConfig.test_dict_to_dict] Class 'Config' has no 'from_dict' member) >>ipatests/test_integration/test_testconfig.py:122: [E1101(no-member), >>CheckConfig.test_dict_roundtrip] Class 'Config' has no 'from_dict' >>member) >> >>=============================================================================== >>Errors were found during the static code check. >> >>If you are certain that any of the reported errors are false positives, >>please >>mark them in the source code according to the pylint documentation. >>=============================================================================== >> >>Makefile:119: recipe for target 'lint' failed >>make: *** [lint] Error 2 >> >> >> >>Checking if I have all the needed dependencies: with the command: >> >>sudo yum install rpm-build `grep "^BuildRequires" freeipa.spec.in | awk >>'{ print $2 }' | grep -v "^/"` >> >> >>No package python-pytest-multihost available. >>No package python-pytest-sourceorder available. >> >>Where do you get these two packages? Are they the cause of my build >>failure? If not, what is the cause of these build failures? >> >>On a more general note, I have attempted to build freeipa on and off >>every few weeks for the last few months and regularly hit lint errors >>like this. I have fresh git checkouts on f21. What are the other >>developers doing to make their environment actually work? I can't seem >>to make it possible to have a stable, building working freeipa dev >>environment ... >> >> >Hello Wiliam, > >you may need to enable copr repo mkosek/freeipa-master to have proper >dependencies for git master branch. > Packages are already in fedora-rawhide and should be in fdeora 21 soon http://koji.fedoraproject.org/koji/packageinfo?packageID=19868 http://koji.fedoraproject.org/koji/packageinfo?packageID=19869 LS From tbordaz at redhat.com Tue Feb 3 10:43:23 2015 From: tbordaz at redhat.com (thierry bordaz) Date: Tue, 03 Feb 2015 11:43:23 +0100 Subject: [Freeipa-devel] [PATCH] 0003-2 User life cycle: new stageuser plugin with add verb In-Reply-To: <54196346.5070500@redhat.com> References: <53E4D6AE.6050505@redhat.com> <54045399.3030404@redhat.com> <54196346.5070500@redhat.com> Message-ID: <54D0A64B.3090902@redhat.com> On 09/17/2014 12:32 PM, thierry bordaz wrote: > On 09/01/2014 01:08 PM, Petr Viktorin wrote: >> On 08/08/2014 03:54 PM, thierry bordaz wrote: >>> Hi, >>> >>> The attached patch is related to 'User Life Cycle' >>> (https://fedorahosted.org/freeipa/ticket/3813) >>> >>> It creates a stageuser plugin with a first function stageuser-add. >>> Stage >>> user entries are provisioned under 'cn=staged >>> users,cn=accounts,cn=provisioning,SUFFIX'. >>> >>> Thanks >>> thierry >> >> Avoid `from ipalib.plugins.baseldap import *` in new code; instead >> import the module itself and use e.g. `baseldap.LDAPObject`. >> >> The stageuser help (docstring) is copied from the user plugin, and >> discusses things like account lockout and disabling users. It should >> rather explain what stageuser itself does. (And I don't very much >> like the Note about the interface being badly designed...) >> Also decide if the docs should call it "staged user" or "stage user" >> or "stageuser". >> >> A lot of the code is copied and pasted over from the users plugin. >> Don't do that. Either import things (e.g. validate_nsaccountlock) >> from the users plugin, or move the reused code into a shared module. >> >> For the `user` object, since so much is the same, it might be best to >> create a common base class for user and stageuser; and similarly for >> the Command plugins. >> >> The default permissions need different names, and you don't need >> another copy of the 'non_object' ones. Also, run the makeaci script. >> > Hello, > > This modified patch is mainly moving common base class into a new > plugin: accounts.py. user/stageuser plugin inherits from accounts. > It also creates a better description of what are stage user, how > to add a new stage user, updates ACI.txt and separate active/stage > user managed permissions. > > thanks > thierry > > > > > > > _______________________________________________ > Freeipa-devel mailing list > Freeipa-devel at redhat.com > https://www.redhat.com/mailman/listinfo/freeipa-devel Modified patches with David inputs.. thanks for the reviews -------------- next part -------------- An HTML attachment was scrubbed... URL: -------------- next part -------------- A non-text attachment was scrubbed... Name: freeipa-tbordaz-0002-User-Life-Cycle-Exclude-tree-ipaUniqueID-generation.patch Type: text/x-patch Size: 2556 bytes Desc: not available URL: -------------- next part -------------- A non-text attachment was scrubbed... Name: freeipa-tbordaz-0003-3-User-life-cycle-stageuser-add-verb.patch Type: text/x-patch Size: 88905 bytes Desc: not available URL: From tbordaz at redhat.com Tue Feb 3 10:50:19 2015 From: tbordaz at redhat.com (thierry bordaz) Date: Tue, 03 Feb 2015 11:50:19 +0100 Subject: [Freeipa-devel] [PATCH] 0003-2 User life cycle: new stageuser plugin with add verb In-Reply-To: <54196346.5070500@redhat.com> References: <53E4D6AE.6050505@redhat.com> <54045399.3030404@redhat.com> <54196346.5070500@redhat.com> Message-ID: <54D0A7EB.1010700@redhat.com> On 09/17/2014 12:32 PM, thierry bordaz wrote: > On 09/01/2014 01:08 PM, Petr Viktorin wrote: >> On 08/08/2014 03:54 PM, thierry bordaz wrote: >>> Hi, >>> >>> The attached patch is related to 'User Life Cycle' >>> (https://fedorahosted.org/freeipa/ticket/3813) >>> >>> It creates a stageuser plugin with a first function stageuser-add. >>> Stage >>> user entries are provisioned under 'cn=staged >>> users,cn=accounts,cn=provisioning,SUFFIX'. >>> >>> Thanks >>> thierry >> >> Avoid `from ipalib.plugins.baseldap import *` in new code; instead >> import the module itself and use e.g. `baseldap.LDAPObject`. >> >> The stageuser help (docstring) is copied from the user plugin, and >> discusses things like account lockout and disabling users. It should >> rather explain what stageuser itself does. (And I don't very much >> like the Note about the interface being badly designed...) >> Also decide if the docs should call it "staged user" or "stage user" >> or "stageuser". >> >> A lot of the code is copied and pasted over from the users plugin. >> Don't do that. Either import things (e.g. validate_nsaccountlock) >> from the users plugin, or move the reused code into a shared module. >> >> For the `user` object, since so much is the same, it might be best to >> create a common base class for user and stageuser; and similarly for >> the Command plugins. >> >> The default permissions need different names, and you don't need >> another copy of the 'non_object' ones. Also, run the makeaci script. >> > Hello, > > This modified patch is mainly moving common base class into a new > plugin: accounts.py. user/stageuser plugin inherits from accounts. > It also creates a better description of what are stage user, how > to add a new stage user, updates ACI.txt and separate active/stage > user managed permissions. > > thanks > thierry > > > > > > > _______________________________________________ > Freeipa-devel mailing list > Freeipa-devel at redhat.com > https://www.redhat.com/mailman/listinfo/freeipa-devel Thanks David for the reviews. Here the last patches -------------- next part -------------- An HTML attachment was scrubbed... URL: -------------- next part -------------- A non-text attachment was scrubbed... Name: freeipa-tbordaz-0002-User-Life-Cycle-Exclude-tree-ipaUniqueID-generation.patch Type: text/x-patch Size: 2556 bytes Desc: not available URL: -------------- next part -------------- A non-text attachment was scrubbed... Name: freeipa-tbordaz-0003-3-User-life-cycle-stageuser-add-verb.patch Type: text/x-patch Size: 88905 bytes Desc: not available URL: From ssorce at redhat.com Tue Feb 3 17:15:36 2015 From: ssorce at redhat.com (Simo Sorce) Date: Tue, 3 Feb 2015 12:15:36 -0500 (EST) Subject: [Freeipa-devel] [PATCH] Fix for 4861 In-Reply-To: <384106912.6102018.1422983715956.JavaMail.zimbra@redhat.com> Message-ID: <704017459.6102057.1422983736634.JavaMail.zimbra@redhat.com> See subject :-) -- Simo Sorce * Red Hat, Inc. * New York -------------- next part -------------- A non-text attachment was scrubbed... Name: 0001-Handle-DAL-ABI-change-in-MIT-1.13.patch Type: text/x-patch Size: 2625 bytes Desc: not available URL: From abokovoy at redhat.com Wed Feb 4 09:15:49 2015 From: abokovoy at redhat.com (Alexander Bokovoy) Date: Wed, 4 Feb 2015 11:15:49 +0200 Subject: [Freeipa-devel] [PATCH] Fix for 4861 In-Reply-To: <704017459.6102057.1422983736634.JavaMail.zimbra@redhat.com> References: <384106912.6102018.1422983715956.JavaMail.zimbra@redhat.com> <704017459.6102057.1422983736634.JavaMail.zimbra@redhat.com> Message-ID: <20150204091549.GA9247@redhat.com> On Tue, 03 Feb 2015, Simo Sorce wrote: >See subject :-) > >-- >Simo Sorce * Red Hat, Inc. * New York >From 245b307a99722bd4ca61e799f1a2708b6689f773 Mon Sep 17 00:00:00 2001 >From: Simo Sorce >Date: Tue, 3 Feb 2015 12:06:24 -0500 >Subject: [PATCH] Handle DAL ABI change in MIT 1.13 > >In this new MIT version the DAL interface changes slightly but >KRB5_KDB_DAL_MAJOR_VERSION was not changed. > >Luckily KRB5_KDB_API_VERSION did change and that's enough to know >what to compile in. > >Resolves: https://fedorahosted.org/freeipa/ticket/4861 > >Signed-off-by: Simo Sorce >--- > daemons/ipa-kdb/ipa_kdb.h | 7 +++++++ > daemons/ipa-kdb/ipa_kdb_principals.c | 7 +++++++ > 2 files changed, 14 insertions(+) > >diff --git a/daemons/ipa-kdb/ipa_kdb.h b/daemons/ipa-kdb/ipa_kdb.h >index b92107bab5a259601160a402c54fa8ed440925b3..ba9968bce7cff87f9f4a7fcd056ff7a906ce9e82 100644 >--- a/daemons/ipa-kdb/ipa_kdb.h >+++ b/daemons/ipa-kdb/ipa_kdb.h >@@ -182,10 +182,17 @@ krb5_error_code ipadb_put_principal(krb5_context kcontext, > char **db_args); > krb5_error_code ipadb_delete_principal(krb5_context kcontext, > krb5_const_principal search_for); >+#if KRB5_KDB_API_VERSION < 8 > krb5_error_code ipadb_iterate(krb5_context kcontext, > char *match_entry, > int (*func)(krb5_pointer, krb5_db_entry *), > krb5_pointer func_arg); >+#else >+krb5_error_code ipadb_iterate(krb5_context kcontext, >+ char *match_entry, >+ int (*func)(krb5_pointer, krb5_db_entry *), >+ krb5_pointer func_arg, krb5_flags iterflags); >+#endif > > /* POLICY FUNCTIONS */ > >diff --git a/daemons/ipa-kdb/ipa_kdb_principals.c b/daemons/ipa-kdb/ipa_kdb_principals.c >index e158c236eab5c7c5a7c12664dbde5d51cc55406d..600c4ee41c74a2fc154a5372ad3e3b4e8b94a635 100644 >--- a/daemons/ipa-kdb/ipa_kdb_principals.c >+++ b/daemons/ipa-kdb/ipa_kdb_principals.c >@@ -2087,10 +2087,17 @@ done: > return kerr; > } > >+#if KRB5_KDB_API_VERSION < 8 > krb5_error_code ipadb_iterate(krb5_context kcontext, > char *match_entry, > int (*func)(krb5_pointer, krb5_db_entry *), > krb5_pointer func_arg) >+#else >+krb5_error_code ipadb_iterate(krb5_context kcontext, >+ char *match_entry, >+ int (*func)(krb5_pointer, krb5_db_entry *), >+ krb5_pointer func_arg, krb5_flags iterflags) >+#endif > { > struct ipadb_context *ipactx; > krb5_error_code kerr; ACK. I think we need this patch in both master and ipa-4-1. There is no functional change and I'd like to keep a code in plugins synchronized across branches if possible to avoid maintenance hurdles in future. -- / Alexander Bokovoy From dkupka at redhat.com Wed Feb 4 14:25:38 2015 From: dkupka at redhat.com (David Kupka) Date: Wed, 04 Feb 2015 15:25:38 +0100 Subject: [Freeipa-devel] [PATCH] 0003-2 User life cycle: new stageuser plugin with add verb In-Reply-To: <54D0A7EB.1010700@redhat.com> References: <53E4D6AE.6050505@redhat.com> <54045399.3030404@redhat.com> <54196346.5070500@redhat.com> <54D0A7EB.1010700@redhat.com> Message-ID: <54D22BE2.9050407@redhat.com> On 02/03/2015 11:50 AM, thierry bordaz wrote: > On 09/17/2014 12:32 PM, thierry bordaz wrote: >> On 09/01/2014 01:08 PM, Petr Viktorin wrote: >>> On 08/08/2014 03:54 PM, thierry bordaz wrote: >>>> Hi, >>>> >>>> The attached patch is related to 'User Life Cycle' >>>> (https://fedorahosted.org/freeipa/ticket/3813) >>>> >>>> It creates a stageuser plugin with a first function stageuser-add. >>>> Stage >>>> user entries are provisioned under 'cn=staged >>>> users,cn=accounts,cn=provisioning,SUFFIX'. >>>> >>>> Thanks >>>> thierry >>> >>> Avoid `from ipalib.plugins.baseldap import *` in new code; instead >>> import the module itself and use e.g. `baseldap.LDAPObject`. >>> >>> The stageuser help (docstring) is copied from the user plugin, and >>> discusses things like account lockout and disabling users. It should >>> rather explain what stageuser itself does. (And I don't very much >>> like the Note about the interface being badly designed...) >>> Also decide if the docs should call it "staged user" or "stage user" >>> or "stageuser". >>> >>> A lot of the code is copied and pasted over from the users plugin. >>> Don't do that. Either import things (e.g. validate_nsaccountlock) >>> from the users plugin, or move the reused code into a shared module. >>> >>> For the `user` object, since so much is the same, it might be best to >>> create a common base class for user and stageuser; and similarly for >>> the Command plugins. >>> >>> The default permissions need different names, and you don't need >>> another copy of the 'non_object' ones. Also, run the makeaci script. >>> >> Hello, >> >> This modified patch is mainly moving common base class into a new >> plugin: accounts.py. user/stageuser plugin inherits from accounts. >> It also creates a better description of what are stage user, how >> to add a new stage user, updates ACI.txt and separate active/stage >> user managed permissions. >> >> thanks >> thierry >> >> >> >> >> >> >> _______________________________________________ >> Freeipa-devel mailing list >> Freeipa-devel at redhat.com >> https://www.redhat.com/mailman/listinfo/freeipa-devel > > > Thanks David for the reviews. Here the last patches > > > > > _______________________________________________ > Freeipa-devel mailing list > Freeipa-devel at redhat.com > https://www.redhat.com/mailman/listinfo/freeipa-devel > The freeipa-tbordaz-0002 patch had trailing whitespaces on few lines so I'm attaching fixed version (and unchanged patch freeipa-tbordaz-0003-3 to keep them together). The ULC feature is still WIP but these patches look good to me and don't break anything as far as I tested. We should push them now to avoid further rebases. Thierry can then prepare other patches delivering the rest of ULC functionality. -- David Kupka -------------- next part -------------- A non-text attachment was scrubbed... Name: freeipa-tbordaz-0002-2-User-Life-Cycle-Exclude-tree-ipaUniqueID-generation.patch Type: text/x-patch Size: 2580 bytes Desc: not available URL: -------------- next part -------------- A non-text attachment was scrubbed... Name: freeipa-tbordaz-0003-3-User-life-cycle-stageuser-add-verb.patch Type: text/x-patch Size: 88905 bytes Desc: not available URL: From jcholast at redhat.com Wed Feb 4 16:14:31 2015 From: jcholast at redhat.com (Jan Cholasta) Date: Wed, 04 Feb 2015 17:14:31 +0100 Subject: [Freeipa-devel] [PATCH] 0003-2 User life cycle: new stageuser plugin with add verb In-Reply-To: <54D22BE2.9050407@redhat.com> References: <53E4D6AE.6050505@redhat.com> <54045399.3030404@redhat.com> <54196346.5070500@redhat.com> <54D0A7EB.1010700@redhat.com> <54D22BE2.9050407@redhat.com> Message-ID: <54D24567.4010103@redhat.com> Hi, Dne 4.2.2015 v 15:25 David Kupka napsal(a): > On 02/03/2015 11:50 AM, thierry bordaz wrote: >> On 09/17/2014 12:32 PM, thierry bordaz wrote: >>> On 09/01/2014 01:08 PM, Petr Viktorin wrote: >>>> On 08/08/2014 03:54 PM, thierry bordaz wrote: >>>>> Hi, >>>>> >>>>> The attached patch is related to 'User Life Cycle' >>>>> (https://fedorahosted.org/freeipa/ticket/3813) >>>>> >>>>> It creates a stageuser plugin with a first function stageuser-add. >>>>> Stage >>>>> user entries are provisioned under 'cn=staged >>>>> users,cn=accounts,cn=provisioning,SUFFIX'. >>>>> >>>>> Thanks >>>>> thierry >>>> >>>> Avoid `from ipalib.plugins.baseldap import *` in new code; instead >>>> import the module itself and use e.g. `baseldap.LDAPObject`. >>>> >>>> The stageuser help (docstring) is copied from the user plugin, and >>>> discusses things like account lockout and disabling users. It should >>>> rather explain what stageuser itself does. (And I don't very much >>>> like the Note about the interface being badly designed...) >>>> Also decide if the docs should call it "staged user" or "stage user" >>>> or "stageuser". >>>> >>>> A lot of the code is copied and pasted over from the users plugin. >>>> Don't do that. Either import things (e.g. validate_nsaccountlock) >>>> from the users plugin, or move the reused code into a shared module. >>>> >>>> For the `user` object, since so much is the same, it might be best to >>>> create a common base class for user and stageuser; and similarly for >>>> the Command plugins. >>>> >>>> The default permissions need different names, and you don't need >>>> another copy of the 'non_object' ones. Also, run the makeaci script. >>>> >>> Hello, >>> >>> This modified patch is mainly moving common base class into a new >>> plugin: accounts.py. user/stageuser plugin inherits from accounts. >>> It also creates a better description of what are stage user, how >>> to add a new stage user, updates ACI.txt and separate active/stage >>> user managed permissions. >>> >>> thanks >>> thierry >>> >>> >>> >>> >>> >>> >>> _______________________________________________ >>> Freeipa-devel mailing list >>> Freeipa-devel at redhat.com >>> https://www.redhat.com/mailman/listinfo/freeipa-devel >> >> >> Thanks David for the reviews. Here the last patches >> >> >> >> >> _______________________________________________ >> Freeipa-devel mailing list >> Freeipa-devel at redhat.com >> https://www.redhat.com/mailman/listinfo/freeipa-devel >> > > The freeipa-tbordaz-0002 patch had trailing whitespaces on few lines so > I'm attaching fixed version (and unchanged patch freeipa-tbordaz-0003-3 > to keep them together). > > The ULC feature is still WIP but these patches look good to me and don't > break anything as far as I tested. > We should push them now to avoid further rebases. Thierry can then > prepare other patches delivering the rest of ULC functionality. Few comments from just reading the patches: 1) I would name the base class "baseuser", "account" does not necessarily mean user account. 2) This is very wrong: -class user_add(LDAPCreate): +class user_add(user, LDAPCreate): You are creating a plugin which is both an object and an command. 3) This is purely subjective, but I don't like the name "deleteuser", as it has a verb in it. We usually don't do that and IMHO we shouldn't do that. Honza -- Jan Cholasta From mbasti at redhat.com Thu Feb 5 11:29:19 2015 From: mbasti at redhat.com (Martin Basti) Date: Thu, 05 Feb 2015 12:29:19 +0100 Subject: [Freeipa-devel] [PATCH 0177] Fix add version warning only on server side In-Reply-To: <54AE4337.2040804@redhat.com> References: <5487102F.10806@redhat.com> <54896FBF.7010204@redhat.com> <54897C72.90401@redhat.com> <549017D5.30909@redhat.com> <54901EDA.3060601@redhat.com> <54901FB7.40900@redhat.com> <54AE4337.2040804@redhat.com> Message-ID: <54D3540F.9040505@redhat.com> On 08/01/15 09:43, Martin Basti wrote: > On 16/12/14 13:04, Martin Basti wrote: >> On 16/12/14 13:00, Martin Kosek wrote: >>> On 12/16/2014 12:30 PM, Martin Basti wrote: >>>> On 11/12/14 12:13, Martin Basti wrote: >>>>> On 11/12/14 11:19, Jan Cholasta wrote: >>>>>> Hi, >>>>>> >>>>>> Dne 9.12.2014 v 16:07 Martin Basti napsal(a): >>>>>>> Ticket: https://fedorahosted.org/freeipa/ticket/4793 >>>>>>> >>>>>>> I'm able to reproduce it only in one nose test. >>>>>> Which test? >>>>> If you apply my patch 170 and add a random forwardzone, then DNS >>>>> root zone >>>>> tests failed. >>>>>>> Patch attached. >>>>>> What about: >>>>>> >>>>>> result['messages'] = result.get('messages', ()) + >>>>>> (message.to_dict(),) >>>>>> >>>>>> (My point is, don't support both lists and tuples, pick just one.) >>>>>> >>>>>> Honza >>>>>> >>>>> This is question for framework guru (you?), I tried to preserve >>>>> format >>>>> unchanged. >>>>> Shouldn't be all values in lists in server part? >>>>> >>>>> Martin^2 >>>>> >>>> As was requested, I convert tuple to list instead handling both types. >>>> >>>> Updated patch attached. >>> I assume you do not want to track the .idea/ files in FreeIPA git :-) >>> >> Oh, thanks. My IDE was too smart again and add those files there itself. >> >> updated patch attached >> > Please review this patch. > Modified patch attached. Message should be added only on server side -- Martin Basti -------------- next part -------------- A non-text attachment was scrubbed... Name: freeipa-mbasti-0177.4-Fix-warning-message-on-client-side.patch Type: text/x-patch Size: 1115 bytes Desc: not available URL: From pvoborni at redhat.com Thu Feb 5 11:59:47 2015 From: pvoborni at redhat.com (Petr Vobornik) Date: Thu, 05 Feb 2015 12:59:47 +0100 Subject: [Freeipa-devel] [PATCH] 491 Replication Administrators cannot remove replication In-Reply-To: <54BD3C88.1040504@redhat.com> References: <54BCEE66.8000804@redhat.com> <54BD3C88.1040504@redhat.com> Message-ID: <54D35B33.50106@redhat.com> On 01/19/2015 06:19 PM, Martin Basti wrote: > On 19/01/15 12:45, Martin Kosek wrote: >> Replication agreement deletion requires read access to DNA range >> setting. The read access was accidently removed during PermissionV2 >> refactoring. >> >> Add the read ACI back as a special SYSTEM permission. >> >> https://fedorahosted.org/freeipa/ticket/4848 >> >> > Works for me. ACK > Was pushed by mkosek (2015-01-20): master: 251c97cf96edccaec5ce034007068609ad69227f ipa-4-1: 338831aea3cdf04a27f5ea9159f84f9ce933e0c1 -- Petr Vobornik From pvoborni at redhat.com Thu Feb 5 12:05:49 2015 From: pvoborni at redhat.com (Petr Vobornik) Date: Thu, 05 Feb 2015 13:05:49 +0100 Subject: [Freeipa-devel] [PATCH] 488-489 PermissionsV2 related winsync fixes In-Reply-To: <54BCF6A0.9030809@redhat.com> References: <54B552DB.7090309@redhat.com> <20150113155529.60f19e65@willson.usersys.redhat.com> <54B59385.2070206@redhat.com> <54B633A5.2020904@redhat.com> <54B63DB3.9070107@redhat.com> <54B64CE9.7030204@redhat.com> <54B66412.3050607@redhat.com> <20150114093452.14b0c665@willson.usersys.redhat.com> <54B6841E.4040306@redhat.com> <54B69DB7.7030901@redhat.com> <20150114115838.747ff066@willson.usersys.redhat.com> <54B6DF2D.8020907@redhat.com> <54BCF6A0.9030809@redhat.com> Message-ID: <54D35C9D.4070107@redhat.com> On 01/19/2015 01:20 PM, David Kupka wrote: > > Hi! > This works for me. If all concerns regarding PermissionV2 and ACIs in > general are resolved we can push. > Both patches were pushed by mkosek on 2015-01-19: master: 6652c4eb2ebece71b6d60001246bd0fee5909099 Allow PassSync user to locate and update NT users ipa-4-1: 282d1ec2f9346c4a38b9867cff2ecf9151c0a794 Allow PassSync user to locate and update NT users master: 1537ac8138bf4371ae38147e8979904c756b3800 Allow Replication Administrators manipulate Winsync Agreements ipa-4-1: 794c9e6c31c4db2ae5c5869dbf782fab84eafd9f Allow Replication Administrators manipulate Winsync Agreements -- Petr Vobornik From jcholast at redhat.com Mon Feb 9 10:37:38 2015 From: jcholast at redhat.com (Jan Cholasta) Date: Mon, 09 Feb 2015 11:37:38 +0100 Subject: [Freeipa-devel] [PATCH] 398 Bump 389-ds-base and pki-ca dependencies for POODLE fixes Message-ID: <54D88DF2.5040809@redhat.com> Hi, the attached patch fixes . Honza -- Jan Cholasta -------------- next part -------------- A non-text attachment was scrubbed... Name: freeipa-jcholast-398-Bump-389-ds-base-and-pki-ca-dependencies-for-POODLE-.patch Type: text/x-patch Size: 1967 bytes Desc: not available URL: From mkosek at redhat.com Mon Feb 9 12:08:36 2015 From: mkosek at redhat.com (Martin Kosek) Date: Mon, 09 Feb 2015 13:08:36 +0100 Subject: [Freeipa-devel] [PATCH] 398 Bump 389-ds-base and pki-ca dependencies for POODLE fixes In-Reply-To: <54D88DF2.5040809@redhat.com> References: <54D88DF2.5040809@redhat.com> Message-ID: <54D8A344.8090909@redhat.com> On 02/09/2015 11:37 AM, Jan Cholasta wrote: > Hi, > > the attached patch fixes . > > Honza Thanks, I was looking for having this one completed. I just fired the builds for the updated deps in mkosek/freeipa copr. Other than that, ACK from me (I did not push, there were slight conflicts). Martin From pviktori at redhat.com Mon Feb 9 15:44:47 2015 From: pviktori at redhat.com (Petr Viktorin) Date: Mon, 09 Feb 2015 16:44:47 +0100 Subject: [Freeipa-devel] Pytest plugins are now in Fedora Message-ID: <54D8D5EF.6050606@redhat.com> Hello, The Pytest plugins needed for FreeIPA's test suite just hit Fedora 21 updates repo, and should be available from mirrors shortly. They already are in Rawhide. So, you can remove them from IPA's COPR if you wish. For more info on these plugins, see their project pages: https://fedorahosted.org/python-pytest-multihost/ https://fedorahosted.org/python-pytest-sourceorder/ Note that there is one more plugin waiting for Fedora review: python-pytest-beakerlib. This one isn't required by FreeIPA RPMs, but it's necessary if you want to run the test suite with Beakerlib. https://bugzilla.redhat.com/show_bug.cgi?id=1179350 -- Petr Viktorin From redhatrises at gmail.com Tue Feb 10 03:51:04 2015 From: redhatrises at gmail.com (Gabe Alford) Date: Mon, 9 Feb 2015 20:51:04 -0700 Subject: [Freeipa-devel] [PATCH 0042] Typos in ipa-rmkeytab Message-ID: Hello, Fix for https://fedorahosted.org/freeipa/ticket/4890 Thanks, Gabe -------------- next part -------------- An HTML attachment was scrubbed... URL: -------------- next part -------------- A non-text attachment was scrubbed... Name: freeipa-rga-0042-Typos-in-ipa-rmkeytab-options-help-and-man-page.patch Type: text/x-patch Size: 2009 bytes Desc: not available URL: From mkosek at redhat.com Tue Feb 10 07:32:25 2015 From: mkosek at redhat.com (Martin Kosek) Date: Tue, 10 Feb 2015 08:32:25 +0100 Subject: [Freeipa-devel] [PATCH 0042] Typos in ipa-rmkeytab In-Reply-To: References: Message-ID: <54D9B409.1000602@redhat.com> On 02/10/2015 04:51 AM, Gabe Alford wrote: > Hello, > > Fix for https://fedorahosted.org/freeipa/ticket/4890 > > Thanks, > > Gabe Thanks Gabe! This is a clear ACK. Pushed to: master: 959b0efa386e60d9b72c1c8852ba95349f1e0d2c ipa-4-1: d251e5219ef829ec6c559ffef9501ada882a5945 Martin From jcholast at redhat.com Tue Feb 10 09:11:03 2015 From: jcholast at redhat.com (Jan Cholasta) Date: Tue, 10 Feb 2015 10:11:03 +0100 Subject: [Freeipa-devel] [PATCH 0182] Fix pkcs11 python extension reference counting In-Reply-To: <54B3B9E7.501@redhat.com> References: <54B3B9E7.501@redhat.com> Message-ID: <54D9CB27.5060604@redhat.com> Hi, Dne 12.1.2015 v 13:11 Martin Basti napsal(a): > Part of DNSSEC (https://fedorahosted.org/freeipa/ticket/4657) > > Patch attached. 1) In P11_Helper_set_attribute, the return value can be only Py_None or NULL, so why don't you use Py_XINCREF instead of the if/Py_INCREF? 2) In P11_Helper_find_keys, you sometimes return without decreasing reference count on result_list or any items it may contain. Honza -- Jan Cholasta From mbasti at redhat.com Tue Feb 10 09:38:10 2015 From: mbasti at redhat.com (Martin Basti) Date: Tue, 10 Feb 2015 10:38:10 +0100 Subject: [Freeipa-devel] [PATCH 0013] ipa-client-install: put eol character after the last line of altered config file(s) In-Reply-To: <54CB8FC1.4000003@redhat.com> References: <54CB8FC1.4000003@redhat.com> Message-ID: <54D9D182.7060000@redhat.com> On 30/01/15 15:05, Martin Babinsky wrote: > https://fedorahosted.org/freeipa/ticket/4864 > > > > _______________________________________________ > Freeipa-devel mailing list > Freeipa-devel at redhat.com > https://www.redhat.com/mailman/listinfo/freeipa-devel Thanks, works for me, ACK -- Martin Basti -------------- next part -------------- An HTML attachment was scrubbed... URL: From mkosek at redhat.com Tue Feb 10 11:55:37 2015 From: mkosek at redhat.com (Martin Kosek) Date: Tue, 10 Feb 2015 12:55:37 +0100 Subject: [Freeipa-devel] [PATCH 0013] ipa-client-install: put eol character after the last line of altered config file(s) In-Reply-To: <54D9D182.7060000@redhat.com> References: <54CB8FC1.4000003@redhat.com> <54D9D182.7060000@redhat.com> Message-ID: <54D9F1B9.1000003@redhat.com> On 02/10/2015 10:38 AM, Martin Basti wrote: > On 30/01/15 15:05, Martin Babinsky wrote: >> https://fedorahosted.org/freeipa/ticket/4864 >> >> >> >> _______________________________________________ >> Freeipa-devel mailing list >> Freeipa-devel at redhat.com >> https://www.redhat.com/mailman/listinfo/freeipa-devel > Thanks, works for me, ACK Pushed to: master: a4481023474772a073553bf2395801cdd08c1088 ipa-4-1: 919f0db93f46b891030d26e76ee6e90f1c6f07be From mbasti at redhat.com Tue Feb 10 13:56:25 2015 From: mbasti at redhat.com (Martin Basti) Date: Tue, 10 Feb 2015 14:56:25 +0100 Subject: [Freeipa-devel] [PATCH 0182] Fix pkcs11 python extension reference counting In-Reply-To: <54D9CB27.5060604@redhat.com> References: <54B3B9E7.501@redhat.com> <54D9CB27.5060604@redhat.com> Message-ID: <54DA0E09.50700@redhat.com> On 10/02/15 10:11, Jan Cholasta wrote: > Hi, > > Dne 12.1.2015 v 13:11 Martin Basti napsal(a): >> Part of DNSSEC (https://fedorahosted.org/freeipa/ticket/4657) >> >> Patch attached. > > 1) In P11_Helper_set_attribute, the return value can be only Py_None > or NULL, so why don't you use Py_XINCREF instead of the if/Py_INCREF? > > 2) In P11_Helper_find_keys, you sometimes return without decreasing > reference count on result_list or any items it may contain. > > Honza > Thank you, updated patch attached. -- Martin Basti -------------- next part -------------- A non-text attachment was scrubbed... Name: freeipa-mbasti-0182.2-Fix-reference-counting-in-pkcs11-extension.patch Type: text/x-patch Size: 7205 bytes Desc: not available URL: From jcholast at redhat.com Tue Feb 10 15:30:54 2015 From: jcholast at redhat.com (Jan Cholasta) Date: Tue, 10 Feb 2015 16:30:54 +0100 Subject: [Freeipa-devel] [PATCH 0182] Fix pkcs11 python extension reference counting In-Reply-To: <54DA0E09.50700@redhat.com> References: <54B3B9E7.501@redhat.com> <54D9CB27.5060604@redhat.com> <54DA0E09.50700@redhat.com> Message-ID: <54DA242E.1090200@redhat.com> Dne 10.2.2015 v 14:56 Martin Basti napsal(a): > On 10/02/15 10:11, Jan Cholasta wrote: >> Hi, >> >> Dne 12.1.2015 v 13:11 Martin Basti napsal(a): >>> Part of DNSSEC (https://fedorahosted.org/freeipa/ticket/4657) >>> >>> Patch attached. >> >> 1) In P11_Helper_set_attribute, the return value can be only Py_None >> or NULL, so why don't you use Py_XINCREF instead of the if/Py_INCREF? >> >> 2) In P11_Helper_find_keys, you sometimes return without decreasing >> reference count on result_list or any items it may contain. >> >> Honza >> > Thank you, > > updated patch attached. > Thanks, ACK. Pushed to: master: dd607a35bcdad125c098f0e80c8b25efbe935acc ipa-4-1: c198b5ab6537f26936554a9e2e5bfcbe3f371320 -- Jan Cholasta From jcholast at redhat.com Tue Feb 10 15:38:04 2015 From: jcholast at redhat.com (Jan Cholasta) Date: Tue, 10 Feb 2015 16:38:04 +0100 Subject: [Freeipa-devel] [PATCH] 398 Bump 389-ds-base and pki-ca dependencies for POODLE fixes In-Reply-To: <54D8A344.8090909@redhat.com> References: <54D88DF2.5040809@redhat.com> <54D8A344.8090909@redhat.com> Message-ID: <54DA25DC.5060909@redhat.com> Dne 9.2.2015 v 13:08 Martin Kosek napsal(a): > On 02/09/2015 11:37 AM, Jan Cholasta wrote: >> Hi, >> >> the attached patch fixes . >> >> Honza > > Thanks, I was looking for having this one completed. I just fired the builds > for the updated deps in mkosek/freeipa copr. > > Other than that, ACK from me (I did not push, there were slight conflicts). > > Martin > Thanks for the review. Pushed to: master: c438d9be9152d64408e8e39ba4ebe696d0d4fe94 ipa-4-1: caf70a11b28edbe1e0ba1e7aac89b34e1cff6edb -- Jan Cholasta From mbasti at redhat.com Tue Feb 10 18:42:58 2015 From: mbasti at redhat.com (Martin Basti) Date: Tue, 10 Feb 2015 19:42:58 +0100 Subject: [Freeipa-devel] [PATCH 0041] permission-add does not prompt for ipapermright option in interactive mode In-Reply-To: References: Message-ID: <54DA5132.7050003@redhat.com> On 29/01/15 17:10, Gabe Alford wrote: > Hello, > > Fix for https://fedorahosted.org/freeipa/ticket/4872 > > Thanks, > > Gabe > > > _______________________________________________ > Freeipa-devel mailing list > Freeipa-devel at redhat.com > https://www.redhat.com/mailman/listinfo/freeipa-devel Thank you for your patch. IMO, would be better to use flag, alwaysask for ipapermright, instead of creating new callback: StrEnum( 'ipapermright*', cli_name='right', deprecated_cli_aliases={'permissions'}, label=_('Granted rights'), doc=_('Rights to grant ' '(read, search, compare, write, add, delete, all)'), values=(u'read', u'search', u'compare', u'write', u'add', u'delete', u'all'), + alwaysask=True, ), This change requires to generate new API.txt please run ./makeapi and increment API version in VERSION file. Thank you in advance :-) Martin^2 -- Martin Basti -------------- next part -------------- An HTML attachment was scrubbed... URL: From mbasti at redhat.com Wed Feb 11 13:10:54 2015 From: mbasti at redhat.com (Martin Basti) Date: Wed, 11 Feb 2015 14:10:54 +0100 Subject: [Freeipa-devel] [PATCH 0190] DNSSEC: add support for CKM_RSA_PKCS_OAEP mechanism Message-ID: <54DB54DE.4030606@redhat.com> https://fedorahosted.org/freeipa/ticket/4657#comment:13 Patch attached. -- Martin Basti -------------- next part -------------- A non-text attachment was scrubbed... Name: freeipa-mbasti-0190-DNSSEC-add-support-for-CKM_RSA_PKCS_OAEP-mechanism.patch Type: text/x-patch Size: 4184 bytes Desc: not available URL: From redhatrises at gmail.com Wed Feb 11 14:06:07 2015 From: redhatrises at gmail.com (Gabe Alford) Date: Wed, 11 Feb 2015 07:06:07 -0700 Subject: [Freeipa-devel] [PATCH 0041] permission-add does not prompt for ipapermright option in interactive mode In-Reply-To: <54DA5132.7050003@redhat.com> References: <54DA5132.7050003@redhat.com> Message-ID: Good point. I personally was not aware of all that the API can do. Thanks Martin^2! Updated patch attached. On Tue, Feb 10, 2015 at 11:42 AM, Martin Basti wrote: > On 29/01/15 17:10, Gabe Alford wrote: > > Hello, > > Fix for https://fedorahosted.org/freeipa/ticket/4872 > > Thanks, > > Gabe > > > _______________________________________________ > Freeipa-devel mailing listFreeipa-devel at redhat.comhttps://www.redhat.com/mailman/listinfo/freeipa-devel > > Thank you for your patch. > > IMO, would be better to use flag, alwaysask for ipapermright, instead of > creating new callback: > > StrEnum( > 'ipapermright*', > cli_name='right', > deprecated_cli_aliases={'permissions'}, > label=_('Granted rights'), > doc=_('Rights to grant ' > '(read, search, compare, write, add, delete, all)'), > values=(u'read', u'search', u'compare', > u'write', u'add', u'delete', u'all'), > + alwaysask=True, > ), > > This change requires to generate new API.txt > > please run ./makeapi and increment API version in VERSION file. > > Thank you in advance :-) > > Martin^2 > > -- > Martin Basti > > -------------- next part -------------- An HTML attachment was scrubbed... URL: -------------- next part -------------- A non-text attachment was scrubbed... Name: freeipa-rga-0041-2-permission-add-does-not-prompt-for-ipapermright-in-i.patch Type: application/octet-stream Size: 5895 bytes Desc: not available URL: From mbasti at redhat.com Wed Feb 11 15:59:26 2015 From: mbasti at redhat.com (Martin Basti) Date: Wed, 11 Feb 2015 16:59:26 +0100 Subject: [Freeipa-devel] [PATCH 0041] permission-add does not prompt for ipapermright option in interactive mode In-Reply-To: References: <54DA5132.7050003@redhat.com> Message-ID: <54DB7C5E.1040807@redhat.com> Sorry, alwaysask didnt work. It was asking for rights during permission-mod. I replaced alwaysask with flag "ask_create". Sorry for late catch. Updated patch attached. PS: your name+email is missing in commit message, is it on purpose? And time wasn't correct in previous patch. On 11/02/15 15:06, Gabe Alford wrote: > Good point. I personally was not aware of all that the API can do. > Thanks Martin^2! Updated patch attached. > > On Tue, Feb 10, 2015 at 11:42 AM, Martin Basti > wrote: > > On 29/01/15 17:10, Gabe Alford wrote: >> Hello, >> >> Fix for https://fedorahosted.org/freeipa/ticket/4872 >> >> Thanks, >> >> Gabe >> >> >> _______________________________________________ >> Freeipa-devel mailing list >> Freeipa-devel at redhat.com >> https://www.redhat.com/mailman/listinfo/freeipa-devel > Thank you for your patch. > > IMO, would be better to use flag, alwaysask for ipapermright, > instead of creating new callback: > > StrEnum( > 'ipapermright*', > cli_name='right', > deprecated_cli_aliases={'permissions'}, > label=_('Granted rights'), > doc=_('Rights to grant ' > '(read, search, compare, write, add, delete, > all)'), > values=(u'read', u'search', u'compare', > u'write', u'add', u'delete', u'all'), > + alwaysask=True, > ), > > This change requires to generate new API.txt > > please run ./makeapi and increment API version in VERSION file. > > Thank you in advance :-) > > Martin^2 > > -- > Martin Basti > > -- Martin Basti -------------- next part -------------- An HTML attachment was scrubbed... URL: -------------- next part -------------- A non-text attachment was scrubbed... Name: freeipa-rga+mbasti-0041.3-permission-add-does-not-prompt-for-ipapermright-in-i.patch Type: text/x-patch Size: 3182 bytes Desc: not available URL: From mbasti at redhat.com Wed Feb 11 16:13:58 2015 From: mbasti at redhat.com (Martin Basti) Date: Wed, 11 Feb 2015 17:13:58 +0100 Subject: [Freeipa-devel] [PATCHES 0191-0194] Fix restoring states of services after uninstalling Message-ID: <54DB7FC6.1040507@redhat.com> https://fedorahosted.org/freeipa/ticket/4869 Fixes: - enable/start a service after uninstallation if the service was enabled/running before in correct way - store status of service before disable/stop/start/enable operation - run uninstall only for configured services Uninstall for ipa-dnskeysyncd is not executed because of https://fedorahosted.org/freeipa/ticket/4901 Patches attached. -- Martin Basti -------------- next part -------------- A non-text attachment was scrubbed... Name: freeipa-mbasti-0191-Fix-restoring-services-status-during-uninstall.patch Type: text/x-patch Size: 11817 bytes Desc: not available URL: -------------- next part -------------- A non-text attachment was scrubbed... Name: freeipa-mbasti-0192-Fix-do-not-enable-service-before-storing-status.patch Type: text/x-patch Size: 847 bytes Desc: not available URL: -------------- next part -------------- A non-text attachment was scrubbed... Name: freeipa-mbasti-0193-Uninstall-configured-services-only.patch Type: text/x-patch Size: 2667 bytes Desc: not available URL: -------------- next part -------------- A non-text attachment was scrubbed... Name: freeipa-mbasti-0194-Fix-saving-named-restore-status.patch Type: text/x-patch Size: 1655 bytes Desc: not available URL: From redhatrises at gmail.com Wed Feb 11 16:28:41 2015 From: redhatrises at gmail.com (Gabe Alford) Date: Wed, 11 Feb 2015 09:28:41 -0700 Subject: [Freeipa-devel] [PATCH 0041] permission-add does not prompt for ipapermright option in interactive mode In-Reply-To: <54DB7C5E.1040807@redhat.com> References: <54DA5132.7050003@redhat.com> <54DB7C5E.1040807@redhat.com> Message-ID: Oops. My mistake. Corrected patch attached. On Wed, Feb 11, 2015 at 8:59 AM, Martin Basti wrote: > Sorry, alwaysask didnt work. > It was asking for rights during permission-mod. > > I replaced alwaysask with flag "ask_create". > Sorry for late catch. > > Updated patch attached. > > PS: your name+email is missing in commit message, is it on purpose? And > time wasn't correct in previous patch. > > > > On 11/02/15 15:06, Gabe Alford wrote: > > Good point. I personally was not aware of all that the API can do. Thanks > Martin^2! Updated patch attached. > > On Tue, Feb 10, 2015 at 11:42 AM, Martin Basti wrote: > >> On 29/01/15 17:10, Gabe Alford wrote: >> >> Hello, >> >> Fix for https://fedorahosted.org/freeipa/ticket/4872 >> >> Thanks, >> >> Gabe >> >> >> _______________________________________________ >> Freeipa-devel mailing listFreeipa-devel at redhat.comhttps://www.redhat.com/mailman/listinfo/freeipa-devel >> >> Thank you for your patch. >> >> IMO, would be better to use flag, alwaysask for ipapermright, instead of >> creating new callback: >> >> StrEnum( >> 'ipapermright*', >> cli_name='right', >> deprecated_cli_aliases={'permissions'}, >> label=_('Granted rights'), >> doc=_('Rights to grant ' >> '(read, search, compare, write, add, delete, all)'), >> values=(u'read', u'search', u'compare', >> u'write', u'add', u'delete', u'all'), >> + alwaysask=True, >> ), >> >> This change requires to generate new API.txt >> >> please run ./makeapi and increment API version in VERSION file. >> >> Thank you in advance :-) >> >> Martin^2 >> >> -- >> Martin Basti >> >> > > > -- > Martin Basti > > -------------- next part -------------- An HTML attachment was scrubbed... URL: -------------- next part -------------- A non-text attachment was scrubbed... Name: freeipa-rga-0041-3-permission-add-does-not-prompt-for-ipapermright-in-i.patch Type: application/octet-stream Size: 3185 bytes Desc: not available URL: From pvoborni at redhat.com Thu Feb 12 09:32:23 2015 From: pvoborni at redhat.com (Petr Vobornik) Date: Thu, 12 Feb 2015 10:32:23 +0100 Subject: [Freeipa-devel] [PATCH 0080] Expose the disabled User Auth Type In-Reply-To: <54AC17E9.3080803@redhat.com> References: <1415898295.9116.12.camel@redhat.com> <547F37D3.6040701@redhat.com> <1417717539.3111.6.camel@redhat.com> <5480AE4D.2000800@redhat.com> <1418928747.5774.24.camel@redhat.com> <54AC17E9.3080803@redhat.com> Message-ID: <54DC7327.4010305@redhat.com> On 01/06/2015 06:14 PM, Petr Vobornik wrote: >> >> Alright, I was able to reproduce this problem via a bisect. I think you >> hit a bug that was introduced in >> 953c6846b7cb8d75253538ab92a1360fceee0c3c and fixed by >> 9baa93da1cbf56c2a6f7e82e099bc3ff3f19e2e4. Those patches existed in my >> local branch as one patchset, but was merged in two sections. >> Unfortunately, though I had discovered and fixed the bug, the fix went >> in the wrong patch in the series. So you just happened to hit the narrow >> window where the bug existed in master (but not my local tree). On >> current master, everything works. >> >> I also tested on 4.1.2. A similar bug exists there on the old >> ipa-pwd-extop code. So if we want to land this patch on 4.1.x, we will >> need a fix for that code to avoid creating a security hole. >> >> Attached is a rebased patch. It has no changes except the VERSION >> update. >> >> Nathaniel >> > > tested on both master and ipa-4-1. I no longer see the issue. What is > the issue which exists on 4-1 branch? The fix [1] which should have > fixed master was also pushed to 4-1... > > [1] https://fedorahosted.org/freeipa/ticket/4511#comment:11 Was agreed with Nathaniel and mkosek to include it only in master branch. ACK Pushed to master: 9549a5984b5b1d7106035d8126a3ead915b2129b -- Petr Vobornik From dkupka at redhat.com Thu Feb 12 09:45:09 2015 From: dkupka at redhat.com (David Kupka) Date: Thu, 12 Feb 2015 10:45:09 +0100 Subject: [Freeipa-devel] [PATCH 0177] Fix add version warning only on server side In-Reply-To: <54D3540F.9040505@redhat.com> References: <5487102F.10806@redhat.com> <54896FBF.7010204@redhat.com> <54897C72.90401@redhat.com> <549017D5.30909@redhat.com> <54901EDA.3060601@redhat.com> <54901FB7.40900@redhat.com> <54AE4337.2040804@redhat.com> <54D3540F.9040505@redhat.com> Message-ID: <54DC7625.2020000@redhat.com> On 02/05/2015 12:29 PM, Martin Basti wrote: > On 08/01/15 09:43, Martin Basti wrote: >> On 16/12/14 13:04, Martin Basti wrote: >>> On 16/12/14 13:00, Martin Kosek wrote: >>>> On 12/16/2014 12:30 PM, Martin Basti wrote: >>>>> On 11/12/14 12:13, Martin Basti wrote: >>>>>> On 11/12/14 11:19, Jan Cholasta wrote: >>>>>>> Hi, >>>>>>> >>>>>>> Dne 9.12.2014 v 16:07 Martin Basti napsal(a): >>>>>>>> Ticket: https://fedorahosted.org/freeipa/ticket/4793 >>>>>>>> >>>>>>>> I'm able to reproduce it only in one nose test. >>>>>>> Which test? >>>>>> If you apply my patch 170 and add a random forwardzone, then DNS >>>>>> root zone >>>>>> tests failed. >>>>>>>> Patch attached. >>>>>>> What about: >>>>>>> >>>>>>> result['messages'] = result.get('messages', ()) + >>>>>>> (message.to_dict(),) >>>>>>> >>>>>>> (My point is, don't support both lists and tuples, pick just one.) >>>>>>> >>>>>>> Honza >>>>>>> >>>>>> This is question for framework guru (you?), I tried to preserve >>>>>> format >>>>>> unchanged. >>>>>> Shouldn't be all values in lists in server part? >>>>>> >>>>>> Martin^2 >>>>>> >>>>> As was requested, I convert tuple to list instead handling both types. >>>>> >>>>> Updated patch attached. >>>> I assume you do not want to track the .idea/ files in FreeIPA git :-) >>>> >>> Oh, thanks. My IDE was too smart again and add those files there itself. >>> >>> updated patch attached >>> >> Please review this patch. >> > Modified patch attached. > Message should be added only on server side > > > > _______________________________________________ > Freeipa-devel mailing list > Freeipa-devel at redhat.com > https://www.redhat.com/mailman/listinfo/freeipa-devel > Thanks for the patch. I'm unable to reproduce the original bug but after offline discussion I understand that this modification should prevent it. AFAIK it doesn't break anything, ACK. -- David Kupka From mbasti at redhat.com Thu Feb 12 11:08:25 2015 From: mbasti at redhat.com (Martin Basti) Date: Thu, 12 Feb 2015 12:08:25 +0100 Subject: [Freeipa-devel] [PATCH 0041] permission-add does not prompt for ipapermright option in interactive mode In-Reply-To: References: <54DA5132.7050003@redhat.com> <54DB7C5E.1040807@redhat.com> Message-ID: <54DC89A9.2030701@redhat.com> On 11/02/15 17:28, Gabe Alford wrote: > Oops. My mistake. Corrected patch attached. > > On Wed, Feb 11, 2015 at 8:59 AM, Martin Basti > wrote: > > Sorry, alwaysask didnt work. > It was asking for rights during permission-mod. > > I replaced alwaysask with flag "ask_create". > Sorry for late catch. > > Updated patch attached. > > PS: your name+email is missing in commit message, is it on > purpose? And time wasn't correct in previous patch. > > > > On 11/02/15 15:06, Gabe Alford wrote: >> Good point. I personally was not aware of all that the API can >> do. Thanks Martin^2! Updated patch attached. >> >> On Tue, Feb 10, 2015 at 11:42 AM, Martin Basti > > wrote: >> >> On 29/01/15 17:10, Gabe Alford wrote: >>> Hello, >>> >>> Fix for https://fedorahosted.org/freeipa/ticket/4872 >>> >>> Thanks, >>> >>> Gabe >>> >>> >>> _______________________________________________ >>> Freeipa-devel mailing list >>> Freeipa-devel at redhat.com >>> https://www.redhat.com/mailman/listinfo/freeipa-devel >> Thank you for your patch. >> >> IMO, would be better to use flag, alwaysask for ipapermright, >> instead of creating new callback: >> >> StrEnum( >> 'ipapermright*', >> cli_name='right', >> deprecated_cli_aliases={'permissions'}, >> label=_('Granted rights'), >> doc=_('Rights to grant ' >> '(read, search, compare, write, add, >> delete, all)'), >> values=(u'read', u'search', u'compare', >> u'write', u'add', u'delete', u'all'), >> + alwaysask=True, >> ), >> >> This change requires to generate new API.txt >> >> please run ./makeapi and increment API version in VERSION file. >> >> Thank you in advance :-) >> >> Martin^2 >> >> -- >> Martin Basti >> >> > > > -- > Martin Basti > > Thank you ACK -- Martin Basti -------------- next part -------------- An HTML attachment was scrubbed... URL: From tbabej at redhat.com Thu Feb 12 16:12:45 2015 From: tbabej at redhat.com (Tomas Babej) Date: Thu, 12 Feb 2015 11:12:45 -0500 (EST) Subject: [Freeipa-devel] [PATCH 0189] Prevent install scripts to fail silently if timeout exceeded In-Reply-To: <54C122F7.3040606@redhat.com> References: <54C122F7.3040606@redhat.com> Message-ID: <33132581.1688145.1423757565096.JavaMail.zimbra@redhat.com> ACK, pushed to master: d5035c0ed99c03e25d5890e4d6f12f1079e8d892 Tomas From mkosek at redhat.com Fri Feb 13 07:55:34 2015 From: mkosek at redhat.com (Martin Kosek) Date: Fri, 13 Feb 2015 08:55:34 +0100 Subject: [Freeipa-devel] [PATCH 0177] Fix add version warning only on server side In-Reply-To: <54DC7625.2020000@redhat.com> References: <5487102F.10806@redhat.com> <54896FBF.7010204@redhat.com> <54897C72.90401@redhat.com> <549017D5.30909@redhat.com> <54901EDA.3060601@redhat.com> <54901FB7.40900@redhat.com> <54AE4337.2040804@redhat.com> <54D3540F.9040505@redhat.com> <54DC7625.2020000@redhat.com> Message-ID: <54DDADF6.8040506@redhat.com> On 02/12/2015 10:45 AM, David Kupka wrote: > On 02/05/2015 12:29 PM, Martin Basti wrote: >> On 08/01/15 09:43, Martin Basti wrote: >>> On 16/12/14 13:04, Martin Basti wrote: >>>> On 16/12/14 13:00, Martin Kosek wrote: >>>>> On 12/16/2014 12:30 PM, Martin Basti wrote: >>>>>> On 11/12/14 12:13, Martin Basti wrote: >>>>>>> On 11/12/14 11:19, Jan Cholasta wrote: >>>>>>>> Hi, >>>>>>>> >>>>>>>> Dne 9.12.2014 v 16:07 Martin Basti napsal(a): >>>>>>>>> Ticket: https://fedorahosted.org/freeipa/ticket/4793 >>>>>>>>> >>>>>>>>> I'm able to reproduce it only in one nose test. >>>>>>>> Which test? >>>>>>> If you apply my patch 170 and add a random forwardzone, then DNS >>>>>>> root zone >>>>>>> tests failed. >>>>>>>>> Patch attached. >>>>>>>> What about: >>>>>>>> >>>>>>>> result['messages'] = result.get('messages', ()) + >>>>>>>> (message.to_dict(),) >>>>>>>> >>>>>>>> (My point is, don't support both lists and tuples, pick just one.) >>>>>>>> >>>>>>>> Honza >>>>>>>> >>>>>>> This is question for framework guru (you?), I tried to preserve >>>>>>> format >>>>>>> unchanged. >>>>>>> Shouldn't be all values in lists in server part? >>>>>>> >>>>>>> Martin^2 >>>>>>> >>>>>> As was requested, I convert tuple to list instead handling both types. >>>>>> >>>>>> Updated patch attached. >>>>> I assume you do not want to track the .idea/ files in FreeIPA git :-) >>>>> >>>> Oh, thanks. My IDE was too smart again and add those files there itself. >>>> >>>> updated patch attached >>>> >>> Please review this patch. >>> >> Modified patch attached. >> Message should be added only on server side >> >> >> >> _______________________________________________ >> Freeipa-devel mailing list >> Freeipa-devel at redhat.com >> https://www.redhat.com/mailman/listinfo/freeipa-devel >> > > Thanks for the patch. I'm unable to reproduce the original bug but after > offline discussion I understand that this modification should prevent it. > AFAIK it doesn't break anything, ACK. > Pushed to master: 9cbbcadd0471a5e4b695c57f49e5a02400848053 Martin From mkosek at redhat.com Fri Feb 13 07:56:58 2015 From: mkosek at redhat.com (Martin Kosek) Date: Fri, 13 Feb 2015 08:56:58 +0100 Subject: [Freeipa-devel] [PATCH] Fix for 4861 In-Reply-To: <20150204091549.GA9247@redhat.com> References: <384106912.6102018.1422983715956.JavaMail.zimbra@redhat.com> <704017459.6102057.1422983736634.JavaMail.zimbra@redhat.com> <20150204091549.GA9247@redhat.com> Message-ID: <54DDAE4A.6050506@redhat.com> On 02/04/2015 10:15 AM, Alexander Bokovoy wrote: > On Tue, 03 Feb 2015, Simo Sorce wrote: >> See subject :-) >> >> -- >> Simo Sorce * Red Hat, Inc. * New York > >> From 245b307a99722bd4ca61e799f1a2708b6689f773 Mon Sep 17 00:00:00 2001 >> From: Simo Sorce >> Date: Tue, 3 Feb 2015 12:06:24 -0500 >> Subject: [PATCH] Handle DAL ABI change in MIT 1.13 >> >> In this new MIT version the DAL interface changes slightly but >> KRB5_KDB_DAL_MAJOR_VERSION was not changed. >> >> Luckily KRB5_KDB_API_VERSION did change and that's enough to know >> what to compile in. >> >> Resolves: https://fedorahosted.org/freeipa/ticket/4861 >> >> Signed-off-by: Simo Sorce >> --- >> daemons/ipa-kdb/ipa_kdb.h | 7 +++++++ >> daemons/ipa-kdb/ipa_kdb_principals.c | 7 +++++++ >> 2 files changed, 14 insertions(+) >> >> diff --git a/daemons/ipa-kdb/ipa_kdb.h b/daemons/ipa-kdb/ipa_kdb.h >> index >> b92107bab5a259601160a402c54fa8ed440925b3..ba9968bce7cff87f9f4a7fcd056ff7a906ce9e82 >> 100644 >> --- a/daemons/ipa-kdb/ipa_kdb.h >> +++ b/daemons/ipa-kdb/ipa_kdb.h >> @@ -182,10 +182,17 @@ krb5_error_code ipadb_put_principal(krb5_context kcontext, >> char **db_args); >> krb5_error_code ipadb_delete_principal(krb5_context kcontext, >> krb5_const_principal search_for); >> +#if KRB5_KDB_API_VERSION < 8 >> krb5_error_code ipadb_iterate(krb5_context kcontext, >> char *match_entry, >> int (*func)(krb5_pointer, krb5_db_entry *), >> krb5_pointer func_arg); >> +#else >> +krb5_error_code ipadb_iterate(krb5_context kcontext, >> + char *match_entry, >> + int (*func)(krb5_pointer, krb5_db_entry *), >> + krb5_pointer func_arg, krb5_flags iterflags); >> +#endif >> >> /* POLICY FUNCTIONS */ >> >> diff --git a/daemons/ipa-kdb/ipa_kdb_principals.c >> b/daemons/ipa-kdb/ipa_kdb_principals.c >> index >> e158c236eab5c7c5a7c12664dbde5d51cc55406d..600c4ee41c74a2fc154a5372ad3e3b4e8b94a635 >> 100644 >> --- a/daemons/ipa-kdb/ipa_kdb_principals.c >> +++ b/daemons/ipa-kdb/ipa_kdb_principals.c >> @@ -2087,10 +2087,17 @@ done: >> return kerr; >> } >> >> +#if KRB5_KDB_API_VERSION < 8 >> krb5_error_code ipadb_iterate(krb5_context kcontext, >> char *match_entry, >> int (*func)(krb5_pointer, krb5_db_entry *), >> krb5_pointer func_arg) >> +#else >> +krb5_error_code ipadb_iterate(krb5_context kcontext, >> + char *match_entry, >> + int (*func)(krb5_pointer, krb5_db_entry *), >> + krb5_pointer func_arg, krb5_flags iterflags) >> +#endif >> { >> struct ipadb_context *ipactx; >> krb5_error_code kerr; > ACK. > I think we need this patch in both master and ipa-4-1. There is no functional > change > and I'd like to keep a code in plugins synchronized across branches if > possible to avoid maintenance hurdles in future. > I agree with ipa-4-1. We will build FreeIPA 4.1 for F22+ anyway, until we have FreeIPA 4.2 ready. Pushed to: master: 5247c0c4e2e2e02180e4cbf56aa68df57d1a8cf9 ipa-4-1: 6162426999e75fdf907faf13f5a158d72ed91be5 Martin From mkosek at redhat.com Fri Feb 13 07:58:26 2015 From: mkosek at redhat.com (Martin Kosek) Date: Fri, 13 Feb 2015 08:58:26 +0100 Subject: [Freeipa-devel] [PATCH] 0174-0175 ipa-kdb fixes In-Reply-To: <20150126104409.4dd9cb8a@willson.usersys.redhat.com> References: <20150121100348.GR4383@redhat.com> <20150126104409.4dd9cb8a@willson.usersys.redhat.com> Message-ID: <54DDAEA2.9020709@redhat.com> On 01/26/2015 04:44 PM, Simo Sorce wrote: > On Wed, 21 Jan 2015 12:03:48 +0200 > Alexander Bokovoy wrote: > >> Hi, >> >> couple patches to fix Kerberos DAL driver in relation to trusts. >> >> Patch 0174: >> Allow using CA paths defined in krb5.conf on top of what we define >> automatically for trusted domains. >> https://fedorahosted.org/freeipa/ticket/4791 >> >> Patch 0175: >> Change error code reported back to Kerberos client when a principal >> from a disabled trusted domain attempts to access resources we >> control. >> >> The error code will help older SSSD to properly reflect error message >> in the PAM stack. >> https://fedorahosted.org/freeipa/ticket/4788 >> > > > They both LGTM. > > Simo. > Is that an ACK then? Sumit, were you able to test those by any chance? Thanks, Martin From dkupka at redhat.com Fri Feb 13 11:10:07 2015 From: dkupka at redhat.com (David Kupka) Date: Fri, 13 Feb 2015 12:10:07 +0100 Subject: [Freeipa-devel] [PATCH 0012] migrate-ds: exit with error message if no users/groups to migrate are found In-Reply-To: <54CB5632.1070702@redhat.com> References: <54CB54EA.5060106@redhat.com> <54CB5632.1070702@redhat.com> Message-ID: <54DDDB8F.80803@redhat.com> On 01/30/2015 11:00 AM, Martin Babinsky wrote: > On 01/30/2015 10:54 AM, Martin Babinsky wrote: >> Attached patch fixes https://fedorahosted.org/freeipa/ticket/4864. >> >> >> >> _______________________________________________ >> Freeipa-devel mailing list >> Freeipa-devel at redhat.com >> https://www.redhat.com/mailman/listinfo/freeipa-devel >> > > Got the ticket number wrong. Should be > https://fedorahosted.org/freeipa/ticket/4846 > > Attaching patch with fixed description. > > > > _______________________________________________ > Freeipa-devel mailing list > Freeipa-devel at redhat.com > https://www.redhat.com/mailman/listinfo/freeipa-devel > Hi, thanks for the patch. It works as expected but you've done two things in one patch: 1. Added check for empty migration. 2. Removed unused parameter of function. Each of them would be enough to fix the issue. Raising error seems to be better solution. If you think that unused parameters (yes, there is not only one :-) should be removed please do it in separate patch. -- David Kupka From mbabinsk at redhat.com Fri Feb 13 18:13:25 2015 From: mbabinsk at redhat.com (Martin Babinsky) Date: Fri, 13 Feb 2015 19:13:25 +0100 Subject: [Freeipa-devel] [PATCH 0012] migrate-ds: exit with error message if no users/groups to migrate are found In-Reply-To: <54DDDB8F.80803@redhat.com> References: <54CB54EA.5060106@redhat.com> <54CB5632.1070702@redhat.com> <54DDDB8F.80803@redhat.com> Message-ID: <54DE3EC5.3010200@redhat.com> On 02/13/2015 12:10 PM, David Kupka wrote: > On 01/30/2015 11:00 AM, Martin Babinsky wrote: >> On 01/30/2015 10:54 AM, Martin Babinsky wrote: >>> Attached patch fixes https://fedorahosted.org/freeipa/ticket/4864. >>> >>> >>> >>> _______________________________________________ >>> Freeipa-devel mailing list >>> Freeipa-devel at redhat.com >>> https://www.redhat.com/mailman/listinfo/freeipa-devel >>> >> >> Got the ticket number wrong. Should be >> https://fedorahosted.org/freeipa/ticket/4846 >> >> Attaching patch with fixed description. >> >> >> >> _______________________________________________ >> Freeipa-devel mailing list >> Freeipa-devel at redhat.com >> https://www.redhat.com/mailman/listinfo/freeipa-devel >> > > Hi, > thanks for the patch. It works as expected but you've done two things in > one patch: > 1. Added check for empty migration. > 2. Removed unused parameter of function. > > Each of them would be enough to fix the issue. Raising error seems to be > better solution. > If you think that unused parameters (yes, there is not only one :-) > should be removed please do it in separate patch. > Very well, I am attaching patch which features only the check for empty user/group containers during migration. I will also add example steps to reproduce https://fedorahosted.org/freeipa/ticket/4846 since it can be a bit tricky: 1. Setup 389 directory server on one machine using setup-ds.pl script. 2. Delete all user and group entries from LDAP tree. 3. Setup an IPA master on another machine. 4. Enable migration of users/groups from 389DS to IPA master: ipa config-mod --enable-migration=True 5. Force-migrate users/group from 389DS: ipa migrate-ds ${389DS_LDAP_URI} --continue --with-compat 6. See /var/log/httpd/error_log for the resulting traceback. -- Martin^3 Babinsky -------------- next part -------------- A non-text attachment was scrubbed... Name: freeipa-mbabinsk-0012-2-migrate-ds-exit-with-error-message-if-no-users-group.patch Type: text/x-patch Size: 1487 bytes Desc: not available URL: From mbabinsk at redhat.com Fri Feb 13 19:17:44 2015 From: mbabinsk at redhat.com (Martin Babinsky) Date: Fri, 13 Feb 2015 20:17:44 +0100 Subject: [Freeipa-devel] [PATCH 0001] ipa-client-install: attempt to get host TGT several times before aborting client installation In-Reply-To: <54B69EDD.2000800@redhat.com> References: <54B3FA19.6000903@redhat.com> <54B4D5B4.3050301@redhat.com> <54B4DB7A.4080307@redhat.com> <54B53E68.60000@redhat.com> <54B690EE.3070604@redhat.com> <54B69EDD.2000800@redhat.com> Message-ID: <54DE4DD8.7060206@redhat.com> On 01/14/2015 05:52 PM, Martin Babinsky wrote: > On 01/14/2015 04:53 PM, Martin Babinsky wrote: >> On 01/13/2015 04:48 PM, Martin Babinsky wrote: >>> On 01/13/2015 09:46 AM, Jan Cholasta wrote: >>>> Dne 13.1.2015 v 09:22 Martin Kosek napsal(a): >>>>> On 01/12/2015 05:45 PM, Martin Babinsky wrote: >>>>>> related to ticket https://fedorahosted.org/freeipa/ticket/4808 >>>>>> >>>>>> Patch attached. >>>>>> >>>>>> Martin^3 >>>>> >>>>> I think the --tgt-kinit-attempts approach is good one. Couple comments >>>>> I have >>>>> when reading the patch: >>>>> >>>>> 1) Function >>>>> +def get_host_tgt(options, keytab, host, realm, env): >>>>> should be made more general purpose and instead of whole "options", it >>>>> should >>>>> rather accept just "kinit_attemps". It will then enable future >>>>> generations to >>>>> reuse the function for something else. Just a generally good practice, >>>>> nothing >>>>> critical. >>>>> >>>>> 2) I think >>>>> + if returncode == 0: >>>>> + root_logger.info("Attempt %d succeeded." % n_attempts) >>>>> + break >>>>> >>>>> can be just DEBUG level. People do not need to know we will try >>>>> multiple attempts. >>>>> >>>>> 3) It may be even better to print >>>>> "Attempt %d/%d failed." instead of just number. But this is up to you. >>>>> >>>>> 4) I see several C-isms in the code, as a programming practice, let us >>>>> remove >>>>> them :-) In Python, the OK/notOK status is generally passed via >>>>> exceptions, not >>>>> return codes unless you really need them for anything meaningful. >>>>> >>>>> So, you can omit "raiseonerr=False" and have the handling code in an >>>>> Except >>>>> clause. When max number of attempts is breached, you then just raise >>>>> the >>>>> exception further (use bare "raise", to re-raise to keep the original >>>>> stack). >>>> >>>> +1 >>>> >>>> Additionally: >>>> >>>> 5) I would prefer if the option was named --kinit-attempts instead of >>>> --tgt-kinit-attempts (the "tgt" seems redundant). >>>> >>>> 6) Please do not use backslashes for line wrapping, unless it is >>>> absolutely necessary. Instead, enclose the expression in parens for >>>> implicit continuation: >>>> >>>> + help=("number of attempts to obtain host >>>> TGT" >>>> + "if the first one fails (defaults to >>>> %default).")) >>>> >>>> 7) Please follow PEP8 in new code: >>>> >>>> ipa-client/ipa-install/ipa-client-install:151:80: E501 line too long >>>> (93 >>>> > 79 characters) >>>> ipa-client/ipa-install/ipa-client-install:1100:1: E302 expected 2 blank >>>> lines, found 1 >>>> ipa-client/ipa-install/ipa-client-install:1107:29: E126 continuation >>>> line over-indented for hanging indent >>>> ipa-client/ipa-install/ipa-client-install:1107:41: E231 missing >>>> whitespace after ',' >>>> ipa-client/ipa-install/ipa-client-install:1108:29: E128 continuation >>>> line under-indented for visual indent >>>> ipa-client/ipa-install/ipa-client-install:1116:51: E225 missing >>>> whitespace around operator >>>> ipa-client/ipa-install/ipa-client-install:2453:80: E501 line too long >>>> (88 > 79 characters) >>>> ipa-client/ipa-install/ipa-client-install:2454:80: E501 line too long >>>> (89 > 79 characters) >>>> ipa-client/ipa-install/ipa-client-install:2531:80: E501 line too long >>>> (83 > 79 characters) >>>> ipa-client/ipa-install/ipa-client-install:2532:80: E501 line too long >>>> (81 > 79 characters) >>>> >>>> Honza >>>> >>> Thank you for your comments. Attaching the updated patch (I have sent >>> the message much earlier, but only to Jan because I messed up the reply >>> addresses in Thunderbird. Sorry for that). >>> >>> Martin >>> >>> >>> _______________________________________________ >>> Freeipa-devel mailing list >>> Freeipa-devel at redhat.com >>> https://www.redhat.com/mailman/listinfo/freeipa-devel >>> >> >> Attaching updated patch. >> >> https://fedorahosted.org/freeipa/ticket/4808 >> >> Martin^3 >> >> >> >> _______________________________________________ >> Freeipa-devel mailing list >> Freeipa-devel at redhat.com >> https://www.redhat.com/mailman/listinfo/freeipa-devel >> > > Forgot to update 'ipa-client-install' man page. The updated patch fixes > this. Thanks to Rob for pointing it out. > > Martin^3 > > > _______________________________________________ > Freeipa-devel mailing list > Freeipa-devel at redhat.com > https://www.redhat.com/mailman/listinfo/freeipa-devel > Anyone to review this patch? https://fedorahosted.org/freeipa/ticket/4808 -- Martin^3 Babinsky -------------- next part -------------- A non-text attachment was scrubbed... Name: freeipa-mbabinsk-0001-4-ipa-client-install-added-new-option.patch Type: text/x-patch Size: 6010 bytes Desc: not available URL: From edewata at redhat.com Mon Feb 16 08:50:29 2015 From: edewata at redhat.com (Endi Sukma Dewata) Date: Mon, 16 Feb 2015 02:50:29 -0600 Subject: [Freeipa-devel] [PATCH] Password vault Message-ID: <54E1AF55.3060409@redhat.com> Hi, Attached are the updated patches for the password vault, and some new ones (please disregard previous patch submissions). Please give them a try. Thanks. -- Endi S. Dewata -------------- next part -------------- From 743ade7e1242eb66de0b79e3a93014113113271e Mon Sep 17 00:00:00 2001 From: "Endi S. Dewata" Date: Tue, 21 Oct 2014 10:57:08 -0400 Subject: [PATCH] Added initial vault implementation. This patch provides the initial vault implementation which allows the admin to manage vaults and vault containers, archive data, and retrieve data. It also includes the initial LDAP schema. It currently has limitations including: - The vault only supports the standard vault type. - The vault can only be used by the admin user. - The transport certificate has to be installed manually. The LDAPDelete has been refactored to allow overriding the subtree deletion behavior. A test script have been added as well. https://fedorahosted.org/freeipa/ticket/3872 --- API.txt | 170 ++++++ VERSION | 4 +- install/share/60basev3.ldif | 2 + install/updates/40-vault.update | 23 + install/updates/Makefile.am | 1 + ipa-client/man/default.conf.5 | 1 + ipalib/constants.py | 1 + ipalib/plugins/baseldap.py | 78 +-- ipalib/plugins/user.py | 9 + ipalib/plugins/vault.py | 668 +++++++++++++++++++++ ipalib/plugins/vaultcontainer.py | 361 +++++++++++ ipatests/test_xmlrpc/test_vault_plugin.py | 241 ++++++++ ipatests/test_xmlrpc/test_vaultcontainer_plugin.py | 420 +++++++++++++ 13 files changed, 1939 insertions(+), 40 deletions(-) create mode 100644 install/updates/40-vault.update create mode 100644 ipalib/plugins/vault.py create mode 100644 ipalib/plugins/vaultcontainer.py create mode 100644 ipatests/test_xmlrpc/test_vault_plugin.py create mode 100644 ipatests/test_xmlrpc/test_vaultcontainer_plugin.py diff --git a/API.txt b/API.txt index a6a95783c24f20850688a3115118a2c5bec9f21c..95e43390413731b2f5298e7e0cf7fc7accd1f69f 100644 --- a/API.txt +++ b/API.txt @@ -4517,6 +4517,176 @@ option: Str('version?', exclude='webui') output: Output('result', , None) output: Output('summary', (, ), None) output: PrimaryKey('value', None, None) +command: vault_add +args: 1,11,3 +arg: Str('cn', attribute=True, cli_name='vault_name', maxlength=255, multivalue=False, pattern='^[a-zA-Z0-9_.-]+$', primary_key=True, required=True) +option: Str('addattr*', cli_name='addattr', exclude='webui') +option: Flag('all', autofill=True, cli_name='all', default=False, exclude='webui') +option: Str('container', attribute=False, cli_name='container', multivalue=False, pattern='^[a-zA-Z0-9_.-/]+$', required=False) +option: Bytes('data?', cli_name='data') +option: Str('description', attribute=True, cli_name='desc', multivalue=False, required=False) +option: Str('in?', cli_name='in') +option: Flag('raw', autofill=True, cli_name='raw', default=False, exclude='webui') +option: Str('setattr*', cli_name='setattr', exclude='webui') +option: Str('text?', cli_name='text') +option: Str('vault_id', attribute=False, cli_name='vault_id', multivalue=False, required=False) +option: Str('version?', exclude='webui') +output: Entry('result', , Gettext('A dictionary representing an LDAP entry', domain='ipa', localedir=None)) +output: Output('summary', (, ), None) +output: PrimaryKey('value', None, None) +command: vault_archive +args: 1,11,3 +arg: Str('cn', attribute=True, cli_name='vault_name', maxlength=255, multivalue=False, pattern='^[a-zA-Z0-9_.-]+$', primary_key=True, query=True, required=True) +option: Flag('all', autofill=True, cli_name='all', default=False, exclude='webui') +option: Str('container?', cli_name='container') +option: Bytes('data?', cli_name='data') +option: Str('encrypted_data?', cli_name='encrypted_data') +option: Str('in?', cli_name='in') +option: Str('nonce?', cli_name='nonce') +option: Flag('raw', autofill=True, cli_name='raw', default=False, exclude='webui') +option: Flag('rights', autofill=True, default=False) +option: Str('text?', cli_name='text') +option: Str('version?', exclude='webui') +option: Str('wrapped_session_key?', cli_name='wrapped_session_key') +output: Entry('result', , Gettext('A dictionary representing an LDAP entry', domain='ipa', localedir=None)) +output: Output('summary', (, ), None) +output: PrimaryKey('value', None, None) +command: vault_del +args: 1,3,3 +arg: Str('cn', attribute=True, cli_name='vault_name', maxlength=255, multivalue=True, pattern='^[a-zA-Z0-9_.-]+$', primary_key=True, query=True, required=True) +option: Str('container?', cli_name='container') +option: Flag('continue', autofill=True, cli_name='continue', default=False) +option: Str('version?', exclude='webui') +output: Output('result', , None) +output: Output('summary', (, ), None) +output: ListOfPrimaryKeys('value', None, None) +command: vault_find +args: 1,10,4 +arg: Str('criteria?', noextrawhitespace=False) +option: Flag('all', autofill=True, cli_name='all', default=False, exclude='webui') +option: Str('cn', attribute=True, autofill=False, cli_name='vault_name', maxlength=255, multivalue=False, pattern='^[a-zA-Z0-9_.-]+$', primary_key=True, query=True, required=False) +option: Str('container', attribute=False, autofill=False, cli_name='container', multivalue=False, pattern='^[a-zA-Z0-9_.-/]+$', query=True, required=False) +option: Str('description', attribute=True, autofill=False, cli_name='desc', multivalue=False, query=True, required=False) +option: Flag('pkey_only?', autofill=True, default=False) +option: Flag('raw', autofill=True, cli_name='raw', default=False, exclude='webui') +option: Int('sizelimit?', autofill=False, minvalue=0) +option: Int('timelimit?', autofill=False, minvalue=0) +option: Str('vault_id', attribute=False, autofill=False, cli_name='vault_id', multivalue=False, query=True, required=False) +option: Str('version?', exclude='webui') +output: Output('count', , None) +output: ListOfEntries('result', (, ), Gettext('A list of LDAP entries', domain='ipa', localedir=None)) +output: Output('summary', (, ), None) +output: Output('truncated', , None) +command: vault_mod +args: 1,10,3 +arg: Str('cn', attribute=True, cli_name='vault_name', maxlength=255, multivalue=False, pattern='^[a-zA-Z0-9_.-]+$', primary_key=True, query=True, required=True) +option: Str('addattr*', cli_name='addattr', exclude='webui') +option: Flag('all', autofill=True, cli_name='all', default=False, exclude='webui') +option: Str('container', attribute=False, autofill=False, cli_name='container', multivalue=False, pattern='^[a-zA-Z0-9_.-/]+$', required=False) +option: Str('delattr*', cli_name='delattr', exclude='webui') +option: Str('description', attribute=True, autofill=False, cli_name='desc', multivalue=False, required=False) +option: Flag('raw', autofill=True, cli_name='raw', default=False, exclude='webui') +option: Flag('rights', autofill=True, default=False) +option: Str('setattr*', cli_name='setattr', exclude='webui') +option: Str('vault_id', attribute=False, autofill=False, cli_name='vault_id', multivalue=False, required=False) +option: Str('version?', exclude='webui') +output: Entry('result', , Gettext('A dictionary representing an LDAP entry', domain='ipa', localedir=None)) +output: Output('summary', (, ), None) +output: PrimaryKey('value', None, None) +command: vault_retrieve +args: 1,9,3 +arg: Str('cn', attribute=True, cli_name='vault_name', maxlength=255, multivalue=False, pattern='^[a-zA-Z0-9_.-]+$', primary_key=True, query=True, required=True) +option: Flag('all', autofill=True, cli_name='all', default=False, exclude='webui') +option: Str('container?', cli_name='container') +option: Str('out?', cli_name='out') +option: Flag('raw', autofill=True, cli_name='raw', default=False, exclude='webui') +option: Flag('rights', autofill=True, default=False) +option: Flag('show_text?', autofill=True, default=False) +option: Flag('stdout?', autofill=True, default=False) +option: Str('version?', exclude='webui') +option: Str('wrapped_session_key?', cli_name='wrapped_session_key') +output: Entry('result', , Gettext('A dictionary representing an LDAP entry', domain='ipa', localedir=None)) +output: Output('summary', (, ), None) +output: PrimaryKey('value', None, None) +command: vault_show +args: 1,5,3 +arg: Str('cn', attribute=True, cli_name='vault_name', maxlength=255, multivalue=False, pattern='^[a-zA-Z0-9_.-]+$', primary_key=True, query=True, required=True) +option: Flag('all', autofill=True, cli_name='all', default=False, exclude='webui') +option: Str('container?', cli_name='container') +option: Flag('raw', autofill=True, cli_name='raw', default=False, exclude='webui') +option: Flag('rights', autofill=True, default=False) +option: Str('version?', exclude='webui') +output: Entry('result', , Gettext('A dictionary representing an LDAP entry', domain='ipa', localedir=None)) +output: Output('summary', (, ), None) +output: PrimaryKey('value', None, None) +command: vaultcontainer_add +args: 1,8,3 +arg: Str('cn', attribute=True, cli_name='container_name', maxlength=255, multivalue=False, pattern='^[a-zA-Z0-9_.-]+$', primary_key=True, required=True) +option: Str('addattr*', cli_name='addattr', exclude='webui') +option: Flag('all', autofill=True, cli_name='all', default=False, exclude='webui') +option: Str('container_id', attribute=False, cli_name='container_id', multivalue=False, required=False) +option: Str('description', attribute=True, cli_name='desc', multivalue=False, required=False) +option: Str('parent', attribute=False, cli_name='parent', multivalue=False, pattern='^[a-zA-Z0-9_.-/]+$', required=False) +option: Flag('raw', autofill=True, cli_name='raw', default=False, exclude='webui') +option: Str('setattr*', cli_name='setattr', exclude='webui') +option: Str('version?', exclude='webui') +output: Entry('result', , Gettext('A dictionary representing an LDAP entry', domain='ipa', localedir=None)) +output: Output('summary', (, ), None) +output: PrimaryKey('value', None, None) +command: vaultcontainer_del +args: 1,4,3 +arg: Str('cn', attribute=True, cli_name='container_name', maxlength=255, multivalue=True, pattern='^[a-zA-Z0-9_.-]+$', primary_key=True, query=True, required=True) +option: Flag('continue', autofill=True, cli_name='continue', default=False) +option: Flag('force?', autofill=True, default=False) +option: Str('parent?', cli_name='parent') +option: Str('version?', exclude='webui') +output: Output('result', , None) +output: Output('summary', (, ), None) +output: ListOfPrimaryKeys('value', None, None) +command: vaultcontainer_find +args: 1,10,4 +arg: Str('criteria?', noextrawhitespace=False) +option: Flag('all', autofill=True, cli_name='all', default=False, exclude='webui') +option: Str('cn', attribute=True, autofill=False, cli_name='container_name', maxlength=255, multivalue=False, pattern='^[a-zA-Z0-9_.-]+$', primary_key=True, query=True, required=False) +option: Str('container_id', attribute=False, autofill=False, cli_name='container_id', multivalue=False, query=True, required=False) +option: Str('description', attribute=True, autofill=False, cli_name='desc', multivalue=False, query=True, required=False) +option: Str('parent', attribute=False, autofill=False, cli_name='parent', multivalue=False, pattern='^[a-zA-Z0-9_.-/]+$', query=True, required=False) +option: Flag('pkey_only?', autofill=True, default=False) +option: Flag('raw', autofill=True, cli_name='raw', default=False, exclude='webui') +option: Int('sizelimit?', autofill=False, minvalue=0) +option: Int('timelimit?', autofill=False, minvalue=0) +option: Str('version?', exclude='webui') +output: Output('count', , None) +output: ListOfEntries('result', (, ), Gettext('A list of LDAP entries', domain='ipa', localedir=None)) +output: Output('summary', (, ), None) +output: Output('truncated', , None) +command: vaultcontainer_mod +args: 1,10,3 +arg: Str('cn', attribute=True, cli_name='container_name', maxlength=255, multivalue=False, pattern='^[a-zA-Z0-9_.-]+$', primary_key=True, query=True, required=True) +option: Str('addattr*', cli_name='addattr', exclude='webui') +option: Flag('all', autofill=True, cli_name='all', default=False, exclude='webui') +option: Str('container_id', attribute=False, autofill=False, cli_name='container_id', multivalue=False, required=False) +option: Str('delattr*', cli_name='delattr', exclude='webui') +option: Str('description', attribute=True, autofill=False, cli_name='desc', multivalue=False, required=False) +option: Str('parent', attribute=False, autofill=False, cli_name='parent', multivalue=False, pattern='^[a-zA-Z0-9_.-/]+$', required=False) +option: Flag('raw', autofill=True, cli_name='raw', default=False, exclude='webui') +option: Flag('rights', autofill=True, default=False) +option: Str('setattr*', cli_name='setattr', exclude='webui') +option: Str('version?', exclude='webui') +output: Entry('result', , Gettext('A dictionary representing an LDAP entry', domain='ipa', localedir=None)) +output: Output('summary', (, ), None) +output: PrimaryKey('value', None, None) +command: vaultcontainer_show +args: 1,5,3 +arg: Str('cn', attribute=True, cli_name='container_name', maxlength=255, multivalue=False, pattern='^[a-zA-Z0-9_.-]+$', primary_key=True, query=True, required=True) +option: Flag('all', autofill=True, cli_name='all', default=False, exclude='webui') +option: Str('parent?', cli_name='parent') +option: Flag('raw', autofill=True, cli_name='raw', default=False, exclude='webui') +option: Flag('rights', autofill=True, default=False) +option: Str('version?', exclude='webui') +output: Entry('result', , Gettext('A dictionary representing an LDAP entry', domain='ipa', localedir=None)) +output: Output('summary', (, ), None) +output: PrimaryKey('value', None, None) capability: messages 2.52 capability: optional_uid_params 2.54 capability: permissions2 2.69 diff --git a/VERSION b/VERSION index 122594726d622ed95a55211d5d25cb481413d0d4..f02f749cd0bd01ccb38de8acdc4c44f4ae411476 100644 --- a/VERSION +++ b/VERSION @@ -90,5 +90,5 @@ IPA_DATA_VERSION=20100614120000 # # ######################################################## IPA_API_VERSION_MAJOR=2 -IPA_API_VERSION_MINOR=113 -# Last change: npmccallum - expose disabled user auth type +IPA_API_VERSION_MINOR=114 +# Last change: edewata - initial vault implementation diff --git a/install/share/60basev3.ldif b/install/share/60basev3.ldif index 4efb1fe8ba8a91d3a8b920d39d217124066728c0..e0bc4151d88fc394cea0d81a20d8da9537dd5cd3 100644 --- a/install/share/60basev3.ldif +++ b/install/share/60basev3.ldif @@ -77,3 +77,5 @@ objectClasses: (2.16.840.1.113730.3.8.12.24 NAME 'ipaPublicKeyObject' DESC 'Wrap objectClasses: (2.16.840.1.113730.3.8.12.25 NAME 'ipaPrivateKeyObject' DESC 'Wrapped private keys' SUP top AUXILIARY MUST ( ipaPrivateKey $ ipaWrappingKey $ ipaWrappingMech ) X-ORIGIN 'IPA v4.1' ) objectClasses: (2.16.840.1.113730.3.8.12.26 NAME 'ipaSecretKeyObject' DESC 'Wrapped secret keys' SUP top AUXILIARY MUST ( ipaSecretKey $ ipaWrappingKey $ ipaWrappingMech ) X-ORIGIN 'IPA v4.1' ) objectClasses: (2.16.840.1.113730.3.8.12.34 NAME 'ipaSecretKeyRefObject' DESC 'Indirect storage for encoded key material' SUP top AUXILIARY MUST ( ipaSecretKeyRef ) X-ORIGIN 'IPA v4.1' ) +objectClasses: (2.16.840.1.113730.3.8.18.1.1 NAME 'ipaVault' DESC 'IPA vault' SUP top STRUCTURAL MUST ( cn ) MAY ( description ) X-ORIGIN 'IPA v4.2' ) +objectClasses: (2.16.840.1.113730.3.8.18.1.2 NAME 'ipaVaultContainer' DESC 'IPA vault container' SUP top STRUCTURAL MUST ( cn ) MAY ( description ) X-ORIGIN 'IPA v4.2' ) diff --git a/install/updates/40-vault.update b/install/updates/40-vault.update new file mode 100644 index 0000000000000000000000000000000000000000..dac2f67112dc33f012c6d559285464fb7c944d1a --- /dev/null +++ b/install/updates/40-vault.update @@ -0,0 +1,23 @@ +dn: cn=vaults,$SUFFIX +default: objectClass: top +default: objectClass: ipaVaultContainer +default: cn: vaults +default: description: Root vault container + +dn: cn=services,cn=vaults,$SUFFIX +default: objectClass: top +default: objectClass: ipaVaultContainer +default: cn: services +default: description: Services vault container + +dn: cn=shared,cn=vaults,$SUFFIX +default: objectClass: top +default: objectClass: ipaVaultContainer +default: cn: shared +default: description: Shared vault container + +dn: cn=users,cn=vaults,$SUFFIX +default: objectClass: top +default: objectClass: ipaVaultContainer +default: cn: users +default: description: Users vault container diff --git a/install/updates/Makefile.am b/install/updates/Makefile.am index 40de5635621071d34b6475d51ca598ed41a8ba09..34bb0981c44a3fcc3242401873769f332b95988b 100644 --- a/install/updates/Makefile.am +++ b/install/updates/Makefile.am @@ -32,6 +32,7 @@ app_DATA = \ 40-dns.update \ 40-automember.update \ 40-otp.update \ + 40-vault.update \ 45-roles.update \ 50-7_bit_check.update \ 50-dogtag10-migration.update \ diff --git a/ipa-client/man/default.conf.5 b/ipa-client/man/default.conf.5 index dbc8a5b4647439de4de7c01152d098eb0561e236..0973f1a07179ad64daa326a02803cdc9ba1870aa 100644 --- a/ipa-client/man/default.conf.5 +++ b/ipa-client/man/default.conf.5 @@ -221,6 +221,7 @@ The following define the containers for the IPA server. Containers define where container_sudocmdgroup: cn=sudocmdgroups,cn=sudo container_sudorule: cn=sudorules,cn=sudo container_user: cn=users,cn=accounts + container_vault: cn=vaults container_virtual: cn=virtual operations,cn=etc .SH "FILES" diff --git a/ipalib/constants.py b/ipalib/constants.py index 50a2b1f7aa7f0d447bacfd005b102c7451e670ce..baaf9be8d0329e89cb92a03de302095fe7acb847 100644 --- a/ipalib/constants.py +++ b/ipalib/constants.py @@ -97,6 +97,7 @@ DEFAULT_CONFIG = ( ('container_hbacservice', DN(('cn', 'hbacservices'), ('cn', 'hbac'))), ('container_hbacservicegroup', DN(('cn', 'hbacservicegroups'), ('cn', 'hbac'))), ('container_dns', DN(('cn', 'dns'))), + ('container_vault', DN(('cn', 'vaults'))), ('container_virtual', DN(('cn', 'virtual operations'), ('cn', 'etc'))), ('container_sudorule', DN(('cn', 'sudorules'), ('cn', 'sudo'))), ('container_sudocmd', DN(('cn', 'sudocmds'), ('cn', 'sudo'))), diff --git a/ipalib/plugins/baseldap.py b/ipalib/plugins/baseldap.py index 4b1c701924d57919538e0c428ea181c2e898505e..d693709ac1ba7ddb3c559199c199039b6f8bd9ac 100644 --- a/ipalib/plugins/baseldap.py +++ b/ipalib/plugins/baseldap.py @@ -1498,48 +1498,50 @@ class LDAPDelete(LDAPMultiQuery): has_output_params = global_output_params - def execute(self, *keys, **options): + def delete_subtree(self, base_dn, *nkeys, **options): ldap = self.obj.backend - - def delete_entry(pkey): - nkeys = keys[:-1] + (pkey, ) - dn = self.obj.get_dn(*nkeys, **options) - assert isinstance(dn, DN) - - for callback in self.get_callbacks('pre'): - dn = callback(self, ldap, dn, *nkeys, **options) - assert isinstance(dn, DN) - - def delete_subtree(base_dn): - assert isinstance(base_dn, DN) - truncated = True - while truncated: - try: - (subentries, truncated) = ldap.find_entries( - None, [''], base_dn, ldap.SCOPE_ONELEVEL - ) - except errors.NotFound: - break - else: - for entry_attrs in subentries: - delete_subtree(entry_attrs.dn) - try: - self._exc_wrapper(nkeys, options, ldap.delete_entry)(base_dn) - except errors.NotFound: - self.obj.handle_not_found(*nkeys) - + assert isinstance(base_dn, DN) + truncated = True + while truncated: try: - self._exc_wrapper(nkeys, options, ldap.delete_entry)(dn) + (subentries, truncated) = ldap.find_entries( + None, [''], base_dn, ldap.SCOPE_ONELEVEL + ) except errors.NotFound: - self.obj.handle_not_found(*nkeys) - except errors.NotAllowedOnNonLeaf: - # this entry is not a leaf entry, delete all child nodes - delete_subtree(dn) + break + else: + for entry_attrs in subentries: + self.delete_subtree(entry_attrs.dn, *nkeys, **options) + try: + self._exc_wrapper(nkeys, options, ldap.delete_entry)(base_dn) + except errors.NotFound: + self.obj.handle_not_found(*nkeys) - for callback in self.get_callbacks('post'): - result = callback(self, ldap, dn, *nkeys, **options) + def delete_entry(self, pkey, *keys, **options): + ldap = self.obj.backend + nkeys = keys[:-1] + (pkey, ) + dn = self.obj.get_dn(*nkeys, **options) + assert isinstance(dn, DN) - return result + for callback in self.get_callbacks('pre'): + dn = callback(self, ldap, dn, *nkeys, **options) + assert isinstance(dn, DN) + + try: + self._exc_wrapper(nkeys, options, ldap.delete_entry)(dn) + except errors.NotFound: + self.obj.handle_not_found(*nkeys) + except errors.NotAllowedOnNonLeaf: + # this entry is not a leaf entry, delete all child nodes + self.delete_subtree(dn, *nkeys, **options) + + for callback in self.get_callbacks('post'): + result = callback(self, ldap, dn, *nkeys, **options) + + return result + + def execute(self, *keys, **options): + ldap = self.obj.backend if self.obj.primary_key and isinstance(keys[-1], (list, tuple)): pkeyiter = keys[-1] @@ -1552,7 +1554,7 @@ class LDAPDelete(LDAPMultiQuery): failed = [] for pkey in pkeyiter: try: - delete_entry(pkey) + self.delete_entry(pkey, *keys, **options) except errors.ExecutionError: if not options.get('continue', False): raise diff --git a/ipalib/plugins/user.py b/ipalib/plugins/user.py index 56585b9f86593c0c5879139103bc71707b88e15f..7322e2b69efbcc0a655caeb9e0863ad93dbf1ce9 100644 --- a/ipalib/plugins/user.py +++ b/ipalib/plugins/user.py @@ -901,6 +901,15 @@ class user_del(LDAPDelete): else: self.api.Command.otptoken_del(token) + # Delete user's private vault container. + vaultcontainer_id = self.api.Object.vaultcontainer.get_private_id(owner) + (vaultcontainer_name, vaultcontainer_parent_id) = self.api.Object.vaultcontainer.split_id(vaultcontainer_id) + + try: + self.api.Command.vaultcontainer_del(vaultcontainer_name, parent=vaultcontainer_parent_id) + except errors.NotFound: + pass + return dn diff --git a/ipalib/plugins/vault.py b/ipalib/plugins/vault.py new file mode 100644 index 0000000000000000000000000000000000000000..437d57d29d0f6dd6b29f376fb6bc46d2acc9dab9 --- /dev/null +++ b/ipalib/plugins/vault.py @@ -0,0 +1,668 @@ +# Authors: +# Endi S. Dewata +# +# Copyright (C) 2015 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 . + +import base64 +import sys + +import pki.account +import pki.crypto +import pki.key + +from ipalib import api, errors +from ipalib import Str, Bytes, Flag +from ipalib.plugable import Registry +from ipalib.plugins.baseldap import LDAPObject, LDAPCreate, LDAPDelete, LDAPSearch, LDAPUpdate, LDAPRetrieve +from ipalib import _, ngettext +from ipaplatform.paths import paths +from ipapython.dn import DN +import ipapython.nsslib + +__doc__ = _(""" +Vaults +""") + _(""" +Manage vaults. +""") + _(""" +EXAMPLES: +""") + _(""" + List private vaults: + ipa vault-find +""") + _(""" + List shared vaults: + ipa vault-find --container /shared +""") + _(""" + Add a standard vault: + ipa vault-add MyVault +""") + _(""" + Show a vault: + ipa vault-show MyVault +""") + _(""" + Modify a vault: + ipa vault-mod MyVault --desc "My vault" +""") + _(""" + Archive data into standard vault: + ipa vault-archive MyVault --in data.bin +""") + _(""" + Retrieve data from standard vault: + ipa vault-retrieve MyVault --out data.bin +""") + _(""" + Delete a vault: + ipa vault-del MyVault +""") + +register = Registry() +transport_cert_nickname = 'KRA Transport Certificate' + + at register() +class vault(LDAPObject): + __doc__ = _(""" + Vault object. + """) + + object_name = _('vault') + object_name_plural = _('vaults') + + object_class = ['ipaVault'] + default_attributes = [ + 'cn', + 'vault_id', + 'description', + ] + search_display_attributes = [ + 'cn', + 'vault_id', + 'description', + ] + + label = _('Vaults') + label_singular = _('Vault') + + takes_params = ( + Str('cn', + cli_name='vault_name', + label=_('Vault name'), + primary_key=True, + pattern='^[a-zA-Z0-9_.-]+$', + pattern_errmsg='may only include letters, numbers, _, ., and -', + maxlength=255, + ), + Str('container?', + cli_name='container', + label=_('Container'), + doc=_('Container'), + flags=('virtual_attribute'), + pattern='^[a-zA-Z0-9_.-/]+$', + pattern_errmsg='may only include letters, numbers, _, ., -, and /', + ), + Str('vault_id?', + cli_name='vault_id', + label=_('Vault ID'), + doc=_('Vault ID'), + flags=('virtual_attribute'), + ), + Str('description?', + cli_name='desc', + label=_('Description'), + doc=_('Vault description'), + ), + ) + + def get_dn(self, *keys, **options): + __doc__ = _(""" + Generates vault DN from vault ID. + """) + + # get vault ID from parameters + name = None + if keys: + name = keys[0] + + container_id = api.Object.vaultcontainer.normalize_id(options.get('container')) + + vault_id = container_id + if name: + vault_id = container_id + name + + dn = api.Object.vaultcontainer.base_dn + + # for each name in the ID, prepend the base DN + for name in vault_id.split(u'/'): + if name: + dn = DN(('cn', name), dn) + + return dn + + def get_id(self, dn): + __doc__ = _(""" + Generates vault ID from vault DN. + """) + + # make sure the DN is a vault DN + if not dn.endswith(api.Object.vaultcontainer.base_dn): + raise ValueError('Invalid vault DN: %s' % dn) + + # vault DN cannot be the container base DN + if len(dn) == len(api.Object.vaultcontainer.base_dn): + raise ValueError('Invalid vault DN: %s' % dn) + + # construct the vault ID from the bottom up + id = u'' + while len(dn) > len(api.Object.vaultcontainer.base_dn): + + rdn = dn[0] + name = rdn['cn'] + id = u'/' + name + id + + dn = DN(*dn[1:]) + + return id + + def split_id(self, id): + __doc__ = _(""" + Splits a vault ID into (vault name, container ID) tuple. + """) + + # split ID into container ID and vault name + parts = id.rsplit(u'/', 1) + + # return vault name and container ID + return (parts[1], parts[0] + u'/') + + def get_kra_id(self, id): + __doc__ = _(""" + Generates a client key ID to store/retrieve data in KRA. + """) + return 'ipa' + id + + + at register() +class vault_add(LDAPCreate): + __doc__ = _('Create a new vault.') + + takes_options = LDAPCreate.takes_options + ( + Bytes('data?', + cli_name='data', + doc=_('Base-64 encoded binary data to archive'), + ), + Str('text?', + cli_name='text', + doc=_('Text data to archive'), + ), + Str('in?', + cli_name='in', + doc=_('File containing data to archive'), + ), + ) + + msg_summary = _('Added vault "%(value)s"') + + def forward(self, *args, **options): + + vault_name = args[0] + container_id = api.Object.vaultcontainer.normalize_id(options.get('container')) + + data = options.get('data') + text = options.get('text') + input_file = options.get('in') + + # don't send these parameters to server + if 'data' in options: + del options['data'] + if 'text' in options: + del options['text'] + if 'in' in options: + del options['in'] + + # get data + if data: + if text: + raise errors.ValidationError(name='text', + error=_('Input data already specified')) + + if input_file: + raise errors.ValidationError(name='input_file', + error=_('Input data already specified')) + + elif text: + if input_file: + raise errors.ValidationError(name='input_file', + error=_('Input data already specified')) + + data = text.encode('utf-8') + + elif input_file: + with open(input_file, 'rb') as f: + data = f.read() + + else: + data = '' + + # create the vault + response = super(vault_add, self).forward(*args, **options) + + # archive initial data + api.Command.vault_archive( + vault_name, + container=container_id, + data=data) + + return response + + def pre_callback(self, ldap, dn, entry_attrs, attrs_list, *keys, **options): + assert isinstance(dn, DN) + + container_dn = DN(*dn[1:]) + api.Object.vaultcontainer.create_entry(container_dn) + + return dn + + def post_callback(self, ldap, dn, entry_attrs, *keys, **options): + assert isinstance(dn, DN) + + entry_attrs['vault_id'] = self.obj.get_id(dn) + + return dn + + + at register() +class vault_del(LDAPDelete): + __doc__ = _('Delete a vault.') + + msg_summary = _('Deleted vault "%(value)s"') + + takes_options = LDAPDelete.takes_options + ( + Str('container?', + cli_name='container', + doc=_('Container'), + ), + ) + + def post_callback(self, ldap, dn, *keys, **options): + assert isinstance(dn, DN) + + vault_id = self.obj.get_id(dn) + + kra_client = api.Backend.kra.get_client() + + kra_account = pki.account.AccountClient(kra_client.connection) + kra_account.login() + + client_key_id = self.api.Object.vault.get_kra_id(vault_id) + + # deactivate vault record in KRA + response = kra_client.keys.list_keys(client_key_id, pki.key.KeyClient.KEY_STATUS_ACTIVE) + + for key_info in response.key_infos: + kra_client.keys.modify_key_status( + key_info.get_key_id(), + pki.key.KeyClient.KEY_STATUS_INACTIVE) + + kra_account.logout() + + return True + + + at register() +class vault_find(LDAPSearch): + __doc__ = _('Search for vaults.') + + msg_summary = ngettext( + '%(count)d vault matched', '%(count)d vaults matched', 0 + ) + + def pre_callback(self, ldap, filter, attrs_list, base_dn, scope, *keys, **options): + assert isinstance(base_dn, DN) + + container_id = self.Object.vaultcontainer.normalize_id(options.get('container')) + base_dn = self.Object.vaultcontainer.get_dn(parent=container_id) + + api.Object.vaultcontainer.create_entry(base_dn) + + return (filter, base_dn, scope) + + + def post_callback(self, ldap, entries, truncated, *args, **options): + + for entry in entries: + entry['vault_id'] = self.obj.get_id(entry.dn) + + return truncated + + + at register() +class vault_mod(LDAPUpdate): + __doc__ = _('Modify a vault.') + + msg_summary = _('Modified vault "%(value)s"') + + def post_callback(self, ldap, dn, entry_attrs, *keys, **options): + assert isinstance(dn, DN) + + entry_attrs['vault_id'] = self.obj.get_id(dn) + + return dn + + + at register() +class vault_show(LDAPRetrieve): + __doc__ = _('Display information about a vault.') + + takes_options = LDAPRetrieve.takes_options + ( + Str('container?', + cli_name='container', + doc=_('Container'), + ), + ) + + def post_callback(self, ldap, dn, entry_attrs, *keys, **options): + assert isinstance(dn, DN) + + entry_attrs['vault_id'] = self.obj.get_id(dn) + + return dn + + + at register() +class vault_archive(LDAPRetrieve): + __doc__ = _('Archive data into a vault.') + + takes_options = LDAPRetrieve.takes_options + ( + Str('container?', + cli_name='container', + doc=_('Container'), + ), + Bytes('data?', + cli_name='data', + doc=_('Base-64 encoded binary data to archive'), + ), + Str('text?', + cli_name='text', + doc=_('Text data to archive'), + ), + Str('in?', + cli_name='in', + doc=_('File containing data to archive'), + ), + Str('wrapped_session_key?', + cli_name='wrapped_session_key', + doc=_('Session key wrapped with transport certificate and encoded in base-64'), + ), + Str('encrypted_data?', + cli_name='encrypted_data', + doc=_('Data encrypted with session key and encoded in base-64'), + ), + Str('nonce?', + cli_name='nonce', + doc=_('Nonce encrypted encoded in base-64'), + ), + ) + + msg_summary = _('Archived data into vault "%(value)s"') + + def forward(self, *args, **options): + + vault_name = args[0] + container_id = api.Object.vaultcontainer.normalize_id(options.get('container')) + + data = options.get('data') + text = options.get('text') + input_file = options.get('in') + + # don't send these parameters to server + if 'data' in options: + del options['data'] + if 'text' in options: + del options['text'] + if 'in' in options: + del options['in'] + + # get data + if data: + if text: + raise errors.ValidationError(name='text', + error=_('Input data already specified')) + + if input_file: + raise errors.ValidationError(name='input_file', + error=_('Input data already specified')) + + elif text: + if input_file: + raise errors.ValidationError(name='input_file', + error=_('Input data already specified')) + + data = text.encode('utf-8') + + elif input_file: + with open(input_file, 'rb') as f: + data = f.read() + + else: + data = '' + + # initialize NSS database + crypto = pki.crypto.NSSCryptoProvider(paths.IPA_NSSDB_DIR) + crypto.initialize() + ipapython.nsslib.current_dbdir = paths.IPA_NSSDB_DIR + + # generate session key + session_key = crypto.generate_session_key() + + # retrieve transport certificate + nss_transport_cert = crypto.get_cert(transport_cert_nickname) + + # wrap session key with transport certificate + wrapped_session_key = crypto.asymmetric_wrap( + session_key, + nss_transport_cert + ) + + # encrypt data with session key + nonce = crypto.generate_nonce_iv() + encrypted_data = crypto.symmetric_wrap( + data, + session_key, + nonce_iv=nonce + ) + + # send archival request to server + options['wrapped_session_key'] = unicode(base64.b64encode(wrapped_session_key)) + options['encrypted_data'] = unicode(base64.b64encode(encrypted_data)) + options['nonce'] = unicode(base64.b64encode(nonce)) + + return super(vault_archive, self).forward(*args, **options) + + def post_callback(self, ldap, dn, entry_attrs, *keys, **options): + assert isinstance(dn, DN) + + vault_id = self.obj.get_id(dn) + entry_attrs['vault_id'] = vault_id + + # connect to KRA + kra_client = api.Backend.kra.get_client() + + kra_account = pki.account.AccountClient(kra_client.connection) + kra_account.login() + + client_key_id = self.api.Object.vault.get_kra_id(vault_id) + + # deactivate existing vault record in KRA + response = kra_client.keys.list_keys(client_key_id, pki.key.KeyClient.KEY_STATUS_ACTIVE) + + for key_info in response.key_infos: + kra_client.keys.modify_key_status( + key_info.get_key_id(), + pki.key.KeyClient.KEY_STATUS_INACTIVE) + + wrapped_session_key = base64.b64decode(options['wrapped_session_key']) + encrypted_data = base64.b64decode(options['encrypted_data']) + nonce = base64.b64decode(options['nonce']) + + # forward encrypted data to KRA + kra_client.keys.archive_encrypted_data( + client_key_id, + pki.key.KeyClient.PASS_PHRASE_TYPE, + encrypted_data, + wrapped_session_key, + None, + nonce, + ) + + kra_account.logout() + + return dn + + + at register() +class vault_retrieve(LDAPRetrieve): + __doc__ = _('Retrieve a data from a vault.') + + takes_options = LDAPRetrieve.takes_options + ( + Str('container?', + cli_name='container', + doc=_('Container'), + ), + Flag('show_text?', + doc=_('Show text data'), + autofill=False, + ), + Flag('stdout?', + doc=_('Show data on standard output'), + autofill=False, + ), + Str('out?', + cli_name='out', + doc=_('File to store retrieved data'), + ), + Str('wrapped_session_key?', + cli_name='wrapped_session_key', + doc=_('Session key wrapped with transport certificate and encoded in base-64'), + ), + ) + + has_output_params = ( + Bytes('data', + label=_('Data'), + ), + Bytes('text', + label=_('Text'), + ), + ) + + msg_summary = _('Retrieved data from vault "%(value)s"') + + def forward(self, *args, **options): + + vault_name = args[0] + container_id = api.Object.vaultcontainer.normalize_id(options.get('container')) + + show_text = options.get('show_text') + stdout = options.get('stdout') + output_file = options.get('out') + + # don't send these parameters to server + if 'show_text' in options: + del options['show_text'] + if 'stdout' in options: + del options['stdout'] + if 'out' in options: + del options['out'] + + # initialize NSS database + crypto = pki.crypto.NSSCryptoProvider(paths.IPA_NSSDB_DIR) + crypto.initialize() + ipapython.nsslib.current_dbdir = paths.IPA_NSSDB_DIR + + # generate session key + session_key = crypto.generate_session_key() + + # retrieve transport certificate + nss_transport_cert = crypto.get_cert(transport_cert_nickname) + + # wrap session key with transport certificate + wrapped_session_key = crypto.asymmetric_wrap( + session_key, + nss_transport_cert + ) + + # send retrieval request to server + options['wrapped_session_key'] = unicode(base64.b64encode(wrapped_session_key)) + + response = super(vault_retrieve, self).forward(*args, **options) + + encrypted_data = base64.b64decode(response['result']['encrypted_data']) + nonce = base64.b64decode(response['result']['nonce']) + + # decrypt encrypted data with session key + data = crypto.symmetric_unwrap( + encrypted_data, + session_key, + nonce_iv=nonce) + + if stdout: + sys.stdout.write(data) + response['result'] = {} + response['summary'] = None + + elif output_file: + with open(output_file, 'w') as f: + f.write(data) + + elif show_text: + response['result']['text'] = unicode(data) + + else: + response['result']['data'] = data + + return response + + def post_callback(self, ldap, dn, entry_attrs, *keys, **options): + assert isinstance(dn, DN) + + vault_id = self.obj.get_id(dn) + entry_attrs['vault_id'] = vault_id + + wrapped_session_key = base64.b64decode(options['wrapped_session_key']) + + # connect to KRA + kra_client = api.Backend.kra.get_client() + + kra_account = pki.account.AccountClient(kra_client.connection) + kra_account.login() + + client_key_id = self.api.Object.vault.get_kra_id(vault_id) + + # find vault record in KRA + response = kra_client.keys.list_keys(client_key_id, pki.key.KeyClient.KEY_STATUS_ACTIVE) + + if not len(response.key_infos): + raise errors.NotFound(reason=_('Missing archived data.')) + + key_info = response.key_infos[0] + + # retrieve encrypted data from KRA + key = kra_client.keys.retrieve_key( + key_info.get_key_id(), + wrapped_session_key) + + entry_attrs['encrypted_data'] = unicode(base64.b64encode(key.encrypted_data)) + entry_attrs['nonce'] = unicode(base64.b64encode(key.nonce_data)) + + kra_account.logout() + + return dn diff --git a/ipalib/plugins/vaultcontainer.py b/ipalib/plugins/vaultcontainer.py new file mode 100644 index 0000000000000000000000000000000000000000..22d36eecc6a842240cbadd3ff6b6518efbfe524e --- /dev/null +++ b/ipalib/plugins/vaultcontainer.py @@ -0,0 +1,361 @@ +# Authors: +# Endi S. Dewata +# +# Copyright (C) 2015 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 . + +import base64 + +from ipalib import api, errors +from ipalib import Str, Flag +from ipalib.plugable import Registry +from ipalib.plugins.baseldap import LDAPObject, LDAPCreate, LDAPDelete, LDAPSearch, LDAPUpdate, LDAPRetrieve +from ipalib.request import context +from ipalib.plugins.user import split_principal +from ipalib import _, ngettext +from ipapython.dn import DN + +__doc__ = _(""" +Vault containers +""") + _(""" +Manage vault containers. +""") + _(""" +EXAMPLES: +""") + _(""" + List private vault containers: + ipa vaultcontainer-find +""") + _(""" + List top-level vault containers: + ipa vaultcontainer-find --parent / +""") + _(""" + Add a vault container: + ipa vaultcontainer-add MyContainer +""") + _(""" + Show a vault container: + ipa vaultcontainer-show MyContainer +""") + _(""" + Modify a vault container: + ipa vaultcontainer-mod MyContainer --desc "My container" +""") + _(""" + Delete a vault container: + ipa vaultcontainer-del MyContainer +""") + +register = Registry() + + at register() +class vaultcontainer(LDAPObject): + __doc__ = _(""" + Vault container object. + """) + + base_dn = DN(api.env.container_vault, api.env.basedn) + object_name = _('vault container') + object_name_plural = _('vault containers') + + object_class = ['ipaVaultContainer'] + default_attributes = [ + 'cn', + 'container_id', + 'description', + ] + search_display_attributes = [ + 'cn', + 'container_id', + 'description', + ] + + label = _('Vault Containers') + label_singular = _('Vault Container') + + takes_params = ( + Str('cn', + cli_name='container_name', + label=_('Container name'), + primary_key=True, + pattern='^[a-zA-Z0-9_.-]+$', + pattern_errmsg='may only include letters, numbers, _, ., and -', + maxlength=255, + ), + Str('parent?', + cli_name='parent', + label=_('Parent'), + doc=_('Parent container'), + flags=('virtual_attribute'), + pattern='^[a-zA-Z0-9_.-/]+$', + pattern_errmsg='may only include letters, numbers, _, ., -, and /', + ), + Str('container_id?', + cli_name='container_id', + label=_('Container ID'), + doc=_('Container ID'), + flags=('virtual_attribute'), + ), + Str('description?', + cli_name='desc', + label=_('Description'), + doc=_('Container description'), + ), + ) + + def get_dn(self, *keys, **options): + __doc__ = _(""" + Generates vault container DN from container ID. + """) + + # get container ID from parameters + name = None + if keys: + name = keys[0] + + parent_id = api.Object.vaultcontainer.normalize_id(options.get('parent')) + + container_id = parent_id + if name: + container_id = parent_id + name + u'/' + + dn = self.base_dn + + # for each name in the ID, prepend the base DN + for name in container_id.split(u'/'): + if name: + dn = DN(('cn', name), dn) + + return dn + + def get_id(self, dn): + __doc__ = _(""" + Generates container ID from container DN. + """) + + # make sure the DN is a container DN + if not dn.endswith(self.base_dn): + raise ValueError('Invalid container DN: %s' % dn) + + # construct container ID from the bottom up + id = u'/' + while len(dn) > len(self.base_dn): + + rdn = dn[0] + name = rdn['cn'] + id = u'/' + name + id + + dn = DN(*dn[1:]) + + return id + + def get_private_id(self, username=None): + __doc__ = _(""" + Returns user's private container ID (i.e. /users//). + """) + + if not username: + principal = getattr(context, 'principal') + (username, realm) = split_principal(principal) + + return u'/users/' + username + u'/' + + def normalize_id(self, id): + __doc__ = _(""" + Normalizes container ID. + """) + + # if ID is empty, return user's private container ID + if not id: + return self.get_private_id() + + # make sure ID ends with slash + if not id.endswith(u'/'): + id += u'/' + + # if it's an absolute ID, do nothing + if id.startswith(u'/'): + return id + + # otherwise, prepend with user's private container ID + return self.get_private_id() + id + + def split_id(self, id): + __doc__ = _(""" + Splits a normalized container ID into (container name, parent ID) tuple. + """) + + # handle root ID + if id == u'/': + return (None, None) + + # split ID into parent ID, container name, and empty string + parts = id.rsplit(u'/', 2) + + # return container name and parent ID + return (parts[1], parts[0] + u'/') + + def create_entry(self, dn): + __doc__ = _(""" + Creates a container entry and its parents. + """) + + # if entry already exists, return + try: + self.backend.get_entry(dn) + return + + except errors.NotFound: + pass + + # otherwise, create parent entry first + parent_dn = DN(*dn[1:]) + self.create_entry(parent_dn) + + # then create the entry itself + rdn = dn[0] + entry = self.backend.make_entry( + dn, + { + 'objectclass': self.object_class, + 'cn': rdn['cn'], + }) + self.backend.add_entry(entry) + + + at register() +class vaultcontainer_add(LDAPCreate): + __doc__ = _('Create a new vault container.') + + msg_summary = _('Added vault container "%(value)s"') + + def pre_callback(self, ldap, dn, entry_attrs, attrs_list, *keys, **options): + assert isinstance(dn, DN) + + parent_dn = DN(*dn[1:]) + self.obj.create_entry(parent_dn) + + return dn + + def post_callback(self, ldap, dn, entry_attrs, *keys, **options): + assert isinstance(dn, DN) + + entry_attrs['container_id'] = self.obj.get_id(dn) + + return dn + + + at register() +class vaultcontainer_del(LDAPDelete): + __doc__ = _('Delete a vault container.') + + msg_summary = _('Deleted vault container "%(value)s"') + + takes_options = LDAPDelete.takes_options + ( + Str('parent?', + cli_name='parent', + doc=_('Parent container'), + ), + Flag('force?', + doc=_('Force deletion'), + autofill=False, + ), + ) + + def delete_entry(self, pkey, *keys, **options): + __doc__ = _(""" + Overwrites the base method to control deleting subtree with force option. + """) + + ldap = self.obj.backend + nkeys = keys[:-1] + (pkey, ) + dn = self.obj.get_dn(*nkeys, **options) + assert isinstance(dn, DN) + + for callback in self.get_callbacks('pre'): + dn = callback(self, ldap, dn, *nkeys, **options) + assert isinstance(dn, DN) + + try: + self._exc_wrapper(nkeys, options, ldap.delete_entry)(dn) + except errors.NotFound: + self.obj.handle_not_found(*nkeys) + except errors.NotAllowedOnNonLeaf: + # this entry is not a leaf entry + # if forced, delete all child nodes + if options.get('force'): + self.delete_subtree(dn, *nkeys, **options) + else: + raise + + for callback in self.get_callbacks('post'): + result = callback(self, ldap, dn, *nkeys, **options) + + return result + + + at register() +class vaultcontainer_find(LDAPSearch): + __doc__ = _('Search for vault containers.') + + msg_summary = ngettext( + '%(count)d vault container matched', '%(count)d vault containers matched', 0 + ) + + def pre_callback(self, ldap, filter, attrs_list, base_dn, scope, *keys, **options): + assert isinstance(base_dn, DN) + + parent_id = self.obj.normalize_id(options.get('parent')) + base_dn = self.obj.get_dn(parent=parent_id) + + self.obj.create_entry(base_dn) + + return (filter, base_dn, scope) + + def post_callback(self, ldap, entries, truncated, *args, **options): + + for entry in entries: + entry['container_id'] = self.obj.get_id(entry.dn) + + return truncated + + + at register() +class vaultcontainer_mod(LDAPUpdate): + __doc__ = _('Modify a vault container.') + + msg_summary = _('Modified vault container "%(value)s"') + + def post_callback(self, ldap, dn, entry_attrs, *keys, **options): + assert isinstance(dn, DN) + + entry_attrs['container_id'] = self.obj.get_id(dn) + + return dn + + + at register() +class vaultcontainer_show(LDAPRetrieve): + __doc__ = _('Display information about a vault container.') + + takes_options = LDAPRetrieve.takes_options + ( + Str('parent?', + cli_name='parent', + doc=_('Parent container'), + ), + ) + + def post_callback(self, ldap, dn, entry_attrs, *keys, **options): + assert isinstance(dn, DN) + + entry_attrs['container_id'] = self.obj.get_id(dn) + + return dn diff --git a/ipatests/test_xmlrpc/test_vault_plugin.py b/ipatests/test_xmlrpc/test_vault_plugin.py new file mode 100644 index 0000000000000000000000000000000000000000..0f8b56f45d5130f7b80075475de7ad7349360f81 --- /dev/null +++ b/ipatests/test_xmlrpc/test_vault_plugin.py @@ -0,0 +1,241 @@ +# Authors: +# Endi S. Dewata +# +# Copyright (C) 2015 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 . + +""" +Test the `ipalib/plugins/vault.py` module. +""" + +from ipalib import api, errors +from xmlrpc_test import Declarative, fuzzy_string + +test_vault = u'test_vault' +binary_data = '\x01\x02\x03\x04' +text_data = u'secret' + +class test_vault(Declarative): + + cleanup_commands = [ + ('vault_del', [test_vault], {'continue': True}), + ] + + tests = [ + + { + 'desc': 'Create test vault', + 'command': ( + 'vault_add', + [test_vault], + {}, + ), + 'expected': { + 'value': test_vault, + 'summary': 'Added vault "%s"' % test_vault, + 'result': { + 'dn': u'cn=%s,cn=admin,cn=users,cn=vaults,%s' % (test_vault, api.env.basedn), + 'objectclass': (u'ipaVault', u'top'), + 'cn': [test_vault], + 'vault_id': u'/users/admin/%s' % test_vault, + }, + }, + }, + + { + 'desc': 'Create duplicate vault', + 'command': ( + 'vault_add', + [test_vault], + {}, + ), + 'expected': errors.DuplicateEntry(message=u'vault with name "%s" already exists' % test_vault), + }, + + { + 'desc': 'Find test vaults', + 'command': ( + 'vault_find', + [], + {}, + ), + 'expected': { + 'count': 1, + 'truncated': False, + 'summary': u'1 vault matched', + 'result': [ + { + 'dn': u'cn=%s,cn=admin,cn=users,cn=vaults,%s' % (test_vault, api.env.basedn), + 'cn': [test_vault], + 'vault_id': u'/users/admin/%s' % test_vault, + }, + ], + }, + }, + + { + 'desc': 'Show test vault', + 'command': ( + 'vault_show', + [test_vault], + {}, + ), + 'expected': { + 'value': test_vault, + 'summary': None, + 'result': { + 'dn': u'cn=%s,cn=admin,cn=users,cn=vaults,%s' % (test_vault, api.env.basedn), + 'cn': [test_vault], + 'vault_id': u'/users/admin/%s' % test_vault, + }, + }, + }, + + { + 'desc': 'Modify test vault', + 'command': ( + 'vault_mod', + [test_vault], + { + 'description': u'Test vault', + }, + ), + 'expected': { + 'value': test_vault, + 'summary': u'Modified vault "%s"' % test_vault, + 'result': { + 'cn': [test_vault], + 'vault_id': u'/users/admin/%s' % test_vault, + 'description': [u'Test vault'], + }, + }, + }, + + { + 'desc': 'Archive binary data', + 'command': ( + 'vault_archive', + [test_vault], + { + 'data': binary_data, + }, + ), + 'expected': { + 'value': test_vault, + 'summary': u'Archived data into vault "%s"' % test_vault, + 'result': { + 'dn': u'cn=%s,cn=admin,cn=users,cn=vaults,%s' % (test_vault, api.env.basedn), + 'cn': [test_vault], + 'vault_id': u'/users/admin/%s' % test_vault, + 'description': [u'Test vault'], + }, + }, + }, + + { + 'desc': 'Retrieve binary data', + 'command': ( + 'vault_retrieve', + [test_vault], + {}, + ), + 'expected': { + 'value': test_vault, + 'summary': u'Retrieved data from vault "%s"' % test_vault, + 'result': { + 'dn': u'cn=%s,cn=admin,cn=users,cn=vaults,%s' % (test_vault, api.env.basedn), + 'cn': [test_vault], + 'vault_id': u'/users/admin/%s' % test_vault, + 'description': [u'Test vault'], + 'nonce': fuzzy_string, + 'encrypted_data': fuzzy_string, + 'data': binary_data, + }, + }, + }, + + { + 'desc': 'Archive text data', + 'command': ( + 'vault_archive', + [test_vault], + { + 'text': text_data, + }, + ), + 'expected': { + 'value': test_vault, + 'summary': u'Archived data into vault "%s"' % test_vault, + 'result': { + 'dn': u'cn=%s,cn=admin,cn=users,cn=vaults,%s' % (test_vault, api.env.basedn), + 'cn': [test_vault], + 'vault_id': u'/users/admin/%s' % test_vault, + 'description': [u'Test vault'], + }, + }, + }, + + { + 'desc': 'Retrieve text data', + 'command': ( + 'vault_retrieve', + [test_vault], + { + 'show_text': True, + }, + ), + 'expected': { + 'value': test_vault, + 'summary': u'Retrieved data from vault "%s"' % test_vault, + 'result': { + 'dn': u'cn=%s,cn=admin,cn=users,cn=vaults,%s' % (test_vault, api.env.basedn), + 'cn': [test_vault], + 'vault_id': u'/users/admin/%s' % test_vault, + 'description': [u'Test vault'], + 'nonce': fuzzy_string, + 'encrypted_data': fuzzy_string, + 'text': text_data, + }, + }, + }, + + { + 'desc': 'Delete test vault', + 'command': ( + 'vault_del', + [test_vault], + {}, + ), + 'expected': { + 'value': [test_vault], + 'summary': u'Deleted vault "%s"' % test_vault, + 'result': { + 'failed': (), + }, + }, + }, + + { + 'desc': 'Delete non-existent vault', + 'command': ( + 'vault_del', + [test_vault], + {}, + ), + 'expected': errors.NotFound(reason=u'%s: vault not found' % test_vault), + }, + + ] diff --git a/ipatests/test_xmlrpc/test_vaultcontainer_plugin.py b/ipatests/test_xmlrpc/test_vaultcontainer_plugin.py new file mode 100644 index 0000000000000000000000000000000000000000..957228b8f57623e93cda9a5295f9977dd6b84110 --- /dev/null +++ b/ipatests/test_xmlrpc/test_vaultcontainer_plugin.py @@ -0,0 +1,420 @@ +# Authors: +# Endi S. Dewata +# +# Copyright (C) 2015 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 . + +""" +Test the `ipalib/plugins/vaultcontainer.py` module. +""" + +from ipalib import api, errors +from xmlrpc_test import Declarative + +private_container = u'private_container' +shared_container = u'shared_container' +service_container = u'service_container' + +base_container = u'base_container' +child_container = u'child_container' +grandchild_container = u'grandchild_container' + +class test_vault(Declarative): + + cleanup_commands = [ + ('vaultcontainer_del', [private_container], {'continue': True}), + ('vaultcontainer_del', [shared_container], {'parent': u'/shared/', 'continue': True}), + ('vaultcontainer_del', [service_container], {'parent': u'/services/', 'continue': True}), + ('vaultcontainer_del', [base_container], {'force': True, 'continue': True}), + ] + + tests = [ + + { + 'desc': 'Find top-level containers', + 'command': ( + 'vaultcontainer_find', + [], + { + 'parent': u'/', + }, + ), + 'expected': { + 'count': 3, + 'truncated': False, + 'summary': u'3 vault containers matched', + 'result': [ + { + 'dn': u'cn=services,cn=vaults,%s' % api.env.basedn, + 'cn': [u'services'], + 'container_id': u'/services/', + 'description': [u'Services vault container'], + }, + { + 'dn': u'cn=shared,cn=vaults,%s' % api.env.basedn, + 'cn': [u'shared'], + 'container_id': u'/shared/', + 'description': [u'Shared vault container'], + }, + { + 'dn': u'cn=users,cn=vaults,%s' % api.env.basedn, + 'cn': [u'users'], + 'container_id': u'/users/', + 'description': [u'Users vault container'], + }, + ], + }, + }, + + { + 'desc': 'Create private container', + 'command': ( + 'vaultcontainer_add', + [private_container], + {}, + ), + 'expected': { + 'value': private_container, + 'summary': 'Added vault container "%s"' % private_container, + 'result': { + 'dn': u'cn=%s,cn=admin,cn=users,cn=vaults,%s' % (private_container, api.env.basedn), + 'objectclass': (u'ipaVaultContainer', u'top'), + 'cn': [private_container], + 'container_id': u'/users/admin/%s/' % private_container, + }, + }, + }, + + { + 'desc': 'Create duplicate container', + 'command': ( + 'vaultcontainer_add', + [private_container], + {}, + ), + 'expected': errors.DuplicateEntry(message=u'vault container with name "%s" already exists' % private_container), + }, + + { + 'desc': 'Find private containers', + 'command': ( + 'vaultcontainer_find', + [], + {}, + ), + 'expected': { + 'count': 1, + 'truncated': False, + 'summary': u'1 vault container matched', + 'result': [ + { + 'dn': u'cn=%s,cn=admin,cn=users,cn=vaults,%s' % (private_container, api.env.basedn), + 'cn': [private_container], + 'container_id': u'/users/admin/%s/' % private_container, + }, + ], + }, + }, + + { + 'desc': 'Show private container', + 'command': ( + 'vaultcontainer_show', + [private_container], + {}, + ), + 'expected': { + 'value': private_container, + 'summary': None, + 'result': { + 'dn': u'cn=%s,cn=admin,cn=users,cn=vaults,%s' % (private_container, api.env.basedn), + 'cn': [private_container], + 'container_id': u'/users/admin/%s/' % private_container, + }, + }, + }, + + { + 'desc': 'Modify private container', + 'command': ( + 'vaultcontainer_mod', + [private_container], + { + 'description': u'Private container', + }, + ), + 'expected': { + 'value': private_container, + 'summary': 'Modified vault container "%s"' % private_container, + 'result': { + 'cn': [private_container], + 'container_id': u'/users/admin/%s/' % private_container, + 'description': [u'Private container'], + }, + }, + }, + + { + 'desc': 'Delete private container', + 'command': ( + 'vaultcontainer_del', + [private_container], + {}, + ), + 'expected': { + 'value': [private_container], + 'summary': u'Deleted vault container "%s"' % private_container, + 'result': { + 'failed': (), + }, + }, + }, + + { + 'desc': 'Delete non-existent container', + 'command': ( + 'vaultcontainer_del', + [private_container], + {}, + ), + 'expected': errors.NotFound(reason=u'%s: vault container not found' % private_container), + }, + + { + 'desc': 'Create shared container', + 'command': ( + 'vaultcontainer_add', + [shared_container], + { + 'parent': u'/shared/', + }, + ), + 'expected': { + 'value': shared_container, + 'summary': 'Added vault container "%s"' % shared_container, + 'result': { + 'dn': u'cn=%s,cn=shared,cn=vaults,%s' % (shared_container, api.env.basedn), + 'objectclass': (u'ipaVaultContainer', u'top'), + 'cn': [shared_container], + 'container_id': u'/shared/%s/' % shared_container, + }, + }, + }, + + { + 'desc': 'Find shared containers', + 'command': ( + 'vaultcontainer_find', + [], + { + 'parent': u'/shared/', + }, + ), + 'expected': { + 'count': 1, + 'truncated': False, + 'summary': u'1 vault container matched', + 'result': [ + { + 'dn': u'cn=%s,cn=shared,cn=vaults,%s' % (shared_container, api.env.basedn), + 'cn': [shared_container], + 'container_id': u'/shared/%s/' % shared_container, + }, + ], + }, + }, + + { + 'desc': 'Show shared container', + 'command': ( + 'vaultcontainer_show', + [shared_container], + { + 'parent': u'/shared/', + }, + ), + 'expected': { + 'value': shared_container, + 'summary': None, + 'result': { + 'dn': u'cn=%s,cn=shared,cn=vaults,%s' % (shared_container, api.env.basedn), + 'cn': [shared_container], + 'container_id': u'/shared/%s/' % shared_container, + }, + }, + }, + + { + 'desc': 'Modify shared container', + 'command': ( + 'vaultcontainer_mod', + [shared_container], + { + 'parent': u'/shared/', + 'description': u'shared container', + }, + ), + 'expected': { + 'value': shared_container, + 'summary': 'Modified vault container "%s"' % shared_container, + 'result': { + 'cn': [shared_container], + 'container_id': u'/shared/%s/' % shared_container, + 'description': [u'shared container'], + }, + }, + }, + + { + 'desc': 'Delete shared container', + 'command': ( + 'vaultcontainer_del', + [shared_container], + { + 'parent': u'/shared/', + }, + ), + 'expected': { + 'value': [shared_container], + 'summary': u'Deleted vault container "%s"' % shared_container, + 'result': { + 'failed': (), + }, + }, + }, + + { + 'desc': 'Create service container', + 'command': ( + 'vaultcontainer_add', + [service_container], + { + 'parent': u'/services/', + }, + ), + 'expected': { + 'value': service_container, + 'summary': 'Added vault container "%s"' % service_container, + 'result': { + 'dn': u'cn=%s,cn=services,cn=vaults,%s' % (service_container, api.env.basedn), + 'objectclass': (u'ipaVaultContainer', u'top'), + 'cn': [service_container], + 'container_id': u'/services/%s/' % service_container, + }, + }, + }, + { + 'desc': 'Create base container', + 'command': ( + 'vaultcontainer_add', + [base_container], + {}, + ), + 'expected': { + 'value': base_container, + 'summary': 'Added vault container "%s"' % base_container, + 'result': { + 'dn': u'cn=%s,cn=admin,cn=users,cn=vaults,%s' % (base_container, api.env.basedn), + 'objectclass': (u'ipaVaultContainer', u'top'), + 'cn': [base_container], + 'container_id': u'/users/admin/%s/' % base_container, + }, + }, + }, + + { + 'desc': 'Create child container', + 'command': ( + 'vaultcontainer_add', + [child_container], + { + 'parent': base_container, + }, + ), + 'expected': { + 'value': child_container, + 'summary': 'Added vault container "%s"' % child_container, + 'result': { + 'dn': u'cn=%s,cn=%s,cn=admin,cn=users,cn=vaults,%s' % (child_container, base_container, api.env.basedn), + 'objectclass': (u'ipaVaultContainer', u'top'), + 'cn': [child_container], + 'container_id': u'/users/admin/%s/%s/' % (base_container, child_container), + }, + }, + }, + + { + 'desc': 'Create grandchild container', + 'command': ( + 'vaultcontainer_add', + [grandchild_container], + { + 'parent': base_container + u'/' + child_container, + }, + ), + 'expected': { + 'value': grandchild_container, + 'summary': 'Added vault container "%s"' % grandchild_container, + 'result': { + 'dn': u'cn=%s,cn=%s,cn=%s,cn=admin,cn=users,cn=vaults,%s' + % (grandchild_container, child_container, base_container, api.env.basedn), + 'objectclass': (u'ipaVaultContainer', u'top'), + 'cn': [grandchild_container], + 'container_id': u'/users/admin/%s/%s/%s/' + % (base_container, child_container, grandchild_container), + }, + }, + }, + + { + 'desc': 'Delete base container', + 'command': ( + 'vaultcontainer_del', + [base_container], + {}, + ), + 'expected': errors.NotAllowedOnNonLeaf(error=u'Not allowed on non-leaf entry'), + }, + + { + 'desc': 'Delete base container with force', + 'command': ( + 'vaultcontainer_del', + [base_container], + { + 'force': True, + }, + ), + 'expected': { + 'value': [base_container], + 'summary': u'Deleted vault container "%s"' % base_container, + 'result': { + 'failed': (), + }, + }, + }, + + { + 'desc': 'Delete non-existent container', + 'command': ( + 'vaultcontainer_del', + [base_container], + {}, + ), + 'expected': errors.NotFound(reason=u'%s: vault container not found' % base_container), + }, + + ] -- 1.9.0 -------------- next part -------------- From 1bc707bb66ab65a90906efa06d1bf4161ffbaa42 Mon Sep 17 00:00:00 2001 From: "Endi S. Dewata" Date: Fri, 17 Oct 2014 12:05:34 -0400 Subject: [PATCH] Added vault access control. New LDAP ACIs have been added to allow users to create their own private vault container, to allow owners to manage vaults and containers, and to allow members to use the vaults. New CLIs have been added to manage the owner and member list. For archive and retrieve operations the access control has to be enforced by the plugins because the operations only affects KRA. The LDAP schema has been updated as well. https://fedorahosted.org/freeipa/ticket/3872 --- API.txt | 134 ++++++++++++++++++-- VERSION | 4 +- install/share/60basev3.ldif | 4 +- install/updates/40-vault.update | 7 ++ ipalib/plugins/vault.py | 140 ++++++++++++++++++++- ipalib/plugins/vaultcontainer.py | 122 +++++++++++++++++- ipatests/test_xmlrpc/test_vault_plugin.py | 7 ++ ipatests/test_xmlrpc/test_vaultcontainer_plugin.py | 10 ++ 8 files changed, 406 insertions(+), 22 deletions(-) diff --git a/API.txt b/API.txt index 95e43390413731b2f5298e7e0cf7fc7accd1f69f..72ecf647bf21cddfaaea719ff5a74639897bd8e0 100644 --- a/API.txt +++ b/API.txt @@ -4518,7 +4518,7 @@ output: Output('result', , None) output: Output('summary', (, ), None) output: PrimaryKey('value', None, None) command: vault_add -args: 1,11,3 +args: 1,12,3 arg: Str('cn', attribute=True, cli_name='vault_name', maxlength=255, multivalue=False, pattern='^[a-zA-Z0-9_.-]+$', primary_key=True, required=True) option: Str('addattr*', cli_name='addattr', exclude='webui') option: Flag('all', autofill=True, cli_name='all', default=False, exclude='webui') @@ -4526,6 +4526,7 @@ option: Str('container', attribute=False, cli_name='container', multivalue=False option: Bytes('data?', cli_name='data') option: Str('description', attribute=True, cli_name='desc', multivalue=False, required=False) option: Str('in?', cli_name='in') +option: Flag('no_members', autofill=True, default=False, exclude='webui') option: Flag('raw', autofill=True, cli_name='raw', default=False, exclude='webui') option: Str('setattr*', cli_name='setattr', exclude='webui') option: Str('text?', cli_name='text') @@ -4534,14 +4535,41 @@ option: Str('version?', exclude='webui') output: Entry('result', , Gettext('A dictionary representing an LDAP entry', domain='ipa', localedir=None)) output: Output('summary', (, ), None) output: PrimaryKey('value', None, None) +command: vault_add_member +args: 1,7,3 +arg: Str('cn', attribute=True, cli_name='vault_name', maxlength=255, multivalue=False, pattern='^[a-zA-Z0-9_.-]+$', primary_key=True, query=True, required=True) +option: Flag('all', autofill=True, cli_name='all', default=False, exclude='webui') +option: Str('container?', cli_name='container') +option: Str('group*', alwaysask=True, cli_name='groups', csv=True) +option: Flag('no_members', autofill=True, default=False, exclude='webui') +option: Flag('raw', autofill=True, cli_name='raw', default=False, exclude='webui') +option: Str('user*', alwaysask=True, cli_name='users', csv=True) +option: Str('version?', exclude='webui') +output: Output('completed', , None) +output: Output('failed', , None) +output: Entry('result', , Gettext('A dictionary representing an LDAP entry', domain='ipa', localedir=None)) +command: vault_add_owner +args: 1,7,3 +arg: Str('cn', attribute=True, cli_name='vault_name', maxlength=255, multivalue=False, pattern='^[a-zA-Z0-9_.-]+$', primary_key=True, query=True, required=True) +option: Flag('all', autofill=True, cli_name='all', default=False, exclude='webui') +option: Str('container?', cli_name='container') +option: Str('group*', alwaysask=True, cli_name='groups', csv=True) +option: Flag('no_members', autofill=True, default=False, exclude='webui') +option: Flag('raw', autofill=True, cli_name='raw', default=False, exclude='webui') +option: Str('user*', alwaysask=True, cli_name='users', csv=True) +option: Str('version?', exclude='webui') +output: Output('completed', , None) +output: Output('failed', , None) +output: Entry('result', , Gettext('A dictionary representing an LDAP entry', domain='ipa', localedir=None)) command: vault_archive -args: 1,11,3 +args: 1,12,3 arg: Str('cn', attribute=True, cli_name='vault_name', maxlength=255, multivalue=False, pattern='^[a-zA-Z0-9_.-]+$', primary_key=True, query=True, required=True) option: Flag('all', autofill=True, cli_name='all', default=False, exclude='webui') option: Str('container?', cli_name='container') option: Bytes('data?', cli_name='data') option: Str('encrypted_data?', cli_name='encrypted_data') option: Str('in?', cli_name='in') +option: Flag('no_members', autofill=True, default=False, exclude='webui') option: Str('nonce?', cli_name='nonce') option: Flag('raw', autofill=True, cli_name='raw', default=False, exclude='webui') option: Flag('rights', autofill=True, default=False) @@ -4561,12 +4589,13 @@ output: Output('result', , None) output: Output('summary', (, ), None) output: ListOfPrimaryKeys('value', None, None) command: vault_find -args: 1,10,4 +args: 1,11,4 arg: Str('criteria?', noextrawhitespace=False) option: Flag('all', autofill=True, cli_name='all', default=False, exclude='webui') option: Str('cn', attribute=True, autofill=False, cli_name='vault_name', maxlength=255, multivalue=False, pattern='^[a-zA-Z0-9_.-]+$', primary_key=True, query=True, required=False) option: Str('container', attribute=False, autofill=False, cli_name='container', multivalue=False, pattern='^[a-zA-Z0-9_.-/]+$', query=True, required=False) option: Str('description', attribute=True, autofill=False, cli_name='desc', multivalue=False, query=True, required=False) +option: Flag('no_members', autofill=True, default=False, exclude='webui') option: Flag('pkey_only?', autofill=True, default=False) option: Flag('raw', autofill=True, cli_name='raw', default=False, exclude='webui') option: Int('sizelimit?', autofill=False, minvalue=0) @@ -4578,13 +4607,14 @@ output: ListOfEntries('result', (, ), Gettext('A list output: Output('summary', (, ), None) output: Output('truncated', , None) command: vault_mod -args: 1,10,3 +args: 1,11,3 arg: Str('cn', attribute=True, cli_name='vault_name', maxlength=255, multivalue=False, pattern='^[a-zA-Z0-9_.-]+$', primary_key=True, query=True, required=True) option: Str('addattr*', cli_name='addattr', exclude='webui') option: Flag('all', autofill=True, cli_name='all', default=False, exclude='webui') option: Str('container', attribute=False, autofill=False, cli_name='container', multivalue=False, pattern='^[a-zA-Z0-9_.-/]+$', required=False) option: Str('delattr*', cli_name='delattr', exclude='webui') option: Str('description', attribute=True, autofill=False, cli_name='desc', multivalue=False, required=False) +option: Flag('no_members', autofill=True, default=False, exclude='webui') option: Flag('raw', autofill=True, cli_name='raw', default=False, exclude='webui') option: Flag('rights', autofill=True, default=False) option: Str('setattr*', cli_name='setattr', exclude='webui') @@ -4593,11 +4623,38 @@ option: Str('version?', exclude='webui') output: Entry('result', , Gettext('A dictionary representing an LDAP entry', domain='ipa', localedir=None)) output: Output('summary', (, ), None) output: PrimaryKey('value', None, None) +command: vault_remove_member +args: 1,7,3 +arg: Str('cn', attribute=True, cli_name='vault_name', maxlength=255, multivalue=False, pattern='^[a-zA-Z0-9_.-]+$', primary_key=True, query=True, required=True) +option: Flag('all', autofill=True, cli_name='all', default=False, exclude='webui') +option: Str('container?', cli_name='container') +option: Str('group*', alwaysask=True, cli_name='groups', csv=True) +option: Flag('no_members', autofill=True, default=False, exclude='webui') +option: Flag('raw', autofill=True, cli_name='raw', default=False, exclude='webui') +option: Str('user*', alwaysask=True, cli_name='users', csv=True) +option: Str('version?', exclude='webui') +output: Output('completed', , None) +output: Output('failed', , None) +output: Entry('result', , Gettext('A dictionary representing an LDAP entry', domain='ipa', localedir=None)) +command: vault_remove_owner +args: 1,7,3 +arg: Str('cn', attribute=True, cli_name='vault_name', maxlength=255, multivalue=False, pattern='^[a-zA-Z0-9_.-]+$', primary_key=True, query=True, required=True) +option: Flag('all', autofill=True, cli_name='all', default=False, exclude='webui') +option: Str('container?', cli_name='container') +option: Str('group*', alwaysask=True, cli_name='groups', csv=True) +option: Flag('no_members', autofill=True, default=False, exclude='webui') +option: Flag('raw', autofill=True, cli_name='raw', default=False, exclude='webui') +option: Str('user*', alwaysask=True, cli_name='users', csv=True) +option: Str('version?', exclude='webui') +output: Output('completed', , None) +output: Output('failed', , None) +output: Entry('result', , Gettext('A dictionary representing an LDAP entry', domain='ipa', localedir=None)) command: vault_retrieve -args: 1,9,3 +args: 1,10,3 arg: Str('cn', attribute=True, cli_name='vault_name', maxlength=255, multivalue=False, pattern='^[a-zA-Z0-9_.-]+$', primary_key=True, query=True, required=True) option: Flag('all', autofill=True, cli_name='all', default=False, exclude='webui') option: Str('container?', cli_name='container') +option: Flag('no_members', autofill=True, default=False, exclude='webui') option: Str('out?', cli_name='out') option: Flag('raw', autofill=True, cli_name='raw', default=False, exclude='webui') option: Flag('rights', autofill=True, default=False) @@ -4609,10 +4666,11 @@ output: Entry('result', , Gettext('A dictionary representing an LDA output: Output('summary', (, ), None) output: PrimaryKey('value', None, None) command: vault_show -args: 1,5,3 +args: 1,6,3 arg: Str('cn', attribute=True, cli_name='vault_name', maxlength=255, multivalue=False, pattern='^[a-zA-Z0-9_.-]+$', primary_key=True, query=True, required=True) option: Flag('all', autofill=True, cli_name='all', default=False, exclude='webui') option: Str('container?', cli_name='container') +option: Flag('no_members', autofill=True, default=False, exclude='webui') option: Flag('raw', autofill=True, cli_name='raw', default=False, exclude='webui') option: Flag('rights', autofill=True, default=False) option: Str('version?', exclude='webui') @@ -4620,12 +4678,13 @@ output: Entry('result', , Gettext('A dictionary representing an LDA output: Output('summary', (, ), None) output: PrimaryKey('value', None, None) command: vaultcontainer_add -args: 1,8,3 +args: 1,9,3 arg: Str('cn', attribute=True, cli_name='container_name', maxlength=255, multivalue=False, pattern='^[a-zA-Z0-9_.-]+$', primary_key=True, required=True) option: Str('addattr*', cli_name='addattr', exclude='webui') option: Flag('all', autofill=True, cli_name='all', default=False, exclude='webui') option: Str('container_id', attribute=False, cli_name='container_id', multivalue=False, required=False) option: Str('description', attribute=True, cli_name='desc', multivalue=False, required=False) +option: Flag('no_members', autofill=True, default=False, exclude='webui') option: Str('parent', attribute=False, cli_name='parent', multivalue=False, pattern='^[a-zA-Z0-9_.-/]+$', required=False) option: Flag('raw', autofill=True, cli_name='raw', default=False, exclude='webui') option: Str('setattr*', cli_name='setattr', exclude='webui') @@ -4633,6 +4692,32 @@ option: Str('version?', exclude='webui') output: Entry('result', , Gettext('A dictionary representing an LDAP entry', domain='ipa', localedir=None)) output: Output('summary', (, ), None) output: PrimaryKey('value', None, None) +command: vaultcontainer_add_member +args: 1,7,3 +arg: Str('cn', attribute=True, cli_name='container_name', maxlength=255, multivalue=False, pattern='^[a-zA-Z0-9_.-]+$', primary_key=True, query=True, required=True) +option: Flag('all', autofill=True, cli_name='all', default=False, exclude='webui') +option: Str('group*', alwaysask=True, cli_name='groups', csv=True) +option: Flag('no_members', autofill=True, default=False, exclude='webui') +option: Str('parent?', cli_name='parent') +option: Flag('raw', autofill=True, cli_name='raw', default=False, exclude='webui') +option: Str('user*', alwaysask=True, cli_name='users', csv=True) +option: Str('version?', exclude='webui') +output: Output('completed', , None) +output: Output('failed', , None) +output: Entry('result', , Gettext('A dictionary representing an LDAP entry', domain='ipa', localedir=None)) +command: vaultcontainer_add_owner +args: 1,7,3 +arg: Str('cn', attribute=True, cli_name='container_name', maxlength=255, multivalue=False, pattern='^[a-zA-Z0-9_.-]+$', primary_key=True, query=True, required=True) +option: Flag('all', autofill=True, cli_name='all', default=False, exclude='webui') +option: Str('group*', alwaysask=True, cli_name='groups', csv=True) +option: Flag('no_members', autofill=True, default=False, exclude='webui') +option: Str('parent?', cli_name='parent') +option: Flag('raw', autofill=True, cli_name='raw', default=False, exclude='webui') +option: Str('user*', alwaysask=True, cli_name='users', csv=True) +option: Str('version?', exclude='webui') +output: Output('completed', , None) +output: Output('failed', , None) +output: Entry('result', , Gettext('A dictionary representing an LDAP entry', domain='ipa', localedir=None)) command: vaultcontainer_del args: 1,4,3 arg: Str('cn', attribute=True, cli_name='container_name', maxlength=255, multivalue=True, pattern='^[a-zA-Z0-9_.-]+$', primary_key=True, query=True, required=True) @@ -4644,12 +4729,13 @@ output: Output('result', , None) output: Output('summary', (, ), None) output: ListOfPrimaryKeys('value', None, None) command: vaultcontainer_find -args: 1,10,4 +args: 1,11,4 arg: Str('criteria?', noextrawhitespace=False) option: Flag('all', autofill=True, cli_name='all', default=False, exclude='webui') option: Str('cn', attribute=True, autofill=False, cli_name='container_name', maxlength=255, multivalue=False, pattern='^[a-zA-Z0-9_.-]+$', primary_key=True, query=True, required=False) option: Str('container_id', attribute=False, autofill=False, cli_name='container_id', multivalue=False, query=True, required=False) option: Str('description', attribute=True, autofill=False, cli_name='desc', multivalue=False, query=True, required=False) +option: Flag('no_members', autofill=True, default=False, exclude='webui') option: Str('parent', attribute=False, autofill=False, cli_name='parent', multivalue=False, pattern='^[a-zA-Z0-9_.-/]+$', query=True, required=False) option: Flag('pkey_only?', autofill=True, default=False) option: Flag('raw', autofill=True, cli_name='raw', default=False, exclude='webui') @@ -4661,13 +4747,14 @@ output: ListOfEntries('result', (, ), Gettext('A list output: Output('summary', (, ), None) output: Output('truncated', , None) command: vaultcontainer_mod -args: 1,10,3 +args: 1,11,3 arg: Str('cn', attribute=True, cli_name='container_name', maxlength=255, multivalue=False, pattern='^[a-zA-Z0-9_.-]+$', primary_key=True, query=True, required=True) option: Str('addattr*', cli_name='addattr', exclude='webui') option: Flag('all', autofill=True, cli_name='all', default=False, exclude='webui') option: Str('container_id', attribute=False, autofill=False, cli_name='container_id', multivalue=False, required=False) option: Str('delattr*', cli_name='delattr', exclude='webui') option: Str('description', attribute=True, autofill=False, cli_name='desc', multivalue=False, required=False) +option: Flag('no_members', autofill=True, default=False, exclude='webui') option: Str('parent', attribute=False, autofill=False, cli_name='parent', multivalue=False, pattern='^[a-zA-Z0-9_.-/]+$', required=False) option: Flag('raw', autofill=True, cli_name='raw', default=False, exclude='webui') option: Flag('rights', autofill=True, default=False) @@ -4676,10 +4763,37 @@ option: Str('version?', exclude='webui') output: Entry('result', , Gettext('A dictionary representing an LDAP entry', domain='ipa', localedir=None)) output: Output('summary', (, ), None) output: PrimaryKey('value', None, None) +command: vaultcontainer_remove_member +args: 1,7,3 +arg: Str('cn', attribute=True, cli_name='container_name', maxlength=255, multivalue=False, pattern='^[a-zA-Z0-9_.-]+$', primary_key=True, query=True, required=True) +option: Flag('all', autofill=True, cli_name='all', default=False, exclude='webui') +option: Str('group*', alwaysask=True, cli_name='groups', csv=True) +option: Flag('no_members', autofill=True, default=False, exclude='webui') +option: Str('parent?', cli_name='parent') +option: Flag('raw', autofill=True, cli_name='raw', default=False, exclude='webui') +option: Str('user*', alwaysask=True, cli_name='users', csv=True) +option: Str('version?', exclude='webui') +output: Output('completed', , None) +output: Output('failed', , None) +output: Entry('result', , Gettext('A dictionary representing an LDAP entry', domain='ipa', localedir=None)) +command: vaultcontainer_remove_owner +args: 1,7,3 +arg: Str('cn', attribute=True, cli_name='container_name', maxlength=255, multivalue=False, pattern='^[a-zA-Z0-9_.-]+$', primary_key=True, query=True, required=True) +option: Flag('all', autofill=True, cli_name='all', default=False, exclude='webui') +option: Str('group*', alwaysask=True, cli_name='groups', csv=True) +option: Flag('no_members', autofill=True, default=False, exclude='webui') +option: Str('parent?', cli_name='parent') +option: Flag('raw', autofill=True, cli_name='raw', default=False, exclude='webui') +option: Str('user*', alwaysask=True, cli_name='users', csv=True) +option: Str('version?', exclude='webui') +output: Output('completed', , None) +output: Output('failed', , None) +output: Entry('result', , Gettext('A dictionary representing an LDAP entry', domain='ipa', localedir=None)) command: vaultcontainer_show -args: 1,5,3 +args: 1,6,3 arg: Str('cn', attribute=True, cli_name='container_name', maxlength=255, multivalue=False, pattern='^[a-zA-Z0-9_.-]+$', primary_key=True, query=True, required=True) option: Flag('all', autofill=True, cli_name='all', default=False, exclude='webui') +option: Flag('no_members', autofill=True, default=False, exclude='webui') option: Str('parent?', cli_name='parent') option: Flag('raw', autofill=True, cli_name='raw', default=False, exclude='webui') option: Flag('rights', autofill=True, default=False) diff --git a/VERSION b/VERSION index f02f749cd0bd01ccb38de8acdc4c44f4ae411476..afe37045b1b75b194696bf21574fff25b8e994b1 100644 --- a/VERSION +++ b/VERSION @@ -90,5 +90,5 @@ IPA_DATA_VERSION=20100614120000 # # ######################################################## IPA_API_VERSION_MAJOR=2 -IPA_API_VERSION_MINOR=114 -# Last change: edewata - initial vault implementation +IPA_API_VERSION_MINOR=115 +# Last change: edewata - added vault access control diff --git a/install/share/60basev3.ldif b/install/share/60basev3.ldif index e0bc4151d88fc394cea0d81a20d8da9537dd5cd3..4883132f1627e16a16b6e7effed2af4be287e251 100644 --- a/install/share/60basev3.ldif +++ b/install/share/60basev3.ldif @@ -77,5 +77,5 @@ objectClasses: (2.16.840.1.113730.3.8.12.24 NAME 'ipaPublicKeyObject' DESC 'Wrap objectClasses: (2.16.840.1.113730.3.8.12.25 NAME 'ipaPrivateKeyObject' DESC 'Wrapped private keys' SUP top AUXILIARY MUST ( ipaPrivateKey $ ipaWrappingKey $ ipaWrappingMech ) X-ORIGIN 'IPA v4.1' ) objectClasses: (2.16.840.1.113730.3.8.12.26 NAME 'ipaSecretKeyObject' DESC 'Wrapped secret keys' SUP top AUXILIARY MUST ( ipaSecretKey $ ipaWrappingKey $ ipaWrappingMech ) X-ORIGIN 'IPA v4.1' ) objectClasses: (2.16.840.1.113730.3.8.12.34 NAME 'ipaSecretKeyRefObject' DESC 'Indirect storage for encoded key material' SUP top AUXILIARY MUST ( ipaSecretKeyRef ) X-ORIGIN 'IPA v4.1' ) -objectClasses: (2.16.840.1.113730.3.8.18.1.1 NAME 'ipaVault' DESC 'IPA vault' SUP top STRUCTURAL MUST ( cn ) MAY ( description ) X-ORIGIN 'IPA v4.2' ) -objectClasses: (2.16.840.1.113730.3.8.18.1.2 NAME 'ipaVaultContainer' DESC 'IPA vault container' SUP top STRUCTURAL MUST ( cn ) MAY ( description ) X-ORIGIN 'IPA v4.2' ) +objectClasses: (2.16.840.1.113730.3.8.18.1.1 NAME 'ipaVault' DESC 'IPA vault' SUP top STRUCTURAL MUST ( cn ) MAY ( description $ owner $ member ) X-ORIGIN 'IPA v4.2' ) +objectClasses: (2.16.840.1.113730.3.8.18.1.2 NAME 'ipaVaultContainer' DESC 'IPA vault container' SUP top STRUCTURAL MUST ( cn ) MAY ( description $ owner $ member ) X-ORIGIN 'IPA v4.2' ) diff --git a/install/updates/40-vault.update b/install/updates/40-vault.update index dac2f67112dc33f012c6d559285464fb7c944d1a..4f36a8066504fb3577fc9f78c830412e9702f617 100644 --- a/install/updates/40-vault.update +++ b/install/updates/40-vault.update @@ -3,6 +3,13 @@ default: objectClass: top default: objectClass: ipaVaultContainer default: cn: vaults default: description: Root vault container +default: aci: (target="ldap:///cn=*,cn=users,cn=vaults,$SUFFIX")(targetattr="*")(version 3.0; acl "Allow add macro dn"; allow (add) userdn = "ldap:///uid=($$attr.cn),cn=users,cn=accounts,$SUFFIX";) +default: aci: (targetfilter="(objectClass=ipaVaultContainer)")(targetattr="*")(version 3.0; acl "Container members can access the container"; allow(read, search, compare) userattr="member#USERDN";) +default: aci: (targetfilter="(objectClass=ipaVaultContainer)")(targetattr="*")(version 3.0; acl "Container owners can modify the container"; allow(read, search, compare, write) userattr="owner#USERDN";) +default: aci: (targetfilter="(objectClass=ipaVaultContainer)")(targetattr="*")(version 3.0; acl "Container owners can manage sub-containers"; allow(read, search, compare, add, delete) userattr="parent[1].owner#USERDN";) +default: aci: (targetfilter="(objectClass=ipaVault)")(targetattr="*")(version 3.0; acl "Container owners can manage vaults in the container"; allow(read, search, compare, add, delete) userattr="parent[1].owner#USERDN";) +default: aci: (targetfilter="(objectClass=ipaVault)")(targetattr="*")(version 3.0; acl "Vault members can access the vault"; allow(read, search, compare) userattr="member#USERDN";) +default: aci: (targetfilter="(objectClass=ipaVault)")(targetattr="*")(version 3.0; acl "Vault owners can modify the vault"; allow(read, search, compare, write) userattr="owner#USERDN";) dn: cn=services,cn=vaults,$SUFFIX default: objectClass: top diff --git a/ipalib/plugins/vault.py b/ipalib/plugins/vault.py index 437d57d29d0f6dd6b29f376fb6bc46d2acc9dab9..45dc596562f2eaf7c88e56c76085be1afc94b235 100644 --- a/ipalib/plugins/vault.py +++ b/ipalib/plugins/vault.py @@ -27,7 +27,10 @@ import pki.key from ipalib import api, errors from ipalib import Str, Bytes, Flag from ipalib.plugable import Registry -from ipalib.plugins.baseldap import LDAPObject, LDAPCreate, LDAPDelete, LDAPSearch, LDAPUpdate, LDAPRetrieve +from ipalib.plugins.baseldap import LDAPObject, LDAPCreate, LDAPDelete, LDAPSearch, LDAPUpdate, LDAPRetrieve,\ + LDAPAddMember, LDAPRemoveMember +from ipalib.request import context +from ipalib.plugins.user import split_principal from ipalib import _, ngettext from ipaplatform.paths import paths from ipapython.dn import DN @@ -63,6 +66,18 @@ EXAMPLES: """) + _(""" Delete a vault: ipa vault-del MyVault +""") + _(""" + Add a vault owner: + ipa vault-add-owner MyVault --users testuser +""") + _(""" + Delete a vault owner: + ipa vault-remove-owner MyVault --users testuser +""") + _(""" + Add a vault member: + ipa vault-add-member MyVault --users testuser +""") + _(""" + Delete a vault member: + ipa vault-remove-member MyVault --users testuser """) register = Registry() @@ -82,12 +97,18 @@ class vault(LDAPObject): 'cn', 'vault_id', 'description', + 'owner', + 'member', ] search_display_attributes = [ 'cn', 'vault_id', 'description', ] + attribute_members = { + 'owner': ['user', 'group'], + 'member': ['user', 'group'], + } label = _('Vaults') label_singular = _('Vault') @@ -266,8 +287,13 @@ class vault_add(LDAPCreate): def pre_callback(self, ldap, dn, entry_attrs, attrs_list, *keys, **options): assert isinstance(dn, DN) + principal = getattr(context, 'principal') + (username, realm) = split_principal(principal) + owner_dn = self.api.Object['user'].get_dn(username) + entry_attrs['owner'] = owner_dn + container_dn = DN(*dn[1:]) - api.Object.vaultcontainer.create_entry(container_dn) + api.Object.vaultcontainer.create_entry(container_dn, owner=owner_dn) return dn @@ -328,10 +354,14 @@ class vault_find(LDAPSearch): def pre_callback(self, ldap, filter, attrs_list, base_dn, scope, *keys, **options): assert isinstance(base_dn, DN) + principal = getattr(context, 'principal') + (username, realm) = split_principal(principal) + owner_dn = self.api.Object['user'].get_dn(username) + container_id = self.Object.vaultcontainer.normalize_id(options.get('container')) base_dn = self.Object.vaultcontainer.get_dn(parent=container_id) - api.Object.vaultcontainer.create_entry(base_dn) + api.Object.vaultcontainer.create_entry(base_dn, owner=owner_dn) return (filter, base_dn, scope) @@ -491,6 +521,17 @@ class vault_archive(LDAPRetrieve): assert isinstance(dn, DN) vault_id = self.obj.get_id(dn) + + owners = entry_attrs.get('owner', []) + members = entry_attrs.get('member', []) + + principal = getattr(context, 'principal') + (username, realm) = split_principal(principal) + user_dn = self.api.Object['user'].get_dn(username) + + if user_dn not in owners and user_dn not in members: + raise errors.ACIError(info=_("Insufficient access to vault '%s'.") % vault_id) + entry_attrs['vault_id'] = vault_id # connect to KRA @@ -635,6 +676,17 @@ class vault_retrieve(LDAPRetrieve): assert isinstance(dn, DN) vault_id = self.obj.get_id(dn) + + owners = entry_attrs.get('owner', []) + members = entry_attrs.get('member', []) + + principal = getattr(context, 'principal') + (username, realm) = split_principal(principal) + user_dn = self.api.Object['user'].get_dn(username) + + if user_dn not in owners and user_dn not in members: + raise errors.ACIError(info=_("Insufficient access to vault '%s'.") % vault_id) + entry_attrs['vault_id'] = vault_id wrapped_session_key = base64.b64decode(options['wrapped_session_key']) @@ -666,3 +718,85 @@ class vault_retrieve(LDAPRetrieve): kra_account.logout() return dn + + + at register() +class vault_add_owner(LDAPAddMember): + __doc__ = _('Add owners to a vault.') + + takes_options = LDAPAddMember.takes_options + ( + Str('container?', + cli_name='container', + doc=_('Container'), + ), + ) + + member_attributes = ['owner'] + member_count_out = ('%i owner added.', '%i owners added.') + + def post_callback(self, ldap, completed, failed, dn, entry_attrs, *keys, **options): + assert isinstance(dn, DN) + + entry_attrs['vault_id'] = self.obj.get_id(dn) + + return (completed, dn) + + + at register() +class vault_remove_owner(LDAPRemoveMember): + __doc__ = _('Remove owners from a vault.') + + takes_options = LDAPRemoveMember.takes_options + ( + Str('container?', + cli_name='container', + doc=_('Container'), + ), + ) + + member_attributes = ['owner'] + member_count_out = ('%i owner removed.', '%i owners removed.') + + def post_callback(self, ldap, completed, failed, dn, entry_attrs, *keys, **options): + assert isinstance(dn, DN) + + entry_attrs['vault_id'] = self.obj.get_id(dn) + + return (completed, dn) + + + at register() +class vault_add_member(LDAPAddMember): + __doc__ = _('Add members to a vault.') + + takes_options = LDAPAddMember.takes_options + ( + Str('container?', + cli_name='container', + doc=_('Container'), + ), + ) + + def post_callback(self, ldap, completed, failed, dn, entry_attrs, *keys, **options): + assert isinstance(dn, DN) + + entry_attrs['vault_id'] = self.obj.get_id(dn) + + return (completed, dn) + + + at register() +class vault_remove_member(LDAPRemoveMember): + __doc__ = _('Remove members from a vault.') + + takes_options = LDAPRemoveMember.takes_options + ( + Str('container?', + cli_name='container', + doc=_('Container'), + ), + ) + + def post_callback(self, ldap, completed, failed, dn, entry_attrs, *keys, **options): + assert isinstance(dn, DN) + + entry_attrs['vault_id'] = self.obj.get_id(dn) + + return (completed, dn) diff --git a/ipalib/plugins/vaultcontainer.py b/ipalib/plugins/vaultcontainer.py index 22d36eecc6a842240cbadd3ff6b6518efbfe524e..881bbea7886e06356489cd49cc32f2a6a6460a5e 100644 --- a/ipalib/plugins/vaultcontainer.py +++ b/ipalib/plugins/vaultcontainer.py @@ -22,7 +22,8 @@ import base64 from ipalib import api, errors from ipalib import Str, Flag from ipalib.plugable import Registry -from ipalib.plugins.baseldap import LDAPObject, LDAPCreate, LDAPDelete, LDAPSearch, LDAPUpdate, LDAPRetrieve +from ipalib.plugins.baseldap import LDAPObject, LDAPCreate, LDAPDelete, LDAPSearch, LDAPUpdate, LDAPRetrieve,\ + LDAPAddMember, LDAPRemoveMember from ipalib.request import context from ipalib.plugins.user import split_principal from ipalib import _, ngettext @@ -52,6 +53,18 @@ EXAMPLES: """) + _(""" Delete a vault container: ipa vaultcontainer-del MyContainer +""") + _(""" + Add a vault container owner: + ipa vaultcontainer-add-owner MyContainer --users testuser +""") + _(""" + Delete a vault container owner: + ipa vaultcontainer-remove-owner MyContainer --users testuser +""") + _(""" + Add a vault container member: + ipa vaultcontainer-add-member MyContainer --users testuser +""") + _(""" + Delete a vault container member: + ipa vaultcontainer-remove-member MyContainer --users testuser """) register = Registry() @@ -71,12 +84,18 @@ class vaultcontainer(LDAPObject): 'cn', 'container_id', 'description', + 'owner', + 'member', ] search_display_attributes = [ 'cn', 'container_id', 'description', ] + attribute_members = { + 'owner': ['user', 'group'], + 'member': ['user', 'group'], + } label = _('Vault Containers') label_singular = _('Vault Container') @@ -203,7 +222,7 @@ class vaultcontainer(LDAPObject): # return container name and parent ID return (parts[1], parts[0] + u'/') - def create_entry(self, dn): + def create_entry(self, dn, owner=None): __doc__ = _(""" Creates a container entry and its parents. """) @@ -218,7 +237,7 @@ class vaultcontainer(LDAPObject): # otherwise, create parent entry first parent_dn = DN(*dn[1:]) - self.create_entry(parent_dn) + self.create_entry(parent_dn, owner=owner) # then create the entry itself rdn = dn[0] @@ -227,6 +246,7 @@ class vaultcontainer(LDAPObject): { 'objectclass': self.object_class, 'cn': rdn['cn'], + 'owner': owner }) self.backend.add_entry(entry) @@ -240,8 +260,13 @@ class vaultcontainer_add(LDAPCreate): def pre_callback(self, ldap, dn, entry_attrs, attrs_list, *keys, **options): assert isinstance(dn, DN) + principal = getattr(context, 'principal') + (username, realm) = split_principal(principal) + owner_dn = self.api.Object['user'].get_dn(username) + entry_attrs['owner'] = owner_dn + parent_dn = DN(*dn[1:]) - self.obj.create_entry(parent_dn) + self.obj.create_entry(parent_dn, owner=owner_dn) return dn @@ -313,10 +338,14 @@ class vaultcontainer_find(LDAPSearch): def pre_callback(self, ldap, filter, attrs_list, base_dn, scope, *keys, **options): assert isinstance(base_dn, DN) + principal = getattr(context, 'principal') + (username, realm) = split_principal(principal) + owner_dn = self.api.Object['user'].get_dn(username) + parent_id = self.obj.normalize_id(options.get('parent')) base_dn = self.obj.get_dn(parent=parent_id) - self.obj.create_entry(base_dn) + self.obj.create_entry(base_dn, owner=owner_dn) return (filter, base_dn, scope) @@ -359,3 +388,86 @@ class vaultcontainer_show(LDAPRetrieve): entry_attrs['container_id'] = self.obj.get_id(dn) return dn + + + at register() +class vaultcontainer_add_owner(LDAPAddMember): + __doc__ = _('Add owners to a vault container.') + + takes_options = LDAPAddMember.takes_options + ( + Str('parent?', + cli_name='parent', + doc=_('Parent container'), + ), + ) + + member_attributes = ['owner'] + member_count_out = ('%i owner added.', '%i owners added.') + + def post_callback(self, ldap, completed, failed, dn, entry_attrs, *keys, **options): + assert isinstance(dn, DN) + + entry_attrs['container_id'] = self.obj.get_id(dn) + + return (completed, dn) + + + at register() +class vaultcontainer_remove_owner(LDAPRemoveMember): + __doc__ = _('Remove owners from a vault container.') + + takes_options = LDAPRemoveMember.takes_options + ( + Str('parent?', + cli_name='parent', + doc=_('Parent container'), + ), + ) + + member_attributes = ['owner'] + member_count_out = ('%i owner removed.', '%i owners removed.') + + def post_callback(self, ldap, completed, failed, dn, entry_attrs, *keys, **options): + assert isinstance(dn, DN) + + entry_attrs['container_id'] = self.obj.get_id(dn) + + return (completed, dn) + + + at register() +class vaultcontainer_add_member(LDAPAddMember): + __doc__ = _('Add members to a vault container.') + + takes_options = LDAPAddMember.takes_options + ( + Str('parent?', + cli_name='parent', + doc=_('Parent container'), + ), + ) + + def post_callback(self, ldap, completed, failed, dn, entry_attrs, *keys, **options): + assert isinstance(dn, DN) + + entry_attrs['container_id'] = self.obj.get_id(dn) + + return (completed, dn) + + + at register() +class vaultcontainer_remove_member(LDAPRemoveMember): + __doc__ = _('Remove members from a vault container.') + + + takes_options = LDAPRemoveMember.takes_options + ( + Str('parent?', + cli_name='parent', + doc=_('Parent container'), + ), + ) + + def post_callback(self, ldap, completed, failed, dn, entry_attrs, *keys, **options): + assert isinstance(dn, DN) + + entry_attrs['container_id'] = self.obj.get_id(dn) + + return (completed, dn) diff --git a/ipatests/test_xmlrpc/test_vault_plugin.py b/ipatests/test_xmlrpc/test_vault_plugin.py index 0f8b56f45d5130f7b80075475de7ad7349360f81..6d74967588230cda49b326197d97ee6425af0ed5 100644 --- a/ipatests/test_xmlrpc/test_vault_plugin.py +++ b/ipatests/test_xmlrpc/test_vault_plugin.py @@ -51,6 +51,7 @@ class test_vault(Declarative): 'objectclass': (u'ipaVault', u'top'), 'cn': [test_vault], 'vault_id': u'/users/admin/%s' % test_vault, + 'owner_user': [u'admin'], }, }, }, @@ -100,6 +101,7 @@ class test_vault(Declarative): 'dn': u'cn=%s,cn=admin,cn=users,cn=vaults,%s' % (test_vault, api.env.basedn), 'cn': [test_vault], 'vault_id': u'/users/admin/%s' % test_vault, + 'owner_user': [u'admin'], }, }, }, @@ -120,6 +122,7 @@ class test_vault(Declarative): 'cn': [test_vault], 'vault_id': u'/users/admin/%s' % test_vault, 'description': [u'Test vault'], + 'owner_user': [u'admin'], }, }, }, @@ -141,6 +144,7 @@ class test_vault(Declarative): 'cn': [test_vault], 'vault_id': u'/users/admin/%s' % test_vault, 'description': [u'Test vault'], + 'owner_user': [u'admin'], }, }, }, @@ -160,6 +164,7 @@ class test_vault(Declarative): 'cn': [test_vault], 'vault_id': u'/users/admin/%s' % test_vault, 'description': [u'Test vault'], + 'owner_user': [u'admin'], 'nonce': fuzzy_string, 'encrypted_data': fuzzy_string, 'data': binary_data, @@ -184,6 +189,7 @@ class test_vault(Declarative): 'cn': [test_vault], 'vault_id': u'/users/admin/%s' % test_vault, 'description': [u'Test vault'], + 'owner_user': [u'admin'], }, }, }, @@ -205,6 +211,7 @@ class test_vault(Declarative): 'cn': [test_vault], 'vault_id': u'/users/admin/%s' % test_vault, 'description': [u'Test vault'], + 'owner_user': [u'admin'], 'nonce': fuzzy_string, 'encrypted_data': fuzzy_string, 'text': text_data, diff --git a/ipatests/test_xmlrpc/test_vaultcontainer_plugin.py b/ipatests/test_xmlrpc/test_vaultcontainer_plugin.py index 957228b8f57623e93cda9a5295f9977dd6b84110..b99f9f68b8a480908602503d726205eeec36c2d2 100644 --- a/ipatests/test_xmlrpc/test_vaultcontainer_plugin.py +++ b/ipatests/test_xmlrpc/test_vaultcontainer_plugin.py @@ -94,6 +94,7 @@ class test_vault(Declarative): 'objectclass': (u'ipaVaultContainer', u'top'), 'cn': [private_container], 'container_id': u'/users/admin/%s/' % private_container, + 'owner_user': [u'admin'], }, }, }, @@ -143,6 +144,7 @@ class test_vault(Declarative): 'dn': u'cn=%s,cn=admin,cn=users,cn=vaults,%s' % (private_container, api.env.basedn), 'cn': [private_container], 'container_id': u'/users/admin/%s/' % private_container, + 'owner_user': [u'admin'], }, }, }, @@ -163,6 +165,7 @@ class test_vault(Declarative): 'cn': [private_container], 'container_id': u'/users/admin/%s/' % private_container, 'description': [u'Private container'], + 'owner_user': [u'admin'], }, }, }, @@ -210,6 +213,7 @@ class test_vault(Declarative): 'objectclass': (u'ipaVaultContainer', u'top'), 'cn': [shared_container], 'container_id': u'/shared/%s/' % shared_container, + 'owner_user': [u'admin'], }, }, }, @@ -253,6 +257,7 @@ class test_vault(Declarative): 'dn': u'cn=%s,cn=shared,cn=vaults,%s' % (shared_container, api.env.basedn), 'cn': [shared_container], 'container_id': u'/shared/%s/' % shared_container, + 'owner_user': [u'admin'], }, }, }, @@ -274,6 +279,7 @@ class test_vault(Declarative): 'cn': [shared_container], 'container_id': u'/shared/%s/' % shared_container, 'description': [u'shared container'], + 'owner_user': [u'admin'], }, }, }, @@ -313,6 +319,7 @@ class test_vault(Declarative): 'objectclass': (u'ipaVaultContainer', u'top'), 'cn': [service_container], 'container_id': u'/services/%s/' % service_container, + 'owner_user': [u'admin'], }, }, }, @@ -331,6 +338,7 @@ class test_vault(Declarative): 'objectclass': (u'ipaVaultContainer', u'top'), 'cn': [base_container], 'container_id': u'/users/admin/%s/' % base_container, + 'owner_user': [u'admin'], }, }, }, @@ -352,6 +360,7 @@ class test_vault(Declarative): 'objectclass': (u'ipaVaultContainer', u'top'), 'cn': [child_container], 'container_id': u'/users/admin/%s/%s/' % (base_container, child_container), + 'owner_user': [u'admin'], }, }, }, @@ -375,6 +384,7 @@ class test_vault(Declarative): 'cn': [grandchild_container], 'container_id': u'/users/admin/%s/%s/%s/' % (base_container, child_container, grandchild_container), + 'owner_user': [u'admin'], }, }, }, -- 1.9.0 -------------- next part -------------- From 6f324c32bde2aa3b6b456f85d8c6b9e882737dd7 Mon Sep 17 00:00:00 2001 From: "Endi S. Dewata" Date: Wed, 22 Oct 2014 10:02:25 -0400 Subject: [PATCH] Added command to retrieve vault transport certificate. A new command has been added to retrieve the vault transport certificate and optionally save it into a file. The vault archive and retrieve command has been modified to retrieve the transport certificate and store it locally for subsequent usage. This way it's no longer necessary to manually import the transport certificate into the client's NSS database. https://fedorahosted.org/freeipa/ticket/3872 --- API.txt | 5 +++ VERSION | 4 +-- ipalib/plugins/vault.py | 83 +++++++++++++++++++++++++++++++++++++++++++++++-- 3 files changed, 87 insertions(+), 5 deletions(-) diff --git a/API.txt b/API.txt index 72ecf647bf21cddfaaea719ff5a74639897bd8e0..9061176e6ae31ff6fa12fe9c415e2c851c75dc4c 100644 --- a/API.txt +++ b/API.txt @@ -4677,6 +4677,11 @@ option: Str('version?', exclude='webui') output: Entry('result', , Gettext('A dictionary representing an LDAP entry', domain='ipa', localedir=None)) output: Output('summary', (, ), None) output: PrimaryKey('value', None, None) +command: vault_transport_cert +args: 0,2,1 +option: Str('out?', cli_name='out') +option: Str('version?', exclude='webui') +output: Output('result', None, None) command: vaultcontainer_add args: 1,9,3 arg: Str('cn', attribute=True, cli_name='container_name', maxlength=255, multivalue=False, pattern='^[a-zA-Z0-9_.-]+$', primary_key=True, required=True) diff --git a/VERSION b/VERSION index afe37045b1b75b194696bf21574fff25b8e994b1..8fa6af2ccd6b7d8cccba64f17454bf7b40352cc2 100644 --- a/VERSION +++ b/VERSION @@ -90,5 +90,5 @@ IPA_DATA_VERSION=20100614120000 # # ######################################################## IPA_API_VERSION_MAJOR=2 -IPA_API_VERSION_MINOR=115 -# Last change: edewata - added vault access control +IPA_API_VERSION_MINOR=116 +# Last change: edewata - added vault transport certificate diff --git a/ipalib/plugins/vault.py b/ipalib/plugins/vault.py index 45dc596562f2eaf7c88e56c76085be1afc94b235..96849ff0d41d8f8a5817a1834aa22b3cf1ce96eb 100644 --- a/ipalib/plugins/vault.py +++ b/ipalib/plugins/vault.py @@ -18,12 +18,17 @@ # along with this program. If not, see . import base64 +import os import sys +import tempfile + +import nss.nss as nss import pki.account import pki.crypto import pki.key +from ipalib.frontend import Command from ipalib import api, errors from ipalib import Str, Bytes, Flag from ipalib.plugable import Registry @@ -81,7 +86,6 @@ EXAMPLES: """) register = Registry() -transport_cert_nickname = 'KRA Transport Certificate' @register() class vault(LDAPObject): @@ -408,6 +412,63 @@ class vault_show(LDAPRetrieve): @register() +class vault_transport_cert(Command): + __doc__ = _('Retrieve vault transport certificate.') + + + # list of attributes we want exported to JSON + json_friendly_attributes = ( + 'takes_args', + ) + + takes_options = ( + Str('out?', + cli_name='out', + doc=_('Output file to store the transport certificate'), + ), + ) + + has_output_params = ( + Str('certificate', + label=_('Certificate'), + ), + ) + + def __json__(self): + json_dict = dict( + (a, getattr(self, a)) for a in self.json_friendly_attributes + ) + json_dict['takes_options'] = list(self.get_json_options()) + return json_dict + + def forward(self, *args, **options): + + file = options.get('out') + + # don't send these parameters to server + if 'out' in options: + del options['out'] + + response = super(vault_transport_cert, self).forward(*args, **options) + + if file: + with open(file, 'w') as f: + f.write(response['result']['certificate']) + + return response + + def execute(self, *args, **options): + + kra_client = api.Backend.kra.get_client() + transport_cert = kra_client.system_certs.get_transport_cert() + return { + 'result': { + 'certificate': transport_cert.encoded + } + } + + + at register() class vault_archive(LDAPRetrieve): __doc__ = _('Archive data into a vault.') @@ -494,7 +555,15 @@ class vault_archive(LDAPRetrieve): session_key = crypto.generate_session_key() # retrieve transport certificate - nss_transport_cert = crypto.get_cert(transport_cert_nickname) + (file, filename) = tempfile.mkstemp() + os.close(file) + try: + api.Command.vault_transport_cert(out=unicode(filename)) + transport_cert_der = nss.read_der_from_file(filename, True) + nss_transport_cert = nss.Certificate(transport_cert_der) + + finally: + os.remove(filename) # wrap session key with transport certificate wrapped_session_key = crypto.asymmetric_wrap( @@ -633,7 +702,15 @@ class vault_retrieve(LDAPRetrieve): session_key = crypto.generate_session_key() # retrieve transport certificate - nss_transport_cert = crypto.get_cert(transport_cert_nickname) + (file, filename) = tempfile.mkstemp() + os.close(file) + try: + api.Command.vault_transport_cert(out=unicode(filename)) + transport_cert_der = nss.read_der_from_file(filename, True) + nss_transport_cert = nss.Certificate(transport_cert_der) + + finally: + os.remove(filename) # wrap session key with transport certificate wrapped_session_key = crypto.asymmetric_wrap( -- 1.9.0 -------------- next part -------------- From 219dfde63d53a54d6990a531e67e30062f9c0f53 Mon Sep 17 00:00:00 2001 From: "Endi S. Dewata" Date: Fri, 24 Oct 2014 19:53:16 -0400 Subject: [PATCH] Added symmetric and asymmetric vaults. The IPA vault has been modified to support symmetric and asymmetric vaults to allow client to pre-encrypt the data. Due to the status of the crypto library the actual encryption will be added separately later. New LDAP attribute types have been added to store vault type, salt and public key. https://fedorahosted.org/freeipa/ticket/3872 --- API.txt | 28 ++- VERSION | 4 +- install/share/60basev3.ldif | 4 +- ipalib/plugins/vault.py | 367 +++++++++++++++++++++++++++++- ipatests/test_xmlrpc/test_vault_plugin.py | 216 ++++++++++++++++++ 5 files changed, 608 insertions(+), 11 deletions(-) diff --git a/API.txt b/API.txt index 9061176e6ae31ff6fa12fe9c415e2c851c75dc4c..e3fe9d0c75c789e2e3b07b52fa947e77cd91c73d 100644 --- a/API.txt +++ b/API.txt @@ -4518,7 +4518,7 @@ output: Output('result', , None) output: Output('summary', (, ), None) output: PrimaryKey('value', None, None) command: vault_add -args: 1,12,3 +args: 1,18,3 arg: Str('cn', attribute=True, cli_name='vault_name', maxlength=255, multivalue=False, pattern='^[a-zA-Z0-9_.-]+$', primary_key=True, required=True) option: Str('addattr*', cli_name='addattr', exclude='webui') option: Flag('all', autofill=True, cli_name='all', default=False, exclude='webui') @@ -4526,7 +4526,13 @@ option: Str('container', attribute=False, cli_name='container', multivalue=False option: Bytes('data?', cli_name='data') option: Str('description', attribute=True, cli_name='desc', multivalue=False, required=False) option: Str('in?', cli_name='in') +option: Bytes('ipapublickey', attribute=True, cli_name='public_key', multivalue=False, required=False) +option: Str('ipavaultsalt', attribute=True, cli_name='salt', multivalue=False, required=False) +option: Str('ipavaulttype', attribute=True, autofill=True, cli_name='type', default=u'standard', multivalue=False, required=False) option: Flag('no_members', autofill=True, default=False, exclude='webui') +option: Str('password?', cli_name='password') +option: Str('password_file?', cli_name='password_file') +option: Str('public_key_file?', cli_name='public_key_file') option: Flag('raw', autofill=True, cli_name='raw', default=False, exclude='webui') option: Str('setattr*', cli_name='setattr', exclude='webui') option: Str('text?', cli_name='text') @@ -4562,7 +4568,7 @@ output: Output('completed', , None) output: Output('failed', , None) output: Entry('result', , Gettext('A dictionary representing an LDAP entry', domain='ipa', localedir=None)) command: vault_archive -args: 1,12,3 +args: 1,14,3 arg: Str('cn', attribute=True, cli_name='vault_name', maxlength=255, multivalue=False, pattern='^[a-zA-Z0-9_.-]+$', primary_key=True, query=True, required=True) option: Flag('all', autofill=True, cli_name='all', default=False, exclude='webui') option: Str('container?', cli_name='container') @@ -4571,6 +4577,8 @@ option: Str('encrypted_data?', cli_name='encrypted_data') option: Str('in?', cli_name='in') option: Flag('no_members', autofill=True, default=False, exclude='webui') option: Str('nonce?', cli_name='nonce') +option: Str('password?', cli_name='password') +option: Str('password_file?', cli_name='password_file') option: Flag('raw', autofill=True, cli_name='raw', default=False, exclude='webui') option: Flag('rights', autofill=True, default=False) option: Str('text?', cli_name='text') @@ -4589,12 +4597,15 @@ output: Output('result', , None) output: Output('summary', (, ), None) output: ListOfPrimaryKeys('value', None, None) command: vault_find -args: 1,11,4 +args: 1,14,4 arg: Str('criteria?', noextrawhitespace=False) option: Flag('all', autofill=True, cli_name='all', default=False, exclude='webui') option: Str('cn', attribute=True, autofill=False, cli_name='vault_name', maxlength=255, multivalue=False, pattern='^[a-zA-Z0-9_.-]+$', primary_key=True, query=True, required=False) option: Str('container', attribute=False, autofill=False, cli_name='container', multivalue=False, pattern='^[a-zA-Z0-9_.-/]+$', query=True, required=False) option: Str('description', attribute=True, autofill=False, cli_name='desc', multivalue=False, query=True, required=False) +option: Bytes('ipapublickey', attribute=True, autofill=False, cli_name='public_key', multivalue=False, query=True, required=False) +option: Str('ipavaultsalt', attribute=True, autofill=False, cli_name='salt', multivalue=False, query=True, required=False) +option: Str('ipavaulttype', attribute=True, autofill=False, cli_name='type', default=u'standard', multivalue=False, query=True, required=False) option: Flag('no_members', autofill=True, default=False, exclude='webui') option: Flag('pkey_only?', autofill=True, default=False) option: Flag('raw', autofill=True, cli_name='raw', default=False, exclude='webui') @@ -4607,13 +4618,16 @@ output: ListOfEntries('result', (, ), Gettext('A list output: Output('summary', (, ), None) output: Output('truncated', , None) command: vault_mod -args: 1,11,3 +args: 1,14,3 arg: Str('cn', attribute=True, cli_name='vault_name', maxlength=255, multivalue=False, pattern='^[a-zA-Z0-9_.-]+$', primary_key=True, query=True, required=True) option: Str('addattr*', cli_name='addattr', exclude='webui') option: Flag('all', autofill=True, cli_name='all', default=False, exclude='webui') option: Str('container', attribute=False, autofill=False, cli_name='container', multivalue=False, pattern='^[a-zA-Z0-9_.-/]+$', required=False) option: Str('delattr*', cli_name='delattr', exclude='webui') option: Str('description', attribute=True, autofill=False, cli_name='desc', multivalue=False, required=False) +option: Bytes('ipapublickey', attribute=True, autofill=False, cli_name='public_key', multivalue=False, required=False) +option: Str('ipavaultsalt', attribute=True, autofill=False, cli_name='salt', multivalue=False, required=False) +option: Str('ipavaulttype', attribute=True, autofill=False, cli_name='type', default=u'standard', multivalue=False, required=False) option: Flag('no_members', autofill=True, default=False, exclude='webui') option: Flag('raw', autofill=True, cli_name='raw', default=False, exclude='webui') option: Flag('rights', autofill=True, default=False) @@ -4650,12 +4664,16 @@ output: Output('completed', , None) output: Output('failed', , None) output: Entry('result', , Gettext('A dictionary representing an LDAP entry', domain='ipa', localedir=None)) command: vault_retrieve -args: 1,10,3 +args: 1,14,3 arg: Str('cn', attribute=True, cli_name='vault_name', maxlength=255, multivalue=False, pattern='^[a-zA-Z0-9_.-]+$', primary_key=True, query=True, required=True) option: Flag('all', autofill=True, cli_name='all', default=False, exclude='webui') option: Str('container?', cli_name='container') option: Flag('no_members', autofill=True, default=False, exclude='webui') option: Str('out?', cli_name='out') +option: Str('password?', cli_name='password') +option: Str('password_file?', cli_name='password_file') +option: Bytes('private_key?', cli_name='private_key') +option: Str('private_key_file?', cli_name='private_key_file') option: Flag('raw', autofill=True, cli_name='raw', default=False, exclude='webui') option: Flag('rights', autofill=True, default=False) option: Flag('show_text?', autofill=True, default=False) diff --git a/VERSION b/VERSION index 8fa6af2ccd6b7d8cccba64f17454bf7b40352cc2..564ed8eb148fce12d5951a47cf73c2eb156579cf 100644 --- a/VERSION +++ b/VERSION @@ -90,5 +90,5 @@ IPA_DATA_VERSION=20100614120000 # # ######################################################## IPA_API_VERSION_MAJOR=2 -IPA_API_VERSION_MINOR=116 -# Last change: edewata - added vault transport certificate +IPA_API_VERSION_MINOR=117 +# Last change: edewata - added symmetric and asymmetric vaults diff --git a/install/share/60basev3.ldif b/install/share/60basev3.ldif index 4883132f1627e16a16b6e7effed2af4be287e251..ec47d428ee0814dd66cf4e04a7322c4a82805fd5 100644 --- a/install/share/60basev3.ldif +++ b/install/share/60basev3.ldif @@ -54,6 +54,8 @@ attributeTypes: (2.16.840.1.113730.3.8.11.55 NAME 'ipaSecretKey' DESC 'Encrypted attributeTypes: (2.16.840.1.113730.3.8.11.61 NAME 'ipaWrappingKey' DESC 'PKCS#11 URI of the wrapping key' EQUALITY caseExactMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 SINGLE-VALUE X-ORIGIN 'IPA v4.1' ) attributeTypes: (2.16.840.1.113730.3.8.11.64 NAME 'ipaSecretKeyRef' DESC 'DN of the ipa key object' EQUALITY distinguishedNameMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.12 X-ORIGIN 'IPA v4.1' ) attributeTypes: (2.16.840.1.113730.3.8.11.65 NAME 'ipaWrappingMech' DESC 'PKCS#11 wrapping mechanism equivalent to CK_MECHANISM_TYPE' EQUALITY caseIgnoreMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 SINGLE-VALUE X-ORIGIN 'IPA v4.1') +attributeTypes: (2.16.840.1.113730.3.8.18.2.1 NAME 'ipaVaultType' DESC 'IPA vault type' EQUALITY caseExactMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 X-ORIGIN 'IPA v4.2') +attributeTypes: (2.16.840.1.113730.3.8.18.2.2 NAME 'ipaVaultSalt' DESC 'IPA vault salt' EQUALITY caseExactMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 X-ORIGIN 'IPA v4.2') objectClasses: (2.16.840.1.113730.3.8.12.1 NAME 'ipaExternalGroup' SUP top STRUCTURAL MUST ( cn ) MAY ( ipaExternalMember $ memberOf $ description $ owner) X-ORIGIN 'IPA v3' ) objectClasses: (2.16.840.1.113730.3.8.12.2 NAME 'ipaNTUserAttrs' SUP top AUXILIARY MUST ( ipaNTSecurityIdentifier ) MAY ( ipaNTHash $ ipaNTLogonScript $ ipaNTProfilePath $ ipaNTHomeDirectory $ ipaNTHomeDirectoryDrive ) X-ORIGIN 'IPA v3' ) objectClasses: (2.16.840.1.113730.3.8.12.3 NAME 'ipaNTGroupAttrs' SUP top AUXILIARY MUST ( ipaNTSecurityIdentifier ) X-ORIGIN 'IPA v3' ) @@ -77,5 +79,5 @@ objectClasses: (2.16.840.1.113730.3.8.12.24 NAME 'ipaPublicKeyObject' DESC 'Wrap objectClasses: (2.16.840.1.113730.3.8.12.25 NAME 'ipaPrivateKeyObject' DESC 'Wrapped private keys' SUP top AUXILIARY MUST ( ipaPrivateKey $ ipaWrappingKey $ ipaWrappingMech ) X-ORIGIN 'IPA v4.1' ) objectClasses: (2.16.840.1.113730.3.8.12.26 NAME 'ipaSecretKeyObject' DESC 'Wrapped secret keys' SUP top AUXILIARY MUST ( ipaSecretKey $ ipaWrappingKey $ ipaWrappingMech ) X-ORIGIN 'IPA v4.1' ) objectClasses: (2.16.840.1.113730.3.8.12.34 NAME 'ipaSecretKeyRefObject' DESC 'Indirect storage for encoded key material' SUP top AUXILIARY MUST ( ipaSecretKeyRef ) X-ORIGIN 'IPA v4.1' ) -objectClasses: (2.16.840.1.113730.3.8.18.1.1 NAME 'ipaVault' DESC 'IPA vault' SUP top STRUCTURAL MUST ( cn ) MAY ( description $ owner $ member ) X-ORIGIN 'IPA v4.2' ) +objectClasses: (2.16.840.1.113730.3.8.18.1.1 NAME 'ipaVault' DESC 'IPA vault' SUP top STRUCTURAL MUST ( cn ) MAY ( description $ owner $ member $ ipaVaultType $ ipaVaultSalt $ ipaPublicKey ) X-ORIGIN 'IPA v4.2' ) objectClasses: (2.16.840.1.113730.3.8.18.1.2 NAME 'ipaVaultContainer' DESC 'IPA vault container' SUP top STRUCTURAL MUST ( cn ) MAY ( description $ owner $ member ) X-ORIGIN 'IPA v4.2' ) diff --git a/ipalib/plugins/vault.py b/ipalib/plugins/vault.py index 96849ff0d41d8f8a5817a1834aa22b3cf1ce96eb..3533fa38805513a20be43fd5bee778d12315558e 100644 --- a/ipalib/plugins/vault.py +++ b/ipalib/plugins/vault.py @@ -18,6 +18,7 @@ # along with this program. If not, see . import base64 +import getpass import os import sys import tempfile @@ -57,6 +58,12 @@ EXAMPLES: Add a standard vault: ipa vault-add MyVault """) + _(""" + Add a symmetric vault: + ipa vault-add MyVault --type symmetric --password-file password.txt +""") + _(""" + Add an asymmetric vault: + ipa vault-add MyVault --type asymmetric --public-key-file public.pem +""") + _(""" Show a vault: ipa vault-show MyVault """) + _(""" @@ -69,6 +76,21 @@ EXAMPLES: Retrieve data from standard vault: ipa vault-retrieve MyVault --out data.bin """) + _(""" + Archive data into symmetric vault: + ipa vault-archive MyVault --in data.bin --password-file password.txt +""") + _(""" + Archive data into asymmetric vault: + ipa vault-archive MyVault --in data.bin +""") + _(""" + Retrieve data from standard vault: + ipa vault-retrieve MyVault --out data.bin +""") + _(""" + Retrieve data from symmetric vault: + ipa vault-retrieve MyVault --out data.bin --password-file password.txt +""") + _(""" + Retrieve data from asymmetric vault: + ipa vault-retrieve MyVault --out data.bin --private-key-file private.pem +""") + _(""" Delete a vault: ipa vault-del MyVault """) + _(""" @@ -103,6 +125,9 @@ class vault(LDAPObject): 'description', 'owner', 'member', + 'ipavaulttype', + 'ipavaultsalt', + 'ipapublickey', ] search_display_attributes = [ 'cn', @@ -145,6 +170,23 @@ class vault(LDAPObject): label=_('Description'), doc=_('Vault description'), ), + Str('ipavaulttype?', + cli_name='type', + label=_('Type'), + doc=_('Vault type'), + default=u'standard', + autofill=True, + ), + Str('ipavaultsalt?', + cli_name='salt', + label=_('Salt'), + doc=_('Vault salt in base-64'), + ), + Bytes('ipapublickey?', + cli_name='public_key', + label=_('Public key'), + doc=_('Vault public key'), + ), ) def get_dn(self, *keys, **options): @@ -214,6 +256,24 @@ class vault(LDAPObject): """) return 'ipa' + id + def generate_symmetric_key(self, password, salt): + """ + Generates symmetric key from password and salt. + """ + return None + + def encrypt(self, data, symmetric_key=None, public_key=None): + """ + Encrypts data with symmetric key or public key. + """ + return data + + def decrypt(self, data, symmetric_key=None, private_key=None): + """ + Decrypts data with symmetric key or public key. + """ + return data + @register() class vault_add(LDAPCreate): @@ -232,6 +292,18 @@ class vault_add(LDAPCreate): cli_name='in', doc=_('File containing data to archive'), ), + Str('password?', + cli_name='password', + doc=_('Vault password'), + ), + Str('password_file?', + cli_name='password_file', + doc=_('File containing the vault password'), + ), + Str('public_key_file?', + cli_name='public_key_file', + doc=_('File containing the vault public key'), + ), ) msg_summary = _('Added vault "%(value)s"') @@ -241,9 +313,14 @@ class vault_add(LDAPCreate): vault_name = args[0] container_id = api.Object.vaultcontainer.normalize_id(options.get('container')) + vault_type = options.get('ipavaulttype') data = options.get('data') text = options.get('text') input_file = options.get('in') + password = options.get('password') + password_file = options.get('password_file') + public_key = options.get('ipapublickey') + public_key_file = options.get('public_key_file') # don't send these parameters to server if 'data' in options: @@ -252,6 +329,12 @@ class vault_add(LDAPCreate): del options['text'] if 'in' in options: del options['in'] + if 'password' in options: + del options['password'] + if 'password_file' in options: + del options['password_file'] + if 'public_key_file' in options: + del options['public_key_file'] # get data if data: @@ -277,6 +360,83 @@ class vault_add(LDAPCreate): else: data = '' + # type-specific initialization + if vault_type == 'standard': + + if password: + raise errors.ValidationError(name='password', + error=_('Invalid parameter for %s vault' % vault_type)) + + if password_file: + raise errors.ValidationError(name='password_file', + error=_('Invalid parameter for %s vault' % vault_type)) + + if public_key: + raise errors.ValidationError(name='ipapublickey', + error=_('Invalid parameter for %s vault' % vault_type)) + + if public_key_file: + raise errors.ValidationError(name='public_key_file', + error=_('Invalid parameter for %s vault' % vault_type)) + + elif vault_type == 'symmetric': + + if public_key: + raise errors.ValidationError(name='ipapublickey', + error=_('Invalid parameter for %s vault' % vault_type)) + + if public_key_file: + raise errors.ValidationError(name='public_key_file', + error=_('Invalid parameter for %s vault' % vault_type)) + + # get vault password + if password: + pass + + elif password_file: + with open(password_file) as f: + password = unicode(f.read().rstrip('\n')) + + else: + while True: + password = unicode(getpass.getpass('New password: ')) + password2 = unicode(getpass.getpass('Verify password: ')) + if password == password2: + break + print ' ** Passwords do not match! **' + + # generate vault salt + options['ipavaultsalt'] = unicode(base64.b64encode(os.urandom(16))) + + elif vault_type == 'asymmetric': + + if password: + raise errors.ValidationError(name='password', + error=_('Invalid parameter for %s vault' % vault_type)) + + if password_file: + raise errors.ValidationError(name='password_file', + error=_('Invalid parameter for %s vault' % vault_type)) + + # get vault public key + if public_key: + pass + + elif public_key_file: + with open(public_key_file, 'rb') as f: + public_key = f.read() + + else: + raise errors.ValidationError(name='ipapublickey', + error=_('Missing vault public key')) + + # store vault public key + options['ipapublickey'] = public_key + + else: + raise errors.ValidationError(name='vault_type', + error=_('Invalid vault type')) + # create the vault response = super(vault_add, self).forward(*args, **options) @@ -284,7 +444,8 @@ class vault_add(LDAPCreate): api.Command.vault_archive( vault_name, container=container_id, - data=data) + data=data, + password=password) return response @@ -415,7 +576,6 @@ class vault_show(LDAPRetrieve): class vault_transport_cert(Command): __doc__ = _('Retrieve vault transport certificate.') - # list of attributes we want exported to JSON json_friendly_attributes = ( 'takes_args', @@ -501,6 +661,14 @@ class vault_archive(LDAPRetrieve): cli_name='nonce', doc=_('Nonce encrypted encoded in base-64'), ), + Str('password?', + cli_name='password', + doc=_('Vault password'), + ), + Str('password_file?', + cli_name='password_file', + doc=_('File containing the vault password'), + ), ) msg_summary = _('Archived data into vault "%(value)s"') @@ -510,9 +678,28 @@ class vault_archive(LDAPRetrieve): vault_name = args[0] container_id = api.Object.vaultcontainer.normalize_id(options.get('container')) + vault_type = 'standard' + salt = None + public_key = None + + # retrieve vault info + response = api.Command.vault_show(vault_name, container=container_id) + result = response['result'] + + if result.has_key('ipavaulttype'): + vault_type = result['ipavaulttype'][0] + + if result.has_key('ipavaultsalt'): + salt = str(result['ipavaultsalt'][0]) + + if result.has_key('ipapublickey'): + public_key = str(result['ipapublickey'][0]) + data = options.get('data') text = options.get('text') input_file = options.get('in') + password = options.get('password') + password_file = options.get('password_file') # don't send these parameters to server if 'data' in options: @@ -521,6 +708,10 @@ class vault_archive(LDAPRetrieve): del options['text'] if 'in' in options: del options['in'] + if 'password' in options: + del options['password'] + if 'password_file' in options: + del options['password_file'] # get data if data: @@ -546,6 +737,61 @@ class vault_archive(LDAPRetrieve): else: data = '' + # pre-encrypt data + if vault_type == 'standard': + + if password: + raise errors.ValidationError(name='password', + error=_('Invalid parameter for %s vault' % vault_type)) + + if password_file: + raise errors.ValidationError(name='password_file', + error=_('Invalid parameter for %s vault' % vault_type)) + + elif vault_type == 'symmetric': + + # get vault password + if password: + pass + + elif password_file: + with open(password_file) as f: + password = unicode(f.read().rstrip('\n')) + else: + password = unicode(getpass.getpass('Password: ')) + + try: + api.Command.vault_retrieve( + vault_name, + container=container_id, + password=password) + + except errors.NotFound: + pass + + # generate symmetric key from vault password + symmetric_key = self.obj.generate_symmetric_key(password, salt) + + # pre-encrypt data with symmetric key + data = self.obj.encrypt(data, symmetric_key=symmetric_key) + + elif vault_type == 'asymmetric': + + if password: + raise errors.ValidationError(name='password', + error=_('Invalid parameter for %s vault' % vault_type)) + + if password_file: + raise errors.ValidationError(name='password_file', + error=_('Invalid parameter for %s vault' % vault_type)) + + # pre-encrypt data with public key + data = self.obj.encrypt(data, public_key=public_key) + + else: + raise errors.ValidationError(name='vault_type', + error=_('Invalid vault type')) + # initialize NSS database crypto = pki.crypto.NSSCryptoProvider(paths.IPA_NSSDB_DIR) crypto.initialize() @@ -663,13 +909,29 @@ class vault_retrieve(LDAPRetrieve): cli_name='wrapped_session_key', doc=_('Session key wrapped with transport certificate and encoded in base-64'), ), + Str('password?', + cli_name='password', + doc=_('Vault password'), + ), + Str('password_file?', + cli_name='password_file', + doc=_('File containing the vault password'), + ), + Bytes('private_key?', + cli_name='private_key', + doc=_('Vault private key'), + ), + Str('private_key_file?', + cli_name='private_key_file', + doc=_('File containing the vault private key'), + ), ) has_output_params = ( Bytes('data', label=_('Data'), ), - Bytes('text', + Str('text', label=_('Text'), ), ) @@ -681,9 +943,26 @@ class vault_retrieve(LDAPRetrieve): vault_name = args[0] container_id = api.Object.vaultcontainer.normalize_id(options.get('container')) + vault_type = 'standard' + salt = None + + # retrieve vault info + response = api.Command.vault_show(vault_name, container=container_id) + result = response['result'] + + if result.has_key('ipavaulttype'): + vault_type = result['ipavaulttype'][0] + + if result.has_key('ipavaultsalt'): + salt = str(result['ipavaultsalt'][0]) + show_text = options.get('show_text') stdout = options.get('stdout') output_file = options.get('out') + password = options.get('password') + password_file = options.get('password_file') + private_key = options.get('private_key') + private_key_file = options.get('private_key_file') # don't send these parameters to server if 'show_text' in options: @@ -692,6 +971,14 @@ class vault_retrieve(LDAPRetrieve): del options['stdout'] if 'out' in options: del options['out'] + if 'password' in options: + del options['password'] + if 'password_file' in options: + del options['password_file'] + if 'private_key' in options: + del options['private_key'] + if 'private_key_file' in options: + del options['private_key_file'] # initialize NSS database crypto = pki.crypto.NSSCryptoProvider(paths.IPA_NSSDB_DIR) @@ -732,6 +1019,80 @@ class vault_retrieve(LDAPRetrieve): session_key, nonce_iv=nonce) + if vault_type == 'standard': + + if password: + raise errors.ValidationError(name='password', + error=_('Invalid parameter for %s vault' % vault_type)) + + if password_file: + raise errors.ValidationError(name='password_file', + error=_('Invalid parameter for %s vault' % vault_type)) + + if private_key: + raise errors.ValidationError(name='private_key', + error=_('Invalid parameter for %s vault' % vault_type)) + + if private_key_file: + raise errors.ValidationError(name='private_key_file', + error=_('Invalid parameter for %s vault' % vault_type)) + + elif vault_type == 'symmetric': + + if private_key: + raise errors.ValidationError(name='private_key', + error=_('Invalid parameter for %s vault' % vault_type)) + + if private_key_file: + raise errors.ValidationError(name='private_key_file', + error=_('Invalid parameter for %s vault' % vault_type)) + + # get vault password + if password: + pass + + elif password_file: + with open(password_file) as f: + password = unicode(f.read().rstrip('\n')) + + else: + password = unicode(getpass.getpass('Password: ')) + + # generate symmetric key from vault password + symmetric_key = self.obj.generate_symmetric_key(password, salt) + + # post-decrypt data with symmetric key + data = self.obj.decrypt(data, symmetric_key=symmetric_key) + + elif vault_type == 'asymmetric': + + if password: + raise errors.ValidationError(name='password', + error=_('Invalid parameter for %s vault' % vault_type)) + + if password_file: + raise errors.ValidationError(name='password_file', + error=_('Invalid parameter for %s vault' % vault_type)) + + # get vault private key + if private_key: + pass + + elif private_key_file: + with open(private_key_file, 'rb') as f: + private_key = f.read() + + else: + raise errors.ValidationError(name='private_key', + error=_('Missing vault private key')) + + # post-decrypt data with private key + data = self.obj.decrypt(data, private_key=private_key) + + else: + raise errors.ValidationError(name='vault_type', + error=_('Invalid vault type')) + if stdout: sys.stdout.write(data) response['result'] = {} diff --git a/ipatests/test_xmlrpc/test_vault_plugin.py b/ipatests/test_xmlrpc/test_vault_plugin.py index 6d74967588230cda49b326197d97ee6425af0ed5..92fc9a2e9d8129dcbee86c460921db57b01b1a01 100644 --- a/ipatests/test_xmlrpc/test_vault_plugin.py +++ b/ipatests/test_xmlrpc/test_vault_plugin.py @@ -28,10 +28,89 @@ test_vault = u'test_vault' binary_data = '\x01\x02\x03\x04' text_data = u'secret' +symmetric_vault = u'symmetric_vault' +asymmetric_vault = u'asymmetric_vault' + +password = u'password' + +public_key = """ +-----BEGIN PUBLIC KEY----- +MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAnT61EFxUOQgCJdM0tmw/ +pRRPDPGchTClnU1eBtiQD3ItKYf1+weMGwGOSJXPtkto7NlE7Qs8WHAr0UjyeBDe +k/zeB6nSVdk47OdaW1AHrJL+44r238Jbm/+7VO5lTu6Z4N5p0VqoWNLi0Uh/CkqB +tsxXaaAgjMp0AGq2U/aO/akeEYWQOYIdqUKVgAEKX5MmIA8tmbmoYIQ+B4Q3vX7N +otG4eR6c2o9Fyjd+M4Gai5Ce0fSrigRvxAYi8xpRkQ5yQn5gf4WVrn+UKTfOIjLO +pVThop+Xivcre3SpI0kt6oZPhBw9i8gbMnqifVmGFpVdhq+QVBqp+MVJvTbhRPG6 +3wIDAQAB +-----END PUBLIC KEY----- +""" + +private_key = """ +-----BEGIN RSA PRIVATE KEY----- +MIIEpAIBAAKCAQEAnT61EFxUOQgCJdM0tmw/pRRPDPGchTClnU1eBtiQD3ItKYf1 ++weMGwGOSJXPtkto7NlE7Qs8WHAr0UjyeBDek/zeB6nSVdk47OdaW1AHrJL+44r2 +38Jbm/+7VO5lTu6Z4N5p0VqoWNLi0Uh/CkqBtsxXaaAgjMp0AGq2U/aO/akeEYWQ +OYIdqUKVgAEKX5MmIA8tmbmoYIQ+B4Q3vX7NotG4eR6c2o9Fyjd+M4Gai5Ce0fSr +igRvxAYi8xpRkQ5yQn5gf4WVrn+UKTfOIjLOpVThop+Xivcre3SpI0kt6oZPhBw9 +i8gbMnqifVmGFpVdhq+QVBqp+MVJvTbhRPG63wIDAQABAoIBAQCD2bXnfxPcMnvi +jaPwpvoDCPF0EBBHmk/0g5ApO2Qon3uBDJFUqbJwXrCY6o2d9MOJfnGONlKmcYA8 +X+d4h+SqwGjIkjxdYeSauS+Jy6Rzr1ptH/P8EjPQrfG9uJxYQDflV3nxYwwwVrx7 +8kccMPdteRB+8Bb7FzOHufMimmayCNFETnVT5CKH2PrYoPB+fr0itCipWOenDp33 +e73OV+K9U3rclmtHaoRxGohqByKfQRUkipjw4m+T3qfZZc5eN77RGW8J+oL1GVom +fwtiH7N1HVte0Dmd13nhiASg355kjqRPcIMPsRHvXkOpgg5HRUTKG5elqAyvvm27 +Fzj1YdeRAoGBAMnE61+FYh8qCyEGe8r6RGjO8iuoyk1t+0gBWbmILLBiRnj4K8Tc +k7HBG/pg3XCNbCuRwiLg8tk3VAAXzn6o+IJr3QnKbNCGa1lKfYU4mt11sBEyuL5V +NpZcZ8IiPhMlGyDA9cFbTMKOE08RqbOIdxOmTizFt0R5sYZAwOjEvBIZAoGBAMeC +N/P0bdrScFZGeS51wEdiWme/CO0IyGoqU6saI8L0dbmMJquiaAeIEjIKLqxH1RON +axhsyk97e0PCcc5QK62Utf50UUAbL/v7CpIG+qdSRYDO4bVHSCkwF32N3pYh/iVU +EsEBEkZiJi0dWa/0asDbsACutxcHda3RI5pi7oO3AoGAcbGNs/CUHt1xEfX2UaT+ +YVSjb2iYPlNH8gYYygvqqqVl8opdF3v3mYUoP8jPXrnCBzcF/uNk1HNx2O+RQxvx +lIQ1NGwlLsdfvBvWaPhBg6LqSHadVVrs/IMrUGA9PEp/Y9B3arIIqeSnCrn4Nxsh +higDCwWKRIKSPwVD7qXVGBkCgYEAu5/CASIRIeYgEXMLSd8hKcDcJo8o1MoauIT/ +1Hyrvw9pm0qrn2QHk3WrLvYWeJzBTTcEzZ6aEG+fN9UodA8/VGnzUc6QDsrCsKWh +hj0cArlDdeSZrYLQ4TNCFCiUePqU6QQM8weP6TMqlejxTKF+t8qi1bF5rCWuzP1P +D0UU7DcCgYAUvmEGckugS+FTatop8S/rmkcQ4Bf5M/YCZfsySavucDiHcBt0QtXt +Swh0XdDsYS3W1yj2XqqsQ7R58KNaffCHjjulWFzb5IiuSvvdxzWtiXHisOpO36MJ +kUlCMj24a8XsShzYTWBIyW2ngvGe3pQ9PfjkUdm0LGZjYITCBvgOKw== +-----END RSA PRIVATE KEY----- +""" + +wrong_private_key = """ +-----BEGIN RSA PRIVATE KEY----- +MIIEpAIBAAKCAQEAuQVrFCTFzc8EQ1TPR/fB2vZqcNFn7KVFYN5UuGSLm8JwdSyX +aJHFdgmdWS5NHRQS7l4kd3/mn1pk4BuhmC1tO+tPM1gunCtFvgB5fBC6WJzytBEC +y/ikvSIyOQ33/KCLdTQ/oFTIp3f5dUO62a3gjgQ9Y0Z9krRXYiB5uxMyHrr/zuxL +qRqP7pQYIkROr5VqGUgFUjFcovLvzP2sabobEnctCKm+nIE4s31QR3Kqb4l31qmX +TkbiGkoEkwoqHDuu0eH50bJwRFjjx3h+5U0bmoff1ipi+Aan5gZKoOOlnkc9d2dZ +gPBHyth6+9N3V8jrjs+VMJ8j2GzI9pnVxD93awIDAQABAoIBAC5neny54FaHBmWw +vrApJpi5Vubmzm7e4LCz8oGwzgcJ1FS/E1ZpwSGitbEpWLPjVgAs4m6KSJhM/qHq +rDPTqOLvWJTjGAWZIMvPqKiCNYqGCqU44v+vY/n/uqLuqvTUe0WxDggzW4QNJibQ +DuwLnRdhXtgoVNRXoNb+mClgXiCwjgX8j+A2lb6H4iQsQOLjLr4ifH13CqDgJk3k +2BjI1wOeesxooNk3I7ZKdVL1iV/qaeysLvtWoEmoQvrn6Fbo4HAR04tQQXEG7GzM +kZ+RVmHyn5b4tCMqnzubM+IYf7k9cJfgZIyaVxO8NSfwaJkWbQiGwzvUebRiHwPs +cK+F4bkCgYEA9VKtHX6Kr9Ht6Ho0q5eZAzRrOqgB2Uj4duFAI40bQ1q9+p+VSoKR +IqU6I16HO7mbgjlOySVg5RVIcp+9DUoNoK2KlGFswX6Q0f1J/v0CDavQoqOcpyFh +F9Egtx+5VNhknDZ0GTNWNm6bTWZUHK6ZICXs/mZkn4rHdId2Xe0Ls38CgYEAwRLg +GXSctadY0Qowy2YLe+I2EjsdXgE82TvOorytjFhOPVB/GsCOJ3IjyHJyGdpkm/85 +U0SUoeNXQHIgiMGG+xxQ/ErJhpHxiaQKHFow48x3k8R7tlO68Mfnl+BZ9goRgxio +rFwiea2MtCl2j8szh9UJ1z57aG1L0PfUctd9QhUCgYEA3O2T0ZgANc6MvmwvushP +mD9AwhZDc/bvK8A3Ds0o3EOAC5Bj1jI3mkfKT8f1aagBkAkkFql+1U+RawjILIug +Mi+XOYFze94LddDxLp2Tl9Q/k/hcP3ckBVrkZ4Y+VVZ7ZOL1Myy0W1jIq6+X2Cy0 +4erFv2VfAP7uGNdVlcjAXOkCgYABT5x/77/Ep/89ZCFSsD2xuKZ/VzFq2v1LyFEt +37QZ+NuHJQ3H47jTYb4GdWh67nWybXg5LYUI2F9WS7AW3aGKAPY30FYv+Lu4IIoF +CUO9uDyznyjr4wOo8OKMsHRL7GOUDU3P5cxCIUCMVJ++eDXAXVz0vjLeUaerIpOp +t/bcxQKBgQC4ehPJQH8WWG1FnfXh62AJBgb+yUDmp+4bTRtr/GENF9W62GN+g7or +RQqtdO1WxoXsC2qbKT4ArwOpV877KlWuLfiJMe5JS8vU/n711DCFIIfkXpeXPUVc +MszdQuc/FTSJ2DYsIwx7qq5c8mtargOjWRgZU22IgY9PKeIcitQjqw== +-----END RSA PRIVATE KEY----- +""" + class test_vault(Declarative): cleanup_commands = [ ('vault_del', [test_vault], {'continue': True}), + ('vault_del', [symmetric_vault], {'continue': True}), + ('vault_del', [asymmetric_vault], {'continue': True}), ] tests = [ @@ -52,6 +131,7 @@ class test_vault(Declarative): 'cn': [test_vault], 'vault_id': u'/users/admin/%s' % test_vault, 'owner_user': [u'admin'], + 'ipavaulttype': [u'standard'], }, }, }, @@ -102,6 +182,7 @@ class test_vault(Declarative): 'cn': [test_vault], 'vault_id': u'/users/admin/%s' % test_vault, 'owner_user': [u'admin'], + 'ipavaulttype': [u'standard'], }, }, }, @@ -123,6 +204,7 @@ class test_vault(Declarative): 'vault_id': u'/users/admin/%s' % test_vault, 'description': [u'Test vault'], 'owner_user': [u'admin'], + 'ipavaulttype': [u'standard'], }, }, }, @@ -145,6 +227,7 @@ class test_vault(Declarative): 'vault_id': u'/users/admin/%s' % test_vault, 'description': [u'Test vault'], 'owner_user': [u'admin'], + 'ipavaulttype': [u'standard'], }, }, }, @@ -165,6 +248,7 @@ class test_vault(Declarative): 'vault_id': u'/users/admin/%s' % test_vault, 'description': [u'Test vault'], 'owner_user': [u'admin'], + 'ipavaulttype': [u'standard'], 'nonce': fuzzy_string, 'encrypted_data': fuzzy_string, 'data': binary_data, @@ -190,6 +274,7 @@ class test_vault(Declarative): 'vault_id': u'/users/admin/%s' % test_vault, 'description': [u'Test vault'], 'owner_user': [u'admin'], + 'ipavaulttype': [u'standard'], }, }, }, @@ -212,6 +297,7 @@ class test_vault(Declarative): 'vault_id': u'/users/admin/%s' % test_vault, 'description': [u'Test vault'], 'owner_user': [u'admin'], + 'ipavaulttype': [u'standard'], 'nonce': fuzzy_string, 'encrypted_data': fuzzy_string, 'text': text_data, @@ -245,4 +331,134 @@ class test_vault(Declarative): 'expected': errors.NotFound(reason=u'%s: vault not found' % test_vault), }, + { + 'desc': 'Create symmetric vault', + 'command': ( + 'vault_add', + [symmetric_vault], + { + 'ipavaulttype': u'symmetric', + 'password': password, + 'data': binary_data, + }, + ), + 'expected': { + 'value': symmetric_vault, + 'summary': 'Added vault "%s"' % symmetric_vault, + 'result': { + 'dn': u'cn=%s,cn=admin,cn=users,cn=vaults,%s' % (symmetric_vault, api.env.basedn), + 'objectclass': (u'ipaVault', u'top'), + 'cn': [symmetric_vault], + 'vault_id': u'/users/admin/%s' % symmetric_vault, + 'owner_user': [u'admin'], + 'ipavaulttype': [u'symmetric'], + 'ipavaultsalt': [fuzzy_string], + }, + }, + }, + + { + 'desc': 'Retrieve symmetric vault', + 'command': ( + 'vault_retrieve', + [symmetric_vault], + { + 'password': password, + }, + ), + 'expected': { + 'value': symmetric_vault, + 'summary': u'Retrieved data from vault "%s"' % symmetric_vault, + 'result': { + 'dn': u'cn=%s,cn=admin,cn=users,cn=vaults,%s' % (symmetric_vault, api.env.basedn), + 'cn': [symmetric_vault], + 'vault_id': u'/users/admin/%s' % symmetric_vault, + 'owner_user': [u'admin'], + 'ipavaulttype': [u'symmetric'], + 'ipavaultsalt': [fuzzy_string], + 'nonce': fuzzy_string, + 'encrypted_data': fuzzy_string, + 'data': binary_data, + }, + }, + }, + + + { + 'desc': 'Retrieve symmetric vault with wrong password', + 'command': ( + 'vault_retrieve', + [symmetric_vault], + { + 'password': u'wrong', + }, + ), + 'expected': errors.AuthenticationError(message=u'Invalid credentials'), + }, + + { + 'desc': 'Create asymmetric vault', + 'command': ( + 'vault_add', + [asymmetric_vault], + { + 'ipavaulttype': u'asymmetric', + 'ipapublickey': public_key, + 'data': binary_data, + }, + ), + 'expected': { + 'value': asymmetric_vault, + 'summary': 'Added vault "%s"' % asymmetric_vault, + 'result': { + 'dn': u'cn=%s,cn=admin,cn=users,cn=vaults,%s' % (asymmetric_vault, api.env.basedn), + 'objectclass': (u'ipaVault', u'top'), + 'cn': [asymmetric_vault], + 'vault_id': u'/users/admin/%s' % asymmetric_vault, + 'owner_user': [u'admin'], + 'ipavaulttype': [u'asymmetric'], + 'ipapublickey': [public_key], + }, + }, + }, + + { + 'desc': 'Retrieve asymmetric vault', + 'command': ( + 'vault_retrieve', + [asymmetric_vault], + { + 'private_key': private_key, + }, + ), + 'expected': { + 'value': asymmetric_vault, + 'summary': u'Retrieved data from vault "%s"' % asymmetric_vault, + 'result': { + 'dn': u'cn=%s,cn=admin,cn=users,cn=vaults,%s' % (asymmetric_vault, api.env.basedn), + 'cn': [asymmetric_vault], + 'vault_id': u'/users/admin/%s' % asymmetric_vault, + 'owner_user': [u'admin'], + 'ipavaulttype': [u'asymmetric'], + 'ipapublickey': [public_key], + 'nonce': fuzzy_string, + 'encrypted_data': fuzzy_string, + 'data': binary_data, + }, + }, + }, + + + { + 'desc': 'Retrieve asymmetric vault with wrong private key', + 'command': ( + 'vault_retrieve', + [asymmetric_vault], + { + 'private_key': wrong_private_key, + }, + ), + 'expected': errors.AuthenticationError(message=u'Invalid credentials'), + }, + ] -- 1.9.0 -------------- next part -------------- From 8d46a8ef1ad3f75a3a024186a92bbb9123bcc605 Mon Sep 17 00:00:00 2001 From: "Endi S. Dewata" Date: Fri, 24 Oct 2014 19:53:16 -0400 Subject: [PATCH] Enabled vault encryption using python-cryptography. The IPA vault has been modified to enable the symmetric and asymmetric encryption using python-cryptography. https://fedorahosted.org/freeipa/ticket/3872 --- freeipa.spec.in | 2 ++ ipalib/plugins/vault.py | 60 ++++++++++++++++++++++++++++++++++++++++++++++--- 2 files changed, 59 insertions(+), 3 deletions(-) diff --git a/freeipa.spec.in b/freeipa.spec.in index b186d9fdff31118ea4d929f024f4dc16a75b1d0b..e4f92d6b23925eef6714ca53c89778efae2becb6 100644 --- a/freeipa.spec.in +++ b/freeipa.spec.in @@ -94,6 +94,7 @@ BuildRequires: p11-kit-devel BuildRequires: pki-base >= 10.2.1-0.1 BuildRequires: python-pytest-multihost >= 0.5 BuildRequires: python-pytest-sourceorder +BuildRequires: python-cryptography %description IPA is an integrated solution to provide centrally managed Identity (machine, @@ -149,6 +150,7 @@ Requires: openssl Requires: softhsm >= 2.0.0b1-3 Requires: p11-kit Requires: systemd-python +Requires: python-cryptography Conflicts: %{alt_name}-server Obsoletes: %{alt_name}-server < %{version} diff --git a/ipalib/plugins/vault.py b/ipalib/plugins/vault.py index 3533fa38805513a20be43fd5bee778d12315558e..d9956596421efbac889f181faf8d87292bf61579 100644 --- a/ipalib/plugins/vault.py +++ b/ipalib/plugins/vault.py @@ -23,6 +23,13 @@ import os import sys import tempfile +from cryptography.fernet import Fernet, InvalidToken +from cryptography.hazmat.backends import default_backend +from cryptography.hazmat.primitives import hashes +from cryptography.hazmat.primitives.kdf.pbkdf2 import PBKDF2HMAC +from cryptography.hazmat.primitives.asymmetric import padding +from cryptography.hazmat.primitives.serialization import load_pem_public_key, load_pem_private_key + import nss.nss as nss import pki.account @@ -260,19 +267,66 @@ class vault(LDAPObject): """ Generates symmetric key from password and salt. """ - return None + kdf = PBKDF2HMAC( + algorithm=hashes.SHA256(), + length=32, + salt=salt, + iterations=100000, + backend=default_backend() + ) + + return base64.b64encode(kdf.derive(password.encode('utf-8'))) def encrypt(self, data, symmetric_key=None, public_key=None): """ Encrypts data with symmetric key or public key. """ - return data + if symmetric_key: + fernet = Fernet(symmetric_key) + return fernet.encrypt(data) + + elif public_key: + rsa_public_key = load_pem_public_key( + data=public_key, + backend=default_backend() + ) + return rsa_public_key.encrypt( + data, + padding.OAEP( + mgf=padding.MGF1(algorithm=hashes.SHA1()), + algorithm=hashes.SHA1(), + label=None + ) + ) def decrypt(self, data, symmetric_key=None, private_key=None): """ Decrypts data with symmetric key or public key. """ - return data + if symmetric_key: + try: + fernet = Fernet(symmetric_key) + return fernet.decrypt(data) + except InvalidToken: + raise errors.AuthenticationError(message=_('Invalid credentials')) + + elif private_key: + try: + rsa_private_key = load_pem_private_key( + data=private_key, + password=None, + backend=default_backend() + ) + return rsa_private_key.decrypt( + data, + padding.OAEP( + mgf=padding.MGF1(algorithm=hashes.SHA1()), + algorithm=hashes.SHA1(), + label=None + ) + ) + except AssertionError: + raise errors.AuthenticationError(message=_('Invalid credentials')) @register() -- 1.9.0 -------------- next part -------------- From 30bd3a2173b13a32c40548d83dbcb9fb82741491 Mon Sep 17 00:00:00 2001 From: "Endi S. Dewata" Date: Sat, 8 Nov 2014 02:04:03 -0500 Subject: [PATCH] Added vault secret plugin. A new plugin has been have been added to provide the interface to manage secrets within a vault. A test script have been added as well. https://fedorahosted.org/freeipa/ticket/3872 --- API.txt | 91 ++ VERSION | 4 +- ipalib/plugins/vaultsecret.py | 1118 +++++++++++++++++++++++ ipatests/test_xmlrpc/test_vaultsecret_plugin.py | 211 +++++ 4 files changed, 1422 insertions(+), 2 deletions(-) create mode 100644 ipalib/plugins/vaultsecret.py create mode 100644 ipatests/test_xmlrpc/test_vaultsecret_plugin.py diff --git a/API.txt b/API.txt index e3fe9d0c75c789e2e3b07b52fa947e77cd91c73d..6735996046413190dbbb49b74467de09d6641c8d 100644 --- a/API.txt +++ b/API.txt @@ -4824,6 +4824,97 @@ option: Str('version?', exclude='webui') output: Entry('result', , Gettext('A dictionary representing an LDAP entry', domain='ipa', localedir=None)) output: Output('summary', (, ), None) output: PrimaryKey('value', None, None) +command: vaultsecret_add +args: 2,12,3 +arg: Str('vaultcn', cli_name='vault', maxlength=255, multivalue=False, pattern='^[a-zA-Z0-9_.-]+$', primary_key=True, query=True, required=True) +arg: Str('secret_name', attribute=True, cli_name='secret', maxlength=255, multivalue=False, pattern='^[a-zA-Z0-9_.-]+$', primary_key=True, query=True, required=True) +option: Flag('all', autofill=True, cli_name='all', default=False, exclude='webui') +option: Str('container?', cli_name='container') +option: Bytes('data?', cli_name='data') +option: Str('description?', cli_name='desc') +option: Str('in?', cli_name='in') +option: Str('password?', cli_name='password') +option: Str('password_file?', cli_name='password_file') +option: Bytes('private_key?', cli_name='private_key') +option: Str('private_key_file?', cli_name='private_key_file') +option: Flag('raw', autofill=True, cli_name='raw', default=False, exclude='webui') +option: Str('text?', cli_name='text') +option: Str('version?', exclude='webui') +output: Entry('result', , Gettext('A dictionary representing an LDAP entry', domain='ipa', localedir=None)) +output: Output('summary', (, ), None) +output: PrimaryKey('value', None, None) +command: vaultsecret_del +args: 2,8,3 +arg: Str('vaultcn', cli_name='vault', maxlength=255, multivalue=False, pattern='^[a-zA-Z0-9_.-]+$', primary_key=True, query=True, required=True) +arg: Str('secret_name', attribute=True, cli_name='secret', maxlength=255, multivalue=False, pattern='^[a-zA-Z0-9_.-]+$', primary_key=True, query=True, required=True) +option: Flag('all', autofill=True, cli_name='all', default=False, exclude='webui') +option: Str('container?', cli_name='container') +option: Str('password?', cli_name='password') +option: Str('password_file?', cli_name='password_file') +option: Bytes('private_key?', cli_name='private_key') +option: Str('private_key_file?', cli_name='private_key_file') +option: Flag('raw', autofill=True, cli_name='raw', default=False, exclude='webui') +option: Str('version?', exclude='webui') +output: Entry('result', , Gettext('A dictionary representing an LDAP entry', domain='ipa', localedir=None)) +output: Output('summary', (, ), None) +output: PrimaryKey('value', None, None) +command: vaultsecret_find +args: 2,12,4 +arg: Str('vaultcn', cli_name='vault', maxlength=255, multivalue=False, pattern='^[a-zA-Z0-9_.-]+$', primary_key=True, query=True, required=True) +arg: Str('criteria?', noextrawhitespace=False) +option: Flag('all', autofill=True, cli_name='all', default=False, exclude='webui') +option: Str('container?', cli_name='container') +option: Bytes('data', attribute=True, autofill=False, cli_name='data', multivalue=False, query=True, required=False) +option: Str('description', attribute=True, autofill=False, cli_name='desc', multivalue=False, query=True, required=False) +option: Str('password?', cli_name='password') +option: Str('password_file?', cli_name='password_file') +option: Flag('pkey_only?', autofill=True, default=False) +option: Bytes('private_key?', cli_name='private_key') +option: Str('private_key_file?', cli_name='private_key_file') +option: Flag('raw', autofill=True, cli_name='raw', default=False, exclude='webui') +option: Str('secret_name', attribute=True, autofill=False, cli_name='secret', maxlength=255, multivalue=False, pattern='^[a-zA-Z0-9_.-]+$', primary_key=True, query=True, required=False) +option: Str('version?', exclude='webui') +output: Output('count', , None) +output: ListOfEntries('result', (, ), Gettext('A list of LDAP entries', domain='ipa', localedir=None)) +output: Output('summary', (, ), None) +output: Output('truncated', , None) +command: vaultsecret_mod +args: 2,12,3 +arg: Str('vaultcn', cli_name='vault', maxlength=255, multivalue=False, pattern='^[a-zA-Z0-9_.-]+$', primary_key=True, query=True, required=True) +arg: Str('secret_name', attribute=True, cli_name='secret', maxlength=255, multivalue=False, pattern='^[a-zA-Z0-9_.-]+$', primary_key=True, query=True, required=True) +option: Flag('all', autofill=True, cli_name='all', default=False, exclude='webui') +option: Str('container?', cli_name='container') +option: Bytes('data?', cli_name='data') +option: Str('description?', cli_name='desc') +option: Str('in?', cli_name='in') +option: Str('password?', cli_name='password') +option: Str('password_file?', cli_name='password_file') +option: Bytes('private_key?', cli_name='private_key') +option: Str('private_key_file?', cli_name='private_key_file') +option: Flag('raw', autofill=True, cli_name='raw', default=False, exclude='webui') +option: Str('text?', cli_name='text') +option: Str('version?', exclude='webui') +output: Entry('result', , Gettext('A dictionary representing an LDAP entry', domain='ipa', localedir=None)) +output: Output('summary', (, ), None) +output: PrimaryKey('value', None, None) +command: vaultsecret_show +args: 2,11,3 +arg: Str('vaultcn', cli_name='vault', maxlength=255, multivalue=False, pattern='^[a-zA-Z0-9_.-]+$', primary_key=True, query=True, required=True) +arg: Str('secret_name', attribute=True, cli_name='secret', maxlength=255, multivalue=False, pattern='^[a-zA-Z0-9_.-]+$', primary_key=True, query=True, required=True) +option: Flag('all', autofill=True, cli_name='all', default=False, exclude='webui') +option: Str('container?', cli_name='container') +option: Str('out?', cli_name='out') +option: Str('password?', cli_name='password') +option: Str('password_file?', cli_name='password_file') +option: Bytes('private_key?', cli_name='private_key') +option: Str('private_key_file?', cli_name='private_key_file') +option: Flag('raw', autofill=True, cli_name='raw', default=False, exclude='webui') +option: Flag('show_text?', autofill=True, default=False) +option: Flag('stdout?', autofill=True, default=False) +option: Str('version?', exclude='webui') +output: Entry('result', , Gettext('A dictionary representing an LDAP entry', domain='ipa', localedir=None)) +output: Output('summary', (, ), None) +output: PrimaryKey('value', None, None) capability: messages 2.52 capability: optional_uid_params 2.54 capability: permissions2 2.69 diff --git a/VERSION b/VERSION index 564ed8eb148fce12d5951a47cf73c2eb156579cf..4c93d3c16d08421defee6492b2475e5e8444697b 100644 --- a/VERSION +++ b/VERSION @@ -90,5 +90,5 @@ IPA_DATA_VERSION=20100614120000 # # ######################################################## IPA_API_VERSION_MAJOR=2 -IPA_API_VERSION_MINOR=117 -# Last change: edewata - added symmetric and asymmetric vaults +IPA_API_VERSION_MINOR=118 +# Last change: edewata - added vault secret plugin diff --git a/ipalib/plugins/vaultsecret.py b/ipalib/plugins/vaultsecret.py new file mode 100644 index 0000000000000000000000000000000000000000..b0896155a5af13013f13f2b3ae586da2a8832c30 --- /dev/null +++ b/ipalib/plugins/vaultsecret.py @@ -0,0 +1,1118 @@ +# Authors: +# Endi S. Dewata +# +# Copyright (C) 2015 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 . + +import base64 +import getpass +import json +import sys + +from ipalib import api, errors +from ipalib import Str, Bytes, Flag +from ipalib.plugable import Registry +from ipalib.plugins.baseldap import LDAPObject, LDAPSearch, LDAPRetrieve +from ipalib import _, ngettext + +__doc__ = _(""" +Vault secrets +""") + _(""" +Manage vault secrets. +""") + _(""" +EXAMPLES: +""") + _(""" + List vault secrets: + ipa vaultsecret-find MyVault +""") + _(""" + Add a vault secret: + ipa vaultsecret-add MyVault MySecret --in data.bin --desc "My vault secret" +""") + _(""" + Retrieve a vault secret: + ipa vaultsecret-show MyVault MySecret --out data.bin +""") + _(""" + Modify a vault secret: + ipa vaultsecret-mod MyVault MySecret --desc "My vault secret" +""") + _(""" + Delete a vault secret: + ipa vaultsecret-del MyVault MySecret +""") + +register = Registry() + + at register() +class vaultsecret(LDAPObject): + __doc__ = _(""" + Vault secret object. + """) + + parent_object = 'vault' + object_name = _('vault secret') + object_name_plural = _('vault secrets') + + default_attributes = [ + 'cn', + 'description', + 'data', + ] + search_display_attributes = [ + 'cn', + 'description', + ] + + label = _('Vault secrets') + label_singular = _('Vault secret') + + takes_params = ( + Str('secret_name', + cli_name='secret', + label=_('Secret name'), + primary_key=True, + pattern='^[a-zA-Z0-9_.-]+$', + pattern_errmsg='may only include letters, numbers, _, ., and -', + maxlength=255, + ), + Str('description?', + cli_name='desc', + label=_('Description'), + doc=_('Secret description'), + ), + Bytes('data?', + cli_name='data', + label=_('Data'), + doc=_('Base-64 encoded binary secret data'), + ), + ) + + + at register() +class vaultsecret_add(LDAPRetrieve): + __doc__ = _('Add a new vault secret.') + + takes_options = ( + Str('container?', + cli_name='container', + doc=_('Container ID'), + ), + Str('description?', + cli_name='desc', + doc=_('Secret description'), + ), + Bytes('data?', + cli_name='data', + doc=_('Base-64 encoded binary secret data'), + ), + Str('text?', + cli_name='text', + doc=_('Text secret data'), + ), + Str('in?', + cli_name='in', + doc=_('File containing secret data'), + ), + Str('password?', + cli_name='password', + doc=_('Vault password'), + ), + Str('password_file?', + cli_name='password_file', + doc=_('File containing the vault password'), + ), + Bytes('private_key?', + cli_name='private_key', + doc=_('Vault private key'), + ), + Str('private_key_file?', + cli_name='private_key_file', + doc=_('File containing the vault private key'), + ), + ) + + msg_summary = _('Added vault secret "%(value)s"') + + def forward(self, *args, **options): + + vault_name = args[0] + secret_name = args[1] + container_id = api.Object.vaultcontainer.normalize_id(options.get('container')) + + vault_type = 'standard' + salt = None + + # retrieve vault info + response = api.Command.vault_show(vault_name, container=container_id) + result = response['result'] + + if result.has_key('ipavaulttype'): + vault_type = result['ipavaulttype'][0] + + if result.has_key('ipavaultsalt'): + salt = str(result['ipavaultsalt'][0]) + + description = options.get('description') + data = options.get('data') + text = options.get('text') + input_file = options.get('in') + password = options.get('password') + password_file = options.get('password_file') + private_key = options.get('private_key') + private_key_file = options.get('private_key_file') + + # don't send these parameters to server + if 'data' in options: + del options['data'] + if 'text' in options: + del options['text'] + if 'in' in options: + del options['in'] + if 'password' in options: + del options['password'] + if 'password_file' in options: + del options['password_file'] + if 'private_key' in options: + del options['private_key'] + if 'private_key_file' in options: + del options['private_key_file'] + if 'source_secret' in options: + del options['source_secret'] + + # type-specific initialization + if vault_type == 'standard': + + if password: + raise errors.ValidationError(name='password', + error=_('Invalid parameter for %s vault' % vault_type)) + + if password_file: + raise errors.ValidationError(name='password_file', + error=_('Invalid parameter for %s vault' % vault_type)) + + if private_key: + raise errors.ValidationError(name='private_key', + error=_('Invalid parameter for %s vault' % vault_type)) + + if private_key_file: + raise errors.ValidationError(name='private_key_file', + error=_('Invalid parameter for %s vault' % vault_type)) + + elif vault_type == 'symmetric': + + if private_key: + raise errors.ValidationError(name='private_key', + error=_('Invalid parameter for %s vault' % vault_type)) + + if private_key_file: + raise errors.ValidationError(name='private_key_file', + error=_('Invalid parameter for %s vault' % vault_type)) + + # get vault password + if password: + pass + + elif password_file: + with open(password_file) as f: + password = unicode(f.read().rstrip('\n')) + + else: + password = unicode(getpass.getpass('Password: ')) + + elif vault_type == 'asymmetric': + + if password: + raise errors.ValidationError(name='password', + error=_('Invalid parameter for %s vault' % vault_type)) + + if password_file: + raise errors.ValidationError(name='password_file', + error=_('Invalid parameter for %s vault' % vault_type)) + + # get vault public key + if private_key: + pass + + elif private_key_file: + with open(private_key_file, 'rb') as f: + private_key = f.read() + + else: + raise errors.ValidationError(name='private_key', + error=_('Missing vault private key')) + + else: + raise errors.ValidationError(name='vault_type', + error=_('Invalid vault type')) + + # retrieve secrets + response = api.Command.vault_retrieve( + vault_name, + container=container_id, + password=password, + private_key=private_key) + + vault_data = response['result']['data'] + + if not vault_data: + json_data = { + 'secrets': [] + } + + else: + json_data = json.loads(vault_data) + + secrets = json_data['secrets'] + + # get data + if data: + if text: + raise errors.ValidationError(name='text', + error=_('Input data already specified')) + + if input_file: + raise errors.ValidationError(name='input_file', + error=_('Input data already specified')) + + elif text: + if input_file: + raise errors.ValidationError(name='input_file', + error=_('Input data already specified')) + + data = text.encode('utf-8') + + elif input_file: + with open(input_file, 'rb') as f: + data = f.read() + + else: + data = '' + + # add new secret + for secret in secrets: + if secret['secret_name'] == secret_name: + raise errors.DuplicateEntry(message=_('vault secret with name "%s" already exists' % secret_name)) + + # store encoded data for storage + secret = { + 'secret_name': secret_name, + 'data': unicode(base64.b64encode(data)), + } + if description: + secret['description'] = description + + secrets.append(secret) + + # rearchive secrets + vault_data = json.dumps(json_data) + response = api.Command.vault_archive( + vault_name, + container=container_id, + data=vault_data, + password=password) + + # restore binary data for response + secret['data'] = data + + response = { + 'value': secret_name, + 'summary': u'Added vault secret "%s"' % secret_name, + 'result': secret, + } + + return response + + + at register() +class vaultsecret_del(LDAPRetrieve): + __doc__ = _('Delete a vault secret.') + + takes_options = ( + Str('container?', + cli_name='container', + doc=_('Container ID'), + ), + Str('password?', + cli_name='password', + doc=_('Vault password'), + ), + Str('password_file?', + cli_name='password_file', + doc=_('File containing the vault password'), + ), + Bytes('private_key?', + cli_name='private_key', + doc=_('Vault private key'), + ), + Str('private_key_file?', + cli_name='private_key_file', + doc=_('File containing the vault private key'), + ), + ) + + msg_summary = _('Deleted vault secret "%(value)s"') + + def forward(self, *args, **options): + + vault_name = args[0] + secret_name = args[1] + container_id = api.Object.vaultcontainer.normalize_id(options.get('container')) + + vault_type = 'standard' + salt = None + + # retrieve vault info + response = api.Command.vault_show(vault_name, container=container_id) + result = response['result'] + + if result.has_key('ipavaulttype'): + vault_type = result['ipavaulttype'][0] + + if result.has_key('ipavaultsalt'): + salt = str(result['ipavaultsalt'][0]) + + password = options.get('password') + password_file = options.get('password_file') + private_key = options.get('private_key') + private_key_file = options.get('private_key_file') + + # don't send these parameters to server + if 'password' in options: + del options['password'] + if 'password_file' in options: + del options['password_file'] + if 'private_key' in options: + del options['private_key'] + if 'private_key_file' in options: + del options['private_key_file'] + + # type-specific initialization + if vault_type == 'standard': + + if password: + raise errors.ValidationError(name='password', + error=_('Invalid parameter for %s vault' % vault_type)) + + if password_file: + raise errors.ValidationError(name='password_file', + error=_('Invalid parameter for %s vault' % vault_type)) + + if private_key: + raise errors.ValidationError(name='private_key', + error=_('Invalid parameter for %s vault' % vault_type)) + + if private_key_file: + raise errors.ValidationError(name='private_key_file', + error=_('Invalid parameter for %s vault' % vault_type)) + + elif vault_type == 'symmetric': + + if private_key: + raise errors.ValidationError(name='private_key', + error=_('Invalid parameter for %s vault' % vault_type)) + + if private_key_file: + raise errors.ValidationError(name='private_key_file', + error=_('Invalid parameter for %s vault' % vault_type)) + + # get vault password + if password: + pass + + elif password_file: + with open(password_file) as f: + password = unicode(f.read().rstrip('\n')) + + else: + password = unicode(getpass.getpass('Password: ')) + + elif vault_type == 'asymmetric': + + if password: + raise errors.ValidationError(name='password', + error=_('Invalid parameter for %s vault' % vault_type)) + + if password_file: + raise errors.ValidationError(name='password_file', + error=_('Invalid parameter for %s vault' % vault_type)) + + # get vault public key + if private_key: + pass + + elif private_key_file: + with open(private_key_file, 'rb') as f: + private_key = f.read() + + else: + raise errors.ValidationError(name='private_key', + error=_('Missing vault private key')) + + else: + raise errors.ValidationError(name='vault_type', + error=_('Invalid vault type')) + + # retrieve secrets + response = api.Command.vault_retrieve( + vault_name, + container=container_id, + password=password, + private_key=private_key) + + vault_data = response['result']['data'] + + if not vault_data: + json_data = { + 'secrets': [] + } + + else: + json_data = json.loads(vault_data) + + secrets = json_data['secrets'] + + # find the secret + secret = None + for s in secrets: + if s['secret_name'] == secret_name: + secret = s + break + + if not secret: + raise errors.NotFound(reason=_('%s: vault secret not found' % secret_name)) + + # delete secret + secrets.remove(secret) + + # rearchive secrets + vault_data = json.dumps(json_data) + response = api.Command.vault_archive( + vault_name, + container=container_id, + data=vault_data, + password=password) + + response = { + 'value': secret_name, + 'summary': u'Deleted vault secret "%s"' % secret_name, + 'result': { + 'failed': (), + }, + } + + return response + + + at register() +class vaultsecret_find(LDAPSearch): + __doc__ = _(""" + Search for vault secrets. + """) + + takes_options = ( + Str('container?', + cli_name='container', + doc=_('Container ID'), + ), + Str('password?', + cli_name='password', + doc=_('Vault password'), + ), + Str('password_file?', + cli_name='password_file', + doc=_('File containing the vault password'), + ), + Bytes('private_key?', + cli_name='private_key', + doc=_('Vault private key'), + ), + Str('private_key_file?', + cli_name='private_key_file', + doc=_('File containing the vault private key'), + ), + ) + + def forward(self, *args, **options): + + vault_name = args[0] + container_id = api.Object.vaultcontainer.normalize_id(options.get('container')) + + vault_type = 'standard' + salt = None + + # retrieve vault info + response = api.Command.vault_show(vault_name, container=container_id) + result = response['result'] + + if result.has_key('ipavaulttype'): + vault_type = result['ipavaulttype'][0] + + if result.has_key('ipavaultsalt'): + salt = str(result['ipavaultsalt'][0]) + + password = options.get('password') + password_file = options.get('password_file') + private_key = options.get('private_key') + private_key_file = options.get('private_key_file') + + # don't send these parameters to server + if 'password' in options: + del options['password'] + if 'password_file' in options: + del options['password_file'] + if 'private_key' in options: + del options['private_key'] + if 'private_key_file' in options: + del options['private_key_file'] + + # type-specific initialization + if vault_type == 'standard': + + if password: + raise errors.ValidationError(name='password', + error=_('Invalid parameter for %s vault' % vault_type)) + + if password_file: + raise errors.ValidationError(name='password_file', + error=_('Invalid parameter for %s vault' % vault_type)) + + if private_key: + raise errors.ValidationError(name='private_key', + error=_('Invalid parameter for %s vault' % vault_type)) + + if private_key_file: + raise errors.ValidationError(name='private_key_file', + error=_('Invalid parameter for %s vault' % vault_type)) + + elif vault_type == 'symmetric': + + if private_key: + raise errors.ValidationError(name='private_key', + error=_('Invalid parameter for %s vault' % vault_type)) + + if private_key_file: + raise errors.ValidationError(name='private_key_file', + error=_('Invalid parameter for %s vault' % vault_type)) + + # get vault password + if password: + pass + + elif password_file: + with open(password_file) as f: + password = unicode(f.read().rstrip('\n')) + + else: + password = unicode(getpass.getpass('Password: ')) + + elif vault_type == 'asymmetric': + + if password: + raise errors.ValidationError(name='password', + error=_('Invalid parameter for %s vault' % vault_type)) + + if password_file: + raise errors.ValidationError(name='password_file', + error=_('Invalid parameter for %s vault' % vault_type)) + + # get vault public key + if private_key: + pass + + elif private_key_file: + with open(private_key_file, 'rb') as f: + private_key = f.read() + + else: + raise errors.ValidationError(name='private_key', + error=_('Missing vault private key')) + + else: + raise errors.ValidationError(name='vault_type', + error=_('Invalid vault type')) + + response = api.Command.vault_retrieve( + vault_name, + container=container_id, + password=password, + private_key=private_key) + + vault_data = response['result']['data'] + + if not vault_data: + json_data = { + 'secrets': [] + } + + else: + json_data = json.loads(vault_data) + + secrets = json_data['secrets'] + + # decode data for response + for secret in secrets: + secret['data'] = base64.b64decode(secret['data']) + + response = { + 'count': len(secrets), + 'truncated': False, + 'summary': u'%d vault secret matched' % len(secrets), + 'result': secrets, + } + + return response + + + at register() +class vaultsecret_mod(LDAPRetrieve): + __doc__ = _('Modify a vault secret.') + + takes_options = ( + Str('container?', + cli_name='container', + doc=_('Container ID'), + ), + Str('description?', + cli_name='desc', + doc=_('Secret description'), + ), + Bytes('data?', + cli_name='data', + doc=_('Binary secret data'), + ), + Str('text?', + cli_name='text', + doc=_('Text secret data'), + ), + Str('in?', + cli_name='in', + doc=_('File containing secret data'), + ), + Str('password?', + cli_name='password', + doc=_('Vault password'), + ), + Str('password_file?', + cli_name='password_file', + doc=_('File containing the vault password'), + ), + Bytes('private_key?', + cli_name='private_key', + doc=_('Vault private key'), + ), + Str('private_key_file?', + cli_name='private_key_file', + doc=_('File containing the vault private key'), + ), + ) + + msg_summary = _('Modified vault secret "%(value)s"') + + def forward(self, *args, **options): + + vault_name = args[0] + secret_name = args[1] + container_id = api.Object.vaultcontainer.normalize_id(options.get('container')) + + vault_type = 'standard' + salt = None + + # retrieve vault info + response = api.Command.vault_show(vault_name, container=container_id) + result = response['result'] + + if result.has_key('ipavaulttype'): + vault_type = result['ipavaulttype'][0] + + if result.has_key('ipavaultsalt'): + salt = str(result['ipavaultsalt'][0]) + + description = options.get('description') + data = options.get('data') + text = options.get('text') + input_file = options.get('in') + password = options.get('password') + password_file = options.get('password_file') + private_key = options.get('private_key') + private_key_file = options.get('private_key_file') + + # don't send these parameters to server + if 'data' in options: + del options['data'] + if 'text' in options: + del options['text'] + if 'in' in options: + del options['in'] + if 'password' in options: + del options['password'] + if 'password_file' in options: + del options['password_file'] + if 'private_key' in options: + del options['private_key'] + if 'private_key_file' in options: + del options['private_key_file'] + + # type-specific initialization + if vault_type == 'standard': + + if password: + raise errors.ValidationError(name='password', + error=_('Invalid parameter for %s vault' % vault_type)) + + if password_file: + raise errors.ValidationError(name='password_file', + error=_('Invalid parameter for %s vault' % vault_type)) + + if private_key: + raise errors.ValidationError(name='private_key', + error=_('Invalid parameter for %s vault' % vault_type)) + + if private_key_file: + raise errors.ValidationError(name='private_key_file', + error=_('Invalid parameter for %s vault' % vault_type)) + + elif vault_type == 'symmetric': + + if private_key: + raise errors.ValidationError(name='private_key', + error=_('Invalid parameter for %s vault' % vault_type)) + + if private_key_file: + raise errors.ValidationError(name='private_key_file', + error=_('Invalid parameter for %s vault' % vault_type)) + + # get vault password + if password: + pass + + elif password_file: + with open(password_file) as f: + password = unicode(f.read().rstrip('\n')) + + else: + password = unicode(getpass.getpass('Password: ')) + + elif vault_type == 'asymmetric': + + if password: + raise errors.ValidationError(name='password', + error=_('Invalid parameter for %s vault' % vault_type)) + + if password_file: + raise errors.ValidationError(name='password_file', + error=_('Invalid parameter for %s vault' % vault_type)) + + # get vault public key + if private_key: + pass + + elif private_key_file: + with open(private_key_file, 'rb') as f: + private_key = f.read() + + else: + raise errors.ValidationError(name='private_key', + error=_('Missing vault private key')) + + else: + raise errors.ValidationError(name='vault_type', + error=_('Invalid vault type')) + + # get data + if data: + if text: + raise errors.ValidationError(name='text', + error=_('Input data already specified')) + + if input_file: + raise errors.ValidationError(name='input_file', + error=_('Input data already specified')) + + elif text: + if input_file: + raise errors.ValidationError(name='input_file', + error=_('Input data already specified')) + + data = text.encode('utf-8') + + elif input_file: + with open(input_file, 'rb') as f: + data = f.read() + + else: + pass + + # retrieve secrets + response = api.Command.vault_retrieve( + vault_name, + container=container_id, + password=password, + private_key=private_key) + + vault_data = response['result']['data'] + + if not vault_data: + json_data = { + 'secrets': [] + } + + else: + json_data = json.loads(vault_data) + + secrets = json_data['secrets'] + + # find the secret + secret = None + for s in secrets: + if s['secret_name'] == secret_name: + secret = s + break + + if not secret: + raise errors.NotFound(reason=_('%s: vault secret not found' % secret_name)) + + # modify the secret + if description: + secret['description'] = description + if data: + secret['data'] = unicode(base64.b64encode(data)) + + # rearchive secrets + vault_data = json.dumps(json_data) + response = api.Command.vault_archive( + vault_name, + container=container_id, + data=vault_data, + password=password) + + # decode data for response + secret['data'] = base64.b64decode(secret['data']) + + response = { + 'value': secret_name, + 'summary': u'Modified vault secret "%s"' % secret_name, + 'result': secret, + } + + return response + + + at register() +class vaultsecret_show(LDAPRetrieve): + __doc__ = _('Display information about a vault secret.') + + takes_options = ( + Str('container?', + cli_name='container', + doc=_('Container ID'), + ), + Flag('show_text?', + doc=_('Show text data'), + autofill=False, + ), + Flag('stdout?', + doc=_('Show data on standard output'), + autofill=False, + ), + Str('out?', + cli_name='out', + doc=_('File to store retrieved data'), + ), + Str('password?', + cli_name='password', + doc=_('Vault password'), + ), + Str('password_file?', + cli_name='password_file', + doc=_('File containing the vault password'), + ), + Bytes('private_key?', + cli_name='private_key', + doc=_('Vault private key'), + ), + Str('private_key_file?', + cli_name='private_key_file', + doc=_('File containing the vault private key'), + ), + ) + + has_output_params = ( + Str('text', + label=_('Text'), + ), + ) + + def forward(self, *args, **options): + + vault_name = args[0] + secret_name = args[1] + container_id = api.Object.vaultcontainer.normalize_id(options.get('container')) + + vault_type = 'standard' + salt = None + + # retrieve vault info + response = api.Command.vault_show(vault_name, container=container_id) + result = response['result'] + + if result.has_key('ipavaulttype'): + vault_type = result['ipavaulttype'][0] + + if result.has_key('ipavaultsalt'): + salt = str(result['ipavaultsalt'][0]) + + show_text = options.get('show_text') + stdout = options.get('stdout') + output_file = options.get('out') + password = options.get('password') + password_file = options.get('password_file') + private_key = options.get('private_key') + private_key_file = options.get('private_key_file') + + # don't send these parameters to server + if 'show_text' in options: + del options['show_text'] + if 'stdout' in options: + del options['stdout'] + if 'out' in options: + del options['out'] + if 'password' in options: + del options['password'] + if 'password_file' in options: + del options['password_file'] + if 'private_key' in options: + del options['private_key'] + if 'private_key_file' in options: + del options['private_key_file'] + + # type-specific initialization + if vault_type == 'standard': + + if password: + raise errors.ValidationError(name='password', + error=_('Invalid parameter for %s vault' % vault_type)) + + if password_file: + raise errors.ValidationError(name='password_file', + error=_('Invalid parameter for %s vault' % vault_type)) + + if private_key: + raise errors.ValidationError(name='private_key', + error=_('Invalid parameter for %s vault' % vault_type)) + + if private_key_file: + raise errors.ValidationError(name='private_key_file', + error=_('Invalid parameter for %s vault' % vault_type)) + + elif vault_type == 'symmetric': + + if private_key: + raise errors.ValidationError(name='private_key', + error=_('Invalid parameter for %s vault' % vault_type)) + + if private_key_file: + raise errors.ValidationError(name='private_key_file', + error=_('Invalid parameter for %s vault' % vault_type)) + + # get vault password + if password: + pass + + elif password_file: + with open(password_file) as f: + password = unicode(f.read().rstrip('\n')) + + else: + password = unicode(getpass.getpass('Password: ')) + + elif vault_type == 'asymmetric': + + if password: + raise errors.ValidationError(name='password', + error=_('Invalid parameter for %s vault' % vault_type)) + + if password_file: + raise errors.ValidationError(name='password_file', + error=_('Invalid parameter for %s vault' % vault_type)) + + # get vault public key + if private_key: + pass + + elif private_key_file: + with open(private_key_file, 'rb') as f: + private_key = f.read() + + else: + raise errors.ValidationError(name='private_key', + error=_('Missing vault private key')) + + else: + raise errors.ValidationError(name='vault_type', + error=_('Invalid vault type')) + + # retrieve secrets + response = api.Command.vault_retrieve( + vault_name, + container=container_id, + password=password, + private_key=private_key) + + vault_data = response['result']['data'] + + if not vault_data: + json_data = { + 'secrets': [] + } + + else: + json_data = json.loads(vault_data) + + secrets = json_data['secrets'] + + secret = None + + # find the secret + for s in secrets: + if s['secret_name'] == secret_name: + secret = s + break + + if not secret: + raise errors.NotFound(reason=_('%s: vault secret not found' % secret_name)) + + # decode data for response + secret['data'] = base64.b64decode(secret['data']) + + response = { + 'value': secret_name, + 'result': secret, + } + + if stdout: + sys.stdout.write(secret['data']) + response['result'] = {} + + elif output_file: + with open(output_file, 'w') as f: + f.write(secret['data']) + response['result'] = {} + + elif show_text: + response['result']['text'] = unicode(secret['data']) + del response['result']['data'] + + else: + pass + + return response diff --git a/ipatests/test_xmlrpc/test_vaultsecret_plugin.py b/ipatests/test_xmlrpc/test_vaultsecret_plugin.py new file mode 100644 index 0000000000000000000000000000000000000000..68f0fb0d7085be512939e397ea49abbcf3ca3c7b --- /dev/null +++ b/ipatests/test_xmlrpc/test_vaultsecret_plugin.py @@ -0,0 +1,211 @@ +# Authors: +# Endi S. Dewata +# +# Copyright (C) 2015 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 . + +""" +Test the `ipalib/plugins/vaultsecret.py` module. +""" + +from ipalib import api, errors +from xmlrpc_test import Declarative, fuzzy_string + +test_vault = u'test_vault' +test_vaultsecret = u'test_vaultsecret' +binary_data = '\x01\x02\x03\x04' +text_data = u'secret' + +class test_vault(Declarative): + + cleanup_commands = [ + ('vault_del', [test_vault], {'continue': True}), + ] + + tests = [ + + { + 'desc': 'Create test vault', + 'command': ( + 'vault_add', + [test_vault], + {}, + ), + 'expected': { + 'value': test_vault, + 'summary': 'Added vault "%s"' % test_vault, + 'result': { + 'dn': u'cn=%s,cn=admin,cn=users,cn=vaults,%s' % (test_vault, api.env.basedn), + 'objectclass': (u'ipaVault', u'top'), + 'cn': [test_vault], + 'vault_id': u'/users/admin/%s' % test_vault, + 'owner_user': [u'admin'], + 'ipavaulttype': [u'standard'], + }, + }, + }, + + { + 'desc': 'Create test vault secret with binary data', + 'command': ( + 'vaultsecret_add', + [test_vault, test_vaultsecret], + { + 'data': binary_data, + }, + ), + 'expected': { + 'value': test_vaultsecret, + 'summary': 'Added vault secret "%s"' % test_vaultsecret, + 'result': { + 'secret_name': test_vaultsecret, + 'data': binary_data, + }, + }, + }, + + { + 'desc': 'Create duplicate vault secret', + 'command': ( + 'vaultsecret_add', + [test_vault, test_vaultsecret], + {}, + ), + 'expected': errors.DuplicateEntry(message=u'vault secret with name "%s" already exists' % test_vaultsecret), + }, + + { + 'desc': 'Find vault secrets', + 'command': ( + 'vaultsecret_find', + [test_vault], + {}, + ), + 'expected': { + 'count': 1, + 'truncated': False, + 'summary': u'1 vault secret matched', + 'result': [ + { + 'secret_name': test_vaultsecret, + 'data': binary_data, + }, + ], + }, + }, + + { + 'desc': 'Retrieve test vault secret', + 'command': ( + 'vaultsecret_show', + [test_vault, test_vaultsecret], + {}, + ), + 'expected': { + 'value': test_vaultsecret, + 'summary': None, + 'result': { + 'secret_name': test_vaultsecret, + 'data': binary_data, + }, + }, + }, + + { + 'desc': 'Modify test vault secret', + 'command': ( + 'vaultsecret_mod', + [test_vault, test_vaultsecret], + { + 'description': u'Test vault secret', + }, + ), + 'expected': { + 'value': test_vaultsecret, + 'summary': u'Modified vault secret "%s"' % test_vaultsecret, + 'result': { + 'secret_name': test_vaultsecret, + 'description': u'Test vault secret', + 'data': binary_data, + }, + }, + }, + + { + 'desc': 'Delete test vault secret', + 'command': ( + 'vaultsecret_del', + [test_vault, test_vaultsecret], + {}, + ), + 'expected': { + 'value': test_vaultsecret, + 'summary': u'Deleted vault secret "%s"' % test_vaultsecret, + 'result': { + 'failed': (), + }, + }, + }, + + { + 'desc': 'Delete non-existent vault secret', + 'command': ( + 'vaultsecret_del', + [test_vault, test_vaultsecret], + {}, + ), + 'expected': errors.NotFound(reason=u'%s: vault secret not found' % test_vaultsecret), + }, + + { + 'desc': 'Create test vault secret with text data', + 'command': ( + 'vaultsecret_add', + [test_vault, test_vaultsecret], + { + 'text': text_data, + }, + ), + 'expected': { + 'value': test_vaultsecret, + 'summary': 'Added vault secret "%s"' % test_vaultsecret, + 'result': { + 'secret_name': test_vaultsecret, + 'data': text_data.encode('utf-8'), + }, + }, + }, + + { + 'desc': 'Retrieve test vault secret as text', + 'command': ( + 'vaultsecret_show', + [test_vault, test_vaultsecret], + { + 'show_text': True, + }, + ), + 'expected': { + 'value': test_vaultsecret, + 'summary': None, + 'result': { + 'secret_name': test_vaultsecret, + 'text': text_data, + }, + }, + }, + + ] -- 1.9.0 From mbasti at redhat.com Mon Feb 16 12:41:00 2015 From: mbasti at redhat.com (Martin Basti) Date: Mon, 16 Feb 2015 13:41:00 +0100 Subject: [Freeipa-devel] [PATCH 0041] permission-add does not prompt for ipapermright option in interactive mode In-Reply-To: <54DC89A9.2030701@redhat.com> References: <54DA5132.7050003@redhat.com> <54DB7C5E.1040807@redhat.com> <54DC89A9.2030701@redhat.com> Message-ID: <54E1E55C.4010909@redhat.com> On 12/02/15 12:08, Martin Basti wrote: > On 11/02/15 17:28, Gabe Alford wrote: >> Oops. My mistake. Corrected patch attached. >> >> On Wed, Feb 11, 2015 at 8:59 AM, Martin Basti > > wrote: >> >> Sorry, alwaysask didnt work. >> It was asking for rights during permission-mod. >> >> I replaced alwaysask with flag "ask_create". >> Sorry for late catch. >> >> Updated patch attached. >> >> PS: your name+email is missing in commit message, is it on >> purpose? And time wasn't correct in previous patch. >> >> >> >> On 11/02/15 15:06, Gabe Alford wrote: >>> Good point. I personally was not aware of all that the API can >>> do. Thanks Martin^2! Updated patch attached. >>> >>> On Tue, Feb 10, 2015 at 11:42 AM, Martin Basti >>> > wrote: >>> >>> On 29/01/15 17:10, Gabe Alford wrote: >>>> Hello, >>>> >>>> Fix for https://fedorahosted.org/freeipa/ticket/4872 >>>> >>>> Thanks, >>>> >>>> Gabe >>>> >>>> >>>> _______________________________________________ >>>> Freeipa-devel mailing list >>>> Freeipa-devel at redhat.com >>>> https://www.redhat.com/mailman/listinfo/freeipa-devel >>> Thank you for your patch. >>> >>> IMO, would be better to use flag, alwaysask for >>> ipapermright, instead of creating new callback: >>> >>> StrEnum( >>> 'ipapermright*', >>> cli_name='right', >>> deprecated_cli_aliases={'permissions'}, >>> label=_('Granted rights'), >>> doc=_('Rights to grant ' >>> '(read, search, compare, write, add, >>> delete, all)'), >>> values=(u'read', u'search', u'compare', >>> u'write', u'add', u'delete', u'all'), >>> + alwaysask=True, >>> ), >>> >>> This change requires to generate new API.txt >>> >>> please run ./makeapi and increment API version in VERSION file. >>> >>> Thank you in advance :-) >>> >>> Martin^2 >>> >>> -- >>> Martin Basti >>> >>> >> >> >> -- >> Martin Basti >> >> > Thank you > > ACK > > -- > Martin Basti > > > _______________________________________________ > Freeipa-devel mailing list > Freeipa-devel at redhat.com > https://www.redhat.com/mailman/listinfo/freeipa-devel Somebody, please push this patch. -- Martin Basti -------------- next part -------------- An HTML attachment was scrubbed... URL: From dkupka at redhat.com Mon Feb 16 13:50:11 2015 From: dkupka at redhat.com (David Kupka) Date: Mon, 16 Feb 2015 14:50:11 +0100 Subject: [Freeipa-devel] [PATCH 0012] migrate-ds: exit with error message if no users/groups to migrate are found In-Reply-To: <54DE3EC5.3010200@redhat.com> References: <54CB54EA.5060106@redhat.com> <54CB5632.1070702@redhat.com> <54DDDB8F.80803@redhat.com> <54DE3EC5.3010200@redhat.com> Message-ID: <54E1F593.7030704@redhat.com> On 02/13/2015 07:13 PM, Martin Babinsky wrote: > On 02/13/2015 12:10 PM, David Kupka wrote: >> On 01/30/2015 11:00 AM, Martin Babinsky wrote: >>> On 01/30/2015 10:54 AM, Martin Babinsky wrote: >>>> Attached patch fixes https://fedorahosted.org/freeipa/ticket/4864. >>>> >>>> >>>> >>>> _______________________________________________ >>>> Freeipa-devel mailing list >>>> Freeipa-devel at redhat.com >>>> https://www.redhat.com/mailman/listinfo/freeipa-devel >>>> >>> >>> Got the ticket number wrong. Should be >>> https://fedorahosted.org/freeipa/ticket/4846 >>> >>> Attaching patch with fixed description. >>> >>> >>> >>> _______________________________________________ >>> Freeipa-devel mailing list >>> Freeipa-devel at redhat.com >>> https://www.redhat.com/mailman/listinfo/freeipa-devel >>> >> >> Hi, >> thanks for the patch. It works as expected but you've done two things in >> one patch: >> 1. Added check for empty migration. >> 2. Removed unused parameter of function. >> >> Each of them would be enough to fix the issue. Raising error seems to be >> better solution. >> If you think that unused parameters (yes, there is not only one :-) >> should be removed please do it in separate patch. >> > Very well, > > I am attaching patch which features only the check for empty user/group > containers during migration. > > I will also add example steps to reproduce > https://fedorahosted.org/freeipa/ticket/4846 since it can be a bit tricky: > > 1. Setup 389 directory server on one machine using setup-ds.pl script. > > 2. Delete all user and group entries from LDAP tree. > > 3. Setup an IPA master on another machine. > > 4. Enable migration of users/groups from 389DS to IPA master: > ipa config-mod --enable-migration=True > > 5. Force-migrate users/group from 389DS: > ipa migrate-ds ${389DS_LDAP_URI} --continue --with-compat > > 6. See /var/log/httpd/error_log for the resulting traceback. > Thanks, ACK. -- David Kupka From mkosek at redhat.com Mon Feb 16 15:32:30 2015 From: mkosek at redhat.com (Martin Kosek) Date: Mon, 16 Feb 2015 16:32:30 +0100 Subject: [Freeipa-devel] [PATCH] 0174-0175 ipa-kdb fixes In-Reply-To: <54DDAEA2.9020709@redhat.com> References: <20150121100348.GR4383@redhat.com> <20150126104409.4dd9cb8a@willson.usersys.redhat.com> <54DDAEA2.9020709@redhat.com> Message-ID: <54E20D8E.5020808@redhat.com> On 02/13/2015 08:58 AM, Martin Kosek wrote: > On 01/26/2015 04:44 PM, Simo Sorce wrote: >> On Wed, 21 Jan 2015 12:03:48 +0200 >> Alexander Bokovoy wrote: >> >>> Hi, >>> >>> couple patches to fix Kerberos DAL driver in relation to trusts. >>> >>> Patch 0174: >>> Allow using CA paths defined in krb5.conf on top of what we define >>> automatically for trusted domains. >>> https://fedorahosted.org/freeipa/ticket/4791 >>> >>> Patch 0175: >>> Change error code reported back to Kerberos client when a principal >>> from a disabled trusted domain attempts to access resources we >>> control. >>> >>> The error code will help older SSSD to properly reflect error message >>> in the PAM stack. >>> https://fedorahosted.org/freeipa/ticket/4788 >>> >> >> >> They both LGTM. >> >> Simo. >> > > Is that an ACK then? Sumit, were you able to test those by any chance? > > Thanks, > Martin > Sumit confirmed he tested them, so it is an ACK. Pushed to master, ipa-4-1. Martin From sbose at redhat.com Mon Feb 16 15:32:39 2015 From: sbose at redhat.com (Sumit Bose) Date: Mon, 16 Feb 2015 16:32:39 +0100 Subject: [Freeipa-devel] [PATCH] 0174-0175 ipa-kdb fixes In-Reply-To: <20150126104409.4dd9cb8a@willson.usersys.redhat.com> References: <20150121100348.GR4383@redhat.com> <20150126104409.4dd9cb8a@willson.usersys.redhat.com> Message-ID: <20150216153239.GO9695@p.redhat.com> On Mon, Jan 26, 2015 at 10:44:09AM -0500, Simo Sorce wrote: > On Wed, 21 Jan 2015 12:03:48 +0200 > Alexander Bokovoy wrote: > > > Hi, > > > > couple patches to fix Kerberos DAL driver in relation to trusts. > > > > Patch 0174: > > Allow using CA paths defined in krb5.conf on top of what we define > > automatically for trusted domains. > > https://fedorahosted.org/freeipa/ticket/4791 > > > > Patch 0175: > > Change error code reported back to Kerberos client when a principal > > from a disabled trusted domain attempts to access resources we > > control. > > > > The error code will help older SSSD to properly reflect error message > > in the PAM stack. > > https://fedorahosted.org/freeipa/ticket/4788 > > > > > They both LGTM. sorry for the delay, I tested the patches some time ago, but forgot the send the ACK, so ACK bye, Sumit > > Simo. > > -- > Simo Sorce * Red Hat, Inc * New York > > _______________________________________________ > Freeipa-devel mailing list > Freeipa-devel at redhat.com > https://www.redhat.com/mailman/listinfo/freeipa-devel From mkosek at redhat.com Mon Feb 16 15:35:06 2015 From: mkosek at redhat.com (Martin Kosek) Date: Mon, 16 Feb 2015 16:35:06 +0100 Subject: [Freeipa-devel] [PATCH 0012] migrate-ds: exit with error message if no users/groups to migrate are found In-Reply-To: <54E1F593.7030704@redhat.com> References: <54CB54EA.5060106@redhat.com> <54CB5632.1070702@redhat.com> <54DDDB8F.80803@redhat.com> <54DE3EC5.3010200@redhat.com> <54E1F593.7030704@redhat.com> Message-ID: <54E20E2A.9050706@redhat.com> On 02/16/2015 02:50 PM, David Kupka wrote: > On 02/13/2015 07:13 PM, Martin Babinsky wrote: >> On 02/13/2015 12:10 PM, David Kupka wrote: >>> On 01/30/2015 11:00 AM, Martin Babinsky wrote: >>>> On 01/30/2015 10:54 AM, Martin Babinsky wrote: >>>>> Attached patch fixes https://fedorahosted.org/freeipa/ticket/4864. >>>>> >>>>> >>>>> >>>>> _______________________________________________ >>>>> Freeipa-devel mailing list >>>>> Freeipa-devel at redhat.com >>>>> https://www.redhat.com/mailman/listinfo/freeipa-devel >>>>> >>>> >>>> Got the ticket number wrong. Should be >>>> https://fedorahosted.org/freeipa/ticket/4846 >>>> >>>> Attaching patch with fixed description. >>>> >>>> >>>> >>>> _______________________________________________ >>>> Freeipa-devel mailing list >>>> Freeipa-devel at redhat.com >>>> https://www.redhat.com/mailman/listinfo/freeipa-devel >>>> >>> >>> Hi, >>> thanks for the patch. It works as expected but you've done two things in >>> one patch: >>> 1. Added check for empty migration. >>> 2. Removed unused parameter of function. >>> >>> Each of them would be enough to fix the issue. Raising error seems to be >>> better solution. >>> If you think that unused parameters (yes, there is not only one :-) >>> should be removed please do it in separate patch. >>> >> Very well, >> >> I am attaching patch which features only the check for empty user/group >> containers during migration. >> >> I will also add example steps to reproduce >> https://fedorahosted.org/freeipa/ticket/4846 since it can be a bit tricky: >> >> 1. Setup 389 directory server on one machine using setup-ds.pl script. >> >> 2. Delete all user and group entries from LDAP tree. >> >> 3. Setup an IPA master on another machine. >> >> 4. Enable migration of users/groups from 389DS to IPA master: >> ipa config-mod --enable-migration=True >> >> 5. Force-migrate users/group from 389DS: >> ipa migrate-ds ${389DS_LDAP_URI} --continue --with-compat >> >> 6. See /var/log/httpd/error_log for the resulting traceback. >> > > Thanks, ACK. > Pushed to: master: 06376a48b24949a1144d037af9fbed89d0455d79 ipa-4-1: f7e6102ebfd6e2a87bd584fc2fbbcb9945ac7753 Martin From mkosek at redhat.com Mon Feb 16 15:41:59 2015 From: mkosek at redhat.com (Martin Kosek) Date: Mon, 16 Feb 2015 16:41:59 +0100 Subject: [Freeipa-devel] [PATCH 0041] permission-add does not prompt for ipapermright option in interactive mode In-Reply-To: <54E1E55C.4010909@redhat.com> References: <54DA5132.7050003@redhat.com> <54DB7C5E.1040807@redhat.com> <54DC89A9.2030701@redhat.com> <54E1E55C.4010909@redhat.com> Message-ID: <54E20FC7.4060809@redhat.com> On 02/16/2015 01:41 PM, Martin Basti wrote: > On 12/02/15 12:08, Martin Basti wrote: >> On 11/02/15 17:28, Gabe Alford wrote: >>> Oops. My mistake. Corrected patch attached. >>> >>> On Wed, Feb 11, 2015 at 8:59 AM, Martin Basti >> > wrote: >>> >>> Sorry, alwaysask didnt work. >>> It was asking for rights during permission-mod. >>> >>> I replaced alwaysask with flag "ask_create". >>> Sorry for late catch. >>> >>> Updated patch attached. >>> >>> PS: your name+email is missing in commit message, is it on >>> purpose? And time wasn't correct in previous patch. >>> >>> >>> >>> On 11/02/15 15:06, Gabe Alford wrote: >>>> Good point. I personally was not aware of all that the API can >>>> do. Thanks Martin^2! Updated patch attached. >>>> >>>> On Tue, Feb 10, 2015 at 11:42 AM, Martin Basti >>>> > wrote: >>>> >>>> On 29/01/15 17:10, Gabe Alford wrote: >>>>> Hello, >>>>> >>>>> Fix for https://fedorahosted.org/freeipa/ticket/4872 >>>>> >>>>> Thanks, >>>>> >>>>> Gabe >>>>> >>>>> >>>>> _______________________________________________ >>>>> Freeipa-devel mailing list >>>>> Freeipa-devel at redhat.com >>>>> https://www.redhat.com/mailman/listinfo/freeipa-devel >>>> Thank you for your patch. >>>> >>>> IMO, would be better to use flag, alwaysask for >>>> ipapermright, instead of creating new callback: >>>> >>>> StrEnum( >>>> 'ipapermright*', >>>> cli_name='right', >>>> deprecated_cli_aliases={'permissions'}, >>>> label=_('Granted rights'), >>>> doc=_('Rights to grant ' >>>> '(read, search, compare, write, add, >>>> delete, all)'), >>>> values=(u'read', u'search', u'compare', >>>> u'write', u'add', u'delete', u'all'), >>>> + alwaysask=True, >>>> ), >>>> >>>> This change requires to generate new API.txt >>>> >>>> please run ./makeapi and increment API version in VERSION file. >>>> >>>> Thank you in advance :-) >>>> >>>> Martin^2 >>>> >>>> -- Martin Basti >>>> >>>> >>> >>> >>> -- Martin Basti >>> >>> >> Thank you >> >> ACK >> >> -- >> Martin Basti >> >> >> _______________________________________________ >> Freeipa-devel mailing list >> Freeipa-devel at redhat.com >> https://www.redhat.com/mailman/listinfo/freeipa-devel > Somebody, please push this patch. There was a conflict on master branch. Rebased and pushed to master, ipa-4-1. Martin From pvoborni at redhat.com Mon Feb 16 16:13:00 2015 From: pvoborni at redhat.com (Petr Vobornik) Date: Mon, 16 Feb 2015 17:13:00 +0100 Subject: [Freeipa-devel] FreeIPA 4.1.3 release notes draft Message-ID: <54E2170C.2070705@redhat.com> Hello list, First version of release notes for FreeIPA 4.1.3 release is prepared: http://www.freeipa.org/page/Releases/4.1.3 Please comment/amend. Q: The same upgrade instructions are copied from release to release. Shouldn't we rather move them into separate page which would contain the upgrade steps? Listed by releases which introduced them. Thank you -- Petr Vobornik From mbasti at redhat.com Mon Feb 16 16:45:08 2015 From: mbasti at redhat.com (Martin Basti) Date: Mon, 16 Feb 2015 17:45:08 +0100 Subject: [Freeipa-devel] FreeIPA 4.1.3 release notes draft In-Reply-To: <54E2170C.2070705@redhat.com> References: <54E2170C.2070705@redhat.com> Message-ID: <54E21E94.9030108@redhat.com> On 16/02/15 17:13, Petr Vobornik wrote: > Hello list, > > First version of release notes for FreeIPA 4.1.3 release is prepared: > > http://www.freeipa.org/page/Releases/4.1.3 > > Please comment/amend. > > Q: The same upgrade instructions are copied from release to release. > Shouldn't we rather move them into separate page which would contain > the upgrade steps? Listed by releases which introduced them. > > Thank you number of commits listed do not match number in brackets for Tomas -- Martin Basti From mbasti at redhat.com Mon Feb 16 16:49:18 2015 From: mbasti at redhat.com (Martin Basti) Date: Mon, 16 Feb 2015 17:49:18 +0100 Subject: [Freeipa-devel] FreeIPA 4.1.3 release notes draft In-Reply-To: <54E2170C.2070705@redhat.com> References: <54E2170C.2070705@redhat.com> Message-ID: <54E21F8E.2090206@redhat.com> On 16/02/15 17:13, Petr Vobornik wrote: > Hello list, > > First version of release notes for FreeIPA 4.1.3 release is prepared: > > http://www.freeipa.org/page/Releases/4.1.3 > > Please comment/amend. > > Q: The same upgrade instructions are copied from release to release. > Shouldn't we rather move them into separate page which would contain > the upgrade steps? Listed by releases which introduced them. > > Thank you This commit is missing in detailed changelog https://git.fedorahosted.org/cgit/freeipa.git/commit/?id=3117e7b79cfcb4654a79d7fb79aaa852f53617a1 -- Martin Basti From pvoborni at redhat.com Mon Feb 16 16:53:19 2015 From: pvoborni at redhat.com (Petr Vobornik) Date: Mon, 16 Feb 2015 17:53:19 +0100 Subject: [Freeipa-devel] FreeIPA 4.1.3 release notes draft In-Reply-To: <54E21F8E.2090206@redhat.com> References: <54E2170C.2070705@redhat.com> <54E21F8E.2090206@redhat.com> Message-ID: <54E2207F.3030305@redhat.com> On 02/16/2015 05:49 PM, Martin Basti wrote: > On 16/02/15 17:13, Petr Vobornik wrote: >> Hello list, >> >> First version of release notes for FreeIPA 4.1.3 release is prepared: >> >> http://www.freeipa.org/page/Releases/4.1.3 >> >> Please comment/amend. >> >> Q: The same upgrade instructions are copied from release to release. >> Shouldn't we rather move them into separate page which would contain >> the upgrade steps? Listed by releases which introduced them. >> >> Thank you > > This commit is missing in detailed changelog > https://git.fedorahosted.org/cgit/freeipa.git/commit/?id=3117e7b79cfcb4654a79d7fb79aaa852f53617a1 > I'll add all commits which will be pushed until the release is tagged. Tomas' patch list fixed (stripped by accident). -- Petr Vobornik From pvoborni at redhat.com Mon Feb 16 16:58:26 2015 From: pvoborni at redhat.com (Petr Vobornik) Date: Mon, 16 Feb 2015 17:58:26 +0100 Subject: [Freeipa-devel] [PATCH] 798 Fix TOTP Synchronization Window label Message-ID: <54E221B2.2020205@redhat.com> not consistent with others -- Petr Vobornik -------------- next part -------------- A non-text attachment was scrubbed... Name: freeipa-pvoborni-0798-Fix-TOTP-Synchronization-Window-label.patch Type: text/x-patch Size: 887 bytes Desc: not available URL: From npmccallum at redhat.com Mon Feb 16 17:01:04 2015 From: npmccallum at redhat.com (Nathaniel McCallum) Date: Mon, 16 Feb 2015 12:01:04 -0500 Subject: [Freeipa-devel] [PATCH] 798 Fix TOTP Synchronization Window label In-Reply-To: <54E221B2.2020205@redhat.com> References: <54E221B2.2020205@redhat.com> Message-ID: <1424106064.11207.31.camel@redhat.com> On Mon, 2015-02-16 at 17:58 +0100, Petr Vobornik wrote: > not consistent with others ACK From mkosek at redhat.com Tue Feb 17 07:26:06 2015 From: mkosek at redhat.com (Martin Kosek) Date: Tue, 17 Feb 2015 08:26:06 +0100 Subject: [Freeipa-devel] FreeIPA 4.1.3 release notes draft In-Reply-To: <54E2170C.2070705@redhat.com> References: <54E2170C.2070705@redhat.com> Message-ID: <54E2ED0E.10307@redhat.com> On 02/16/2015 05:13 PM, Petr Vobornik wrote: ... > Q: The same upgrade instructions are copied from release to release. Shouldn't > we rather move them into separate page which would contain the upgrade steps? > Listed by releases which introduced them. I think that release notes could simply just contain the link to the Upgrade page on FreeIPA.org. I would only list special information in the upgrade section if there is an important change to upgrade or a migration needed (as with DNS Forward zones). This way, with information copied around, I do not think people read it anyway. Also, I think there is still the wrong order of upgrade tools mentioned in some release notes. Potentially, same with Feedback - it could just link to http://www.freeipa.org/page/Contribute#Communication Though this is not that critical, given it's short length. Martin From mkosek at redhat.com Tue Feb 17 07:27:12 2015 From: mkosek at redhat.com (Martin Kosek) Date: Tue, 17 Feb 2015 08:27:12 +0100 Subject: [Freeipa-devel] [PATCH] 798 Fix TOTP Synchronization Window label In-Reply-To: <1424106064.11207.31.camel@redhat.com> References: <54E221B2.2020205@redhat.com> <1424106064.11207.31.camel@redhat.com> Message-ID: <54E2ED50.9030908@redhat.com> On 02/16/2015 06:01 PM, Nathaniel McCallum wrote: > On Mon, 2015-02-16 at 17:58 +0100, Petr Vobornik wrote: >> not consistent with others > > ACK Pushed to: master: 76d401bb882283b039de2ceee8300d0c838db473 ipa-4-1: f1abbbca456adb74827230b073fa8a53746340af Martin From dkupka at redhat.com Tue Feb 17 11:18:30 2015 From: dkupka at redhat.com (David Kupka) Date: Tue, 17 Feb 2015 12:18:30 +0100 Subject: [Freeipa-devel] [PATCHES 0191-0194] Fix restoring states of services after uninstalling In-Reply-To: <54DB7FC6.1040507@redhat.com> References: <54DB7FC6.1040507@redhat.com> Message-ID: <54E32386.2050208@redhat.com> On 02/11/2015 05:13 PM, Martin Basti wrote: > https://fedorahosted.org/freeipa/ticket/4869 > > Fixes: > - enable/start a service after uninstallation if the service was > enabled/running before in correct way > - store status of service before disable/stop/start/enable operation > - run uninstall only for configured services > > Uninstall for ipa-dnskeysyncd is not executed because of > https://fedorahosted.org/freeipa/ticket/4901 > > Patches attached. > > > > _______________________________________________ > Freeipa-devel mailing list > Freeipa-devel at redhat.com > https://www.redhat.com/mailman/listinfo/freeipa-devel > Hi, thanks for patches. The code looks good to me but I found 2 issues: 1. httpd is left in non-working state after installing and uninstalling ipa-server. Tried on clean Fedora 21, the httpd was not configured before ipa-server installation. 2. Patch 191 needs (trivial) rebase. -- David Kupka From mbasti at redhat.com Tue Feb 17 13:49:06 2015 From: mbasti at redhat.com (Martin Basti) Date: Tue, 17 Feb 2015 14:49:06 +0100 Subject: [Freeipa-devel] [PATCHES 0191-0194] Fix restoring states of services after uninstalling In-Reply-To: <54E32386.2050208@redhat.com> References: <54DB7FC6.1040507@redhat.com> <54E32386.2050208@redhat.com> Message-ID: <54E346D2.6050206@redhat.com> On 17/02/15 12:18, David Kupka wrote: > On 02/11/2015 05:13 PM, Martin Basti wrote: >> https://fedorahosted.org/freeipa/ticket/4869 >> >> Fixes: >> - enable/start a service after uninstallation if the service was >> enabled/running before in correct way >> - store status of service before disable/stop/start/enable operation >> - run uninstall only for configured services >> >> Uninstall for ipa-dnskeysyncd is not executed because of >> https://fedorahosted.org/freeipa/ticket/4901 >> >> Patches attached. >> >> >> >> _______________________________________________ >> Freeipa-devel mailing list >> Freeipa-devel at redhat.com >> https://www.redhat.com/mailman/listinfo/freeipa-devel >> > Hi, > thanks for patches. The code looks good to me but I found 2 issues: > > 1. httpd is left in non-working state after installing and > uninstalling ipa-server. > Tried on clean Fedora 21, the httpd was not configured before > ipa-server installation. > > 2. Patch 191 needs (trivial) rebase. > Thanks, Updated patches attached. -- Martin Basti -------------- next part -------------- A non-text attachment was scrubbed... Name: freeipa-mbasti-0191.2-Fix-restoring-services-status-during-uninstall.patch Type: text/x-patch Size: 12225 bytes Desc: not available URL: -------------- next part -------------- A non-text attachment was scrubbed... Name: freeipa-mbasti-0192.2-Fix-do-not-enable-service-before-storing-status.patch Type: text/x-patch Size: 847 bytes Desc: not available URL: -------------- next part -------------- A non-text attachment was scrubbed... Name: freeipa-mbasti-0193.2-Uninstall-configured-services-only.patch Type: text/x-patch Size: 2667 bytes Desc: not available URL: -------------- next part -------------- A non-text attachment was scrubbed... Name: freeipa-mbasti-0194.2-Fix-saving-named-restore-status.patch Type: text/x-patch Size: 1655 bytes Desc: not available URL: From redhatrises at gmail.com Tue Feb 17 14:29:16 2015 From: redhatrises at gmail.com (Gabe Alford) Date: Tue, 17 Feb 2015 07:29:16 -0700 Subject: [Freeipa-devel] [PATCH 0039] Add test case for unsupported arg for ipa-advise In-Reply-To: References: <54B69B41.9070404@redhat.com> <54B6A097.9020601@redhat.com> <54B6A1F4.8000807@redhat.com> <54B6A602.9060101@redhat.com> Message-ID: Hello, I was wondering if I could get a review of this patch. Thanks, Gabe On Thursday, January 29, 2015, Gabe Alford wrote: > Hello, > > Here is a patch for https://fedorahosted.org/freeipa/ticket/4029 I > added test cases for valid and invalid advice. > > Thanks, > > Gabe > > On Wed, Jan 14, 2015 at 10:23 AM, Tomas Babej > wrote: > >> >> On 01/14/2015 06:13 PM, Gabe Alford wrote: >> >> On Wed, Jan 14, 2015 at 10:05 AM, Tomas Babej > > wrote: >> >>> >>> On 01/14/2015 06:00 PM, Tomas Babej wrote: >>> >>> >>> On 01/14/2015 05:37 PM, Tomas Babej wrote: >>> >>> >>> On 01/14/2015 02:55 PM, Gabe Alford wrote: >>> >>> Hello, >>> >>> In looking into https://fedorahosted.org/freeipa/ticket/4029 I >>> am wondering if there should be separate ipa-advise test, Yes/No? Could be >>> handy in the future to test more ipa-advise output? Or should this test be >>> added to the test_legacy_clients.py? >>> >>> Thanks, >>> >>> Gabe >>> >>> On Tue, Dec 2, 2014 at 9:21 PM, Gabe Alford >> > wrote: >>> >>>> Hello, >>>> >>>> I was going to try my hand at attempting a patch for ipa-tests. However >>>> in wanting to test my patch, I am not sure how to run ipa-tests to check if >>>> it works or not. Documentation is not really clear on what needs to be done >>>> to start a test and run a test. This is for >>>> https://fedorahosted.org/freeipa/ticket/4029 >>>> >>>> I have attached the patch that I have yet to really test with >>>> ipa-test. Any help on how to test the patch running ipa-tests would be >>>> great. Of course, if one of the reviewers looks at the patch and looks >>>> good, then I would be happy with that as well. >>>> >>>> Thanks, >>>> >>>> Gabe >>>> >>> >>> >>> >>> _______________________________________________ >>> Freeipa-devel mailing listFreeipa-devel at redhat.com https://www.redhat.com/mailman/listinfo/freeipa-devel >>> >>> >>> Hello, >>> >>> TL;DR: feel free to create a separate ipa-advise test file. Test >>> requested in this ticket really does not belong to the legacy clients >>> feature test. >>> >>> As for the any new tests that might come: I think tests for ipa-advise >>> that are specific to that particular feature should be tested with that >>> feature, more so, if they contain parts that are supposed to work >>> copy-pasted. If a tests, however, tests a general behaviour of ipa-advise, >>> it should live in the ipa-advise namespace, hence separate test file. >>> >>> HTH, >>> >>> -- >>> Tomas Babej >>> Associate Software Engineer | Red Hat | Identity Management >>> RHCE | Brno Site | IRC: tbabej | freeipa.org >>> >>> >>> The attached patch looks fine, although, please also test for a >>> non-zero return code number. >>> >>> >>> Upon hitting send I noticed you did not include raiseonerr=False into >>> the run_command call. You need to do that, otherwise a exception will be >>> raised, since ipa-advise exited with non-zero return code. >>> >> Thanks Tomas. >> >> Which do you prefer: a test_advise.py or an update to the existing >> patch? >> >> >> A new test file, as I pointed out in the second email :) sorry for >> splitting. >> >> However, it would be the best if you could spin up a positive test as >> well (maybe listing out available advices), not just this negative one, to >> justify the overhead reinstalling IPA for testing this feature. >> >> >> >>> -- >>> Tomas Babej >>> Associate Software Engineer | Red Hat | Identity Management >>> RHCE | Brno Site | IRC: tbabej | freeipa.org >>> >>> >>> -- >>> Tomas Babej >>> Associate Software Engineer | Red Hat | Identity Management >>> RHCE | Brno Site | IRC: tbabej | freeipa.org >>> >>> >> >> -- >> Tomas Babej >> Associate Software Engineer | Red Hat | Identity Management >> RHCE | Brno Site | IRC: tbabej | freeipa.org >> >> > -------------- next part -------------- An HTML attachment was scrubbed... URL: From dkupka at redhat.com Wed Feb 18 09:02:48 2015 From: dkupka at redhat.com (David Kupka) Date: Wed, 18 Feb 2015 10:02:48 +0100 Subject: [Freeipa-devel] [PATCHES 0191-0194] Fix restoring states of services after uninstalling In-Reply-To: <54E346D2.6050206@redhat.com> References: <54DB7FC6.1040507@redhat.com> <54E32386.2050208@redhat.com> <54E346D2.6050206@redhat.com> Message-ID: <54E45538.4080505@redhat.com> On 02/17/2015 02:49 PM, Martin Basti wrote: > On 17/02/15 12:18, David Kupka wrote: >> On 02/11/2015 05:13 PM, Martin Basti wrote: >>> https://fedorahosted.org/freeipa/ticket/4869 >>> >>> Fixes: >>> - enable/start a service after uninstallation if the service was >>> enabled/running before in correct way >>> - store status of service before disable/stop/start/enable operation >>> - run uninstall only for configured services >>> >>> Uninstall for ipa-dnskeysyncd is not executed because of >>> https://fedorahosted.org/freeipa/ticket/4901 >>> >>> Patches attached. >>> >>> >>> >>> _______________________________________________ >>> Freeipa-devel mailing list >>> Freeipa-devel at redhat.com >>> https://www.redhat.com/mailman/listinfo/freeipa-devel >>> >> Hi, >> thanks for patches. The code looks good to me but I found 2 issues: >> >> 1. httpd is left in non-working state after installing and >> uninstalling ipa-server. >> Tried on clean Fedora 21, the httpd was not configured before >> ipa-server installation. >> >> 2. Patch 191 needs (trivial) rebase. >> > Thanks, > > Updated patches attached. > Thanks, I've no further objections, ACK. -- David Kupka From mkosek at redhat.com Wed Feb 18 09:06:45 2015 From: mkosek at redhat.com (Martin Kosek) Date: Wed, 18 Feb 2015 10:06:45 +0100 Subject: [Freeipa-devel] [PATCHES 0191-0194] Fix restoring states of services after uninstalling In-Reply-To: <54E45538.4080505@redhat.com> References: <54DB7FC6.1040507@redhat.com> <54E32386.2050208@redhat.com> <54E346D2.6050206@redhat.com> <54E45538.4080505@redhat.com> Message-ID: <54E45625.2090703@redhat.com> On 02/18/2015 10:02 AM, David Kupka wrote: > On 02/17/2015 02:49 PM, Martin Basti wrote: >> On 17/02/15 12:18, David Kupka wrote: >>> On 02/11/2015 05:13 PM, Martin Basti wrote: >>>> https://fedorahosted.org/freeipa/ticket/4869 >>>> >>>> Fixes: >>>> - enable/start a service after uninstallation if the service was >>>> enabled/running before in correct way >>>> - store status of service before disable/stop/start/enable operation >>>> - run uninstall only for configured services >>>> >>>> Uninstall for ipa-dnskeysyncd is not executed because of >>>> https://fedorahosted.org/freeipa/ticket/4901 >>>> >>>> Patches attached. >>>> >>>> >>>> >>>> _______________________________________________ >>>> Freeipa-devel mailing list >>>> Freeipa-devel at redhat.com >>>> https://www.redhat.com/mailman/listinfo/freeipa-devel >>>> >>> Hi, >>> thanks for patches. The code looks good to me but I found 2 issues: >>> >>> 1. httpd is left in non-working state after installing and >>> uninstalling ipa-server. >>> Tried on clean Fedora 21, the httpd was not configured before >>> ipa-server installation. >>> >>> 2. Patch 191 needs (trivial) rebase. >>> >> Thanks, >> >> Updated patches attached. >> > Thanks, I've no further objections, ACK. > Pushed to master: d216cab6192398f85e2bcc4916826bc91c2841de Martin From thozza at redhat.com Wed Feb 18 09:36:42 2015 From: thozza at redhat.com (Tomas Hozza) Date: Wed, 18 Feb 2015 10:36:42 +0100 Subject: [Freeipa-devel] [PATCH 0316] Fix crash triggered by zone objects with unexpected DN In-Reply-To: <54905091.5010201@redhat.com> References: <54905091.5010201@redhat.com> Message-ID: <54E45D2A.6050907@redhat.com> On 12/16/2014 04:32 PM, Petr Spacek wrote: > Hello, > > Fix crash triggered by zone objects with unexpected DN. > > https://fedorahosted.org/bind-dyndb-ldap/ticket/148 > NACK. The patch seems to make no difference when using the reproducer from ticket 148 18-Feb-2015 10:34:09.067 running 18-Feb-2015 10:34:09.139 ldap_helper.c:4876: INSIST(task == inst->task) failed, back trace 18-Feb-2015 10:34:09.139 #0 0x555555587a80 in ?? 18-Feb-2015 10:34:09.139 #1 0x7ffff620781a in ?? 18-Feb-2015 10:34:09.139 #2 0x7ffff20b00b2 in ?? 18-Feb-2015 10:34:09.140 #3 0x7ffff1e7ccf9 in ?? 18-Feb-2015 10:34:09.140 #4 0x7ffff1e7d992 in ?? 18-Feb-2015 10:34:09.140 #5 0x7ffff20a7f3b in ?? 18-Feb-2015 10:34:09.140 #6 0x7ffff5dda52a in ?? 18-Feb-2015 10:34:09.140 #7 0x7ffff508d79d in ?? 18-Feb-2015 10:34:09.140 exiting (due to assertion failure) Program received signal SIGABRT, Aborted. [Switching to Thread 0x7fffea7cd700 (LWP 1719)] 0x00007ffff4fc18c7 in __GI_raise (sig=sig at entry=6) at ../sysdeps/unix/sysv/linux/raise.c:55 55 return INLINE_SYSCALL (tgkill, 3, pid, selftid, sig); Missing separate debuginfos, use: debuginfo-install cyrus-sasl-gssapi-2.1.26-19.fc21.x86_64 cyrus-sasl-lib-2.1.26-19.fc21.x86_64 cyrus-sasl-md5-2.1.26-19.fc21.x86_64 cyrus-sasl-plain-2.1.26-19.fc21.x86_64 gssproxy-0.3.1-4.fc21.x86_64 keyutils-libs-1.5.9-4.fc21.x86_64 libattr-2.4.47-9.fc21.x86_64 libdb-5.3.28-9.fc21.x86_64 libgcc-4.9.2-6.fc21.x86_64 libselinux-2.3-5.fc21.x86_64 nspr-4.10.8-1.fc21.x86_64 nss-3.17.4-1.fc21.x86_64 nss-softokn-freebl-3.17.4-1.fc21.x86_64 nss-util-3.17.4-1.fc21.x86_64 pcre-8.35-8.fc21.x86_64 sssd-client-1.12.3-4.fc21.x86_64 xz-libs-5.1.2-14alpha.fc21.x86_64 (gdb) bt #0 0x00007ffff4fc18c7 in __GI_raise (sig=sig at entry=6) at ../sysdeps/unix/sysv/linux/raise.c:55 #1 0x00007ffff4fc352a in __GI_abort () at abort.c:89 #2 0x0000555555587c29 in assertion_failed (file=, line=, type=, cond=) at ./main.c:220 #3 0x00007ffff620781a in isc_assertion_failed (file=file at entry=0x7ffff20bad2a "ldap_helper.c", line=line at entry=4876, type=type at entry=isc_assertiontype_insist, cond=cond at entry=0x7ffff20baf04 "task == inst->task") at assertions.c:57 #4 0x00007ffff20b00b2 in syncrepl_update (chgtype=1, entry=0x7ffff0125590, inst=0x7ffff7fa3160) at ldap_helper.c:4876 #5 ldap_sync_search_entry (ls=, msg=, entryUUID=, phase=LDAP_SYNC_CAPI_ADD) at ldap_helper.c:5031 #6 0x00007ffff1e7ccf9 in ldap_sync_search_entry (ls=ls at entry=0x7fffe40008c0, res=0x7fffe4003870) at ldap_sync.c:228 #7 0x00007ffff1e7d992 in ldap_sync_init (ls=0x7fffe40008c0, mode=mode at entry=3) at ldap_sync.c:792 #8 0x00007ffff20a7f3b in ldap_syncrepl_watcher (arg=0x7ffff7fa3160) at ldap_helper.c:5247 #9 0x00007ffff5dda52a in start_thread (arg=0x7fffea7cd700) at pthread_create.c:310 #10 0x00007ffff508d79d in clone () at ../sysdeps/unix/sysv/linux/x86_64/clone.S:109 -- Tomas Hozza Software Engineer - EMEA ENG Developer Experience PGP: 1D9F3C2D Red Hat Inc. http://cz.redhat.com From pvoborni at redhat.com Wed Feb 18 12:20:59 2015 From: pvoborni at redhat.com (Petr Vobornik) Date: Wed, 18 Feb 2015 13:20:59 +0100 Subject: [Freeipa-devel] [PATCH] 494 group-detach does not add correct objectclasses In-Reply-To: <54CB8F64.7050104@redhat.com> References: <54CB7564.8040106@redhat.com> <54CB8F64.7050104@redhat.com> Message-ID: <54E483AB.6020704@redhat.com> On 01/30/2015 03:04 PM, Rob Crittenden wrote: > Martin Kosek wrote: >> https://fedorahosted.org/freeipa/ticket/4874 > > LGTM but a test please to prevent future regressions. > > rob > ACK Pushed to: master: 8ea8a7038ea0a9ed23e5569c34535e48008c7884 ipa-4-1: 2dd54c9f33c25d6c32f96e7b85850cfa3a990930 ticket for regression tests: https://fedorahosted.org/freeipa/ticket/4909 -- Petr Vobornik From pvoborni at redhat.com Wed Feb 18 12:50:41 2015 From: pvoborni at redhat.com (Petr Vobornik) Date: Wed, 18 Feb 2015 13:50:41 +0100 Subject: [Freeipa-devel] [PATCH 300] Fix incorrect python shebang usage In-Reply-To: <54C11C0F.60203@redhat.com> References: <1319332196.8928270.1421927576992.JavaMail.zimbra@redhat.com> <54C11C0F.60203@redhat.com> Message-ID: <54E48AA1.3000106@redhat.com> On 01/22/2015 04:49 PM, Martin Basti wrote: > On 22/01/15 12:52, Tomas Babej wrote: >> Hi, >> >> attached patch fixes few python2 non-explicit shebangs that lurked >> into the codebase. >> >> Tomas > ACK > was pushed by tbabej to master on 2015-01-26 f30865c5f07bdc4d5f87e89f9ed99148f4d361ce -- Petr Vobornik From pvoborni at redhat.com Wed Feb 18 12:57:11 2015 From: pvoborni at redhat.com (Petr Vobornik) Date: Wed, 18 Feb 2015 13:57:11 +0100 Subject: [Freeipa-devel] [PATCH 0002] Changing the token owner also changes its manager In-Reply-To: <1421262158.2477.67.camel@redhat.com> References: <54B6900D.8070303@redhat.com> <1421252594.2477.39.camel@redhat.com> <54B69E01.4070208@redhat.com> <1421262158.2477.67.camel@redhat.com> Message-ID: <54E48C27.5070203@redhat.com> On 01/14/2015 08:02 PM, Nathaniel McCallum wrote: > On Wed, 2015-01-14 at 17:49 +0100, Martin Babinsky wrote: >> On 01/14/2015 05:23 PM, Nathaniel McCallum wrote: >>> On Wed, 2015-01-14 at 16:49 +0100, Martin Babinsky wrote: >>>> Changing the owner of a token also implicitly sets the new owner as its >>>> manager if following conditions are met: >>>> >>>> 1.) The original token owner was also its manager >>>> >>>> 2.) The new manager is not set explicitly via CLI interface. >>>> >>>> If the owner is unset and the above conditions are met, then the manager >>>> of the token will also be unset. >>>> >>>> https://fedorahosted.org/freeipa/ticket/4681 >>> >>> Nitpicks: >>> 1. The commit message summary line should not have a '.' >>> 2. The commit message is not properly wrapped. >>> 3. The newline before _normalize_owner() is undesirable. >>> 4. Shouldn't prev_managed_by be prev_managedby? >>> >>> The main body of the change looks good to me. >>> >>> Nathaniel >>> >> >> Attaching updated patch. > > ACK > Pushed to: master: b95f4330c9433683f61c46f9605fd1d24bb8b998 ipa-4-1: c985de1ee6429c49e6273a037478212e7ee301c8 -- Petr Vobornik From tbordaz at redhat.com Thu Feb 19 12:01:22 2015 From: tbordaz at redhat.com (thierry bordaz) Date: Thu, 19 Feb 2015 13:01:22 +0100 Subject: [Freeipa-devel] [PATCH] 0003-2 User life cycle: new stageuser plugin with add verb In-Reply-To: <54D24567.4010103@redhat.com> References: <53E4D6AE.6050505@redhat.com> <54045399.3030404@redhat.com> <54196346.5070500@redhat.com> <54D0A7EB.1010700@redhat.com> <54D22BE2.9050407@redhat.com> <54D24567.4010103@redhat.com> Message-ID: <54E5D092.6030708@redhat.com> On 02/04/2015 05:14 PM, Jan Cholasta wrote: > Hi, > > Dne 4.2.2015 v 15:25 David Kupka napsal(a): >> On 02/03/2015 11:50 AM, thierry bordaz wrote: >>> On 09/17/2014 12:32 PM, thierry bordaz wrote: >>>> On 09/01/2014 01:08 PM, Petr Viktorin wrote: >>>>> On 08/08/2014 03:54 PM, thierry bordaz wrote: >>>>>> Hi, >>>>>> >>>>>> The attached patch is related to 'User Life Cycle' >>>>>> (https://fedorahosted.org/freeipa/ticket/3813) >>>>>> >>>>>> It creates a stageuser plugin with a first function stageuser-add. >>>>>> Stage >>>>>> user entries are provisioned under 'cn=staged >>>>>> users,cn=accounts,cn=provisioning,SUFFIX'. >>>>>> >>>>>> Thanks >>>>>> thierry >>>>> >>>>> Avoid `from ipalib.plugins.baseldap import *` in new code; instead >>>>> import the module itself and use e.g. `baseldap.LDAPObject`. >>>>> >>>>> The stageuser help (docstring) is copied from the user plugin, and >>>>> discusses things like account lockout and disabling users. It should >>>>> rather explain what stageuser itself does. (And I don't very much >>>>> like the Note about the interface being badly designed...) >>>>> Also decide if the docs should call it "staged user" or "stage user" >>>>> or "stageuser". >>>>> >>>>> A lot of the code is copied and pasted over from the users plugin. >>>>> Don't do that. Either import things (e.g. validate_nsaccountlock) >>>>> from the users plugin, or move the reused code into a shared module. >>>>> >>>>> For the `user` object, since so much is the same, it might be best to >>>>> create a common base class for user and stageuser; and similarly for >>>>> the Command plugins. >>>>> >>>>> The default permissions need different names, and you don't need >>>>> another copy of the 'non_object' ones. Also, run the makeaci script. >>>>> >>>> Hello, >>>> >>>> This modified patch is mainly moving common base class into a new >>>> plugin: accounts.py. user/stageuser plugin inherits from accounts. >>>> It also creates a better description of what are stage user, how >>>> to add a new stage user, updates ACI.txt and separate active/stage >>>> user managed permissions. >>>> >>>> thanks >>>> thierry >>>> >>>> >>>> >>>> >>>> >>>> >>>> _______________________________________________ >>>> Freeipa-devel mailing list >>>> Freeipa-devel at redhat.com >>>> https://www.redhat.com/mailman/listinfo/freeipa-devel >>> >>> >>> Thanks David for the reviews. Here the last patches >>> >>> >>> >>> >>> _______________________________________________ >>> Freeipa-devel mailing list >>> Freeipa-devel at redhat.com >>> https://www.redhat.com/mailman/listinfo/freeipa-devel >>> >> >> The freeipa-tbordaz-0002 patch had trailing whitespaces on few lines so >> I'm attaching fixed version (and unchanged patch freeipa-tbordaz-0003-3 >> to keep them together). >> >> The ULC feature is still WIP but these patches look good to me and don't >> break anything as far as I tested. >> We should push them now to avoid further rebases. Thierry can then >> prepare other patches delivering the rest of ULC functionality. > > Few comments from just reading the patches: > > 1) I would name the base class "baseuser", "account" does not > necessarily mean user account. > > 2) This is very wrong: > > -class user_add(LDAPCreate): > +class user_add(user, LDAPCreate): > > You are creating a plugin which is both an object and an command. > > 3) This is purely subjective, but I don't like the name "deleteuser", > as it has a verb in it. We usually don't do that and IMHO we shouldn't > do that. > > Honza > Thank you for the review. I am attaching the updates patches -------------- next part -------------- An HTML attachment was scrubbed... URL: -------------- next part -------------- A non-text attachment was scrubbed... Name: 0001-User-Life-Cycle-Exclude-subtree-for-ipaUniqueID-gene.patch Type: text/x-patch Size: 2588 bytes Desc: not available URL: -------------- next part -------------- A non-text attachment was scrubbed... Name: 0002-User-life-cycle-stageuser-add-verb.patch Type: text/x-patch Size: 90093 bytes Desc: not available URL: From pvoborni at redhat.com Thu Feb 19 12:03:58 2015 From: pvoborni at redhat.com (Petr Vobornik) Date: Thu, 19 Feb 2015 13:03:58 +0100 Subject: [Freeipa-devel] [PATCH] 799 ipatests: add missing ssh object classes to idoverrideuser Message-ID: <54E5D12E.8070405@redhat.com> tests in test_xmlrpc/test_idviews_plugin.py were failing - due to https://fedorahosted.org/freeipa/ticket/4868 also attaching amended freeipa-tbabej-0302-ipatests-Add-coverage-for-adding-and-removing-sshpub.patch which abandoned during #4868 review. -- Petr Vobornik -------------- next part -------------- A non-text attachment was scrubbed... Name: freeipa-pvoborni-0799-ipatests-add-missing-ssh-object-classes-to-idoverrid.patch Type: text/x-patch Size: 777 bytes Desc: not available URL: -------------- next part -------------- A non-text attachment was scrubbed... Name: freeipa-tbabej-0302-2-ipatests-Add-coverage-for-adding-and-removing-sshpub.patch Type: text/x-patch Size: 4474 bytes Desc: not available URL: From mkosek at redhat.com Thu Feb 19 14:51:30 2015 From: mkosek at redhat.com (Martin Kosek) Date: Thu, 19 Feb 2015 15:51:30 +0100 Subject: [Freeipa-devel] FreeIPA 4.1.3 release notes draft In-Reply-To: <54E2ED0E.10307@redhat.com> References: <54E2170C.2070705@redhat.com> <54E2ED0E.10307@redhat.com> Message-ID: <54E5F872.8080204@redhat.com> On 02/17/2015 08:26 AM, Martin Kosek wrote: > On 02/16/2015 05:13 PM, Petr Vobornik wrote: > ... >> Q: The same upgrade instructions are copied from release to release. Shouldn't >> we rather move them into separate page which would contain the upgrade steps? >> Listed by releases which introduced them. > > I think that release notes could simply just contain the link to the Upgrade > page on FreeIPA.org. I would only list special information in the upgrade > section if there is an important change to upgrade or a migration needed (as > with DNS Forward zones). > > This way, with information copied around, I do not think people read it anyway. > Also, I think there is still the wrong order of upgrade tools mentioned in some > release notes. > > Potentially, same with Feedback - it could just link to > http://www.freeipa.org/page/Contribute#Communication > > Though this is not that critical, given it's short length. > > Martin No objections? If this is the case, we can just create the Upgrade page and start just referencing it. Regarding 4.1.3, I do not really like the bug fixes/enhancements texts - this are just patch names, people without deeper knowledge will not be able to understand what we actually fixed. I.e., I would rather change: "winsync: fixed password sync" --> "PassSync plugin could not update synchronized users due to too strict access control" Martin From pvoborni at redhat.com Thu Feb 19 14:51:47 2015 From: pvoborni at redhat.com (Petr Vobornik) Date: Thu, 19 Feb 2015 15:51:47 +0100 Subject: [Freeipa-devel] [PATCH] 800 rpc-client: add forms based auth support Message-ID: <54E5F883.1090702@redhat.com> This patch is a prerequisite for patch 801 which will follow. It was developed to enable to use ipalib RPC client in Web UI tests. Plus it will enable to significantly speed up Web UI tests suite (if preparation of data is transformed to use this method). Partly related https://fedorahosted.org/freeipa/ticket/4772 and https://fedorahosted.org/freeipa/ticket/4307 Leverage session support to enable forms-based authenticate in rpc client. In order to do that session support in KerbTransport was moved to new SessionTransport. RPCClient.create_connection is then modified to force forms-based auth if new optional options - user and password are specified. For this case SessionTransport is used and user is authenticated by calling 'https://ipa.server/ipa/session/login_password'. Session cookie is stored and used in subsequent calls. This feature is usable for use cases where one wants to call the API without being on ipa client. Non-being on ipa client also means that IPA's NSS database and configuration is not available. Therefore one has to define "~/.ipa/default.conf" in a similar way as ipa client does and prepare a NSS database with IPA CA cert. Usage: api.Backend.rpcclient.connect( nss_dir=my_nss_dir_path, user=user, password=password ) It's possible to switch users with: api.Backend.rpcclient.disconnect() api.Backend.rpcclient.connect( nss_dir=my_nss_dir_path, user=other_user, password=other_password ) Or check connection with: api.Backend.rpcclient.isconnected() Example: download a CA cert and add it to a new temporary NSS database: from urllib2 import urlparse from ipaplatform.paths import paths from ipapython import certdb, ipautil from ipapython.ipautil import run from ipalib import x509 # create new NSSDatabase tmp_db = certdb.NSSDatabase() pwd_file = ipautil.write_tmp_file(ipautil.ipa_generate_password()) tmp_db.create_db(pwd_file.name) # download and add cert url = urlparse.urlunparse(('http', ipautil.format_netloc(ipa_server), '/ipa/config/ca.crt', '', '', '')) stdout, stderr, rc = run([paths.BIN_WGET, "-O", "-", url]) certs = x509.load_certificate_list(stdout, tmp_db.secdir) ca_certs = [cert.der_data for cert in certs] for i, cert in enumerate(ca_certs): tmp_db.add_cert(cert, 'CA certificate %d' % (i + 1), 'C,,') my_nss_dir_path = tmp_db.secdir -- Petr Vobornik -------------- next part -------------- A non-text attachment was scrubbed... Name: freeipa-pvoborni-0800-rpc-client-add-forms-based-auth-support.patch Type: text/x-patch Size: 20170 bytes Desc: not available URL: From pvoborni at redhat.com Thu Feb 19 14:51:52 2015 From: pvoborni at redhat.com (Petr Vobornik) Date: Thu, 19 Feb 2015 15:51:52 +0100 Subject: [Freeipa-devel] [PATCH] 801-806 webui-ci: otptoken tests Message-ID: <54E5F888.2000402@redhat.com> https://fedorahosted.org/freeipa/ticket/4307 For ipa-4-1 apply: - patch 800 (different thread) - patches 801-806 For master apply: - patch 800 (different thread) - patch 807 (different thread) - patch 801-master - patches 802-806 Patch 801 allows to use ipalib rpc client in Web UI test suite. Patches 802-805 are various ui_driver fixes to allow stuff in patch 806. == [PATCH] 806 webui-ci: otptoken tests == Basic otptoken Web UI CI coverage. tests: * crud for otptokens as admin * crud for normal users * checks fields of adder dialog for both token types and user role (admin/user) * token actions as admin (enable, disable, delete) * token actions as normal user (delete) * login as normal user with hotp and totp token * sync token hotp and totp token as normal user and then login https://fedorahosted.org/freeipa/ticket/4307 == [PATCH] 805 webui-ci: allow custom names for disable/enable actions == Not all disable and enable actions are called 'disable' and 'enable'. == [PATCH] 804 webui-ci: allow to update pkey in post-add in basic-crud tests == == [PATCH] 803 webui-ci: add post_add_action == post add action allows to fill autogenerated values, e.g. a pkey of new otptoken. This value can be then used in other subsequent test which would depend on it - like crud tests. == [PATCH] 802 webui-ci: fix negative visibility check == Allow to define, that element doesn't have to be present on a page for negative visible checks. E.g. if element is added only if it's displayed and is removed otherwise. == [PATCH] 801 webui-ci: support direct IPA API calls == Add IPA API support to ui_driver. It leverages new ipalib RPC client's forms based authentication. It then allows to call an IPA API while the machine is not an IPA client nor is kerberized. api's environment values are taken from test configuration and therefore duplication in ~/.ipa/default.conf is not required. Since the machine doesn't have to be IPA client, it then also doesn't have nss database with IPA's CA certificate. Therefore on each API initialization a new NSS database is created with a CA certificate downloaded from IPA. This db is deleted in tearDown phase. Usage: 1. as admin one can immediately call rpc commands, api will be initialized upon first request and is available under self.api (assuming self is ui_driver): self.api.Command.user_del(USER_ID, **{'continue': True}) 2. to reconnect as other user: self.reconnect_api(USER_ID, USER_PW) 3. reconnect back as admin: self.reconnect_api() -- Petr Vobornik -------------- next part -------------- A non-text attachment was scrubbed... Name: freeipa-pvoborni-0806-webui-ci-otptoken-tests.patch Type: text/x-patch Size: 15040 bytes Desc: not available URL: -------------- next part -------------- A non-text attachment was scrubbed... Name: freeipa-pvoborni-0805-webui-ci-allow-custom-names-for-disable-enable-actio.patch Type: text/x-patch Size: 1705 bytes Desc: not available URL: -------------- next part -------------- A non-text attachment was scrubbed... Name: freeipa-pvoborni-0804-webui-ci-allow-to-update-pkey-in-post-add-in-basic-c.patch Type: text/x-patch Size: 1504 bytes Desc: not available URL: -------------- next part -------------- A non-text attachment was scrubbed... Name: freeipa-pvoborni-0803-webui-ci-add-post_add_action.patch Type: text/x-patch Size: 2138 bytes Desc: not available URL: -------------- next part -------------- A non-text attachment was scrubbed... Name: freeipa-pvoborni-0802-webui-ci-fix-negative-visibility-check.patch Type: text/x-patch Size: 1556 bytes Desc: not available URL: -------------- next part -------------- A non-text attachment was scrubbed... Name: freeipa-pvoborni-0801-webui-ci-support-direct-IPA-API-calls.patch Type: text/x-patch Size: 5783 bytes Desc: not available URL: -------------- next part -------------- A non-text attachment was scrubbed... Name: freeipa-pvoborni-0801-master-webui-ci-support-direct-IPA-API-calls.patch Type: text/x-patch Size: 6003 bytes Desc: not available URL: From pvoborni at redhat.com Thu Feb 19 14:51:57 2015 From: pvoborni at redhat.com (Petr Vobornik) Date: Thu, 19 Feb 2015 15:51:57 +0100 Subject: [Freeipa-devel] [PATCH] 807 webui-ci: do not open 2 browser windows Message-ID: <54E5F88D.5030800@redhat.com> Only for master branch. -- Petr Vobornik -------------- next part -------------- A non-text attachment was scrubbed... Name: freeipa-pvoborni-0807-webui-ci-do-not-open-2-browser-windows.patch Type: text/x-patch Size: 1000 bytes Desc: not available URL: From mbasti at redhat.com Thu Feb 19 15:19:35 2015 From: mbasti at redhat.com (Martin Basti) Date: Thu, 19 Feb 2015 16:19:35 +0100 Subject: [Freeipa-devel] [PATCH] 0003-2 User life cycle: new stageuser plugin with add verb In-Reply-To: <54E5D092.6030708@redhat.com> References: <53E4D6AE.6050505@redhat.com> <54045399.3030404@redhat.com> <54196346.5070500@redhat.com> <54D0A7EB.1010700@redhat.com> <54D22BE2.9050407@redhat.com> <54D24567.4010103@redhat.com> <54E5D092.6030708@redhat.com> Message-ID: <54E5FF07.1080809@redhat.com> On 19/02/15 13:01, thierry bordaz wrote: > On 02/04/2015 05:14 PM, Jan Cholasta wrote: >> Hi, >> >> Dne 4.2.2015 v 15:25 David Kupka napsal(a): >>> On 02/03/2015 11:50 AM, thierry bordaz wrote: >>>> On 09/17/2014 12:32 PM, thierry bordaz wrote: >>>>> On 09/01/2014 01:08 PM, Petr Viktorin wrote: >>>>>> On 08/08/2014 03:54 PM, thierry bordaz wrote: >>>>>>> Hi, >>>>>>> >>>>>>> The attached patch is related to 'User Life Cycle' >>>>>>> (https://fedorahosted.org/freeipa/ticket/3813) >>>>>>> >>>>>>> It creates a stageuser plugin with a first function stageuser-add. >>>>>>> Stage >>>>>>> user entries are provisioned under 'cn=staged >>>>>>> users,cn=accounts,cn=provisioning,SUFFIX'. >>>>>>> >>>>>>> Thanks >>>>>>> thierry >>>>>> >>>>>> Avoid `from ipalib.plugins.baseldap import *` in new code; instead >>>>>> import the module itself and use e.g. `baseldap.LDAPObject`. >>>>>> >>>>>> The stageuser help (docstring) is copied from the user plugin, and >>>>>> discusses things like account lockout and disabling users. It should >>>>>> rather explain what stageuser itself does. (And I don't very much >>>>>> like the Note about the interface being badly designed...) >>>>>> Also decide if the docs should call it "staged user" or "stage user" >>>>>> or "stageuser". >>>>>> >>>>>> A lot of the code is copied and pasted over from the users plugin. >>>>>> Don't do that. Either import things (e.g. validate_nsaccountlock) >>>>>> from the users plugin, or move the reused code into a shared module. >>>>>> >>>>>> For the `user` object, since so much is the same, it might be >>>>>> best to >>>>>> create a common base class for user and stageuser; and similarly for >>>>>> the Command plugins. >>>>>> >>>>>> The default permissions need different names, and you don't need >>>>>> another copy of the 'non_object' ones. Also, run the makeaci script. >>>>>> >>>>> Hello, >>>>> >>>>> This modified patch is mainly moving common base class into a new >>>>> plugin: accounts.py. user/stageuser plugin inherits from >>>>> accounts. >>>>> It also creates a better description of what are stage user, how >>>>> to add a new stage user, updates ACI.txt and separate >>>>> active/stage >>>>> user managed permissions. >>>>> >>>>> thanks >>>>> thierry >>>>> >>>>> >>>>> >>>>> >>>>> >>>>> >>>>> _______________________________________________ >>>>> Freeipa-devel mailing list >>>>> Freeipa-devel at redhat.com >>>>> https://www.redhat.com/mailman/listinfo/freeipa-devel >>>> >>>> >>>> Thanks David for the reviews. Here the last patches >>>> >>>> >>>> >>>> >>>> _______________________________________________ >>>> Freeipa-devel mailing list >>>> Freeipa-devel at redhat.com >>>> https://www.redhat.com/mailman/listinfo/freeipa-devel >>>> >>> >>> The freeipa-tbordaz-0002 patch had trailing whitespaces on few lines so >>> I'm attaching fixed version (and unchanged patch freeipa-tbordaz-0003-3 >>> to keep them together). >>> >>> The ULC feature is still WIP but these patches look good to me and >>> don't >>> break anything as far as I tested. >>> We should push them now to avoid further rebases. Thierry can then >>> prepare other patches delivering the rest of ULC functionality. >> >> Few comments from just reading the patches: >> >> 1) I would name the base class "baseuser", "account" does not >> necessarily mean user account. >> >> 2) This is very wrong: >> >> -class user_add(LDAPCreate): >> +class user_add(user, LDAPCreate): >> >> You are creating a plugin which is both an object and an command. >> >> 3) This is purely subjective, but I don't like the name "deleteuser", >> as it has a verb in it. We usually don't do that and IMHO we >> shouldn't do that. >> >> Honza >> > > Thank you for the review. I am attaching the updates patches > > > > > > > _______________________________________________ > Freeipa-devel mailing list > Freeipa-devel at redhat.com > https://www.redhat.com/mailman/listinfo/freeipa-devel Hello, I'm getting errors during make rpms: if [ "" != "yes" ]; then \ ./makeapi --validate; \ ./makeaci --validate; \ fi /root/freeipa/ipalib/plugins/baseuser.py:641 command "baseuser_add" doc is not internationalized /root/freeipa/ipalib/plugins/baseuser.py:653 command "baseuser_find" doc is not internationalized /root/freeipa/ipalib/plugins/baseuser.py:647 command "baseuser_mod" doc is not internationalized 0 commands without doc, 3 commands whose doc is not i18n Command baseuser_add in ipalib, not in API Command baseuser_find in ipalib, not in API Command baseuser_mod in ipalib, not in API There are one or more new commands defined. Update API.txt and increment the minor version in VERSION. There are one or more documentation problems. You must fix these before preceeding Issues probably caused by this: 1) You should not use the register decorator, if this class is just for inheritance @register() class baseuser_add(LDAPCreate): @register() class baseuser_mod(LDAPUpdate): @register() class baseuser_find(LDAPSearch): see dns.py plugin and "DNSZoneBase" and "dnszone" classes 2) there might be an issue with @register() class baseuser(LDAPObject): the register decorator should not be there, I was warned by Petr^3 to not use permission in parent class. The same permission should be specified only in one place (for example user class), (otherwise they will be generated twice??) I don't know more details about it. -- Martin Basti -------------- next part -------------- An HTML attachment was scrubbed... URL: From mkosek at redhat.com Thu Feb 19 15:31:12 2015 From: mkosek at redhat.com (Martin Kosek) Date: Thu, 19 Feb 2015 16:31:12 +0100 Subject: [Freeipa-devel] [PATCH] 799 ipatests: add missing ssh object classes to idoverrideuser In-Reply-To: <54E5D12E.8070405@redhat.com> References: <54E5D12E.8070405@redhat.com> Message-ID: <54E601C0.1090902@redhat.com> On 02/19/2015 01:03 PM, Petr Vobornik wrote: > tests in test_xmlrpc/test_idviews_plugin.py were failing - due to > https://fedorahosted.org/freeipa/ticket/4868 > > also attaching amended > freeipa-tbabej-0302-ipatests-Add-coverage-for-adding-and-removing-sshpub.patch > which abandoned during #4868 review. ACK, worked in my tests. Martin From pvoborni at redhat.com Thu Feb 19 15:45:50 2015 From: pvoborni at redhat.com (Petr Vobornik) Date: Thu, 19 Feb 2015 16:45:50 +0100 Subject: [Freeipa-devel] FreeIPA 4.1.3 release notes draft In-Reply-To: <54E5F872.8080204@redhat.com> References: <54E2170C.2070705@redhat.com> <54E2ED0E.10307@redhat.com> <54E5F872.8080204@redhat.com> Message-ID: <54E6052E.6020803@redhat.com> On 02/19/2015 03:51 PM, Martin Kosek wrote: > On 02/17/2015 08:26 AM, Martin Kosek wrote: >> On 02/16/2015 05:13 PM, Petr Vobornik wrote: >> ... >>> Q: The same upgrade instructions are copied from release to release. Shouldn't >>> we rather move them into separate page which would contain the upgrade steps? >>> Listed by releases which introduced them. >> >> I think that release notes could simply just contain the link to the Upgrade >> page on FreeIPA.org. I would only list special information in the upgrade >> section if there is an important change to upgrade or a migration needed (as >> with DNS Forward zones). >> >> This way, with information copied around, I do not think people read it anyway. >> Also, I think there is still the wrong order of upgrade tools mentioned in some >> release notes. >> >> Potentially, same with Feedback - it could just link to >> http://www.freeipa.org/page/Contribute#Communication >> >> Though this is not that critical, given it's short length. >> >> Martin > > No objections? If this is the case, we can just create the Upgrade page and > start just referencing it. new page: http://www.freeipa.org/page/Upgrading it's linked from http://www.freeipa.org/page/Releases/4.1.3 and http://www.freeipa.org/page/Downloads#Upgrading > > Regarding 4.1.3, I do not really like the bug fixes/enhancements texts - this > are just patch names, people without deeper knowledge will not be able to > understand what we actually fixed. > > I.e., I would rather change: > > "winsync: fixed password sync" --> "PassSync plugin could not update > synchronized users due to too strict access control" Text improved. > > Martin > -- Petr Vobornik From mkosek at redhat.com Thu Feb 19 16:03:22 2015 From: mkosek at redhat.com (Martin Kosek) Date: Thu, 19 Feb 2015 17:03:22 +0100 Subject: [Freeipa-devel] FreeIPA 4.1.3 release notes draft In-Reply-To: <54E6052E.6020803@redhat.com> References: <54E2170C.2070705@redhat.com> <54E2ED0E.10307@redhat.com> <54E5F872.8080204@redhat.com> <54E6052E.6020803@redhat.com> Message-ID: <54E6094A.9010900@redhat.com> On 02/19/2015 04:45 PM, Petr Vobornik wrote: > On 02/19/2015 03:51 PM, Martin Kosek wrote: >> On 02/17/2015 08:26 AM, Martin Kosek wrote: >>> On 02/16/2015 05:13 PM, Petr Vobornik wrote: >>> ... >>>> Q: The same upgrade instructions are copied from release to release. Shouldn't >>>> we rather move them into separate page which would contain the upgrade steps? >>>> Listed by releases which introduced them. >>> >>> I think that release notes could simply just contain the link to the Upgrade >>> page on FreeIPA.org. I would only list special information in the upgrade >>> section if there is an important change to upgrade or a migration needed (as >>> with DNS Forward zones). >>> >>> This way, with information copied around, I do not think people read it anyway. >>> Also, I think there is still the wrong order of upgrade tools mentioned in some >>> release notes. >>> >>> Potentially, same with Feedback - it could just link to >>> http://www.freeipa.org/page/Contribute#Communication >>> >>> Though this is not that critical, given it's short length. >>> >>> Martin >> >> No objections? If this is the case, we can just create the Upgrade page and >> start just referencing it. > > new page: http://www.freeipa.org/page/Upgrading Thanks. As per our agreement, I just renamed it to http://www.freeipa.org/page/Upgrade > it's linked from http://www.freeipa.org/page/Releases/4.1.3 and > http://www.freeipa.org/page/Downloads#Upgrading Thanks. I restructured the Upgrade page a bit and added special sections per FreeIPA version - we will need it at also for FreeIPA 4.2 :-) >> Regarding 4.1.3, I do not really like the bug fixes/enhancements texts - this >> are just patch names, people without deeper knowledge will not be able to >> understand what we actually fixed. >> >> I.e., I would rather change: >> >> "winsync: fixed password sync" --> "PassSync plugin could not update >> synchronized users due to too strict access control" > > Text improved. Much better, thank you! Martin From pvoborni at redhat.com Thu Feb 19 16:04:48 2015 From: pvoborni at redhat.com (Petr Vobornik) Date: Thu, 19 Feb 2015 17:04:48 +0100 Subject: [Freeipa-devel] [PATCH] 799 ipatests: add missing ssh object classes to idoverrideuser In-Reply-To: <54E601C0.1090902@redhat.com> References: <54E5D12E.8070405@redhat.com> <54E601C0.1090902@redhat.com> Message-ID: <54E609A0.9060409@redhat.com> On 02/19/2015 04:31 PM, Martin Kosek wrote: > On 02/19/2015 01:03 PM, Petr Vobornik wrote: >> tests in test_xmlrpc/test_idviews_plugin.py were failing - due to >> https://fedorahosted.org/freeipa/ticket/4868 >> >> also attaching amended >> freeipa-tbabej-0302-ipatests-Add-coverage-for-adding-and-removing-sshpub.patch >> which abandoned during #4868 review. > > ACK, worked in my tests. > > Martin > Pushed to ipa-4-1: * bfef4d249634042ad95298d307850f194d898115 ipatests: add missing ssh object classes to idoverrideuser * 6667701315ab80986211dd45c8d02a709e6306b8 ipatests: Add coverage for adding and removing sshpubkeys in ID overrides master: * 25f5ee0cb6f6fc2b50c2e4f5f1b19d00e8751e4d ipatests: add missing ssh object classes to idoverrideuser * a34ff7f6c1afda516d3f5a6b995b98cc84b52902 ipatests: Add coverage for adding and removing sshpubkeys in ID overrides -- Petr Vobornik From tbabej at redhat.com Thu Feb 19 16:25:41 2015 From: tbabej at redhat.com (Tomas Babej) Date: Thu, 19 Feb 2015 17:25:41 +0100 Subject: [Freeipa-devel] [PATCH 0303] ipalib: Make sure correct attribute name is referenced for Message-ID: <54E60E85.5010102@redhat.com> Hi, Fixes the invalid attribute name reference in the 'System: Read User Addressbook Attributes' permission. https://fedorahosted.org/freeipa/ticket/4883 Tomas -------------- next part -------------- A non-text attachment was scrubbed... Name: freeipa-tbabej-0303-ipalib-Make-sure-correct-attribute-name-is-reference.patch Type: text/x-patch Size: 1243 bytes Desc: not available URL: From abokovoy at redhat.com Thu Feb 19 16:29:58 2015 From: abokovoy at redhat.com (Alexander Bokovoy) Date: Thu, 19 Feb 2015 18:29:58 +0200 Subject: [Freeipa-devel] [PATCH 0303] ipalib: Make sure correct attribute name is referenced for In-Reply-To: <54E60E85.5010102@redhat.com> References: <54E60E85.5010102@redhat.com> Message-ID: <20150219162958.GN25455@redhat.com> On Thu, 19 Feb 2015, Tomas Babej wrote: >Hi, > >Fixes the invalid attribute name reference in the >'System: Read User Addressbook Attributes' permission. > >https://fedorahosted.org/freeipa/ticket/4883 > >Tomas >>From 93ab1bf897151992df4bd3588386cf8fed4849d2 Mon Sep 17 00:00:00 2001 >From: Tomas Babej >Date: Thu, 19 Feb 2015 17:10:37 +0100 >Subject: [PATCH] ipalib: Make sure correct attribute name is referenced for > fax > >Fixes the invalid attribute name reference in the >'System: Read User Addressbook Attributes' permission. > >https://fedorahosted.org/freeipa/ticket/4883 >--- > ipalib/plugins/user.py | 2 +- > 1 file changed, 1 insertion(+), 1 deletion(-) > >diff --git a/ipalib/plugins/user.py b/ipalib/plugins/user.py >index 56585b9f86593c0c5879139103bc71707b88e15f..abe5ee26b8e48681eeb0cbb3bcff8617e212225c 100644 >--- a/ipalib/plugins/user.py >+++ b/ipalib/plugins/user.py >@@ -276,7 +276,7 @@ class user(LDAPObject): > 'ipapermright': {'read', 'search', 'compare'}, > 'ipapermdefaultattr': { > 'seealso', 'telephonenumber', >- 'fax', 'l', 'ou', 'st', 'postalcode', 'street', >+ 'facsimiletelephonenumber', 'l', 'ou', 'st', 'postalcode', 'street', > 'destinationindicator', 'internationalisdnnumber', > 'physicaldeliveryofficename', 'postaladdress', 'postofficebox', > 'preferreddeliverymethod', 'registeredaddress', 00core.ldif still contains 'fax' definition as an alias to 'facsimileTelephoneNumber' so strictly speaking both should be allowed even though 'fax' attribute name is deprecated. -- / Alexander Bokovoy -------------- next part -------------- A non-text attachment was scrubbed... Name: not available Type: application/pgp-signature Size: 473 bytes Desc: not available URL: From mkosek at redhat.com Thu Feb 19 16:32:22 2015 From: mkosek at redhat.com (Martin Kosek) Date: Thu, 19 Feb 2015 17:32:22 +0100 Subject: [Freeipa-devel] [PATCH 0303] ipalib: Make sure correct attribute name is referenced for In-Reply-To: <20150219162958.GN25455@redhat.com> References: <54E60E85.5010102@redhat.com> <20150219162958.GN25455@redhat.com> Message-ID: <54E61016.50509@redhat.com> On 02/19/2015 05:29 PM, Alexander Bokovoy wrote: > On Thu, 19 Feb 2015, Tomas Babej wrote: >> Hi, >> >> Fixes the invalid attribute name reference in the >> 'System: Read User Addressbook Attributes' permission. >> >> https://fedorahosted.org/freeipa/ticket/4883 >> >> Tomas > >>> From 93ab1bf897151992df4bd3588386cf8fed4849d2 Mon Sep 17 00:00:00 2001 >> From: Tomas Babej >> Date: Thu, 19 Feb 2015 17:10:37 +0100 >> Subject: [PATCH] ipalib: Make sure correct attribute name is referenced for >> fax >> >> Fixes the invalid attribute name reference in the >> 'System: Read User Addressbook Attributes' permission. >> >> https://fedorahosted.org/freeipa/ticket/4883 >> --- >> ipalib/plugins/user.py | 2 +- >> 1 file changed, 1 insertion(+), 1 deletion(-) >> >> diff --git a/ipalib/plugins/user.py b/ipalib/plugins/user.py >> index >> 56585b9f86593c0c5879139103bc71707b88e15f..abe5ee26b8e48681eeb0cbb3bcff8617e212225c >> 100644 >> --- a/ipalib/plugins/user.py >> +++ b/ipalib/plugins/user.py >> @@ -276,7 +276,7 @@ class user(LDAPObject): >> 'ipapermright': {'read', 'search', 'compare'}, >> 'ipapermdefaultattr': { >> 'seealso', 'telephonenumber', >> - 'fax', 'l', 'ou', 'st', 'postalcode', 'street', >> + 'facsimiletelephonenumber', 'l', 'ou', 'st', 'postalcode', >> 'street', >> 'destinationindicator', 'internationalisdnnumber', >> 'physicaldeliveryofficename', 'postaladdress', 'postofficebox', >> 'preferreddeliverymethod', 'registeredaddress', > 00core.ldif still contains 'fax' definition as an alias to > 'facsimileTelephoneNumber' so strictly speaking both should be allowed > even though 'fax' attribute name is deprecated. Should, but does not (I tested). This may be a gap in DS ACI evaluation. However, for FreeIPA side, I prefer Tomas' change, even for compatibility with other DS-es - so ACK from me. From tbabej at redhat.com Thu Feb 19 16:37:02 2015 From: tbabej at redhat.com (Tomas Babej) Date: Thu, 19 Feb 2015 17:37:02 +0100 Subject: [Freeipa-devel] [PATCH 0303] ipalib: Make sure correct attribute name is referenced for In-Reply-To: <54E61016.50509@redhat.com> References: <54E60E85.5010102@redhat.com> <20150219162958.GN25455@redhat.com> <54E61016.50509@redhat.com> Message-ID: <54E6112E.8000504@redhat.com> On 02/19/2015 05:32 PM, Martin Kosek wrote: > On 02/19/2015 05:29 PM, Alexander Bokovoy wrote: >> On Thu, 19 Feb 2015, Tomas Babej wrote: >>> Hi, >>> >>> Fixes the invalid attribute name reference in the >>> 'System: Read User Addressbook Attributes' permission. >>> >>> https://fedorahosted.org/freeipa/ticket/4883 >>> >>> Tomas >>>> From 93ab1bf897151992df4bd3588386cf8fed4849d2 Mon Sep 17 00:00:00 2001 >>> From: Tomas Babej >>> Date: Thu, 19 Feb 2015 17:10:37 +0100 >>> Subject: [PATCH] ipalib: Make sure correct attribute name is referenced for >>> fax >>> >>> Fixes the invalid attribute name reference in the >>> 'System: Read User Addressbook Attributes' permission. >>> >>> https://fedorahosted.org/freeipa/ticket/4883 >>> --- >>> ipalib/plugins/user.py | 2 +- >>> 1 file changed, 1 insertion(+), 1 deletion(-) >>> >>> diff --git a/ipalib/plugins/user.py b/ipalib/plugins/user.py >>> index >>> 56585b9f86593c0c5879139103bc71707b88e15f..abe5ee26b8e48681eeb0cbb3bcff8617e212225c >>> 100644 >>> --- a/ipalib/plugins/user.py >>> +++ b/ipalib/plugins/user.py >>> @@ -276,7 +276,7 @@ class user(LDAPObject): >>> 'ipapermright': {'read', 'search', 'compare'}, >>> 'ipapermdefaultattr': { >>> 'seealso', 'telephonenumber', >>> - 'fax', 'l', 'ou', 'st', 'postalcode', 'street', >>> + 'facsimiletelephonenumber', 'l', 'ou', 'st', 'postalcode', >>> 'street', >>> 'destinationindicator', 'internationalisdnnumber', >>> 'physicaldeliveryofficename', 'postaladdress', 'postofficebox', >>> 'preferreddeliverymethod', 'registeredaddress', >> 00core.ldif still contains 'fax' definition as an alias to >> 'facsimileTelephoneNumber' so strictly speaking both should be allowed >> even though 'fax' attribute name is deprecated. > Should, but does not (I tested). This may be a gap in DS ACI evaluation. > However, for FreeIPA side, I prefer Tomas' change, even for compatibility with > other DS-es - so ACK from me. Martin is right, however, I think Alexander was pointing out that we should support the deprecated name 'fax', as well as 'facsimileTelephoneNumber' directly in the 'System: Read User Addressbook Attributes' read permission. Am I reading this correctly? From abokovoy at redhat.com Thu Feb 19 16:40:25 2015 From: abokovoy at redhat.com (Alexander Bokovoy) Date: Thu, 19 Feb 2015 18:40:25 +0200 Subject: [Freeipa-devel] [PATCH 0303] ipalib: Make sure correct attribute name is referenced for In-Reply-To: <54E6112E.8000504@redhat.com> References: <54E60E85.5010102@redhat.com> <20150219162958.GN25455@redhat.com> <54E61016.50509@redhat.com> <54E6112E.8000504@redhat.com> Message-ID: <20150219164025.GO25455@redhat.com> On Thu, 19 Feb 2015, Tomas Babej wrote: > >On 02/19/2015 05:32 PM, Martin Kosek wrote: >>On 02/19/2015 05:29 PM, Alexander Bokovoy wrote: >>>On Thu, 19 Feb 2015, Tomas Babej wrote: >>>>Hi, >>>> >>>>Fixes the invalid attribute name reference in the >>>>'System: Read User Addressbook Attributes' permission. >>>> >>>>https://fedorahosted.org/freeipa/ticket/4883 >>>> >>>>Tomas >>>>> From 93ab1bf897151992df4bd3588386cf8fed4849d2 Mon Sep 17 00:00:00 2001 >>>>From: Tomas Babej >>>>Date: Thu, 19 Feb 2015 17:10:37 +0100 >>>>Subject: [PATCH] ipalib: Make sure correct attribute name is referenced for >>>>fax >>>> >>>>Fixes the invalid attribute name reference in the >>>>'System: Read User Addressbook Attributes' permission. >>>> >>>>https://fedorahosted.org/freeipa/ticket/4883 >>>>--- >>>>ipalib/plugins/user.py | 2 +- >>>>1 file changed, 1 insertion(+), 1 deletion(-) >>>> >>>>diff --git a/ipalib/plugins/user.py b/ipalib/plugins/user.py >>>>index >>>>56585b9f86593c0c5879139103bc71707b88e15f..abe5ee26b8e48681eeb0cbb3bcff8617e212225c >>>>100644 >>>>--- a/ipalib/plugins/user.py >>>>+++ b/ipalib/plugins/user.py >>>>@@ -276,7 +276,7 @@ class user(LDAPObject): >>>> 'ipapermright': {'read', 'search', 'compare'}, >>>> 'ipapermdefaultattr': { >>>> 'seealso', 'telephonenumber', >>>>- 'fax', 'l', 'ou', 'st', 'postalcode', 'street', >>>>+ 'facsimiletelephonenumber', 'l', 'ou', 'st', 'postalcode', >>>>'street', >>>> 'destinationindicator', 'internationalisdnnumber', >>>> 'physicaldeliveryofficename', 'postaladdress', 'postofficebox', >>>> 'preferreddeliverymethod', 'registeredaddress', >>>00core.ldif still contains 'fax' definition as an alias to >>>'facsimileTelephoneNumber' so strictly speaking both should be allowed >>>even though 'fax' attribute name is deprecated. >>Should, but does not (I tested). This may be a gap in DS ACI evaluation. >>However, for FreeIPA side, I prefer Tomas' change, even for compatibility with >>other DS-es - so ACK from me. > >Martin is right, however, I think Alexander was pointing out that we >should support the deprecated name 'fax', as well as >'facsimileTelephoneNumber' directly in the 'System: Read User >Addressbook Attributes' read permission. > >Am I reading this correctly? Exactly, both names should be supported in the ACI. -- / Alexander Bokovoy -------------- next part -------------- A non-text attachment was scrubbed... Name: not available Type: application/pgp-signature Size: 473 bytes Desc: not available URL: From mkosek at redhat.com Thu Feb 19 16:45:52 2015 From: mkosek at redhat.com (Martin Kosek) Date: Thu, 19 Feb 2015 17:45:52 +0100 Subject: [Freeipa-devel] [PATCH 0303] ipalib: Make sure correct attribute name is referenced for In-Reply-To: <20150219164025.GO25455@redhat.com> References: <54E60E85.5010102@redhat.com> <20150219162958.GN25455@redhat.com> <54E61016.50509@redhat.com> <54E6112E.8000504@redhat.com> <20150219164025.GO25455@redhat.com> Message-ID: <54E61340.10109@redhat.com> On 02/19/2015 05:40 PM, Alexander Bokovoy wrote: > On Thu, 19 Feb 2015, Tomas Babej wrote: >> >> On 02/19/2015 05:32 PM, Martin Kosek wrote: >>> On 02/19/2015 05:29 PM, Alexander Bokovoy wrote: >>>> On Thu, 19 Feb 2015, Tomas Babej wrote: >>>>> Hi, >>>>> >>>>> Fixes the invalid attribute name reference in the >>>>> 'System: Read User Addressbook Attributes' permission. >>>>> >>>>> https://fedorahosted.org/freeipa/ticket/4883 >>>>> >>>>> Tomas >>>>>> From 93ab1bf897151992df4bd3588386cf8fed4849d2 Mon Sep 17 00:00:00 2001 >>>>> From: Tomas Babej >>>>> Date: Thu, 19 Feb 2015 17:10:37 +0100 >>>>> Subject: [PATCH] ipalib: Make sure correct attribute name is referenced for >>>>> fax >>>>> >>>>> Fixes the invalid attribute name reference in the >>>>> 'System: Read User Addressbook Attributes' permission. >>>>> >>>>> https://fedorahosted.org/freeipa/ticket/4883 >>>>> --- >>>>> ipalib/plugins/user.py | 2 +- >>>>> 1 file changed, 1 insertion(+), 1 deletion(-) >>>>> >>>>> diff --git a/ipalib/plugins/user.py b/ipalib/plugins/user.py >>>>> index >>>>> 56585b9f86593c0c5879139103bc71707b88e15f..abe5ee26b8e48681eeb0cbb3bcff8617e212225c >>>>> >>>>> 100644 >>>>> --- a/ipalib/plugins/user.py >>>>> +++ b/ipalib/plugins/user.py >>>>> @@ -276,7 +276,7 @@ class user(LDAPObject): >>>>> 'ipapermright': {'read', 'search', 'compare'}, >>>>> 'ipapermdefaultattr': { >>>>> 'seealso', 'telephonenumber', >>>>> - 'fax', 'l', 'ou', 'st', 'postalcode', 'street', >>>>> + 'facsimiletelephonenumber', 'l', 'ou', 'st', 'postalcode', >>>>> 'street', >>>>> 'destinationindicator', 'internationalisdnnumber', >>>>> 'physicaldeliveryofficename', 'postaladdress', >>>>> 'postofficebox', >>>>> 'preferreddeliverymethod', 'registeredaddress', >>>> 00core.ldif still contains 'fax' definition as an alias to >>>> 'facsimileTelephoneNumber' so strictly speaking both should be allowed >>>> even though 'fax' attribute name is deprecated. >>> Should, but does not (I tested). This may be a gap in DS ACI evaluation. >>> However, for FreeIPA side, I prefer Tomas' change, even for compatibility with >>> other DS-es - so ACK from me. >> >> Martin is right, however, I think Alexander was pointing out that we should >> support the deprecated name 'fax', as well as 'facsimileTelephoneNumber' >> directly in the 'System: Read User Addressbook Attributes' read permission. >> >> Am I reading this correctly? > Exactly, both names should be supported in the ACI. Ah, I thought you were referring to DS, not being to able to recognize the alias. Although following this logic, we should for example also have ACIs for commonName, given it's alias for "cn", right? From tbabej at redhat.com Thu Feb 19 16:55:19 2015 From: tbabej at redhat.com (Tomas Babej) Date: Thu, 19 Feb 2015 17:55:19 +0100 Subject: [Freeipa-devel] [PATCH 0303] ipalib: Make sure correct attribute name is referenced for In-Reply-To: <54E61340.10109@redhat.com> References: <54E60E85.5010102@redhat.com> <20150219162958.GN25455@redhat.com> <54E61016.50509@redhat.com> <54E6112E.8000504@redhat.com> <20150219164025.GO25455@redhat.com> <54E61340.10109@redhat.com> Message-ID: <54E61577.6070605@redhat.com> On 02/19/2015 05:45 PM, Martin Kosek wrote: > On 02/19/2015 05:40 PM, Alexander Bokovoy wrote: >> On Thu, 19 Feb 2015, Tomas Babej wrote: >>> On 02/19/2015 05:32 PM, Martin Kosek wrote: >>>> On 02/19/2015 05:29 PM, Alexander Bokovoy wrote: >>>>> On Thu, 19 Feb 2015, Tomas Babej wrote: >>>>>> Hi, >>>>>> >>>>>> Fixes the invalid attribute name reference in the >>>>>> 'System: Read User Addressbook Attributes' permission. >>>>>> >>>>>> https://fedorahosted.org/freeipa/ticket/4883 >>>>>> >>>>>> Tomas >>>>>>> From 93ab1bf897151992df4bd3588386cf8fed4849d2 Mon Sep 17 00:00:00 2001 >>>>>> From: Tomas Babej >>>>>> Date: Thu, 19 Feb 2015 17:10:37 +0100 >>>>>> Subject: [PATCH] ipalib: Make sure correct attribute name is referenced for >>>>>> fax >>>>>> >>>>>> Fixes the invalid attribute name reference in the >>>>>> 'System: Read User Addressbook Attributes' permission. >>>>>> >>>>>> https://fedorahosted.org/freeipa/ticket/4883 >>>>>> --- >>>>>> ipalib/plugins/user.py | 2 +- >>>>>> 1 file changed, 1 insertion(+), 1 deletion(-) >>>>>> >>>>>> diff --git a/ipalib/plugins/user.py b/ipalib/plugins/user.py >>>>>> index >>>>>> 56585b9f86593c0c5879139103bc71707b88e15f..abe5ee26b8e48681eeb0cbb3bcff8617e212225c >>>>>> >>>>>> 100644 >>>>>> --- a/ipalib/plugins/user.py >>>>>> +++ b/ipalib/plugins/user.py >>>>>> @@ -276,7 +276,7 @@ class user(LDAPObject): >>>>>> 'ipapermright': {'read', 'search', 'compare'}, >>>>>> 'ipapermdefaultattr': { >>>>>> 'seealso', 'telephonenumber', >>>>>> - 'fax', 'l', 'ou', 'st', 'postalcode', 'street', >>>>>> + 'facsimiletelephonenumber', 'l', 'ou', 'st', 'postalcode', >>>>>> 'street', >>>>>> 'destinationindicator', 'internationalisdnnumber', >>>>>> 'physicaldeliveryofficename', 'postaladdress', >>>>>> 'postofficebox', >>>>>> 'preferreddeliverymethod', 'registeredaddress', >>>>> 00core.ldif still contains 'fax' definition as an alias to >>>>> 'facsimileTelephoneNumber' so strictly speaking both should be allowed >>>>> even though 'fax' attribute name is deprecated. >>>> Should, but does not (I tested). This may be a gap in DS ACI evaluation. >>>> However, for FreeIPA side, I prefer Tomas' change, even for compatibility with >>>> other DS-es - so ACK from me. >>> Martin is right, however, I think Alexander was pointing out that we should >>> support the deprecated name 'fax', as well as 'facsimileTelephoneNumber' >>> directly in the 'System: Read User Addressbook Attributes' read permission. >>> >>> Am I reading this correctly? >> Exactly, both names should be supported in the ACI. > Ah, I thought you were referring to DS, not being to able to recognize the > alias. Although following this logic, we should for example also have ACIs for > commonName, given it's alias for "cn", right? Attaching updated patch with both fax and facsimileTelephoneNumber. However, Martin is right, the problem occurs multiple times: attributeTypes: ( 2.5.4.6 NAME ( 'c' 'countryName' ) attributeTypes: ( 2.5.4.3 NAME ( 'cn' 'commonName' ) attributeTypes: ( 0.9.2342.19200300.100.1.25 NAME ( 'dc' 'domaincomponent' ) attributeTypes: ( 2.5.4.49 NAME ( 'distinguishedName' 'dn' ) attributeTypes: ( 2.5.4.23 NAME ( 'facsimileTelephoneNumber' 'fax' ) attributeTypes: ( 2.5.4.7 NAME ( 'l' 'locality' 'localityname' ) attributeTypes: ( 2.5.4.10 NAME ( 'o' 'organizationname' ) attributeTypes: ( 2.5.4.11 NAME ( 'ou' 'organizationalUnitName' ) attributeTypes: ( 2.5.4.4 NAME ( 'sn' 'surName' ) attributeTypes: ( 2.5.4.8 NAME ( 'st' 'stateOrProvinceName' ) attributeTypes: ( 2.5.4.9 NAME ( 'street' 'streetaddress' ) attributeTypes: ( 0.9.2342.19200300.100.1.1 NAME ( 'uid' 'userid' ) -------------- next part -------------- A non-text attachment was scrubbed... Name: freeipa-tbabej-0303-2-ipalib-Make-sure-correct-attribute-name-is-reference.patch Type: text/x-patch Size: 4168 bytes Desc: not available URL: From abokovoy at redhat.com Thu Feb 19 16:55:22 2015 From: abokovoy at redhat.com (Alexander Bokovoy) Date: Thu, 19 Feb 2015 18:55:22 +0200 Subject: [Freeipa-devel] [PATCH 0303] ipalib: Make sure correct attribute name is referenced for In-Reply-To: <54E61340.10109@redhat.com> References: <54E60E85.5010102@redhat.com> <20150219162958.GN25455@redhat.com> <54E61016.50509@redhat.com> <54E6112E.8000504@redhat.com> <20150219164025.GO25455@redhat.com> <54E61340.10109@redhat.com> Message-ID: <20150219165522.GP25455@redhat.com> On Thu, 19 Feb 2015, Martin Kosek wrote: >On 02/19/2015 05:40 PM, Alexander Bokovoy wrote: >> On Thu, 19 Feb 2015, Tomas Babej wrote: >>> >>> On 02/19/2015 05:32 PM, Martin Kosek wrote: >>>> On 02/19/2015 05:29 PM, Alexander Bokovoy wrote: >>>>> On Thu, 19 Feb 2015, Tomas Babej wrote: >>>>>> Hi, >>>>>> >>>>>> Fixes the invalid attribute name reference in the >>>>>> 'System: Read User Addressbook Attributes' permission. >>>>>> >>>>>> https://fedorahosted.org/freeipa/ticket/4883 >>>>>> >>>>>> Tomas >>>>>>> From 93ab1bf897151992df4bd3588386cf8fed4849d2 Mon Sep 17 00:00:00 2001 >>>>>> From: Tomas Babej >>>>>> Date: Thu, 19 Feb 2015 17:10:37 +0100 >>>>>> Subject: [PATCH] ipalib: Make sure correct attribute name is referenced for >>>>>> fax >>>>>> >>>>>> Fixes the invalid attribute name reference in the >>>>>> 'System: Read User Addressbook Attributes' permission. >>>>>> >>>>>> https://fedorahosted.org/freeipa/ticket/4883 >>>>>> --- >>>>>> ipalib/plugins/user.py | 2 +- >>>>>> 1 file changed, 1 insertion(+), 1 deletion(-) >>>>>> >>>>>> diff --git a/ipalib/plugins/user.py b/ipalib/plugins/user.py >>>>>> index >>>>>> 56585b9f86593c0c5879139103bc71707b88e15f..abe5ee26b8e48681eeb0cbb3bcff8617e212225c >>>>>> >>>>>> 100644 >>>>>> --- a/ipalib/plugins/user.py >>>>>> +++ b/ipalib/plugins/user.py >>>>>> @@ -276,7 +276,7 @@ class user(LDAPObject): >>>>>> 'ipapermright': {'read', 'search', 'compare'}, >>>>>> 'ipapermdefaultattr': { >>>>>> 'seealso', 'telephonenumber', >>>>>> - 'fax', 'l', 'ou', 'st', 'postalcode', 'street', >>>>>> + 'facsimiletelephonenumber', 'l', 'ou', 'st', 'postalcode', >>>>>> 'street', >>>>>> 'destinationindicator', 'internationalisdnnumber', >>>>>> 'physicaldeliveryofficename', 'postaladdress', >>>>>> 'postofficebox', >>>>>> 'preferreddeliverymethod', 'registeredaddress', >>>>> 00core.ldif still contains 'fax' definition as an alias to >>>>> 'facsimileTelephoneNumber' so strictly speaking both should be allowed >>>>> even though 'fax' attribute name is deprecated. >>>> Should, but does not (I tested). This may be a gap in DS ACI evaluation. >>>> However, for FreeIPA side, I prefer Tomas' change, even for compatibility with >>>> other DS-es - so ACK from me. >>> >>> Martin is right, however, I think Alexander was pointing out that we should >>> support the deprecated name 'fax', as well as 'facsimileTelephoneNumber' >>> directly in the 'System: Read User Addressbook Attributes' read permission. >>> >>> Am I reading this correctly? >> Exactly, both names should be supported in the ACI. > >Ah, I thought you were referring to DS, not being to able to recognize the >alias. Although following this logic, we should for example also have ACIs for >commonName, given it's alias for "cn", right? Yes, I thought the same though postponed proposing that as it is 'unrelated' to this patch. https://fedorahosted.org/freeipa/ticket/4913 -- / Alexander Bokovoy -------------- next part -------------- A non-text attachment was scrubbed... Name: not available Type: application/pgp-signature Size: 473 bytes Desc: not available URL: From mkosek at redhat.com Thu Feb 19 17:13:16 2015 From: mkosek at redhat.com (Martin Kosek) Date: Thu, 19 Feb 2015 18:13:16 +0100 Subject: [Freeipa-devel] [PATCH 0303] ipalib: Make sure correct attribute name is referenced for In-Reply-To: <54E61577.6070605@redhat.com> References: <54E60E85.5010102@redhat.com> <20150219162958.GN25455@redhat.com> <54E61016.50509@redhat.com> <54E6112E.8000504@redhat.com> <20150219164025.GO25455@redhat.com> <54E61340.10109@redhat.com> <54E61577.6070605@redhat.com> Message-ID: <54E619AC.3030705@redhat.com> On 02/19/2015 05:55 PM, Tomas Babej wrote: > > On 02/19/2015 05:45 PM, Martin Kosek wrote: >> On 02/19/2015 05:40 PM, Alexander Bokovoy wrote: >>> On Thu, 19 Feb 2015, Tomas Babej wrote: >>>> On 02/19/2015 05:32 PM, Martin Kosek wrote: >>>>> On 02/19/2015 05:29 PM, Alexander Bokovoy wrote: >>>>>> On Thu, 19 Feb 2015, Tomas Babej wrote: >>>>>>> Hi, >>>>>>> >>>>>>> Fixes the invalid attribute name reference in the >>>>>>> 'System: Read User Addressbook Attributes' permission. >>>>>>> >>>>>>> https://fedorahosted.org/freeipa/ticket/4883 >>>>>>> >>>>>>> Tomas >>>>>>>> From 93ab1bf897151992df4bd3588386cf8fed4849d2 Mon Sep 17 00:00:00 2001 >>>>>>> From: Tomas Babej >>>>>>> Date: Thu, 19 Feb 2015 17:10:37 +0100 >>>>>>> Subject: [PATCH] ipalib: Make sure correct attribute name is referenced for >>>>>>> fax >>>>>>> >>>>>>> Fixes the invalid attribute name reference in the >>>>>>> 'System: Read User Addressbook Attributes' permission. >>>>>>> >>>>>>> https://fedorahosted.org/freeipa/ticket/4883 >>>>>>> --- >>>>>>> ipalib/plugins/user.py | 2 +- >>>>>>> 1 file changed, 1 insertion(+), 1 deletion(-) >>>>>>> >>>>>>> diff --git a/ipalib/plugins/user.py b/ipalib/plugins/user.py >>>>>>> index >>>>>>> 56585b9f86593c0c5879139103bc71707b88e15f..abe5ee26b8e48681eeb0cbb3bcff8617e212225c >>>>>>> >>>>>>> >>>>>>> 100644 >>>>>>> --- a/ipalib/plugins/user.py >>>>>>> +++ b/ipalib/plugins/user.py >>>>>>> @@ -276,7 +276,7 @@ class user(LDAPObject): >>>>>>> 'ipapermright': {'read', 'search', 'compare'}, >>>>>>> 'ipapermdefaultattr': { >>>>>>> 'seealso', 'telephonenumber', >>>>>>> - 'fax', 'l', 'ou', 'st', 'postalcode', 'street', >>>>>>> + 'facsimiletelephonenumber', 'l', 'ou', 'st', 'postalcode', >>>>>>> 'street', >>>>>>> 'destinationindicator', 'internationalisdnnumber', >>>>>>> 'physicaldeliveryofficename', 'postaladdress', >>>>>>> 'postofficebox', >>>>>>> 'preferreddeliverymethod', 'registeredaddress', >>>>>> 00core.ldif still contains 'fax' definition as an alias to >>>>>> 'facsimileTelephoneNumber' so strictly speaking both should be allowed >>>>>> even though 'fax' attribute name is deprecated. >>>>> Should, but does not (I tested). This may be a gap in DS ACI evaluation. >>>>> However, for FreeIPA side, I prefer Tomas' change, even for compatibility >>>>> with >>>>> other DS-es - so ACK from me. >>>> Martin is right, however, I think Alexander was pointing out that we should >>>> support the deprecated name 'fax', as well as 'facsimileTelephoneNumber' >>>> directly in the 'System: Read User Addressbook Attributes' read permission. >>>> >>>> Am I reading this correctly? >>> Exactly, both names should be supported in the ACI. >> Ah, I thought you were referring to DS, not being to able to recognize the >> alias. Although following this logic, we should for example also have ACIs for >> commonName, given it's alias for "cn", right? > > Attaching updated patch with both fax and facsimileTelephoneNumber. > > However, Martin is right, the problem occurs multiple times: > > attributeTypes: ( 2.5.4.6 NAME ( 'c' 'countryName' ) > attributeTypes: ( 2.5.4.3 NAME ( 'cn' 'commonName' ) > attributeTypes: ( 0.9.2342.19200300.100.1.25 NAME ( 'dc' 'domaincomponent' ) > attributeTypes: ( 2.5.4.49 NAME ( 'distinguishedName' 'dn' ) > attributeTypes: ( 2.5.4.23 NAME ( 'facsimileTelephoneNumber' 'fax' ) > attributeTypes: ( 2.5.4.7 NAME ( 'l' 'locality' 'localityname' ) > attributeTypes: ( 2.5.4.10 NAME ( 'o' 'organizationname' ) > attributeTypes: ( 2.5.4.11 NAME ( 'ou' 'organizationalUnitName' ) > attributeTypes: ( 2.5.4.4 NAME ( 'sn' 'surName' ) > attributeTypes: ( 2.5.4.8 NAME ( 'st' 'stateOrProvinceName' ) > attributeTypes: ( 2.5.4.9 NAME ( 'street' 'streetaddress' ) > attributeTypes: ( 0.9.2342.19200300.100.1.1 NAME ( 'uid' 'userid' ) I would personally still be OK only with the fax attribute (the original patch) - so that our behavior is consistent with these attributes. Should not harm us as our API only supports facsimileTelephoneNumber anyway. Not a blocker though. From tbabej at redhat.com Thu Feb 19 17:19:05 2015 From: tbabej at redhat.com (Tomas Babej) Date: Thu, 19 Feb 2015 18:19:05 +0100 Subject: [Freeipa-devel] [PATCH 0303] ipalib: Make sure correct attribute name is referenced for In-Reply-To: <54E619AC.3030705@redhat.com> References: <54E60E85.5010102@redhat.com> <20150219162958.GN25455@redhat.com> <54E61016.50509@redhat.com> <54E6112E.8000504@redhat.com> <20150219164025.GO25455@redhat.com> <54E61340.10109@redhat.com> <54E61577.6070605@redhat.com> <54E619AC.3030705@redhat.com> Message-ID: <54E61B09.3090703@redhat.com> On 02/19/2015 06:13 PM, Martin Kosek wrote: > On 02/19/2015 05:55 PM, Tomas Babej wrote: >> On 02/19/2015 05:45 PM, Martin Kosek wrote: >>> On 02/19/2015 05:40 PM, Alexander Bokovoy wrote: >>>> On Thu, 19 Feb 2015, Tomas Babej wrote: >>>>> On 02/19/2015 05:32 PM, Martin Kosek wrote: >>>>>> On 02/19/2015 05:29 PM, Alexander Bokovoy wrote: >>>>>>> On Thu, 19 Feb 2015, Tomas Babej wrote: >>>>>>>> Hi, >>>>>>>> >>>>>>>> Fixes the invalid attribute name reference in the >>>>>>>> 'System: Read User Addressbook Attributes' permission. >>>>>>>> >>>>>>>> https://fedorahosted.org/freeipa/ticket/4883 >>>>>>>> >>>>>>>> Tomas >>>>>>>>> From 93ab1bf897151992df4bd3588386cf8fed4849d2 Mon Sep 17 00:00:00 2001 >>>>>>>> From: Tomas Babej >>>>>>>> Date: Thu, 19 Feb 2015 17:10:37 +0100 >>>>>>>> Subject: [PATCH] ipalib: Make sure correct attribute name is referenced for >>>>>>>> fax >>>>>>>> >>>>>>>> Fixes the invalid attribute name reference in the >>>>>>>> 'System: Read User Addressbook Attributes' permission. >>>>>>>> >>>>>>>> https://fedorahosted.org/freeipa/ticket/4883 >>>>>>>> --- >>>>>>>> ipalib/plugins/user.py | 2 +- >>>>>>>> 1 file changed, 1 insertion(+), 1 deletion(-) >>>>>>>> >>>>>>>> diff --git a/ipalib/plugins/user.py b/ipalib/plugins/user.py >>>>>>>> index >>>>>>>> 56585b9f86593c0c5879139103bc71707b88e15f..abe5ee26b8e48681eeb0cbb3bcff8617e212225c >>>>>>>> >>>>>>>> >>>>>>>> 100644 >>>>>>>> --- a/ipalib/plugins/user.py >>>>>>>> +++ b/ipalib/plugins/user.py >>>>>>>> @@ -276,7 +276,7 @@ class user(LDAPObject): >>>>>>>> 'ipapermright': {'read', 'search', 'compare'}, >>>>>>>> 'ipapermdefaultattr': { >>>>>>>> 'seealso', 'telephonenumber', >>>>>>>> - 'fax', 'l', 'ou', 'st', 'postalcode', 'street', >>>>>>>> + 'facsimiletelephonenumber', 'l', 'ou', 'st', 'postalcode', >>>>>>>> 'street', >>>>>>>> 'destinationindicator', 'internationalisdnnumber', >>>>>>>> 'physicaldeliveryofficename', 'postaladdress', >>>>>>>> 'postofficebox', >>>>>>>> 'preferreddeliverymethod', 'registeredaddress', >>>>>>> 00core.ldif still contains 'fax' definition as an alias to >>>>>>> 'facsimileTelephoneNumber' so strictly speaking both should be allowed >>>>>>> even though 'fax' attribute name is deprecated. >>>>>> Should, but does not (I tested). This may be a gap in DS ACI evaluation. >>>>>> However, for FreeIPA side, I prefer Tomas' change, even for compatibility >>>>>> with >>>>>> other DS-es - so ACK from me. >>>>> Martin is right, however, I think Alexander was pointing out that we should >>>>> support the deprecated name 'fax', as well as 'facsimileTelephoneNumber' >>>>> directly in the 'System: Read User Addressbook Attributes' read permission. >>>>> >>>>> Am I reading this correctly? >>>> Exactly, both names should be supported in the ACI. >>> Ah, I thought you were referring to DS, not being to able to recognize the >>> alias. Although following this logic, we should for example also have ACIs for >>> commonName, given it's alias for "cn", right? >> Attaching updated patch with both fax and facsimileTelephoneNumber. >> >> However, Martin is right, the problem occurs multiple times: >> >> attributeTypes: ( 2.5.4.6 NAME ( 'c' 'countryName' ) >> attributeTypes: ( 2.5.4.3 NAME ( 'cn' 'commonName' ) >> attributeTypes: ( 0.9.2342.19200300.100.1.25 NAME ( 'dc' 'domaincomponent' ) >> attributeTypes: ( 2.5.4.49 NAME ( 'distinguishedName' 'dn' ) >> attributeTypes: ( 2.5.4.23 NAME ( 'facsimileTelephoneNumber' 'fax' ) >> attributeTypes: ( 2.5.4.7 NAME ( 'l' 'locality' 'localityname' ) >> attributeTypes: ( 2.5.4.10 NAME ( 'o' 'organizationname' ) >> attributeTypes: ( 2.5.4.11 NAME ( 'ou' 'organizationalUnitName' ) >> attributeTypes: ( 2.5.4.4 NAME ( 'sn' 'surName' ) >> attributeTypes: ( 2.5.4.8 NAME ( 'st' 'stateOrProvinceName' ) >> attributeTypes: ( 2.5.4.9 NAME ( 'street' 'streetaddress' ) >> attributeTypes: ( 0.9.2342.19200300.100.1.1 NAME ( 'uid' 'userid' ) > I would personally still be OK only with the fax attribute (the original patch) > - so that our behavior is consistent with these attributes. Should not harm us > as our API only supports facsimileTelephoneNumber anyway. > > Not a blocker though. I agree here. Attaching the final version, the original patch was missing the ACI.txt update. Tomas -------------- next part -------------- A non-text attachment was scrubbed... Name: freeipa-tbabej-0303-3-ipalib-Make-sure-correct-attribute-name-is-reference.patch Type: text/x-patch Size: 4062 bytes Desc: not available URL: From abokovoy at redhat.com Thu Feb 19 17:33:08 2015 From: abokovoy at redhat.com (Alexander Bokovoy) Date: Thu, 19 Feb 2015 19:33:08 +0200 Subject: [Freeipa-devel] [PATCH 0303] ipalib: Make sure correct attribute name is referenced for In-Reply-To: <54E61B09.3090703@redhat.com> References: <54E60E85.5010102@redhat.com> <20150219162958.GN25455@redhat.com> <54E61016.50509@redhat.com> <54E6112E.8000504@redhat.com> <20150219164025.GO25455@redhat.com> <54E61340.10109@redhat.com> <54E61577.6070605@redhat.com> <54E619AC.3030705@redhat.com> <54E61B09.3090703@redhat.com> Message-ID: <20150219173308.GQ25455@redhat.com> On Thu, 19 Feb 2015, Tomas Babej wrote: > > On 02/19/2015 06:13 PM, Martin Kosek wrote: >> On 02/19/2015 05:55 PM, Tomas Babej wrote: >>> On 02/19/2015 05:45 PM, Martin Kosek wrote: >>>> On 02/19/2015 05:40 PM, Alexander Bokovoy wrote: >>>>> On Thu, 19 Feb 2015, Tomas Babej wrote: >>>>>> On 02/19/2015 05:32 PM, Martin Kosek wrote: >>>>>>> On 02/19/2015 05:29 PM, Alexander Bokovoy wrote: >>>>>>>> On Thu, 19 Feb 2015, Tomas Babej wrote: >>>>>>>>> Hi, >>>>>>>>> >>>>>>>>> Fixes the invalid attribute name reference in the >>>>>>>>> 'System: Read User Addressbook Attributes' permission. >>>>>>>>> >>>>>>>>> https://fedorahosted.org/freeipa/ticket/4883 >>>>>>>>> >>>>>>>>> Tomas >>>>>>>>>> From 93ab1bf897151992df4bd3588386cf8fed4849d2 Mon Sep 17 00:00:00 2001 >>>>>>>>> From: Tomas Babej >>>>>>>>> Date: Thu, 19 Feb 2015 17:10:37 +0100 >>>>>>>>> Subject: [PATCH] ipalib: Make sure correct attribute name is referenced for >>>>>>>>> fax >>>>>>>>> >>>>>>>>> Fixes the invalid attribute name reference in the >>>>>>>>> 'System: Read User Addressbook Attributes' permission. >>>>>>>>> >>>>>>>>> https://fedorahosted.org/freeipa/ticket/4883 >>>>>>>>> --- >>>>>>>>> ipalib/plugins/user.py | 2 +- >>>>>>>>> 1 file changed, 1 insertion(+), 1 deletion(-) >>>>>>>>> >>>>>>>>> diff --git a/ipalib/plugins/user.py b/ipalib/plugins/user.py >>>>>>>>> index >>>>>>>>> 56585b9f86593c0c5879139103bc71707b88e15f..abe5ee26b8e48681eeb0cbb3bcff8617e212225c >>>>>>>>> >>>>>>>>> >>>>>>>>> 100644 >>>>>>>>> --- a/ipalib/plugins/user.py >>>>>>>>> +++ b/ipalib/plugins/user.py >>>>>>>>> @@ -276,7 +276,7 @@ class user(LDAPObject): >>>>>>>>> 'ipapermright': {'read', 'search', 'compare'}, >>>>>>>>> 'ipapermdefaultattr': { >>>>>>>>> 'seealso', 'telephonenumber', >>>>>>>>> - 'fax', 'l', 'ou', 'st', 'postalcode', 'street', >>>>>>>>> + 'facsimiletelephonenumber', 'l', 'ou', 'st', 'postalcode', >>>>>>>>> 'street', >>>>>>>>> 'destinationindicator', 'internationalisdnnumber', >>>>>>>>> 'physicaldeliveryofficename', 'postaladdress', >>>>>>>>> 'postofficebox', >>>>>>>>> 'preferreddeliverymethod', 'registeredaddress', >>>>>>>> 00core.ldif still contains 'fax' definition as an alias to >>>>>>>> 'facsimileTelephoneNumber' so strictly speaking both should be allowed >>>>>>>> even though 'fax' attribute name is deprecated. >>>>>>> Should, but does not (I tested). This may be a gap in DS ACI evaluation. >>>>>>> However, for FreeIPA side, I prefer Tomas' change, even for compatibility >>>>>>> with >>>>>>> other DS-es - so ACK from me. >>>>>> Martin is right, however, I think Alexander was pointing out that we should >>>>>> support the deprecated name 'fax', as well as 'facsimileTelephoneNumber' >>>>>> directly in the 'System: Read User Addressbook Attributes' read permission. >>>>>> >>>>>> Am I reading this correctly? >>>>> Exactly, both names should be supported in the ACI. >>>> Ah, I thought you were referring to DS, not being to able to recognize the >>>> alias. Although following this logic, we should for example also have ACIs for >>>> commonName, given it's alias for "cn", right? >>> Attaching updated patch with both fax and facsimileTelephoneNumber. >>> >>> However, Martin is right, the problem occurs multiple times: >>> >>> attributeTypes: ( 2.5.4.6 NAME ( 'c' 'countryName' ) >>> attributeTypes: ( 2.5.4.3 NAME ( 'cn' 'commonName' ) >>> attributeTypes: ( 0.9.2342.19200300.100.1.25 NAME ( 'dc' 'domaincomponent' ) >>> attributeTypes: ( 2.5.4.49 NAME ( 'distinguishedName' 'dn' ) >>> attributeTypes: ( 2.5.4.23 NAME ( 'facsimileTelephoneNumber' 'fax' ) >>> attributeTypes: ( 2.5.4.7 NAME ( 'l' 'locality' 'localityname' ) >>> attributeTypes: ( 2.5.4.10 NAME ( 'o' 'organizationname' ) >>> attributeTypes: ( 2.5.4.11 NAME ( 'ou' 'organizationalUnitName' ) >>> attributeTypes: ( 2.5.4.4 NAME ( 'sn' 'surName' ) >>> attributeTypes: ( 2.5.4.8 NAME ( 'st' 'stateOrProvinceName' ) >>> attributeTypes: ( 2.5.4.9 NAME ( 'street' 'streetaddress' ) >>> attributeTypes: ( 0.9.2342.19200300.100.1.1 NAME ( 'uid' 'userid' ) >> I would personally still be OK only with the fax attribute (the original patch) >> - so that our behavior is consistent with these attributes. Should not harm us >> as our API only supports facsimileTelephoneNumber anyway. >> >> Not a blocker though. > > I agree here. Attaching the final version, the original patch was missing > the ACI.txt update. ACK. -- / Alexander Bokovoy -------------- next part -------------- A non-text attachment was scrubbed... Name: not available Type: application/pgp-signature Size: 473 bytes Desc: not available URL: From tbabej at redhat.com Thu Feb 19 17:37:07 2015 From: tbabej at redhat.com (Tomas Babej) Date: Thu, 19 Feb 2015 18:37:07 +0100 Subject: [Freeipa-devel] [PATCH 0303] ipalib: Make sure correct attribute name is referenced for In-Reply-To: <20150219173308.GQ25455@redhat.com> References: <54E60E85.5010102@redhat.com> <20150219162958.GN25455@redhat.com> <54E61016.50509@redhat.com> <54E6112E.8000504@redhat.com> <20150219164025.GO25455@redhat.com> <54E61340.10109@redhat.com> <54E61577.6070605@redhat.com> <54E619AC.3030705@redhat.com> <54E61B09.3090703@redhat.com> <20150219173308.GQ25455@redhat.com> Message-ID: <54E61F43.50707@redhat.com> On 02/19/2015 06:33 PM, Alexander Bokovoy wrote: > On Thu, 19 Feb 2015, Tomas Babej wrote: >> >> On 02/19/2015 06:13 PM, Martin Kosek wrote: >>> On 02/19/2015 05:55 PM, Tomas Babej wrote: >>>> On 02/19/2015 05:45 PM, Martin Kosek wrote: >>>>> On 02/19/2015 05:40 PM, Alexander Bokovoy wrote: >>>>>> On Thu, 19 Feb 2015, Tomas Babej wrote: >>>>>>> On 02/19/2015 05:32 PM, Martin Kosek wrote: >>>>>>>> On 02/19/2015 05:29 PM, Alexander Bokovoy wrote: >>>>>>>>> On Thu, 19 Feb 2015, Tomas Babej wrote: >>>>>>>>>> Hi, >>>>>>>>>> >>>>>>>>>> Fixes the invalid attribute name reference in the >>>>>>>>>> 'System: Read User Addressbook Attributes' permission. >>>>>>>>>> >>>>>>>>>> https://fedorahosted.org/freeipa/ticket/4883 >>>>>>>>>> >>>>>>>>>> Tomas >>>>>>>>>>> From 93ab1bf897151992df4bd3588386cf8fed4849d2 Mon Sep 17 >>>>>>>>>>> 00:00:00 2001 >>>>>>>>>> From: Tomas Babej >>>>>>>>>> Date: Thu, 19 Feb 2015 17:10:37 +0100 >>>>>>>>>> Subject: [PATCH] ipalib: Make sure correct attribute name is >>>>>>>>>> referenced for >>>>>>>>>> fax >>>>>>>>>> >>>>>>>>>> Fixes the invalid attribute name reference in the >>>>>>>>>> 'System: Read User Addressbook Attributes' permission. >>>>>>>>>> >>>>>>>>>> https://fedorahosted.org/freeipa/ticket/4883 >>>>>>>>>> --- >>>>>>>>>> ipalib/plugins/user.py | 2 +- >>>>>>>>>> 1 file changed, 1 insertion(+), 1 deletion(-) >>>>>>>>>> >>>>>>>>>> diff --git a/ipalib/plugins/user.py b/ipalib/plugins/user.py >>>>>>>>>> index >>>>>>>>>> 56585b9f86593c0c5879139103bc71707b88e15f..abe5ee26b8e48681eeb0cbb3bcff8617e212225c >>>>>>>>>> >>>>>>>>>> >>>>>>>>>> >>>>>>>>>> 100644 >>>>>>>>>> --- a/ipalib/plugins/user.py >>>>>>>>>> +++ b/ipalib/plugins/user.py >>>>>>>>>> @@ -276,7 +276,7 @@ class user(LDAPObject): >>>>>>>>>> 'ipapermright': {'read', 'search', 'compare'}, >>>>>>>>>> 'ipapermdefaultattr': { >>>>>>>>>> 'seealso', 'telephonenumber', >>>>>>>>>> - 'fax', 'l', 'ou', 'st', 'postalcode', 'street', >>>>>>>>>> + 'facsimiletelephonenumber', 'l', 'ou', 'st', >>>>>>>>>> 'postalcode', >>>>>>>>>> 'street', >>>>>>>>>> 'destinationindicator', >>>>>>>>>> 'internationalisdnnumber', >>>>>>>>>> 'physicaldeliveryofficename', 'postaladdress', >>>>>>>>>> 'postofficebox', >>>>>>>>>> 'preferreddeliverymethod', >>>>>>>>>> 'registeredaddress', >>>>>>>>> 00core.ldif still contains 'fax' definition as an alias to >>>>>>>>> 'facsimileTelephoneNumber' so strictly speaking both should be >>>>>>>>> allowed >>>>>>>>> even though 'fax' attribute name is deprecated. >>>>>>>> Should, but does not (I tested). This may be a gap in DS ACI >>>>>>>> evaluation. >>>>>>>> However, for FreeIPA side, I prefer Tomas' change, even for >>>>>>>> compatibility >>>>>>>> with >>>>>>>> other DS-es - so ACK from me. >>>>>>> Martin is right, however, I think Alexander was pointing out >>>>>>> that we should >>>>>>> support the deprecated name 'fax', as well as >>>>>>> 'facsimileTelephoneNumber' >>>>>>> directly in the 'System: Read User Addressbook Attributes' read >>>>>>> permission. >>>>>>> >>>>>>> Am I reading this correctly? >>>>>> Exactly, both names should be supported in the ACI. >>>>> Ah, I thought you were referring to DS, not being to able to >>>>> recognize the >>>>> alias. Although following this logic, we should for example also >>>>> have ACIs for >>>>> commonName, given it's alias for "cn", right? >>>> Attaching updated patch with both fax and facsimileTelephoneNumber. >>>> >>>> However, Martin is right, the problem occurs multiple times: >>>> >>>> attributeTypes: ( 2.5.4.6 NAME ( 'c' 'countryName' ) >>>> attributeTypes: ( 2.5.4.3 NAME ( 'cn' 'commonName' ) >>>> attributeTypes: ( 0.9.2342.19200300.100.1.25 NAME ( 'dc' >>>> 'domaincomponent' ) >>>> attributeTypes: ( 2.5.4.49 NAME ( 'distinguishedName' 'dn' ) >>>> attributeTypes: ( 2.5.4.23 NAME ( 'facsimileTelephoneNumber' 'fax' ) >>>> attributeTypes: ( 2.5.4.7 NAME ( 'l' 'locality' 'localityname' ) >>>> attributeTypes: ( 2.5.4.10 NAME ( 'o' 'organizationname' ) >>>> attributeTypes: ( 2.5.4.11 NAME ( 'ou' 'organizationalUnitName' ) >>>> attributeTypes: ( 2.5.4.4 NAME ( 'sn' 'surName' ) >>>> attributeTypes: ( 2.5.4.8 NAME ( 'st' 'stateOrProvinceName' ) >>>> attributeTypes: ( 2.5.4.9 NAME ( 'street' 'streetaddress' ) >>>> attributeTypes: ( 0.9.2342.19200300.100.1.1 NAME ( 'uid' 'userid' ) >>> I would personally still be OK only with the fax attribute (the >>> original patch) >>> - so that our behavior is consistent with these attributes. Should >>> not harm us >>> as our API only supports facsimileTelephoneNumber anyway. >>> >>> Not a blocker though. >> >> I agree here. Attaching the final version, the original patch was >> missing the ACI.txt update. > ACK. > Pushed to: master: 72af5fd9757da16c49959bfdecf4e0cb41c36503 ipa-4-1: 73f6d69adfa2c10c9e3534f59d047ade3782b051 Tomas From mkosek at redhat.com Fri Feb 20 14:15:28 2015 From: mkosek at redhat.com (Martin Kosek) Date: Fri, 20 Feb 2015 15:15:28 +0100 Subject: [Freeipa-devel] [PATCH] 495 Remove references to GPL v2.0 license Message-ID: <54E74180.7000409@redhat.com> All FreeIPA original code should be licensed to GPL v3+ license, update the respective files: - daemons/ipa-slapi-plugins/ipa-dns/ipa_dns.c Remove GPL v2.0 license files from LDIFs or template to keep consistency. CCing Simo for review. -- Martin Kosek Supervisor, Software Engineering - Identity Management Team Red Hat Inc. -------------- next part -------------- A non-text attachment was scrubbed... Name: freeipa-mkosek-495-remove-references-to-gpl-v2.0-license.patch Type: text/x-patch Size: 9126 bytes Desc: not available URL: From ssorce at redhat.com Fri Feb 20 14:33:23 2015 From: ssorce at redhat.com (Simo Sorce) Date: Fri, 20 Feb 2015 09:33:23 -0500 Subject: [Freeipa-devel] [PATCH] 495 Remove references to GPL v2.0 license In-Reply-To: <54E74180.7000409@redhat.com> References: <54E74180.7000409@redhat.com> Message-ID: <1424442803.5560.31.camel@willson.usersys.redhat.com> On Fri, 2015-02-20 at 15:15 +0100, Martin Kosek wrote: > All FreeIPA original code should be licensed to GPL v3+ license, > update the respective files: > > - daemons/ipa-slapi-plugins/ipa-dns/ipa_dns.c > > Remove GPL v2.0 license files from LDIFs or template to keep > consistency. > > CCing Simo for review. > ACK From simo at redhat.com Fri Feb 20 14:34:39 2015 From: simo at redhat.com (Simo Sorce) Date: Fri, 20 Feb 2015 09:34:39 -0500 Subject: [Freeipa-devel] [PATCH] Fix license exception Message-ID: <1424442879.5560.33.camel@willson.usersys.redhat.com> During internal conversations it occurred to me we link to OpenSSL but never provided the proper exception for downstreams. Attached patch fixes the problem. Simo. -- Simo Sorce * Red Hat, Inc * New York -------------- next part -------------- A non-text attachment was scrubbed... Name: freeipa-simo-520-1-Add-a-clear-OpenSSL-exception.patch Type: text/x-patch Size: 2838 bytes Desc: not available URL: From simo at redhat.com Fri Feb 20 14:35:21 2015 From: simo at redhat.com (Simo Sorce) Date: Fri, 20 Feb 2015 09:35:21 -0500 Subject: [Freeipa-devel] [PATCH} Remove unneded imports Message-ID: <1424442921.5560.34.camel@willson.usersys.redhat.com> We do not use openssl/des.h anymore, stop checking and importing it. Simo. -- Simo Sorce * Red Hat, Inc * New York -------------- next part -------------- A non-text attachment was scrubbed... Name: freeipa-simo-521-1-Stop-including-the-DES-algorythm-from-openssl.patch Type: text/x-patch Size: 1768 bytes Desc: not available URL: From mkosek at redhat.com Fri Feb 20 14:41:19 2015 From: mkosek at redhat.com (Martin Kosek) Date: Fri, 20 Feb 2015 15:41:19 +0100 Subject: [Freeipa-devel] [PATCH] 495 Remove references to GPL v2.0 license In-Reply-To: <1424442803.5560.31.camel@willson.usersys.redhat.com> References: <54E74180.7000409@redhat.com> <1424442803.5560.31.camel@willson.usersys.redhat.com> Message-ID: <54E7478F.6010608@redhat.com> On 02/20/2015 03:33 PM, Simo Sorce wrote: > On Fri, 2015-02-20 at 15:15 +0100, Martin Kosek wrote: >> All FreeIPA original code should be licensed to GPL v3+ license, >> update the respective files: >> >> - daemons/ipa-slapi-plugins/ipa-dns/ipa_dns.c >> >> Remove GPL v2.0 license files from LDIFs or template to keep >> consistency. >> >> CCing Simo for review. >> > > ACK > Pushed to: master: ffb9a09a0d63f7edae2b647b5c1d503d1d4d7a6e ipa-4-1: 4ddcca6435ad685582293b1bac588ea0615e94e4 Martin From sgallagh at redhat.com Fri Feb 20 15:41:07 2015 From: sgallagh at redhat.com (Stephen Gallagher) Date: Fri, 20 Feb 2015 10:41:07 -0500 Subject: [Freeipa-devel] [PATCH] Fix license exception In-Reply-To: <1424442879.5560.33.camel@willson.usersys.redhat.com> References: <1424442879.5560.33.camel@willson.usersys.redhat.com> Message-ID: <1424446867.5394.6.camel@redhat.com> On Fri, 2015-02-20 at 09:34 -0500, Simo Sorce wrote: > During internal conversations it occurred to me we link to OpenSSL > but never provided the proper exception for downstreams. > > Attached patch fixes the problem. > > Simo. > +this exception statement from your version.i If you delete the exception Small typo there "version.i" -------------- next part -------------- A non-text attachment was scrubbed... Name: signature.asc Type: application/pgp-signature Size: 181 bytes Desc: This is a digitally signed message part URL: From simo at redhat.com Fri Feb 20 19:55:11 2015 From: simo at redhat.com (Simo Sorce) Date: Fri, 20 Feb 2015 14:55:11 -0500 Subject: [Freeipa-devel] [PATCH] Fix license exception In-Reply-To: <1424446867.5394.6.camel@redhat.com> References: <1424442879.5560.33.camel@willson.usersys.redhat.com> <1424446867.5394.6.camel@redhat.com> Message-ID: <1424462111.5560.60.camel@willson.usersys.redhat.com> On Fri, 2015-02-20 at 10:41 -0500, Stephen Gallagher wrote: > On Fri, 2015-02-20 at 09:34 -0500, Simo Sorce wrote: > > During internal conversations it occurred to me we link to OpenSSL > > but never provided the proper exception for downstreams. > > > > Attached patch fixes the problem. > > > > Simo. > > > > > +this exception statement from your version.i If you delete the > exception > > > Small typo there "version.i" Fixed typo. Simo. -- Simo Sorce * Red Hat, Inc * New York -------------- next part -------------- A non-text attachment was scrubbed... Name: freeipa-simo-520-2-Add-a-clear-OpenSSL-exception.patch Type: text/x-patch Size: 2837 bytes Desc: not available URL: From tbabej at redhat.com Mon Feb 23 11:02:35 2015 From: tbabej at redhat.com (Tomas Babej) Date: Mon, 23 Feb 2015 12:02:35 +0100 Subject: [Freeipa-devel] [PATCH 0304] specfile: Add pki-kra to build requirements Message-ID: <54EB08CB.8050906@redhat.com> Hi, Fixes the python lint errors at build time. Tomas -------------- next part -------------- A non-text attachment was scrubbed... Name: freeipa-tbabej-0304-specfile-Add-pki-kra-to-build-requirements.patch Type: text/x-patch Size: 807 bytes Desc: not available URL: From tbabej at redhat.com Mon Feb 23 11:26:16 2015 From: tbabej at redhat.com (Tomas Babej) Date: Mon, 23 Feb 2015 12:26:16 +0100 Subject: [Freeipa-devel] [PATCH 0304] specfile: Add pki-kra to build requirements In-Reply-To: <54EB08CB.8050906@redhat.com> References: <54EB08CB.8050906@redhat.com> Message-ID: <54EB0E58.8070607@redhat.com> Discard, does not fix the problem. Still investigating. On 02/23/2015 12:02 PM, Tomas Babej wrote: > Hi, > > Fixes the python lint errors at build time. > > Tomas > > > _______________________________________________ > Freeipa-devel mailing list > Freeipa-devel at redhat.com > https://www.redhat.com/mailman/listinfo/freeipa-devel -------------- next part -------------- An HTML attachment was scrubbed... URL: From npmccallum at redhat.com Mon Feb 23 15:21:17 2015 From: npmccallum at redhat.com (Nathaniel McCallum) Date: Mon, 23 Feb 2015 10:21:17 -0500 Subject: [Freeipa-devel] [PATCH] Fix license exception In-Reply-To: <1424462111.5560.60.camel@willson.usersys.redhat.com> References: <1424442879.5560.33.camel@willson.usersys.redhat.com> <1424446867.5394.6.camel@redhat.com> <1424462111.5560.60.camel@willson.usersys.redhat.com> Message-ID: <1424704877.2604.34.camel@redhat.com> On Fri, 2015-02-20 at 14:55 -0500, Simo Sorce wrote: > On Fri, 2015-02-20 at 10:41 -0500, Stephen Gallagher wrote: > > On Fri, 2015-02-20 at 09:34 -0500, Simo Sorce wrote: > > > During internal conversations it occurred to me we link to > > > OpenSSL but never provided the proper exception for downstreams. > > > > > > Attached patch fixes the problem. > > > > > > Simo. > > > > > > > > > +this exception statement from your version.i If you delete the > > exception > > > > > > Small typo there "version.i" > > Fixed typo. ACK From npmccallum at redhat.com Mon Feb 23 15:22:03 2015 From: npmccallum at redhat.com (Nathaniel McCallum) Date: Mon, 23 Feb 2015 10:22:03 -0500 Subject: [Freeipa-devel] [PATCH} Remove unneded imports In-Reply-To: <1424442921.5560.34.camel@willson.usersys.redhat.com> References: <1424442921.5560.34.camel@willson.usersys.redhat.com> Message-ID: <1424704923.2604.35.camel@redhat.com> On Fri, 2015-02-20 at 09:35 -0500, Simo Sorce wrote: > We do not use openssl/des.h anymore, stop checking and importing it. ACK From mkosek at redhat.com Mon Feb 23 15:26:50 2015 From: mkosek at redhat.com (Martin Kosek) Date: Mon, 23 Feb 2015 16:26:50 +0100 Subject: [Freeipa-devel] [PATCH] Fix license exception In-Reply-To: <1424704877.2604.34.camel@redhat.com> References: <1424442879.5560.33.camel@willson.usersys.redhat.com> <1424446867.5394.6.camel@redhat.com> <1424462111.5560.60.camel@willson.usersys.redhat.com> <1424704877.2604.34.camel@redhat.com> Message-ID: <54EB46BA.8050305@redhat.com> On 02/23/2015 04:21 PM, Nathaniel McCallum wrote: > On Fri, 2015-02-20 at 14:55 -0500, Simo Sorce wrote: >> On Fri, 2015-02-20 at 10:41 -0500, Stephen Gallagher wrote: >>> On Fri, 2015-02-20 at 09:34 -0500, Simo Sorce wrote: >>>> During internal conversations it occurred to me we link to >>>> OpenSSL but never provided the proper exception for downstreams. >>>> >>>> Attached patch fixes the problem. >>>> >>>> Simo. >>>> >>> >>> >>> +this exception statement from your version.i If you delete the >>> exception >>> >>> >>> Small typo there "version.i" >> >> Fixed typo. > > ACK Pushed to: master: d762f61d25508c1856c0fa7dc0ea1e032671542b ipa-4-1: ecbef04692dd3833a985b96d8d849a651c9b3055 Martin From mkosek at redhat.com Mon Feb 23 15:27:55 2015 From: mkosek at redhat.com (Martin Kosek) Date: Mon, 23 Feb 2015 16:27:55 +0100 Subject: [Freeipa-devel] [PATCH} Remove unneded imports In-Reply-To: <1424704923.2604.35.camel@redhat.com> References: <1424442921.5560.34.camel@willson.usersys.redhat.com> <1424704923.2604.35.camel@redhat.com> Message-ID: <54EB46FB.9060701@redhat.com> On 02/23/2015 04:22 PM, Nathaniel McCallum wrote: > On Fri, 2015-02-20 at 09:35 -0500, Simo Sorce wrote: >> We do not use openssl/des.h anymore, stop checking and importing it. > > ACK Pushed to: master: 8b199b813d8c9e59b514311a0c1fc16eb935ecb9 ipa-4-1: 840903c4970f934a8cab412ca203cb338ecac6ae Martin From abokovoy at redhat.com Mon Feb 23 16:02:53 2015 From: abokovoy at redhat.com (Alexander Bokovoy) Date: Mon, 23 Feb 2015 18:02:53 +0200 Subject: [Freeipa-devel] One-way trust design Message-ID: <20150223160253.GX25455@redhat.com> Hi! I've added a design page for one-way trust to www.freeipa.org/page/V4/One-way_trust Below is the page content for easy discussion: ======================================================================== {{Feature|version=4.2.0|ticket=4546|author=Ab}} = Overview = Active Directory implementation of a trust between domains and forests uses credentials of a trust domain object (TDO) to communicate across the trust boundary. This is made possible on AD side because whole domain controller implementation is seen as a monolith that doesn't pass around the credentials for the trust domain object. This is purely implementation detail though important one. In early stages of a trust feature development FreeIPA also used trust domain object to directly authenticate against Active Directory services. However, as IPA is a combination of several loosely coupled services, access to the trust domain object is highly guarded to prevent unwanted elevation of privileges across the trust boundary. If FreeIPA was to use TDO's credentials everywhere, it would mean most of trust-related functionality would be limited to IPA admins or TDO object in LDAP would have to be more accessible. Given that TDO credentials can be used to compromise access to our domain, it is not advisable to give a wider access to them. As a side-effect of reducing exposure of TDO credentials, FreeIPA lost ability to establish and use one-way trust to Active Directory. The purpose of this feature is to regain the one-way trust support, yet without giving an elevated access to TDO credentials. = Use cases = A primary use case is the following one: * One-way trust to Active Directory where FreeIPA realm trusts Active * Directory forest using cross-forest trust feature of AD but the AD * forest does not trust FreeIPA realm. Users from AD forest can access * resources in FreeIPA realm. No other use cases exist at the moment. = Design = The one-way feature relies on an implementation of FreeIPA trust to AD feature as released in FreeIPA v3.3. The difference between FreeIPA v3.3 and v3.0 is in the way how credentials to access information from a trusted forest are used. == FreeIPA v3.0 and v3.3 == In FreeIPA v3.0 each IPA master initialized with ipa-adtrust-install command was running Samba suite: smbd and winbindd daemons were used to provide both capabilities to resolve AD users from trusted forests, to manage trust forest topology, and to respond on NETLOGON interfaces as Active Directory Domain Controllers expect to complete the sequence of establishing trust relationships. The rest of clients in FreeIPA were connecting to IPA masters through SSSD by means of an extended LDAP control to resolve AD users and groups. FreeIPA LDAP server's plugin which implemented the extended LDAP control was, in turn, talking to winbindd daemon to complete the resolution of AD users and groups. Additionally, in early FreeIPA v3.0 versions a management framework (both CLI and web UI) was using credentials of TDO to directly resolve AD users and groups against Active Directory Domain Controllers. The consequence of this was that only IPA admins were able to map users and groups from trusted Active Directory forests to local groups. The trust by AD DCs means that FreeIPA framework can utilize existing Kerberos service ticket it has (HTTP/ipa.master at IPA.REALM) to authenticate to AD LDAP servers. AD LDAP servers allow access to its information only to authenticated clients but the clients can provide any proof of authenticity allowed by Active Directory. In the case of cross-forest trust in AD, a properly issued Kerberos ticket from a trusted forest is enough. In order to issue such ticket, FreeIPA KDC does generate privilege attribute certificate data (MS-PAC) as required by Microsoft's specification [https://msdn.microsoft.com/en-us/library/cc237917.aspx [MS-PAC]]. In order to limit which Kerberos services are allowed to authenticate against services in a trusting AD forest, only HTTP/ipa.master at IPA.REALM and host/ipa.master at IPA.REALM are given the MS-PAC in their TGT tickets where the services are presented as members of a virtual Domain Controllers group in FreeIPA domain. FreeIPA v3.0 management framework was switched to use HTTP/ipa.master at IPA.REALM Kerberos ticket with attached MS-PAC information to directly resolve AD users and groups. In FreeIPA v3.3 each IPA master initialized with ipa-adtrust-install command still runs Samba suite: smbd and winbindd daemons. They are used to respond on NETLOGON interfaces as Active Directory Domain Controllers expect them, and to manage trust forest topology. However, users and groups from trusted Active Directory forests are now resolved by SSSD running on the IPA masters. SSSD has gained a so-called "IPA server mode" which means the requests to resolve AD users and groups will go directly to Active Directory Domain Controllers. The rest of clients in FreeIPA are connecting to IPA masters through SSSD by means of the same extended LDAP control to resolve AD users and groups. However, FreeIPA LDAP server's plugin which implements the extended LDAP control now talks to SSSD on the IPA master instead of winbindd daemon to complete the resolution of AD users and groups. Finally, a management framework also relies on the SSSD on IPA master to resolve users and groups from trusted Active Directory forests. As a consequence of that, IPA admins can delegate rights to map AD users and groups without giving access to TDO credentials anymore. In its own turn, SSSD 1.11 as used by FreeIPA v3.3, relies on the fact that Active Directory Domain Controllers do trust FreeIPA realm and uses host/ipa.master at IPA.REALM Kerberos ticket to directly resolve AD users and groups. Final consequence of v3.0 and v3.3 trust feature designs was the fact that if IPA clients needed to resolve AD users and groups, they needed to be enrolled to an IPA master which has been initialized with ipa-adtrust-install. As a result, in major deployments all of IPA masters had to run smbd and winbindd processes. While this is an improvement over requirement to run winbindd to every single client as with some other solutions, it is still too fragile for production. This shortcoming is addressed by [[V4/Trust agents]] feature design. == New design == In order to support one-way trust to Active Directory, we need to switch SSSD in IPA master mode to use TDO credentials when resolving AD users and groups. This is a high level description of the design, and majority of work to allow the switch will be done by SSSD team. Corresponding ticket tracker on SSSD side is [https://fedorahosted.org/sssd/ticket/2579 ticket 2579], the text below is an overview of the design. On each IPA master SSSD runs in "IPA master mode". This mode means that in case of existing trust to AD forest, SSSD will directly resolve AD users and groups against Active Directory Domain Controllers. To perform user/group resolution, SSSD needs to authenticate against AD LDAP servers and it does so using Kerberos authentication based on a host/ipa.master at IPA.REALM service ticket. The ticket towards AD LDAP services is issued by FreeIPA KDC with the help of cross-realm trust credentials. For one-way trust SSSD cannot use this approach because Active Directory Domain Controllers do not trust FreeIPA realm and, therefore, no cross-realm trust credentials exist in AD for FreeIPA realm. However, SSSD can use TDO object which always exists in AD for the trusting domain (cross-forest trust is done by forest root domains' trust). This means the ticket SSSD would need to request belongs to a different realm (AD forest root realm) rather than to FreeIPA realm. As FreeIPA supports multiple trusts to separate Active Directory forests, a support for multiple separate tickets is required. SSSD will need to gain ability to use different credentials caches to store TDO tickets and use different keytabs with TDO credentials to obtain the ticket from an Active Directory Domain Controllers. In order to separate privilege access, FreeIPA masters have to provide keytabs for SSSD running on IPA masters, one keytab per trusted AD forest, so that SSSD could request the keys when required. Additionally, FreeIPA management framework will need to change its defaults from producing a two-way trust to a one-way trust. Two-way trust will be added back when support for Global Catalog service will be added so that Active Directory resources could be properly accessed and access to them discretionally granted to FreeIPA users and groups. = Implementation = Following changes will need to be done on FreeIPA side in order to support one-way trust: # Switch two-way trust creation in ipaserver/dcerpc.py to one-way by # default. ## The code needs to be changed to allow specifying either one- or two-way trust and should manipulate trust_direction property (by setting lsa.LSA_TRUST_DIRECTION_OUTBOUND or a combination of lsa.LSA_TRUST_DIRECTION_INBOUND and lsa.LSA_TRUST_DIRECTION_OUTBOUND) in TrustDomainInstance.establish_trust() method. ## One-way trust can be created with full AD administrator credentials too, while shared secret method will rely on the AD administrator creating the remote part of it in AD. # Make sure ipalib/plugins/trust.py passes properly a flag to enable # two-way trust. # Change ipasam to create additional principal named IPA$@AD.REALM form # when creating TDO object for AD.REALM forest trust. This principal has # to be disabled so that KDC cannot use it to issue tickets. = Feature management = == CLI == At first stage nothing needs to be changed. The created trust will become one-way only, no additional options will be needed. At second stage we can add an option to create two-way trust. == Web UI == No changes in Web UI are required if we wouldn't expose two-way trust option. == Replication == Trust-related information is in the replicated subtree already. == Upgrades == No changes on upgrade are needed. When SSSD will gain ability to handle one-way trusts, it will use ipa-getkeytab to obtain the keytabs. -- / Alexander Bokovoy -------------- next part -------------- A non-text attachment was scrubbed... Name: not available Type: application/pgp-signature Size: 473 bytes Desc: not available URL: From tbabej at redhat.com Mon Feb 23 16:04:05 2015 From: tbabej at redhat.com (Tomas Babej) Date: Mon, 23 Feb 2015 17:04:05 +0100 Subject: [Freeipa-devel] [PATCH 0305] idviews: Use case-insensitive detection of Default Trust View Message-ID: <54EB4F75.7040201@redhat.com> Hi, The usage of lowercased varsion of 'Default Trust View' can no longer be used to bypass the validation. https://fedorahosted.org/freeipa/ticket/4915 Tomas -------------- next part -------------- A non-text attachment was scrubbed... Name: freeipa-tbabej-0305-idviews-Use-case-insensitive-detection-of-Default-Tr.patch Type: text/x-patch Size: 2380 bytes Desc: not available URL: From abokovoy at redhat.com Mon Feb 23 16:08:02 2015 From: abokovoy at redhat.com (Alexander Bokovoy) Date: Mon, 23 Feb 2015 18:08:02 +0200 Subject: [Freeipa-devel] [PATCH 0305] idviews: Use case-insensitive detection of Default Trust View In-Reply-To: <54EB4F75.7040201@redhat.com> References: <54EB4F75.7040201@redhat.com> Message-ID: <20150223160802.GY25455@redhat.com> On Mon, 23 Feb 2015, Tomas Babej wrote: >Hi, > >The usage of lowercased varsion of 'Default Trust View' can no >longer be used to bypass the validation. > >https://fedorahosted.org/freeipa/ticket/4915 I'm fine with the direction of the fix but please make a constant string and compare with it. > >Tomas >>From 549bb9a93c07ecf7ffdb913c094700129828017d Mon Sep 17 00:00:00 2001 >From: Tomas Babej >Date: Mon, 23 Feb 2015 16:16:01 +0100 >Subject: [PATCH] idviews: Use case-insensitive detection of Default Trust View > >The usage of lowercased varsion of 'Default Trust View' can no >longer be used to bypass the validation. > >https://fedorahosted.org/freeipa/ticket/4915 >--- > ipalib/plugins/idviews.py | 14 ++++++++------ > 1 file changed, 8 insertions(+), 6 deletions(-) > >diff --git a/ipalib/plugins/idviews.py b/ipalib/plugins/idviews.py >index df403b1193fe18dfadf437a18a3e0b6ffb7575b4..f59dd06e8afc1da7efce321efaa94523ed8a3e53 100644 >--- a/ipalib/plugins/idviews.py >+++ b/ipalib/plugins/idviews.py >@@ -106,8 +106,9 @@ class idview_del(LDAPDelete): > msg_summary = _('Deleted ID View "%(value)s"') > > def pre_callback(self, ldap, dn, *keys, **options): >- if "Default Trust View" in keys: >- raise protected_default_trust_view_error >+ for key in keys: >+ if key.lower() == "default trust view": >+ raise protected_default_trust_view_error > > return dn > >@@ -118,8 +119,9 @@ class idview_mod(LDAPUpdate): > msg_summary = _('Modified an ID View "%(value)s"') > > def pre_callback(self, ldap, dn, entry_attrs, attrs_list, *keys, **options): >- if "Default Trust View" in keys: >- raise protected_default_trust_view_error >+ for key in keys: >+ if key.lower() == "default trust view": >+ raise protected_default_trust_view_error > > return dn > >@@ -240,7 +242,7 @@ class baseidview_apply(LDAPQuery): > # the ipaAssignedIDView to None > view_dn = None > >- if view == 'Default Trust View': >+ if view.lower() == 'default trust view': > raise errors.ValidationError( > name=_('ID View'), > error=_('Default Trust View cannot be applied on hosts') >@@ -584,7 +586,7 @@ class baseidoverride(LDAPObject): > # Check if parent object is Default Trust View, if so, prohibit > # adding overrides for IPA objects > >- if dn[1].value == 'Default Trust View': >+ if dn[1].value.lower() == 'default trust view': > if dn[0].value.startswith(IPA_ANCHOR_PREFIX): > raise errors.ValidationError( > name=_('ID View'), >-- >2.1.0 > >_______________________________________________ >Freeipa-devel mailing list >Freeipa-devel at redhat.com >https://www.redhat.com/mailman/listinfo/freeipa-devel -- / Alexander Bokovoy -------------- next part -------------- A non-text attachment was scrubbed... Name: not available Type: application/pgp-signature Size: 473 bytes Desc: not available URL: From tbabej at redhat.com Mon Feb 23 16:14:21 2015 From: tbabej at redhat.com (Tomas Babej) Date: Mon, 23 Feb 2015 17:14:21 +0100 Subject: [Freeipa-devel] [PATCH 0305] idviews: Use case-insensitive detection of Default Trust View In-Reply-To: <20150223160802.GY25455@redhat.com> References: <54EB4F75.7040201@redhat.com> <20150223160802.GY25455@redhat.com> Message-ID: <54EB51DD.4010704@redhat.com> On 02/23/2015 05:08 PM, Alexander Bokovoy wrote: > On Mon, 23 Feb 2015, Tomas Babej wrote: >> Hi, >> >> The usage of lowercased varsion of 'Default Trust View' can no >> longer be used to bypass the validation. >> >> https://fedorahosted.org/freeipa/ticket/4915 > I'm fine with the direction of the fix but please make a constant string > and compare with it. > Sure thing, you are absolutely right. Updated patch attached. > >> >> Tomas > >>> From 549bb9a93c07ecf7ffdb913c094700129828017d Mon Sep 17 00:00:00 2001 >> From: Tomas Babej >> Date: Mon, 23 Feb 2015 16:16:01 +0100 >> Subject: [PATCH] idviews: Use case-insensitive detection of Default >> Trust View >> >> The usage of lowercased varsion of 'Default Trust View' can no >> longer be used to bypass the validation. >> >> https://fedorahosted.org/freeipa/ticket/4915 >> --- >> ipalib/plugins/idviews.py | 14 ++++++++------ >> 1 file changed, 8 insertions(+), 6 deletions(-) >> >> diff --git a/ipalib/plugins/idviews.py b/ipalib/plugins/idviews.py >> index >> df403b1193fe18dfadf437a18a3e0b6ffb7575b4..f59dd06e8afc1da7efce321efaa94523ed8a3e53 >> 100644 >> --- a/ipalib/plugins/idviews.py >> +++ b/ipalib/plugins/idviews.py >> @@ -106,8 +106,9 @@ class idview_del(LDAPDelete): >> msg_summary = _('Deleted ID View "%(value)s"') >> >> def pre_callback(self, ldap, dn, *keys, **options): >> - if "Default Trust View" in keys: >> - raise protected_default_trust_view_error >> + for key in keys: >> + if key.lower() == "default trust view": >> + raise protected_default_trust_view_error >> >> return dn >> >> @@ -118,8 +119,9 @@ class idview_mod(LDAPUpdate): >> msg_summary = _('Modified an ID View "%(value)s"') >> >> def pre_callback(self, ldap, dn, entry_attrs, attrs_list, *keys, >> **options): >> - if "Default Trust View" in keys: >> - raise protected_default_trust_view_error >> + for key in keys: >> + if key.lower() == "default trust view": >> + raise protected_default_trust_view_error >> >> return dn >> >> @@ -240,7 +242,7 @@ class baseidview_apply(LDAPQuery): >> # the ipaAssignedIDView to None >> view_dn = None >> >> - if view == 'Default Trust View': >> + if view.lower() == 'default trust view': >> raise errors.ValidationError( >> name=_('ID View'), >> error=_('Default Trust View cannot be applied on hosts') >> @@ -584,7 +586,7 @@ class baseidoverride(LDAPObject): >> # Check if parent object is Default Trust View, if so, prohibit >> # adding overrides for IPA objects >> >> - if dn[1].value == 'Default Trust View': >> + if dn[1].value.lower() == 'default trust view': >> if dn[0].value.startswith(IPA_ANCHOR_PREFIX): >> raise errors.ValidationError( >> name=_('ID View'), >> -- >> 2.1.0 >> > >> _______________________________________________ >> Freeipa-devel mailing list >> Freeipa-devel at redhat.com >> https://www.redhat.com/mailman/listinfo/freeipa-devel > > -------------- next part -------------- A non-text attachment was scrubbed... Name: freeipa-tbabej-0305-2-idviews-Use-case-insensitive-detection-of-Default-Tr.patch Type: text/x-patch Size: 2603 bytes Desc: not available URL: From abokovoy at redhat.com Mon Feb 23 16:26:58 2015 From: abokovoy at redhat.com (Alexander Bokovoy) Date: Mon, 23 Feb 2015 18:26:58 +0200 Subject: [Freeipa-devel] [PATCH 0305] idviews: Use case-insensitive detection of Default Trust View In-Reply-To: <54EB51DD.4010704@redhat.com> References: <54EB4F75.7040201@redhat.com> <20150223160802.GY25455@redhat.com> <54EB51DD.4010704@redhat.com> Message-ID: <20150223162658.GZ25455@redhat.com> On Mon, 23 Feb 2015, Tomas Babej wrote: > > On 02/23/2015 05:08 PM, Alexander Bokovoy wrote: >> On Mon, 23 Feb 2015, Tomas Babej wrote: >>> Hi, >>> >>> The usage of lowercased varsion of 'Default Trust View' can no >>> longer be used to bypass the validation. >>> >>> https://fedorahosted.org/freeipa/ticket/4915 >> I'm fine with the direction of the fix but please make a constant string >> and compare with it. >> > Sure thing, you are absolutely right. > > Updated patch attached. ACK. -- / Alexander Bokovoy -------------- next part -------------- A non-text attachment was scrubbed... Name: not available Type: application/pgp-signature Size: 473 bytes Desc: not available URL: From tbabej at redhat.com Mon Feb 23 16:51:54 2015 From: tbabej at redhat.com (Tomas Babej) Date: Mon, 23 Feb 2015 17:51:54 +0100 Subject: [Freeipa-devel] [PATCH 0305] idviews: Use case-insensitive detection of Default Trust View In-Reply-To: <20150223162658.GZ25455@redhat.com> References: <54EB4F75.7040201@redhat.com> <20150223160802.GY25455@redhat.com> <54EB51DD.4010704@redhat.com> <20150223162658.GZ25455@redhat.com> Message-ID: <54EB5AAA.7010501@redhat.com> On 02/23/2015 05:26 PM, Alexander Bokovoy wrote: > On Mon, 23 Feb 2015, Tomas Babej wrote: >> >> On 02/23/2015 05:08 PM, Alexander Bokovoy wrote: >>> On Mon, 23 Feb 2015, Tomas Babej wrote: >>>> Hi, >>>> >>>> The usage of lowercased varsion of 'Default Trust View' can no >>>> longer be used to bypass the validation. >>>> >>>> https://fedorahosted.org/freeipa/ticket/4915 >>> I'm fine with the direction of the fix but please make a constant >>> string >>> and compare with it. >>> >> Sure thing, you are absolutely right. >> >> Updated patch attached. > ACK. > Pushed to: ipa-4-1: 96624f21895cbf66e743a8fa7871c69fcdadab72 master: 93f3bb3ddd5b93a5eb731239d2c03c0a350367ca From edewata at redhat.com Tue Feb 24 03:09:09 2015 From: edewata at redhat.com (Endi Sukma Dewata) Date: Mon, 23 Feb 2015 21:09:09 -0600 Subject: [Freeipa-devel] [PATCH] Password vault In-Reply-To: <54E1AF55.3060409@redhat.com> References: <54E1AF55.3060409@redhat.com> Message-ID: <54EBEB55.6010306@redhat.com> On 2/16/2015 2:50 AM, Endi Sukma Dewata wrote: > Hi, > > Attached are the updated patches for the password vault, and some new > ones (please disregard previous patch submissions). Please give them a > try. Thanks. New patches attached replacing all previous vault patches. They include the new escrow functionality, changes to the asymmetric vault, and some cleanups. Thanks. -- Endi S. Dewata -------------- next part -------------- >From 4688fa7b47f06ca21025a4e98e37cb0c042da74b Mon Sep 17 00:00:00 2001 From: "Endi S. Dewata" Date: Tue, 21 Oct 2014 10:57:08 -0400 Subject: [PATCH] Added initial vault implementation. This patch adds the initial vault implementation which provides the plugins and the basic commands to access vaults and vault containers. Another command is also added to retrieve the vault transport certificate to transmit vault data securely into KRA. The LDAPDelete has been refactored to allow overriding the subtree deletion behavior. Some test cases have been added as well. https://fedorahosted.org/freeipa/ticket/3872 --- API.txt | 175 +++++ install/share/60basev3.ldif | 2 + install/updates/40-vault.update | 23 + install/updates/Makefile.am | 1 + ipa-client/man/default.conf.5 | 1 + ipalib/constants.py | 1 + ipalib/plugins/baseldap.py | 78 ++- ipalib/plugins/user.py | 9 + ipalib/plugins/vault.py | 763 +++++++++++++++++++++ ipalib/plugins/vaultcontainer.py | 361 ++++++++++ ipatests/test_xmlrpc/test_vault_plugin.py | 241 +++++++ ipatests/test_xmlrpc/test_vaultcontainer_plugin.py | 420 ++++++++++++ 12 files changed, 2037 insertions(+), 38 deletions(-) create mode 100644 install/updates/40-vault.update create mode 100644 ipalib/plugins/vault.py create mode 100644 ipalib/plugins/vaultcontainer.py create mode 100644 ipatests/test_xmlrpc/test_vault_plugin.py create mode 100644 ipatests/test_xmlrpc/test_vaultcontainer_plugin.py diff --git a/API.txt b/API.txt index 2a47191424f194993c3943ece25a655fed6ccf86..a0a347803db687717b90c294de8c56a4478bd339 100644 --- a/API.txt +++ b/API.txt @@ -4517,6 +4517,181 @@ option: Str('version?', exclude='webui') output: Output('result', , None) output: Output('summary', (, ), None) output: PrimaryKey('value', None, None) +command: vault_add +args: 1,11,3 +arg: Str('cn', attribute=True, cli_name='vault_name', maxlength=255, multivalue=False, pattern='^[a-zA-Z0-9_.-]+$', primary_key=True, required=True) +option: Str('addattr*', cli_name='addattr', exclude='webui') +option: Flag('all', autofill=True, cli_name='all', default=False, exclude='webui') +option: Str('container', attribute=False, cli_name='container', multivalue=False, pattern='^[a-zA-Z0-9_.-/]+$', required=False) +option: Bytes('data?', cli_name='data') +option: Str('description', attribute=True, cli_name='desc', multivalue=False, required=False) +option: Str('in?', cli_name='in') +option: Flag('raw', autofill=True, cli_name='raw', default=False, exclude='webui') +option: Str('setattr*', cli_name='setattr', exclude='webui') +option: Str('text?', cli_name='text') +option: Str('vault_id', attribute=False, cli_name='vault_id', multivalue=False, required=False) +option: Str('version?', exclude='webui') +output: Entry('result', , Gettext('A dictionary representing an LDAP entry', domain='ipa', localedir=None)) +output: Output('summary', (, ), None) +output: PrimaryKey('value', None, None) +command: vault_archive +args: 1,11,3 +arg: Str('cn', attribute=True, cli_name='vault_name', maxlength=255, multivalue=False, pattern='^[a-zA-Z0-9_.-]+$', primary_key=True, query=True, required=True) +option: Flag('all', autofill=True, cli_name='all', default=False, exclude='webui') +option: Str('container?', cli_name='container') +option: Bytes('data?', cli_name='data') +option: Str('in?', cli_name='in') +option: Str('nonce?', cli_name='nonce') +option: Flag('raw', autofill=True, cli_name='raw', default=False, exclude='webui') +option: Flag('rights', autofill=True, default=False) +option: Str('session_key?', cli_name='session_key') +option: Str('text?', cli_name='text') +option: Str('vault_data?', cli_name='vault_data') +option: Str('version?', exclude='webui') +output: Entry('result', , Gettext('A dictionary representing an LDAP entry', domain='ipa', localedir=None)) +output: Output('summary', (, ), None) +output: PrimaryKey('value', None, None) +command: vault_del +args: 1,3,3 +arg: Str('cn', attribute=True, cli_name='vault_name', maxlength=255, multivalue=True, pattern='^[a-zA-Z0-9_.-]+$', primary_key=True, query=True, required=True) +option: Str('container?', cli_name='container') +option: Flag('continue', autofill=True, cli_name='continue', default=False) +option: Str('version?', exclude='webui') +output: Output('result', , None) +output: Output('summary', (, ), None) +output: ListOfPrimaryKeys('value', None, None) +command: vault_find +args: 1,10,4 +arg: Str('criteria?', noextrawhitespace=False) +option: Flag('all', autofill=True, cli_name='all', default=False, exclude='webui') +option: Str('cn', attribute=True, autofill=False, cli_name='vault_name', maxlength=255, multivalue=False, pattern='^[a-zA-Z0-9_.-]+$', primary_key=True, query=True, required=False) +option: Str('container', attribute=False, autofill=False, cli_name='container', multivalue=False, pattern='^[a-zA-Z0-9_.-/]+$', query=True, required=False) +option: Str('description', attribute=True, autofill=False, cli_name='desc', multivalue=False, query=True, required=False) +option: Flag('pkey_only?', autofill=True, default=False) +option: Flag('raw', autofill=True, cli_name='raw', default=False, exclude='webui') +option: Int('sizelimit?', autofill=False, minvalue=0) +option: Int('timelimit?', autofill=False, minvalue=0) +option: Str('vault_id', attribute=False, autofill=False, cli_name='vault_id', multivalue=False, query=True, required=False) +option: Str('version?', exclude='webui') +output: Output('count', , None) +output: ListOfEntries('result', (, ), Gettext('A list of LDAP entries', domain='ipa', localedir=None)) +output: Output('summary', (, ), None) +output: Output('truncated', , None) +command: vault_mod +args: 1,10,3 +arg: Str('cn', attribute=True, cli_name='vault_name', maxlength=255, multivalue=False, pattern='^[a-zA-Z0-9_.-]+$', primary_key=True, query=True, required=True) +option: Str('addattr*', cli_name='addattr', exclude='webui') +option: Flag('all', autofill=True, cli_name='all', default=False, exclude='webui') +option: Str('container', attribute=False, autofill=False, cli_name='container', multivalue=False, pattern='^[a-zA-Z0-9_.-/]+$', required=False) +option: Str('delattr*', cli_name='delattr', exclude='webui') +option: Str('description', attribute=True, autofill=False, cli_name='desc', multivalue=False, required=False) +option: Flag('raw', autofill=True, cli_name='raw', default=False, exclude='webui') +option: Flag('rights', autofill=True, default=False) +option: Str('setattr*', cli_name='setattr', exclude='webui') +option: Str('vault_id', attribute=False, autofill=False, cli_name='vault_id', multivalue=False, required=False) +option: Str('version?', exclude='webui') +output: Entry('result', , Gettext('A dictionary representing an LDAP entry', domain='ipa', localedir=None)) +output: Output('summary', (, ), None) +output: PrimaryKey('value', None, None) +command: vault_retrieve +args: 1,9,3 +arg: Str('cn', attribute=True, cli_name='vault_name', maxlength=255, multivalue=False, pattern='^[a-zA-Z0-9_.-]+$', primary_key=True, query=True, required=True) +option: Flag('all', autofill=True, cli_name='all', default=False, exclude='webui') +option: Str('container?', cli_name='container') +option: Str('out?', cli_name='out') +option: Flag('raw', autofill=True, cli_name='raw', default=False, exclude='webui') +option: Flag('rights', autofill=True, default=False) +option: Str('session_key?', cli_name='session_key') +option: Flag('show_text?', autofill=True, default=False) +option: Flag('stdout?', autofill=True, default=False) +option: Str('version?', exclude='webui') +output: Entry('result', , Gettext('A dictionary representing an LDAP entry', domain='ipa', localedir=None)) +output: Output('summary', (, ), None) +output: PrimaryKey('value', None, None) +command: vault_show +args: 1,5,3 +arg: Str('cn', attribute=True, cli_name='vault_name', maxlength=255, multivalue=False, pattern='^[a-zA-Z0-9_.-]+$', primary_key=True, query=True, required=True) +option: Flag('all', autofill=True, cli_name='all', default=False, exclude='webui') +option: Str('container?', cli_name='container') +option: Flag('raw', autofill=True, cli_name='raw', default=False, exclude='webui') +option: Flag('rights', autofill=True, default=False) +option: Str('version?', exclude='webui') +output: Entry('result', , Gettext('A dictionary representing an LDAP entry', domain='ipa', localedir=None)) +output: Output('summary', (, ), None) +output: PrimaryKey('value', None, None) +command: vault_transport_cert +args: 0,2,1 +option: Str('out?', cli_name='out') +option: Str('version?', exclude='webui') +output: Output('result', None, None) +command: vaultcontainer_add +args: 1,8,3 +arg: Str('cn', attribute=True, cli_name='container_name', maxlength=255, multivalue=False, pattern='^[a-zA-Z0-9_.-]+$', primary_key=True, required=True) +option: Str('addattr*', cli_name='addattr', exclude='webui') +option: Flag('all', autofill=True, cli_name='all', default=False, exclude='webui') +option: Str('container_id', attribute=False, cli_name='container_id', multivalue=False, required=False) +option: Str('description', attribute=True, cli_name='desc', multivalue=False, required=False) +option: Str('parent', attribute=False, cli_name='parent', multivalue=False, pattern='^[a-zA-Z0-9_.-/]+$', required=False) +option: Flag('raw', autofill=True, cli_name='raw', default=False, exclude='webui') +option: Str('setattr*', cli_name='setattr', exclude='webui') +option: Str('version?', exclude='webui') +output: Entry('result', , Gettext('A dictionary representing an LDAP entry', domain='ipa', localedir=None)) +output: Output('summary', (, ), None) +output: PrimaryKey('value', None, None) +command: vaultcontainer_del +args: 1,4,3 +arg: Str('cn', attribute=True, cli_name='container_name', maxlength=255, multivalue=True, pattern='^[a-zA-Z0-9_.-]+$', primary_key=True, query=True, required=True) +option: Flag('continue', autofill=True, cli_name='continue', default=False) +option: Flag('force?', autofill=True, default=False) +option: Str('parent?', cli_name='parent') +option: Str('version?', exclude='webui') +output: Output('result', , None) +output: Output('summary', (, ), None) +output: ListOfPrimaryKeys('value', None, None) +command: vaultcontainer_find +args: 1,10,4 +arg: Str('criteria?', noextrawhitespace=False) +option: Flag('all', autofill=True, cli_name='all', default=False, exclude='webui') +option: Str('cn', attribute=True, autofill=False, cli_name='container_name', maxlength=255, multivalue=False, pattern='^[a-zA-Z0-9_.-]+$', primary_key=True, query=True, required=False) +option: Str('container_id', attribute=False, autofill=False, cli_name='container_id', multivalue=False, query=True, required=False) +option: Str('description', attribute=True, autofill=False, cli_name='desc', multivalue=False, query=True, required=False) +option: Str('parent', attribute=False, autofill=False, cli_name='parent', multivalue=False, pattern='^[a-zA-Z0-9_.-/]+$', query=True, required=False) +option: Flag('pkey_only?', autofill=True, default=False) +option: Flag('raw', autofill=True, cli_name='raw', default=False, exclude='webui') +option: Int('sizelimit?', autofill=False, minvalue=0) +option: Int('timelimit?', autofill=False, minvalue=0) +option: Str('version?', exclude='webui') +output: Output('count', , None) +output: ListOfEntries('result', (, ), Gettext('A list of LDAP entries', domain='ipa', localedir=None)) +output: Output('summary', (, ), None) +output: Output('truncated', , None) +command: vaultcontainer_mod +args: 1,10,3 +arg: Str('cn', attribute=True, cli_name='container_name', maxlength=255, multivalue=False, pattern='^[a-zA-Z0-9_.-]+$', primary_key=True, query=True, required=True) +option: Str('addattr*', cli_name='addattr', exclude='webui') +option: Flag('all', autofill=True, cli_name='all', default=False, exclude='webui') +option: Str('container_id', attribute=False, autofill=False, cli_name='container_id', multivalue=False, required=False) +option: Str('delattr*', cli_name='delattr', exclude='webui') +option: Str('description', attribute=True, autofill=False, cli_name='desc', multivalue=False, required=False) +option: Str('parent', attribute=False, autofill=False, cli_name='parent', multivalue=False, pattern='^[a-zA-Z0-9_.-/]+$', required=False) +option: Flag('raw', autofill=True, cli_name='raw', default=False, exclude='webui') +option: Flag('rights', autofill=True, default=False) +option: Str('setattr*', cli_name='setattr', exclude='webui') +option: Str('version?', exclude='webui') +output: Entry('result', , Gettext('A dictionary representing an LDAP entry', domain='ipa', localedir=None)) +output: Output('summary', (, ), None) +output: PrimaryKey('value', None, None) +command: vaultcontainer_show +args: 1,5,3 +arg: Str('cn', attribute=True, cli_name='container_name', maxlength=255, multivalue=False, pattern='^[a-zA-Z0-9_.-]+$', primary_key=True, query=True, required=True) +option: Flag('all', autofill=True, cli_name='all', default=False, exclude='webui') +option: Str('parent?', cli_name='parent') +option: Flag('raw', autofill=True, cli_name='raw', default=False, exclude='webui') +option: Flag('rights', autofill=True, default=False) +option: Str('version?', exclude='webui') +output: Entry('result', , Gettext('A dictionary representing an LDAP entry', domain='ipa', localedir=None)) +output: Output('summary', (, ), None) +output: PrimaryKey('value', None, None) capability: messages 2.52 capability: optional_uid_params 2.54 capability: permissions2 2.69 diff --git a/install/share/60basev3.ldif b/install/share/60basev3.ldif index 4efb1fe8ba8a91d3a8b920d39d217124066728c0..c04f8d5d096bc3d91aec3e2f1703f658d76d3779 100644 --- a/install/share/60basev3.ldif +++ b/install/share/60basev3.ldif @@ -77,3 +77,5 @@ objectClasses: (2.16.840.1.113730.3.8.12.24 NAME 'ipaPublicKeyObject' DESC 'Wrap objectClasses: (2.16.840.1.113730.3.8.12.25 NAME 'ipaPrivateKeyObject' DESC 'Wrapped private keys' SUP top AUXILIARY MUST ( ipaPrivateKey $ ipaWrappingKey $ ipaWrappingMech ) X-ORIGIN 'IPA v4.1' ) objectClasses: (2.16.840.1.113730.3.8.12.26 NAME 'ipaSecretKeyObject' DESC 'Wrapped secret keys' SUP top AUXILIARY MUST ( ipaSecretKey $ ipaWrappingKey $ ipaWrappingMech ) X-ORIGIN 'IPA v4.1' ) objectClasses: (2.16.840.1.113730.3.8.12.34 NAME 'ipaSecretKeyRefObject' DESC 'Indirect storage for encoded key material' SUP top AUXILIARY MUST ( ipaSecretKeyRef ) X-ORIGIN 'IPA v4.1' ) +objectClasses: (2.16.840.1.113730.3.8.18.1.1 NAME 'ipaVault' DESC 'IPA vault' SUP top STRUCTURAL MUST ( cn ) MAY ( description ) X-ORIGIN 'IPA v4.2' ) +objectClasses: (2.16.840.1.113730.3.8.18.1.2 NAME 'ipaVaultContainer' DESC 'IPA vault container' SUP top STRUCTURAL MUST ( cn ) MAY ( description ) X-ORIGIN 'IPA v4.2' ) diff --git a/install/updates/40-vault.update b/install/updates/40-vault.update new file mode 100644 index 0000000000000000000000000000000000000000..dac2f67112dc33f012c6d559285464fb7c944d1a --- /dev/null +++ b/install/updates/40-vault.update @@ -0,0 +1,23 @@ +dn: cn=vaults,$SUFFIX +default: objectClass: top +default: objectClass: ipaVaultContainer +default: cn: vaults +default: description: Root vault container + +dn: cn=services,cn=vaults,$SUFFIX +default: objectClass: top +default: objectClass: ipaVaultContainer +default: cn: services +default: description: Services vault container + +dn: cn=shared,cn=vaults,$SUFFIX +default: objectClass: top +default: objectClass: ipaVaultContainer +default: cn: shared +default: description: Shared vault container + +dn: cn=users,cn=vaults,$SUFFIX +default: objectClass: top +default: objectClass: ipaVaultContainer +default: cn: users +default: description: Users vault container diff --git a/install/updates/Makefile.am b/install/updates/Makefile.am index 40de5635621071d34b6475d51ca598ed41a8ba09..34bb0981c44a3fcc3242401873769f332b95988b 100644 --- a/install/updates/Makefile.am +++ b/install/updates/Makefile.am @@ -32,6 +32,7 @@ app_DATA = \ 40-dns.update \ 40-automember.update \ 40-otp.update \ + 40-vault.update \ 45-roles.update \ 50-7_bit_check.update \ 50-dogtag10-migration.update \ diff --git a/ipa-client/man/default.conf.5 b/ipa-client/man/default.conf.5 index dbc8a5b4647439de4de7c01152d098eb0561e236..0973f1a07179ad64daa326a02803cdc9ba1870aa 100644 --- a/ipa-client/man/default.conf.5 +++ b/ipa-client/man/default.conf.5 @@ -221,6 +221,7 @@ The following define the containers for the IPA server. Containers define where container_sudocmdgroup: cn=sudocmdgroups,cn=sudo container_sudorule: cn=sudorules,cn=sudo container_user: cn=users,cn=accounts + container_vault: cn=vaults container_virtual: cn=virtual operations,cn=etc .SH "FILES" diff --git a/ipalib/constants.py b/ipalib/constants.py index 50a2b1f7aa7f0d447bacfd005b102c7451e670ce..baaf9be8d0329e89cb92a03de302095fe7acb847 100644 --- a/ipalib/constants.py +++ b/ipalib/constants.py @@ -97,6 +97,7 @@ DEFAULT_CONFIG = ( ('container_hbacservice', DN(('cn', 'hbacservices'), ('cn', 'hbac'))), ('container_hbacservicegroup', DN(('cn', 'hbacservicegroups'), ('cn', 'hbac'))), ('container_dns', DN(('cn', 'dns'))), + ('container_vault', DN(('cn', 'vaults'))), ('container_virtual', DN(('cn', 'virtual operations'), ('cn', 'etc'))), ('container_sudorule', DN(('cn', 'sudorules'), ('cn', 'sudo'))), ('container_sudocmd', DN(('cn', 'sudocmds'), ('cn', 'sudo'))), diff --git a/ipalib/plugins/baseldap.py b/ipalib/plugins/baseldap.py index 4b1c701924d57919538e0c428ea181c2e898505e..d693709ac1ba7ddb3c559199c199039b6f8bd9ac 100644 --- a/ipalib/plugins/baseldap.py +++ b/ipalib/plugins/baseldap.py @@ -1498,48 +1498,50 @@ class LDAPDelete(LDAPMultiQuery): has_output_params = global_output_params - def execute(self, *keys, **options): + def delete_subtree(self, base_dn, *nkeys, **options): ldap = self.obj.backend - - def delete_entry(pkey): - nkeys = keys[:-1] + (pkey, ) - dn = self.obj.get_dn(*nkeys, **options) - assert isinstance(dn, DN) - - for callback in self.get_callbacks('pre'): - dn = callback(self, ldap, dn, *nkeys, **options) - assert isinstance(dn, DN) - - def delete_subtree(base_dn): - assert isinstance(base_dn, DN) - truncated = True - while truncated: - try: - (subentries, truncated) = ldap.find_entries( - None, [''], base_dn, ldap.SCOPE_ONELEVEL - ) - except errors.NotFound: - break - else: - for entry_attrs in subentries: - delete_subtree(entry_attrs.dn) - try: - self._exc_wrapper(nkeys, options, ldap.delete_entry)(base_dn) - except errors.NotFound: - self.obj.handle_not_found(*nkeys) - + assert isinstance(base_dn, DN) + truncated = True + while truncated: try: - self._exc_wrapper(nkeys, options, ldap.delete_entry)(dn) + (subentries, truncated) = ldap.find_entries( + None, [''], base_dn, ldap.SCOPE_ONELEVEL + ) except errors.NotFound: - self.obj.handle_not_found(*nkeys) - except errors.NotAllowedOnNonLeaf: - # this entry is not a leaf entry, delete all child nodes - delete_subtree(dn) + break + else: + for entry_attrs in subentries: + self.delete_subtree(entry_attrs.dn, *nkeys, **options) + try: + self._exc_wrapper(nkeys, options, ldap.delete_entry)(base_dn) + except errors.NotFound: + self.obj.handle_not_found(*nkeys) - for callback in self.get_callbacks('post'): - result = callback(self, ldap, dn, *nkeys, **options) + def delete_entry(self, pkey, *keys, **options): + ldap = self.obj.backend + nkeys = keys[:-1] + (pkey, ) + dn = self.obj.get_dn(*nkeys, **options) + assert isinstance(dn, DN) - return result + for callback in self.get_callbacks('pre'): + dn = callback(self, ldap, dn, *nkeys, **options) + assert isinstance(dn, DN) + + try: + self._exc_wrapper(nkeys, options, ldap.delete_entry)(dn) + except errors.NotFound: + self.obj.handle_not_found(*nkeys) + except errors.NotAllowedOnNonLeaf: + # this entry is not a leaf entry, delete all child nodes + self.delete_subtree(dn, *nkeys, **options) + + for callback in self.get_callbacks('post'): + result = callback(self, ldap, dn, *nkeys, **options) + + return result + + def execute(self, *keys, **options): + ldap = self.obj.backend if self.obj.primary_key and isinstance(keys[-1], (list, tuple)): pkeyiter = keys[-1] @@ -1552,7 +1554,7 @@ class LDAPDelete(LDAPMultiQuery): failed = [] for pkey in pkeyiter: try: - delete_entry(pkey) + self.delete_entry(pkey, *keys, **options) except errors.ExecutionError: if not options.get('continue', False): raise diff --git a/ipalib/plugins/user.py b/ipalib/plugins/user.py index abe5ee26b8e48681eeb0cbb3bcff8617e212225c..70b237dc102f46ab62e10aab0250aa496dad60c6 100644 --- a/ipalib/plugins/user.py +++ b/ipalib/plugins/user.py @@ -901,6 +901,15 @@ class user_del(LDAPDelete): else: self.api.Command.otptoken_del(token) + # Delete user's private vault container. + vaultcontainer_id = self.api.Object.vaultcontainer.get_private_id(owner) + (vaultcontainer_name, vaultcontainer_parent_id) = self.api.Object.vaultcontainer.split_id(vaultcontainer_id) + + try: + self.api.Command.vaultcontainer_del(vaultcontainer_name, parent=vaultcontainer_parent_id) + except errors.NotFound: + pass + return dn diff --git a/ipalib/plugins/vault.py b/ipalib/plugins/vault.py new file mode 100644 index 0000000000000000000000000000000000000000..82a14ba42d82055a0652349ccc945b8485404c6c --- /dev/null +++ b/ipalib/plugins/vault.py @@ -0,0 +1,763 @@ +# Authors: +# Endi S. Dewata +# +# Copyright (C) 2015 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 . + +import base64 +import json +import os +import sys +import tempfile + +import nss.nss as nss + +import pki.account +import pki.crypto +import pki.key + +from ipalib.frontend import Command +from ipalib import api, errors +from ipalib import Str, Bytes, Flag +from ipalib.plugable import Registry +from ipalib.plugins.baseldap import LDAPObject, LDAPCreate, LDAPDelete, LDAPSearch, LDAPUpdate, LDAPRetrieve +from ipalib import _, ngettext +from ipaplatform.paths import paths +from ipapython.dn import DN +import ipapython.nsslib + +__doc__ = _(""" +Vaults +""") + _(""" +Manage vaults. +""") + _(""" +EXAMPLES: +""") + _(""" + List private vaults: + ipa vault-find +""") + _(""" + List shared vaults: + ipa vault-find --container /shared +""") + _(""" + Add a standard vault: + ipa vault-add MyVault +""") + _(""" + Show a vault: + ipa vault-show MyVault +""") + _(""" + Modify a vault: + ipa vault-mod MyVault --desc "My vault" +""") + _(""" + Archive data into standard vault: + ipa vault-archive MyVault --in data.bin +""") + _(""" + Retrieve data from standard vault: + ipa vault-retrieve MyVault --out data.bin +""") + _(""" + Delete a vault: + ipa vault-del MyVault +""") + +register = Registry() + + at register() +class vault(LDAPObject): + __doc__ = _(""" + Vault object. + """) + + object_name = _('vault') + object_name_plural = _('vaults') + + object_class = ['ipaVault'] + default_attributes = [ + 'cn', + 'vault_id', + 'description', + ] + search_display_attributes = [ + 'cn', + 'vault_id', + 'description', + 'ipavaulttype', + ] + + label = _('Vaults') + label_singular = _('Vault') + + takes_params = ( + Str('cn', + cli_name='vault_name', + label=_('Vault name'), + primary_key=True, + pattern='^[a-zA-Z0-9_.-]+$', + pattern_errmsg='may only include letters, numbers, _, ., and -', + maxlength=255, + ), + Str('container?', + cli_name='container', + label=_('Container'), + doc=_('Container'), + flags=('virtual_attribute'), + pattern='^[a-zA-Z0-9_.-/]+$', + pattern_errmsg='may only include letters, numbers, _, ., -, and /', + ), + Str('vault_id?', + cli_name='vault_id', + label=_('Vault ID'), + doc=_('Vault ID'), + flags=('virtual_attribute'), + ), + Str('description?', + cli_name='desc', + label=_('Description'), + doc=_('Vault description'), + ), + ) + + def get_dn(self, *keys, **options): + __doc__ = _(""" + Generates vault DN from vault ID. + """) + + # get vault ID from parameters + name = None + if keys: + name = keys[0] + + container_id = api.Object.vaultcontainer.normalize_id(options.get('container')) + + vault_id = container_id + if name: + vault_id = container_id + name + + dn = api.Object.vaultcontainer.base_dn + + # for each name in the ID, prepend the base DN + for name in vault_id.split(u'/'): + if name: + dn = DN(('cn', name), dn) + + return dn + + def get_id(self, dn): + __doc__ = _(""" + Generates vault ID from vault DN. + """) + + # make sure the DN is a vault DN + if not dn.endswith(api.Object.vaultcontainer.base_dn): + raise ValueError('Invalid vault DN: %s' % dn) + + # vault DN cannot be the container base DN + if len(dn) == len(api.Object.vaultcontainer.base_dn): + raise ValueError('Invalid vault DN: %s' % dn) + + # construct the vault ID from the bottom up + id = u'' + while len(dn) > len(api.Object.vaultcontainer.base_dn): + + rdn = dn[0] + name = rdn['cn'] + id = u'/' + name + id + + dn = DN(*dn[1:]) + + return id + + def split_id(self, id): + __doc__ = _(""" + Splits a vault ID into (vault name, container ID) tuple. + """) + + # split ID into container ID and vault name + parts = id.rsplit(u'/', 1) + + # return vault name and container ID + return (parts[1], parts[0] + u'/') + + def get_kra_id(self, id): + __doc__ = _(""" + Generates a client key ID to store/retrieve data in KRA. + """) + return 'ipa:' + id + + + at register() +class vault_add(LDAPCreate): + __doc__ = _('Create a new vault.') + + takes_options = LDAPCreate.takes_options + ( + Bytes('data?', + cli_name='data', + doc=_('Base-64 encoded binary data to archive'), + ), + Str('text?', + cli_name='text', + doc=_('Text data to archive'), + ), + Str('in?', + cli_name='in', + doc=_('File containing data to archive'), + ), + ) + + msg_summary = _('Added vault "%(value)s"') + + def forward(self, *args, **options): + + vault_name = args[0] + container_id = api.Object.vaultcontainer.normalize_id(options.get('container')) + + data = options.get('data') + text = options.get('text') + input_file = options.get('in') + + # don't send these parameters to server + if 'data' in options: + del options['data'] + if 'text' in options: + del options['text'] + if 'in' in options: + del options['in'] + + # get data + if data: + if text: + raise errors.ValidationError(name='text', + error=_('Input data already specified')) + + if input_file: + raise errors.ValidationError(name='input_file', + error=_('Input data already specified')) + + elif text: + if input_file: + raise errors.ValidationError(name='input_file', + error=_('Input data already specified')) + + data = text.encode('utf-8') + + elif input_file: + with open(input_file, 'rb') as f: + data = f.read() + + else: + data = '' + + # create the vault + response = super(vault_add, self).forward(*args, **options) + + # archive initial data + api.Command.vault_archive( + vault_name, + container=container_id, + data=data) + + return response + + def pre_callback(self, ldap, dn, entry_attrs, attrs_list, *keys, **options): + assert isinstance(dn, DN) + + container_dn = DN(*dn[1:]) + api.Object.vaultcontainer.create_entry(container_dn) + + return dn + + def post_callback(self, ldap, dn, entry_attrs, *keys, **options): + assert isinstance(dn, DN) + + entry_attrs['vault_id'] = self.obj.get_id(dn) + + return dn + + + at register() +class vault_del(LDAPDelete): + __doc__ = _('Delete a vault.') + + msg_summary = _('Deleted vault "%(value)s"') + + takes_options = LDAPDelete.takes_options + ( + Str('container?', + cli_name='container', + doc=_('Container'), + ), + ) + + def post_callback(self, ldap, dn, *keys, **options): + assert isinstance(dn, DN) + + vault_id = self.obj.get_id(dn) + + kra_client = api.Backend.kra.get_client() + + kra_account = pki.account.AccountClient(kra_client.connection) + kra_account.login() + + client_key_id = self.api.Object.vault.get_kra_id(vault_id) + + # deactivate vault record in KRA + response = kra_client.keys.list_keys(client_key_id, pki.key.KeyClient.KEY_STATUS_ACTIVE) + + for key_info in response.key_infos: + kra_client.keys.modify_key_status( + key_info.get_key_id(), + pki.key.KeyClient.KEY_STATUS_INACTIVE) + + kra_account.logout() + + return True + + + at register() +class vault_find(LDAPSearch): + __doc__ = _('Search for vaults.') + + msg_summary = ngettext( + '%(count)d vault matched', '%(count)d vaults matched', 0 + ) + + def pre_callback(self, ldap, filter, attrs_list, base_dn, scope, *keys, **options): + assert isinstance(base_dn, DN) + + container_id = self.Object.vaultcontainer.normalize_id(options.get('container')) + base_dn = self.Object.vaultcontainer.get_dn(parent=container_id) + + api.Object.vaultcontainer.create_entry(base_dn) + + return (filter, base_dn, scope) + + + def post_callback(self, ldap, entries, truncated, *args, **options): + + for entry in entries: + entry['vault_id'] = self.obj.get_id(entry.dn) + + return truncated + + + at register() +class vault_mod(LDAPUpdate): + __doc__ = _('Modify a vault.') + + msg_summary = _('Modified vault "%(value)s"') + + def post_callback(self, ldap, dn, entry_attrs, *keys, **options): + assert isinstance(dn, DN) + + entry_attrs['vault_id'] = self.obj.get_id(dn) + + return dn + + + at register() +class vault_show(LDAPRetrieve): + __doc__ = _('Display information about a vault.') + + takes_options = LDAPRetrieve.takes_options + ( + Str('container?', + cli_name='container', + doc=_('Container'), + ), + ) + + def post_callback(self, ldap, dn, entry_attrs, *keys, **options): + assert isinstance(dn, DN) + + entry_attrs['vault_id'] = self.obj.get_id(dn) + + return dn + + + at register() +class vault_transport_cert(Command): + __doc__ = _('Retrieve vault transport certificate.') + + + # list of attributes we want exported to JSON + json_friendly_attributes = ( + 'takes_args', + ) + + takes_options = ( + Str('out?', + cli_name='out', + doc=_('Output file to store the transport certificate'), + ), + ) + + has_output_params = ( + Str('certificate', + label=_('Certificate'), + ), + ) + + def __json__(self): + json_dict = dict( + (a, getattr(self, a)) for a in self.json_friendly_attributes + ) + json_dict['takes_options'] = list(self.get_json_options()) + return json_dict + + def forward(self, *args, **options): + + file = options.get('out') + + # don't send these parameters to server + if 'out' in options: + del options['out'] + + response = super(vault_transport_cert, self).forward(*args, **options) + + if file: + with open(file, 'w') as f: + f.write(response['result']['certificate']) + + return response + + def execute(self, *args, **options): + + kra_client = api.Backend.kra.get_client() + transport_cert = kra_client.system_certs.get_transport_cert() + return { + 'result': { + 'certificate': transport_cert.encoded + } + } + + + at register() +class vault_archive(LDAPRetrieve): + __doc__ = _('Archive data into a vault.') + + takes_options = LDAPRetrieve.takes_options + ( + Str('container?', + cli_name='container', + doc=_('Container'), + ), + Bytes('data?', + cli_name='data', + doc=_('Base-64 encoded binary data to archive'), + ), + Str('text?', + cli_name='text', + doc=_('Text data to archive'), + ), + Str('in?', + cli_name='in', + doc=_('File containing data to archive'), + ), + Str('session_key?', + cli_name='session_key', + doc=_('Session key wrapped with transport certificate and encoded in base-64'), + ), + Str('vault_data?', + cli_name='vault_data', + doc=_('Vault data encrypted with session key and encoded in base-64'), + ), + Str('nonce?', + cli_name='nonce', + doc=_('Nonce encrypted encoded in base-64'), + ), + ) + + msg_summary = _('Archived data into vault "%(value)s"') + + def forward(self, *args, **options): + + vault_name = args[0] + container_id = api.Object.vaultcontainer.normalize_id(options.get('container')) + + data = options.get('data') + text = options.get('text') + input_file = options.get('in') + + # don't send these parameters to server + if 'data' in options: + del options['data'] + if 'text' in options: + del options['text'] + if 'in' in options: + del options['in'] + + # get data + if data: + if text: + raise errors.ValidationError(name='text', + error=_('Input data already specified')) + + if input_file: + raise errors.ValidationError(name='input_file', + error=_('Input data already specified')) + + elif text: + if input_file: + raise errors.ValidationError(name='input_file', + error=_('Input data already specified')) + + data = text.encode('utf-8') + + elif input_file: + with open(input_file, 'rb') as f: + data = f.read() + + else: + data = '' + + # initialize NSS database + crypto = pki.crypto.NSSCryptoProvider(paths.IPA_NSSDB_DIR) + crypto.initialize() + ipapython.nsslib.current_dbdir = paths.IPA_NSSDB_DIR + + # retrieve transport certificate + (file, filename) = tempfile.mkstemp() + os.close(file) + try: + api.Command.vault_transport_cert(out=unicode(filename)) + transport_cert_der = nss.read_der_from_file(filename, True) + nss_transport_cert = nss.Certificate(transport_cert_der) + + finally: + os.remove(filename) + + # generate session key + session_key = crypto.generate_session_key() + + # wrap session key with transport certificate + wrapped_session_key = crypto.asymmetric_wrap( + session_key, + nss_transport_cert + ) + + options['session_key'] = unicode(base64.b64encode(wrapped_session_key)) + + nonce = crypto.generate_nonce_iv() + options['nonce'] = unicode(base64.b64encode(nonce)) + + vault_data = {} + vault_data[u'data'] = unicode(data) + + json_vault_data = json.dumps(vault_data) + + # wrap vault_data with session key + wrapped_vault_data = crypto.symmetric_wrap( + json_vault_data, + session_key, + nonce_iv=nonce + ) + + options['vault_data'] = unicode(base64.b64encode(wrapped_vault_data)) + + return super(vault_archive, self).forward(*args, **options) + + def post_callback(self, ldap, dn, entry_attrs, *keys, **options): + assert isinstance(dn, DN) + + vault_id = self.obj.get_id(dn) + entry_attrs['vault_id'] = vault_id + + # connect to KRA + kra_client = api.Backend.kra.get_client() + + kra_account = pki.account.AccountClient(kra_client.connection) + kra_account.login() + + client_key_id = self.api.Object.vault.get_kra_id(vault_id) + + # deactivate existing vault record in KRA + response = kra_client.keys.list_keys( + client_key_id, + pki.key.KeyClient.KEY_STATUS_ACTIVE) + + for key_info in response.key_infos: + kra_client.keys.modify_key_status( + key_info.get_key_id(), + pki.key.KeyClient.KEY_STATUS_INACTIVE) + + wrapped_session_key = base64.b64decode(options['session_key']) + nonce = base64.b64decode(options['nonce']) + + # forward wrapped data to KRA + wrapped_vault_data = base64.b64decode(options['vault_data']) + + kra_client.keys.archive_encrypted_data( + client_key_id, + pki.key.KeyClient.PASS_PHRASE_TYPE, + wrapped_vault_data, + wrapped_session_key, + None, + nonce, + ) + + kra_account.logout() + + return dn + + + at register() +class vault_retrieve(LDAPRetrieve): + __doc__ = _('Retrieve a data from a vault.') + + takes_options = LDAPRetrieve.takes_options + ( + Str('container?', + cli_name='container', + doc=_('Container'), + ), + Flag('show_text?', + doc=_('Show text data'), + autofill=False, + ), + Flag('stdout?', + doc=_('Show data on standard output'), + autofill=False, + ), + Str('out?', + cli_name='out', + doc=_('File to store retrieved data'), + ), + Str('session_key?', + cli_name='session_key', + doc=_('Session key wrapped with transport certificate and encoded in base-64'), + ), + ) + + has_output_params = ( + Bytes('data', + label=_('Data'), + ), + Bytes('text', + label=_('Text'), + ), + ) + + msg_summary = _('Retrieved data from vault "%(value)s"') + + def forward(self, *args, **options): + + vault_name = args[0] + container_id = api.Object.vaultcontainer.normalize_id(options.get('container')) + + show_text = options.get('show_text') + stdout = options.get('stdout') + output_file = options.get('out') + + # don't send these parameters to server + if 'show_text' in options: + del options['show_text'] + if 'stdout' in options: + del options['stdout'] + if 'out' in options: + del options['out'] + + # initialize NSS database + crypto = pki.crypto.NSSCryptoProvider(paths.IPA_NSSDB_DIR) + crypto.initialize() + ipapython.nsslib.current_dbdir = paths.IPA_NSSDB_DIR + + # generate session key + session_key = crypto.generate_session_key() + + # retrieve transport certificate + (file, filename) = tempfile.mkstemp() + os.close(file) + try: + api.Command.vault_transport_cert(out=unicode(filename)) + transport_cert_der = nss.read_der_from_file(filename, True) + nss_transport_cert = nss.Certificate(transport_cert_der) + + finally: + os.remove(filename) + + # wrap session key with transport certificate + wrapped_session_key = crypto.asymmetric_wrap( + session_key, + nss_transport_cert + ) + + # send retrieval request to server + options['session_key'] = unicode(base64.b64encode(wrapped_session_key)) + + response = super(vault_retrieve, self).forward(*args, **options) + + result = response['result'] + nonce = base64.b64decode(result['nonce']) + + # unwrap data with session key + wrapped_vault_data = base64.b64decode(result['vault_data']) + + json_vault_data = crypto.symmetric_unwrap( + wrapped_vault_data, + session_key, + nonce_iv=nonce) + + vault_data = json.loads(json_vault_data) + data = str(vault_data[u'data']) + + if stdout: + sys.stdout.write(data) + response['result'] = {} + response['summary'] = None + + elif output_file: + with open(output_file, 'w') as f: + f.write(data) + + elif show_text: + response['result']['text'] = unicode(data) + + else: + response['result']['data'] = data + + return response + + def post_callback(self, ldap, dn, entry_attrs, *keys, **options): + assert isinstance(dn, DN) + + vault_id = self.obj.get_id(dn) + entry_attrs['vault_id'] = vault_id + + wrapped_session_key = base64.b64decode(options['session_key']) + + # connect to KRA + kra_client = api.Backend.kra.get_client() + + kra_account = pki.account.AccountClient(kra_client.connection) + kra_account.login() + + client_key_id = self.api.Object.vault.get_kra_id(vault_id) + + # find vault record in KRA + response = kra_client.keys.list_keys( + client_key_id, + pki.key.KeyClient.KEY_STATUS_ACTIVE) + + if not len(response.key_infos): + raise errors.NotFound(reason=_('Missing archived data.')) + + key_info = response.key_infos[0] + + # retrieve encrypted data from KRA + key = kra_client.keys.retrieve_key( + key_info.get_key_id(), + wrapped_session_key) + + entry_attrs['vault_data'] = unicode(base64.b64encode(key.encrypted_data)) + entry_attrs['nonce'] = unicode(base64.b64encode(key.nonce_data)) + + kra_account.logout() + + return dn diff --git a/ipalib/plugins/vaultcontainer.py b/ipalib/plugins/vaultcontainer.py new file mode 100644 index 0000000000000000000000000000000000000000..22d36eecc6a842240cbadd3ff6b6518efbfe524e --- /dev/null +++ b/ipalib/plugins/vaultcontainer.py @@ -0,0 +1,361 @@ +# Authors: +# Endi S. Dewata +# +# Copyright (C) 2015 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 . + +import base64 + +from ipalib import api, errors +from ipalib import Str, Flag +from ipalib.plugable import Registry +from ipalib.plugins.baseldap import LDAPObject, LDAPCreate, LDAPDelete, LDAPSearch, LDAPUpdate, LDAPRetrieve +from ipalib.request import context +from ipalib.plugins.user import split_principal +from ipalib import _, ngettext +from ipapython.dn import DN + +__doc__ = _(""" +Vault containers +""") + _(""" +Manage vault containers. +""") + _(""" +EXAMPLES: +""") + _(""" + List private vault containers: + ipa vaultcontainer-find +""") + _(""" + List top-level vault containers: + ipa vaultcontainer-find --parent / +""") + _(""" + Add a vault container: + ipa vaultcontainer-add MyContainer +""") + _(""" + Show a vault container: + ipa vaultcontainer-show MyContainer +""") + _(""" + Modify a vault container: + ipa vaultcontainer-mod MyContainer --desc "My container" +""") + _(""" + Delete a vault container: + ipa vaultcontainer-del MyContainer +""") + +register = Registry() + + at register() +class vaultcontainer(LDAPObject): + __doc__ = _(""" + Vault container object. + """) + + base_dn = DN(api.env.container_vault, api.env.basedn) + object_name = _('vault container') + object_name_plural = _('vault containers') + + object_class = ['ipaVaultContainer'] + default_attributes = [ + 'cn', + 'container_id', + 'description', + ] + search_display_attributes = [ + 'cn', + 'container_id', + 'description', + ] + + label = _('Vault Containers') + label_singular = _('Vault Container') + + takes_params = ( + Str('cn', + cli_name='container_name', + label=_('Container name'), + primary_key=True, + pattern='^[a-zA-Z0-9_.-]+$', + pattern_errmsg='may only include letters, numbers, _, ., and -', + maxlength=255, + ), + Str('parent?', + cli_name='parent', + label=_('Parent'), + doc=_('Parent container'), + flags=('virtual_attribute'), + pattern='^[a-zA-Z0-9_.-/]+$', + pattern_errmsg='may only include letters, numbers, _, ., -, and /', + ), + Str('container_id?', + cli_name='container_id', + label=_('Container ID'), + doc=_('Container ID'), + flags=('virtual_attribute'), + ), + Str('description?', + cli_name='desc', + label=_('Description'), + doc=_('Container description'), + ), + ) + + def get_dn(self, *keys, **options): + __doc__ = _(""" + Generates vault container DN from container ID. + """) + + # get container ID from parameters + name = None + if keys: + name = keys[0] + + parent_id = api.Object.vaultcontainer.normalize_id(options.get('parent')) + + container_id = parent_id + if name: + container_id = parent_id + name + u'/' + + dn = self.base_dn + + # for each name in the ID, prepend the base DN + for name in container_id.split(u'/'): + if name: + dn = DN(('cn', name), dn) + + return dn + + def get_id(self, dn): + __doc__ = _(""" + Generates container ID from container DN. + """) + + # make sure the DN is a container DN + if not dn.endswith(self.base_dn): + raise ValueError('Invalid container DN: %s' % dn) + + # construct container ID from the bottom up + id = u'/' + while len(dn) > len(self.base_dn): + + rdn = dn[0] + name = rdn['cn'] + id = u'/' + name + id + + dn = DN(*dn[1:]) + + return id + + def get_private_id(self, username=None): + __doc__ = _(""" + Returns user's private container ID (i.e. /users//). + """) + + if not username: + principal = getattr(context, 'principal') + (username, realm) = split_principal(principal) + + return u'/users/' + username + u'/' + + def normalize_id(self, id): + __doc__ = _(""" + Normalizes container ID. + """) + + # if ID is empty, return user's private container ID + if not id: + return self.get_private_id() + + # make sure ID ends with slash + if not id.endswith(u'/'): + id += u'/' + + # if it's an absolute ID, do nothing + if id.startswith(u'/'): + return id + + # otherwise, prepend with user's private container ID + return self.get_private_id() + id + + def split_id(self, id): + __doc__ = _(""" + Splits a normalized container ID into (container name, parent ID) tuple. + """) + + # handle root ID + if id == u'/': + return (None, None) + + # split ID into parent ID, container name, and empty string + parts = id.rsplit(u'/', 2) + + # return container name and parent ID + return (parts[1], parts[0] + u'/') + + def create_entry(self, dn): + __doc__ = _(""" + Creates a container entry and its parents. + """) + + # if entry already exists, return + try: + self.backend.get_entry(dn) + return + + except errors.NotFound: + pass + + # otherwise, create parent entry first + parent_dn = DN(*dn[1:]) + self.create_entry(parent_dn) + + # then create the entry itself + rdn = dn[0] + entry = self.backend.make_entry( + dn, + { + 'objectclass': self.object_class, + 'cn': rdn['cn'], + }) + self.backend.add_entry(entry) + + + at register() +class vaultcontainer_add(LDAPCreate): + __doc__ = _('Create a new vault container.') + + msg_summary = _('Added vault container "%(value)s"') + + def pre_callback(self, ldap, dn, entry_attrs, attrs_list, *keys, **options): + assert isinstance(dn, DN) + + parent_dn = DN(*dn[1:]) + self.obj.create_entry(parent_dn) + + return dn + + def post_callback(self, ldap, dn, entry_attrs, *keys, **options): + assert isinstance(dn, DN) + + entry_attrs['container_id'] = self.obj.get_id(dn) + + return dn + + + at register() +class vaultcontainer_del(LDAPDelete): + __doc__ = _('Delete a vault container.') + + msg_summary = _('Deleted vault container "%(value)s"') + + takes_options = LDAPDelete.takes_options + ( + Str('parent?', + cli_name='parent', + doc=_('Parent container'), + ), + Flag('force?', + doc=_('Force deletion'), + autofill=False, + ), + ) + + def delete_entry(self, pkey, *keys, **options): + __doc__ = _(""" + Overwrites the base method to control deleting subtree with force option. + """) + + ldap = self.obj.backend + nkeys = keys[:-1] + (pkey, ) + dn = self.obj.get_dn(*nkeys, **options) + assert isinstance(dn, DN) + + for callback in self.get_callbacks('pre'): + dn = callback(self, ldap, dn, *nkeys, **options) + assert isinstance(dn, DN) + + try: + self._exc_wrapper(nkeys, options, ldap.delete_entry)(dn) + except errors.NotFound: + self.obj.handle_not_found(*nkeys) + except errors.NotAllowedOnNonLeaf: + # this entry is not a leaf entry + # if forced, delete all child nodes + if options.get('force'): + self.delete_subtree(dn, *nkeys, **options) + else: + raise + + for callback in self.get_callbacks('post'): + result = callback(self, ldap, dn, *nkeys, **options) + + return result + + + at register() +class vaultcontainer_find(LDAPSearch): + __doc__ = _('Search for vault containers.') + + msg_summary = ngettext( + '%(count)d vault container matched', '%(count)d vault containers matched', 0 + ) + + def pre_callback(self, ldap, filter, attrs_list, base_dn, scope, *keys, **options): + assert isinstance(base_dn, DN) + + parent_id = self.obj.normalize_id(options.get('parent')) + base_dn = self.obj.get_dn(parent=parent_id) + + self.obj.create_entry(base_dn) + + return (filter, base_dn, scope) + + def post_callback(self, ldap, entries, truncated, *args, **options): + + for entry in entries: + entry['container_id'] = self.obj.get_id(entry.dn) + + return truncated + + + at register() +class vaultcontainer_mod(LDAPUpdate): + __doc__ = _('Modify a vault container.') + + msg_summary = _('Modified vault container "%(value)s"') + + def post_callback(self, ldap, dn, entry_attrs, *keys, **options): + assert isinstance(dn, DN) + + entry_attrs['container_id'] = self.obj.get_id(dn) + + return dn + + + at register() +class vaultcontainer_show(LDAPRetrieve): + __doc__ = _('Display information about a vault container.') + + takes_options = LDAPRetrieve.takes_options + ( + Str('parent?', + cli_name='parent', + doc=_('Parent container'), + ), + ) + + def post_callback(self, ldap, dn, entry_attrs, *keys, **options): + assert isinstance(dn, DN) + + entry_attrs['container_id'] = self.obj.get_id(dn) + + return dn diff --git a/ipatests/test_xmlrpc/test_vault_plugin.py b/ipatests/test_xmlrpc/test_vault_plugin.py new file mode 100644 index 0000000000000000000000000000000000000000..9857ffbda7a768ac0522989c3ccfa3fa1bf1f5a4 --- /dev/null +++ b/ipatests/test_xmlrpc/test_vault_plugin.py @@ -0,0 +1,241 @@ +# Authors: +# Endi S. Dewata +# +# Copyright (C) 2015 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 . + +""" +Test the `ipalib/plugins/vault.py` module. +""" + +from ipalib import api, errors +from xmlrpc_test import Declarative, fuzzy_string + +test_vault = u'test_vault' +binary_data = '\x01\x02\x03\x04' +text_data = u'secret' + +class test_vault(Declarative): + + cleanup_commands = [ + ('vault_del', [test_vault], {'continue': True}), + ] + + tests = [ + + { + 'desc': 'Create test vault', + 'command': ( + 'vault_add', + [test_vault], + {}, + ), + 'expected': { + 'value': test_vault, + 'summary': 'Added vault "%s"' % test_vault, + 'result': { + 'dn': u'cn=%s,cn=admin,cn=users,cn=vaults,%s' % (test_vault, api.env.basedn), + 'objectclass': (u'ipaVault', u'top'), + 'cn': [test_vault], + 'vault_id': u'/users/admin/%s' % test_vault, + }, + }, + }, + + { + 'desc': 'Create duplicate vault', + 'command': ( + 'vault_add', + [test_vault], + {}, + ), + 'expected': errors.DuplicateEntry(message=u'vault with name "%s" already exists' % test_vault), + }, + + { + 'desc': 'Find test vaults', + 'command': ( + 'vault_find', + [], + {}, + ), + 'expected': { + 'count': 1, + 'truncated': False, + 'summary': u'1 vault matched', + 'result': [ + { + 'dn': u'cn=%s,cn=admin,cn=users,cn=vaults,%s' % (test_vault, api.env.basedn), + 'cn': [test_vault], + 'vault_id': u'/users/admin/%s' % test_vault, + }, + ], + }, + }, + + { + 'desc': 'Show test vault', + 'command': ( + 'vault_show', + [test_vault], + {}, + ), + 'expected': { + 'value': test_vault, + 'summary': None, + 'result': { + 'dn': u'cn=%s,cn=admin,cn=users,cn=vaults,%s' % (test_vault, api.env.basedn), + 'cn': [test_vault], + 'vault_id': u'/users/admin/%s' % test_vault, + }, + }, + }, + + { + 'desc': 'Modify test vault', + 'command': ( + 'vault_mod', + [test_vault], + { + 'description': u'Test vault', + }, + ), + 'expected': { + 'value': test_vault, + 'summary': u'Modified vault "%s"' % test_vault, + 'result': { + 'cn': [test_vault], + 'vault_id': u'/users/admin/%s' % test_vault, + 'description': [u'Test vault'], + }, + }, + }, + + { + 'desc': 'Archive binary data', + 'command': ( + 'vault_archive', + [test_vault], + { + 'data': binary_data, + }, + ), + 'expected': { + 'value': test_vault, + 'summary': u'Archived data into vault "%s"' % test_vault, + 'result': { + 'dn': u'cn=%s,cn=admin,cn=users,cn=vaults,%s' % (test_vault, api.env.basedn), + 'cn': [test_vault], + 'vault_id': u'/users/admin/%s' % test_vault, + 'description': [u'Test vault'], + }, + }, + }, + + { + 'desc': 'Retrieve binary data', + 'command': ( + 'vault_retrieve', + [test_vault], + {}, + ), + 'expected': { + 'value': test_vault, + 'summary': u'Retrieved data from vault "%s"' % test_vault, + 'result': { + 'dn': u'cn=%s,cn=admin,cn=users,cn=vaults,%s' % (test_vault, api.env.basedn), + 'cn': [test_vault], + 'vault_id': u'/users/admin/%s' % test_vault, + 'description': [u'Test vault'], + 'nonce': fuzzy_string, + 'vault_data': fuzzy_string, + 'data': binary_data, + }, + }, + }, + + { + 'desc': 'Archive text data', + 'command': ( + 'vault_archive', + [test_vault], + { + 'text': text_data, + }, + ), + 'expected': { + 'value': test_vault, + 'summary': u'Archived data into vault "%s"' % test_vault, + 'result': { + 'dn': u'cn=%s,cn=admin,cn=users,cn=vaults,%s' % (test_vault, api.env.basedn), + 'cn': [test_vault], + 'vault_id': u'/users/admin/%s' % test_vault, + 'description': [u'Test vault'], + }, + }, + }, + + { + 'desc': 'Retrieve text data', + 'command': ( + 'vault_retrieve', + [test_vault], + { + 'show_text': True, + }, + ), + 'expected': { + 'value': test_vault, + 'summary': u'Retrieved data from vault "%s"' % test_vault, + 'result': { + 'dn': u'cn=%s,cn=admin,cn=users,cn=vaults,%s' % (test_vault, api.env.basedn), + 'cn': [test_vault], + 'vault_id': u'/users/admin/%s' % test_vault, + 'description': [u'Test vault'], + 'nonce': fuzzy_string, + 'vault_data': fuzzy_string, + 'text': text_data, + }, + }, + }, + + { + 'desc': 'Delete test vault', + 'command': ( + 'vault_del', + [test_vault], + {}, + ), + 'expected': { + 'value': [test_vault], + 'summary': u'Deleted vault "%s"' % test_vault, + 'result': { + 'failed': (), + }, + }, + }, + + { + 'desc': 'Delete non-existent vault', + 'command': ( + 'vault_del', + [test_vault], + {}, + ), + 'expected': errors.NotFound(reason=u'%s: vault not found' % test_vault), + }, + + ] diff --git a/ipatests/test_xmlrpc/test_vaultcontainer_plugin.py b/ipatests/test_xmlrpc/test_vaultcontainer_plugin.py new file mode 100644 index 0000000000000000000000000000000000000000..957228b8f57623e93cda9a5295f9977dd6b84110 --- /dev/null +++ b/ipatests/test_xmlrpc/test_vaultcontainer_plugin.py @@ -0,0 +1,420 @@ +# Authors: +# Endi S. Dewata +# +# Copyright (C) 2015 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 . + +""" +Test the `ipalib/plugins/vaultcontainer.py` module. +""" + +from ipalib import api, errors +from xmlrpc_test import Declarative + +private_container = u'private_container' +shared_container = u'shared_container' +service_container = u'service_container' + +base_container = u'base_container' +child_container = u'child_container' +grandchild_container = u'grandchild_container' + +class test_vault(Declarative): + + cleanup_commands = [ + ('vaultcontainer_del', [private_container], {'continue': True}), + ('vaultcontainer_del', [shared_container], {'parent': u'/shared/', 'continue': True}), + ('vaultcontainer_del', [service_container], {'parent': u'/services/', 'continue': True}), + ('vaultcontainer_del', [base_container], {'force': True, 'continue': True}), + ] + + tests = [ + + { + 'desc': 'Find top-level containers', + 'command': ( + 'vaultcontainer_find', + [], + { + 'parent': u'/', + }, + ), + 'expected': { + 'count': 3, + 'truncated': False, + 'summary': u'3 vault containers matched', + 'result': [ + { + 'dn': u'cn=services,cn=vaults,%s' % api.env.basedn, + 'cn': [u'services'], + 'container_id': u'/services/', + 'description': [u'Services vault container'], + }, + { + 'dn': u'cn=shared,cn=vaults,%s' % api.env.basedn, + 'cn': [u'shared'], + 'container_id': u'/shared/', + 'description': [u'Shared vault container'], + }, + { + 'dn': u'cn=users,cn=vaults,%s' % api.env.basedn, + 'cn': [u'users'], + 'container_id': u'/users/', + 'description': [u'Users vault container'], + }, + ], + }, + }, + + { + 'desc': 'Create private container', + 'command': ( + 'vaultcontainer_add', + [private_container], + {}, + ), + 'expected': { + 'value': private_container, + 'summary': 'Added vault container "%s"' % private_container, + 'result': { + 'dn': u'cn=%s,cn=admin,cn=users,cn=vaults,%s' % (private_container, api.env.basedn), + 'objectclass': (u'ipaVaultContainer', u'top'), + 'cn': [private_container], + 'container_id': u'/users/admin/%s/' % private_container, + }, + }, + }, + + { + 'desc': 'Create duplicate container', + 'command': ( + 'vaultcontainer_add', + [private_container], + {}, + ), + 'expected': errors.DuplicateEntry(message=u'vault container with name "%s" already exists' % private_container), + }, + + { + 'desc': 'Find private containers', + 'command': ( + 'vaultcontainer_find', + [], + {}, + ), + 'expected': { + 'count': 1, + 'truncated': False, + 'summary': u'1 vault container matched', + 'result': [ + { + 'dn': u'cn=%s,cn=admin,cn=users,cn=vaults,%s' % (private_container, api.env.basedn), + 'cn': [private_container], + 'container_id': u'/users/admin/%s/' % private_container, + }, + ], + }, + }, + + { + 'desc': 'Show private container', + 'command': ( + 'vaultcontainer_show', + [private_container], + {}, + ), + 'expected': { + 'value': private_container, + 'summary': None, + 'result': { + 'dn': u'cn=%s,cn=admin,cn=users,cn=vaults,%s' % (private_container, api.env.basedn), + 'cn': [private_container], + 'container_id': u'/users/admin/%s/' % private_container, + }, + }, + }, + + { + 'desc': 'Modify private container', + 'command': ( + 'vaultcontainer_mod', + [private_container], + { + 'description': u'Private container', + }, + ), + 'expected': { + 'value': private_container, + 'summary': 'Modified vault container "%s"' % private_container, + 'result': { + 'cn': [private_container], + 'container_id': u'/users/admin/%s/' % private_container, + 'description': [u'Private container'], + }, + }, + }, + + { + 'desc': 'Delete private container', + 'command': ( + 'vaultcontainer_del', + [private_container], + {}, + ), + 'expected': { + 'value': [private_container], + 'summary': u'Deleted vault container "%s"' % private_container, + 'result': { + 'failed': (), + }, + }, + }, + + { + 'desc': 'Delete non-existent container', + 'command': ( + 'vaultcontainer_del', + [private_container], + {}, + ), + 'expected': errors.NotFound(reason=u'%s: vault container not found' % private_container), + }, + + { + 'desc': 'Create shared container', + 'command': ( + 'vaultcontainer_add', + [shared_container], + { + 'parent': u'/shared/', + }, + ), + 'expected': { + 'value': shared_container, + 'summary': 'Added vault container "%s"' % shared_container, + 'result': { + 'dn': u'cn=%s,cn=shared,cn=vaults,%s' % (shared_container, api.env.basedn), + 'objectclass': (u'ipaVaultContainer', u'top'), + 'cn': [shared_container], + 'container_id': u'/shared/%s/' % shared_container, + }, + }, + }, + + { + 'desc': 'Find shared containers', + 'command': ( + 'vaultcontainer_find', + [], + { + 'parent': u'/shared/', + }, + ), + 'expected': { + 'count': 1, + 'truncated': False, + 'summary': u'1 vault container matched', + 'result': [ + { + 'dn': u'cn=%s,cn=shared,cn=vaults,%s' % (shared_container, api.env.basedn), + 'cn': [shared_container], + 'container_id': u'/shared/%s/' % shared_container, + }, + ], + }, + }, + + { + 'desc': 'Show shared container', + 'command': ( + 'vaultcontainer_show', + [shared_container], + { + 'parent': u'/shared/', + }, + ), + 'expected': { + 'value': shared_container, + 'summary': None, + 'result': { + 'dn': u'cn=%s,cn=shared,cn=vaults,%s' % (shared_container, api.env.basedn), + 'cn': [shared_container], + 'container_id': u'/shared/%s/' % shared_container, + }, + }, + }, + + { + 'desc': 'Modify shared container', + 'command': ( + 'vaultcontainer_mod', + [shared_container], + { + 'parent': u'/shared/', + 'description': u'shared container', + }, + ), + 'expected': { + 'value': shared_container, + 'summary': 'Modified vault container "%s"' % shared_container, + 'result': { + 'cn': [shared_container], + 'container_id': u'/shared/%s/' % shared_container, + 'description': [u'shared container'], + }, + }, + }, + + { + 'desc': 'Delete shared container', + 'command': ( + 'vaultcontainer_del', + [shared_container], + { + 'parent': u'/shared/', + }, + ), + 'expected': { + 'value': [shared_container], + 'summary': u'Deleted vault container "%s"' % shared_container, + 'result': { + 'failed': (), + }, + }, + }, + + { + 'desc': 'Create service container', + 'command': ( + 'vaultcontainer_add', + [service_container], + { + 'parent': u'/services/', + }, + ), + 'expected': { + 'value': service_container, + 'summary': 'Added vault container "%s"' % service_container, + 'result': { + 'dn': u'cn=%s,cn=services,cn=vaults,%s' % (service_container, api.env.basedn), + 'objectclass': (u'ipaVaultContainer', u'top'), + 'cn': [service_container], + 'container_id': u'/services/%s/' % service_container, + }, + }, + }, + { + 'desc': 'Create base container', + 'command': ( + 'vaultcontainer_add', + [base_container], + {}, + ), + 'expected': { + 'value': base_container, + 'summary': 'Added vault container "%s"' % base_container, + 'result': { + 'dn': u'cn=%s,cn=admin,cn=users,cn=vaults,%s' % (base_container, api.env.basedn), + 'objectclass': (u'ipaVaultContainer', u'top'), + 'cn': [base_container], + 'container_id': u'/users/admin/%s/' % base_container, + }, + }, + }, + + { + 'desc': 'Create child container', + 'command': ( + 'vaultcontainer_add', + [child_container], + { + 'parent': base_container, + }, + ), + 'expected': { + 'value': child_container, + 'summary': 'Added vault container "%s"' % child_container, + 'result': { + 'dn': u'cn=%s,cn=%s,cn=admin,cn=users,cn=vaults,%s' % (child_container, base_container, api.env.basedn), + 'objectclass': (u'ipaVaultContainer', u'top'), + 'cn': [child_container], + 'container_id': u'/users/admin/%s/%s/' % (base_container, child_container), + }, + }, + }, + + { + 'desc': 'Create grandchild container', + 'command': ( + 'vaultcontainer_add', + [grandchild_container], + { + 'parent': base_container + u'/' + child_container, + }, + ), + 'expected': { + 'value': grandchild_container, + 'summary': 'Added vault container "%s"' % grandchild_container, + 'result': { + 'dn': u'cn=%s,cn=%s,cn=%s,cn=admin,cn=users,cn=vaults,%s' + % (grandchild_container, child_container, base_container, api.env.basedn), + 'objectclass': (u'ipaVaultContainer', u'top'), + 'cn': [grandchild_container], + 'container_id': u'/users/admin/%s/%s/%s/' + % (base_container, child_container, grandchild_container), + }, + }, + }, + + { + 'desc': 'Delete base container', + 'command': ( + 'vaultcontainer_del', + [base_container], + {}, + ), + 'expected': errors.NotAllowedOnNonLeaf(error=u'Not allowed on non-leaf entry'), + }, + + { + 'desc': 'Delete base container with force', + 'command': ( + 'vaultcontainer_del', + [base_container], + { + 'force': True, + }, + ), + 'expected': { + 'value': [base_container], + 'summary': u'Deleted vault container "%s"' % base_container, + 'result': { + 'failed': (), + }, + }, + }, + + { + 'desc': 'Delete non-existent container', + 'command': ( + 'vaultcontainer_del', + [base_container], + {}, + ), + 'expected': errors.NotFound(reason=u'%s: vault container not found' % base_container), + }, + + ] -- 1.9.0 -------------- next part -------------- >From 3f5bc2f3d3ed5d81fbbd96409137b94f66cc7773 Mon Sep 17 00:00:00 2001 From: "Endi S. Dewata" Date: Fri, 17 Oct 2014 12:05:34 -0400 Subject: [PATCH] Added vault access control. New LDAP ACIs have been added to allow users to create their own private vault container, to allow owners to manage vaults and containers, and to allow members to use the vaults. New CLIs have been added to manage the owner and member list. For archive and retrieve operations the access control has to be enforced by the plugins because the operations only affects KRA. The LDAP schema has been updated as well. https://fedorahosted.org/freeipa/ticket/3872 --- API.txt | 134 ++++++++++++++++++-- install/share/60basev3.ldif | 4 +- install/updates/40-vault.update | 7 ++ ipalib/plugins/vault.py | 140 ++++++++++++++++++++- ipalib/plugins/vaultcontainer.py | 122 +++++++++++++++++- ipatests/test_xmlrpc/test_vault_plugin.py | 7 ++ ipatests/test_xmlrpc/test_vaultcontainer_plugin.py | 10 ++ 7 files changed, 404 insertions(+), 20 deletions(-) diff --git a/API.txt b/API.txt index a0a347803db687717b90c294de8c56a4478bd339..6babee618c1214f0678e2f063a41d0db43ed75b3 100644 --- a/API.txt +++ b/API.txt @@ -4518,7 +4518,7 @@ output: Output('result', , None) output: Output('summary', (, ), None) output: PrimaryKey('value', None, None) command: vault_add -args: 1,11,3 +args: 1,12,3 arg: Str('cn', attribute=True, cli_name='vault_name', maxlength=255, multivalue=False, pattern='^[a-zA-Z0-9_.-]+$', primary_key=True, required=True) option: Str('addattr*', cli_name='addattr', exclude='webui') option: Flag('all', autofill=True, cli_name='all', default=False, exclude='webui') @@ -4526,6 +4526,7 @@ option: Str('container', attribute=False, cli_name='container', multivalue=False option: Bytes('data?', cli_name='data') option: Str('description', attribute=True, cli_name='desc', multivalue=False, required=False) option: Str('in?', cli_name='in') +option: Flag('no_members', autofill=True, default=False, exclude='webui') option: Flag('raw', autofill=True, cli_name='raw', default=False, exclude='webui') option: Str('setattr*', cli_name='setattr', exclude='webui') option: Str('text?', cli_name='text') @@ -4534,13 +4535,40 @@ option: Str('version?', exclude='webui') output: Entry('result', , Gettext('A dictionary representing an LDAP entry', domain='ipa', localedir=None)) output: Output('summary', (, ), None) output: PrimaryKey('value', None, None) +command: vault_add_member +args: 1,7,3 +arg: Str('cn', attribute=True, cli_name='vault_name', maxlength=255, multivalue=False, pattern='^[a-zA-Z0-9_.-]+$', primary_key=True, query=True, required=True) +option: Flag('all', autofill=True, cli_name='all', default=False, exclude='webui') +option: Str('container?', cli_name='container') +option: Str('group*', alwaysask=True, cli_name='groups', csv=True) +option: Flag('no_members', autofill=True, default=False, exclude='webui') +option: Flag('raw', autofill=True, cli_name='raw', default=False, exclude='webui') +option: Str('user*', alwaysask=True, cli_name='users', csv=True) +option: Str('version?', exclude='webui') +output: Output('completed', , None) +output: Output('failed', , None) +output: Entry('result', , Gettext('A dictionary representing an LDAP entry', domain='ipa', localedir=None)) +command: vault_add_owner +args: 1,7,3 +arg: Str('cn', attribute=True, cli_name='vault_name', maxlength=255, multivalue=False, pattern='^[a-zA-Z0-9_.-]+$', primary_key=True, query=True, required=True) +option: Flag('all', autofill=True, cli_name='all', default=False, exclude='webui') +option: Str('container?', cli_name='container') +option: Str('group*', alwaysask=True, cli_name='groups', csv=True) +option: Flag('no_members', autofill=True, default=False, exclude='webui') +option: Flag('raw', autofill=True, cli_name='raw', default=False, exclude='webui') +option: Str('user*', alwaysask=True, cli_name='users', csv=True) +option: Str('version?', exclude='webui') +output: Output('completed', , None) +output: Output('failed', , None) +output: Entry('result', , Gettext('A dictionary representing an LDAP entry', domain='ipa', localedir=None)) command: vault_archive -args: 1,11,3 +args: 1,12,3 arg: Str('cn', attribute=True, cli_name='vault_name', maxlength=255, multivalue=False, pattern='^[a-zA-Z0-9_.-]+$', primary_key=True, query=True, required=True) option: Flag('all', autofill=True, cli_name='all', default=False, exclude='webui') option: Str('container?', cli_name='container') option: Bytes('data?', cli_name='data') option: Str('in?', cli_name='in') +option: Flag('no_members', autofill=True, default=False, exclude='webui') option: Str('nonce?', cli_name='nonce') option: Flag('raw', autofill=True, cli_name='raw', default=False, exclude='webui') option: Flag('rights', autofill=True, default=False) @@ -4561,12 +4589,13 @@ output: Output('result', , None) output: Output('summary', (, ), None) output: ListOfPrimaryKeys('value', None, None) command: vault_find -args: 1,10,4 +args: 1,11,4 arg: Str('criteria?', noextrawhitespace=False) option: Flag('all', autofill=True, cli_name='all', default=False, exclude='webui') option: Str('cn', attribute=True, autofill=False, cli_name='vault_name', maxlength=255, multivalue=False, pattern='^[a-zA-Z0-9_.-]+$', primary_key=True, query=True, required=False) option: Str('container', attribute=False, autofill=False, cli_name='container', multivalue=False, pattern='^[a-zA-Z0-9_.-/]+$', query=True, required=False) option: Str('description', attribute=True, autofill=False, cli_name='desc', multivalue=False, query=True, required=False) +option: Flag('no_members', autofill=True, default=False, exclude='webui') option: Flag('pkey_only?', autofill=True, default=False) option: Flag('raw', autofill=True, cli_name='raw', default=False, exclude='webui') option: Int('sizelimit?', autofill=False, minvalue=0) @@ -4578,13 +4607,14 @@ output: ListOfEntries('result', (, ), Gettext('A list output: Output('summary', (, ), None) output: Output('truncated', , None) command: vault_mod -args: 1,10,3 +args: 1,11,3 arg: Str('cn', attribute=True, cli_name='vault_name', maxlength=255, multivalue=False, pattern='^[a-zA-Z0-9_.-]+$', primary_key=True, query=True, required=True) option: Str('addattr*', cli_name='addattr', exclude='webui') option: Flag('all', autofill=True, cli_name='all', default=False, exclude='webui') option: Str('container', attribute=False, autofill=False, cli_name='container', multivalue=False, pattern='^[a-zA-Z0-9_.-/]+$', required=False) option: Str('delattr*', cli_name='delattr', exclude='webui') option: Str('description', attribute=True, autofill=False, cli_name='desc', multivalue=False, required=False) +option: Flag('no_members', autofill=True, default=False, exclude='webui') option: Flag('raw', autofill=True, cli_name='raw', default=False, exclude='webui') option: Flag('rights', autofill=True, default=False) option: Str('setattr*', cli_name='setattr', exclude='webui') @@ -4593,11 +4623,38 @@ option: Str('version?', exclude='webui') output: Entry('result', , Gettext('A dictionary representing an LDAP entry', domain='ipa', localedir=None)) output: Output('summary', (, ), None) output: PrimaryKey('value', None, None) +command: vault_remove_member +args: 1,7,3 +arg: Str('cn', attribute=True, cli_name='vault_name', maxlength=255, multivalue=False, pattern='^[a-zA-Z0-9_.-]+$', primary_key=True, query=True, required=True) +option: Flag('all', autofill=True, cli_name='all', default=False, exclude='webui') +option: Str('container?', cli_name='container') +option: Str('group*', alwaysask=True, cli_name='groups', csv=True) +option: Flag('no_members', autofill=True, default=False, exclude='webui') +option: Flag('raw', autofill=True, cli_name='raw', default=False, exclude='webui') +option: Str('user*', alwaysask=True, cli_name='users', csv=True) +option: Str('version?', exclude='webui') +output: Output('completed', , None) +output: Output('failed', , None) +output: Entry('result', , Gettext('A dictionary representing an LDAP entry', domain='ipa', localedir=None)) +command: vault_remove_owner +args: 1,7,3 +arg: Str('cn', attribute=True, cli_name='vault_name', maxlength=255, multivalue=False, pattern='^[a-zA-Z0-9_.-]+$', primary_key=True, query=True, required=True) +option: Flag('all', autofill=True, cli_name='all', default=False, exclude='webui') +option: Str('container?', cli_name='container') +option: Str('group*', alwaysask=True, cli_name='groups', csv=True) +option: Flag('no_members', autofill=True, default=False, exclude='webui') +option: Flag('raw', autofill=True, cli_name='raw', default=False, exclude='webui') +option: Str('user*', alwaysask=True, cli_name='users', csv=True) +option: Str('version?', exclude='webui') +output: Output('completed', , None) +output: Output('failed', , None) +output: Entry('result', , Gettext('A dictionary representing an LDAP entry', domain='ipa', localedir=None)) command: vault_retrieve -args: 1,9,3 +args: 1,10,3 arg: Str('cn', attribute=True, cli_name='vault_name', maxlength=255, multivalue=False, pattern='^[a-zA-Z0-9_.-]+$', primary_key=True, query=True, required=True) option: Flag('all', autofill=True, cli_name='all', default=False, exclude='webui') option: Str('container?', cli_name='container') +option: Flag('no_members', autofill=True, default=False, exclude='webui') option: Str('out?', cli_name='out') option: Flag('raw', autofill=True, cli_name='raw', default=False, exclude='webui') option: Flag('rights', autofill=True, default=False) @@ -4609,10 +4666,11 @@ output: Entry('result', , Gettext('A dictionary representing an LDA output: Output('summary', (, ), None) output: PrimaryKey('value', None, None) command: vault_show -args: 1,5,3 +args: 1,6,3 arg: Str('cn', attribute=True, cli_name='vault_name', maxlength=255, multivalue=False, pattern='^[a-zA-Z0-9_.-]+$', primary_key=True, query=True, required=True) option: Flag('all', autofill=True, cli_name='all', default=False, exclude='webui') option: Str('container?', cli_name='container') +option: Flag('no_members', autofill=True, default=False, exclude='webui') option: Flag('raw', autofill=True, cli_name='raw', default=False, exclude='webui') option: Flag('rights', autofill=True, default=False) option: Str('version?', exclude='webui') @@ -4625,12 +4683,13 @@ option: Str('out?', cli_name='out') option: Str('version?', exclude='webui') output: Output('result', None, None) command: vaultcontainer_add -args: 1,8,3 +args: 1,9,3 arg: Str('cn', attribute=True, cli_name='container_name', maxlength=255, multivalue=False, pattern='^[a-zA-Z0-9_.-]+$', primary_key=True, required=True) option: Str('addattr*', cli_name='addattr', exclude='webui') option: Flag('all', autofill=True, cli_name='all', default=False, exclude='webui') option: Str('container_id', attribute=False, cli_name='container_id', multivalue=False, required=False) option: Str('description', attribute=True, cli_name='desc', multivalue=False, required=False) +option: Flag('no_members', autofill=True, default=False, exclude='webui') option: Str('parent', attribute=False, cli_name='parent', multivalue=False, pattern='^[a-zA-Z0-9_.-/]+$', required=False) option: Flag('raw', autofill=True, cli_name='raw', default=False, exclude='webui') option: Str('setattr*', cli_name='setattr', exclude='webui') @@ -4638,6 +4697,32 @@ option: Str('version?', exclude='webui') output: Entry('result', , Gettext('A dictionary representing an LDAP entry', domain='ipa', localedir=None)) output: Output('summary', (, ), None) output: PrimaryKey('value', None, None) +command: vaultcontainer_add_member +args: 1,7,3 +arg: Str('cn', attribute=True, cli_name='container_name', maxlength=255, multivalue=False, pattern='^[a-zA-Z0-9_.-]+$', primary_key=True, query=True, required=True) +option: Flag('all', autofill=True, cli_name='all', default=False, exclude='webui') +option: Str('group*', alwaysask=True, cli_name='groups', csv=True) +option: Flag('no_members', autofill=True, default=False, exclude='webui') +option: Str('parent?', cli_name='parent') +option: Flag('raw', autofill=True, cli_name='raw', default=False, exclude='webui') +option: Str('user*', alwaysask=True, cli_name='users', csv=True) +option: Str('version?', exclude='webui') +output: Output('completed', , None) +output: Output('failed', , None) +output: Entry('result', , Gettext('A dictionary representing an LDAP entry', domain='ipa', localedir=None)) +command: vaultcontainer_add_owner +args: 1,7,3 +arg: Str('cn', attribute=True, cli_name='container_name', maxlength=255, multivalue=False, pattern='^[a-zA-Z0-9_.-]+$', primary_key=True, query=True, required=True) +option: Flag('all', autofill=True, cli_name='all', default=False, exclude='webui') +option: Str('group*', alwaysask=True, cli_name='groups', csv=True) +option: Flag('no_members', autofill=True, default=False, exclude='webui') +option: Str('parent?', cli_name='parent') +option: Flag('raw', autofill=True, cli_name='raw', default=False, exclude='webui') +option: Str('user*', alwaysask=True, cli_name='users', csv=True) +option: Str('version?', exclude='webui') +output: Output('completed', , None) +output: Output('failed', , None) +output: Entry('result', , Gettext('A dictionary representing an LDAP entry', domain='ipa', localedir=None)) command: vaultcontainer_del args: 1,4,3 arg: Str('cn', attribute=True, cli_name='container_name', maxlength=255, multivalue=True, pattern='^[a-zA-Z0-9_.-]+$', primary_key=True, query=True, required=True) @@ -4649,12 +4734,13 @@ output: Output('result', , None) output: Output('summary', (, ), None) output: ListOfPrimaryKeys('value', None, None) command: vaultcontainer_find -args: 1,10,4 +args: 1,11,4 arg: Str('criteria?', noextrawhitespace=False) option: Flag('all', autofill=True, cli_name='all', default=False, exclude='webui') option: Str('cn', attribute=True, autofill=False, cli_name='container_name', maxlength=255, multivalue=False, pattern='^[a-zA-Z0-9_.-]+$', primary_key=True, query=True, required=False) option: Str('container_id', attribute=False, autofill=False, cli_name='container_id', multivalue=False, query=True, required=False) option: Str('description', attribute=True, autofill=False, cli_name='desc', multivalue=False, query=True, required=False) +option: Flag('no_members', autofill=True, default=False, exclude='webui') option: Str('parent', attribute=False, autofill=False, cli_name='parent', multivalue=False, pattern='^[a-zA-Z0-9_.-/]+$', query=True, required=False) option: Flag('pkey_only?', autofill=True, default=False) option: Flag('raw', autofill=True, cli_name='raw', default=False, exclude='webui') @@ -4666,13 +4752,14 @@ output: ListOfEntries('result', (, ), Gettext('A list output: Output('summary', (, ), None) output: Output('truncated', , None) command: vaultcontainer_mod -args: 1,10,3 +args: 1,11,3 arg: Str('cn', attribute=True, cli_name='container_name', maxlength=255, multivalue=False, pattern='^[a-zA-Z0-9_.-]+$', primary_key=True, query=True, required=True) option: Str('addattr*', cli_name='addattr', exclude='webui') option: Flag('all', autofill=True, cli_name='all', default=False, exclude='webui') option: Str('container_id', attribute=False, autofill=False, cli_name='container_id', multivalue=False, required=False) option: Str('delattr*', cli_name='delattr', exclude='webui') option: Str('description', attribute=True, autofill=False, cli_name='desc', multivalue=False, required=False) +option: Flag('no_members', autofill=True, default=False, exclude='webui') option: Str('parent', attribute=False, autofill=False, cli_name='parent', multivalue=False, pattern='^[a-zA-Z0-9_.-/]+$', required=False) option: Flag('raw', autofill=True, cli_name='raw', default=False, exclude='webui') option: Flag('rights', autofill=True, default=False) @@ -4681,10 +4768,37 @@ option: Str('version?', exclude='webui') output: Entry('result', , Gettext('A dictionary representing an LDAP entry', domain='ipa', localedir=None)) output: Output('summary', (, ), None) output: PrimaryKey('value', None, None) +command: vaultcontainer_remove_member +args: 1,7,3 +arg: Str('cn', attribute=True, cli_name='container_name', maxlength=255, multivalue=False, pattern='^[a-zA-Z0-9_.-]+$', primary_key=True, query=True, required=True) +option: Flag('all', autofill=True, cli_name='all', default=False, exclude='webui') +option: Str('group*', alwaysask=True, cli_name='groups', csv=True) +option: Flag('no_members', autofill=True, default=False, exclude='webui') +option: Str('parent?', cli_name='parent') +option: Flag('raw', autofill=True, cli_name='raw', default=False, exclude='webui') +option: Str('user*', alwaysask=True, cli_name='users', csv=True) +option: Str('version?', exclude='webui') +output: Output('completed', , None) +output: Output('failed', , None) +output: Entry('result', , Gettext('A dictionary representing an LDAP entry', domain='ipa', localedir=None)) +command: vaultcontainer_remove_owner +args: 1,7,3 +arg: Str('cn', attribute=True, cli_name='container_name', maxlength=255, multivalue=False, pattern='^[a-zA-Z0-9_.-]+$', primary_key=True, query=True, required=True) +option: Flag('all', autofill=True, cli_name='all', default=False, exclude='webui') +option: Str('group*', alwaysask=True, cli_name='groups', csv=True) +option: Flag('no_members', autofill=True, default=False, exclude='webui') +option: Str('parent?', cli_name='parent') +option: Flag('raw', autofill=True, cli_name='raw', default=False, exclude='webui') +option: Str('user*', alwaysask=True, cli_name='users', csv=True) +option: Str('version?', exclude='webui') +output: Output('completed', , None) +output: Output('failed', , None) +output: Entry('result', , Gettext('A dictionary representing an LDAP entry', domain='ipa', localedir=None)) command: vaultcontainer_show -args: 1,5,3 +args: 1,6,3 arg: Str('cn', attribute=True, cli_name='container_name', maxlength=255, multivalue=False, pattern='^[a-zA-Z0-9_.-]+$', primary_key=True, query=True, required=True) option: Flag('all', autofill=True, cli_name='all', default=False, exclude='webui') +option: Flag('no_members', autofill=True, default=False, exclude='webui') option: Str('parent?', cli_name='parent') option: Flag('raw', autofill=True, cli_name='raw', default=False, exclude='webui') option: Flag('rights', autofill=True, default=False) diff --git a/install/share/60basev3.ldif b/install/share/60basev3.ldif index c04f8d5d096bc3d91aec3e2f1703f658d76d3779..cdabfac35488bda184af564a174306a681183084 100644 --- a/install/share/60basev3.ldif +++ b/install/share/60basev3.ldif @@ -77,5 +77,5 @@ objectClasses: (2.16.840.1.113730.3.8.12.24 NAME 'ipaPublicKeyObject' DESC 'Wrap objectClasses: (2.16.840.1.113730.3.8.12.25 NAME 'ipaPrivateKeyObject' DESC 'Wrapped private keys' SUP top AUXILIARY MUST ( ipaPrivateKey $ ipaWrappingKey $ ipaWrappingMech ) X-ORIGIN 'IPA v4.1' ) objectClasses: (2.16.840.1.113730.3.8.12.26 NAME 'ipaSecretKeyObject' DESC 'Wrapped secret keys' SUP top AUXILIARY MUST ( ipaSecretKey $ ipaWrappingKey $ ipaWrappingMech ) X-ORIGIN 'IPA v4.1' ) objectClasses: (2.16.840.1.113730.3.8.12.34 NAME 'ipaSecretKeyRefObject' DESC 'Indirect storage for encoded key material' SUP top AUXILIARY MUST ( ipaSecretKeyRef ) X-ORIGIN 'IPA v4.1' ) -objectClasses: (2.16.840.1.113730.3.8.18.1.1 NAME 'ipaVault' DESC 'IPA vault' SUP top STRUCTURAL MUST ( cn ) MAY ( description ) X-ORIGIN 'IPA v4.2' ) -objectClasses: (2.16.840.1.113730.3.8.18.1.2 NAME 'ipaVaultContainer' DESC 'IPA vault container' SUP top STRUCTURAL MUST ( cn ) MAY ( description ) X-ORIGIN 'IPA v4.2' ) +objectClasses: (2.16.840.1.113730.3.8.18.1.1 NAME 'ipaVault' DESC 'IPA vault' SUP top STRUCTURAL MUST ( cn ) MAY ( description $ owner $ member ) X-ORIGIN 'IPA v4.2' ) +objectClasses: (2.16.840.1.113730.3.8.18.1.2 NAME 'ipaVaultContainer' DESC 'IPA vault container' SUP top STRUCTURAL MUST ( cn ) MAY ( description $ owner $ member ) X-ORIGIN 'IPA v4.2' ) diff --git a/install/updates/40-vault.update b/install/updates/40-vault.update index dac2f67112dc33f012c6d559285464fb7c944d1a..4f36a8066504fb3577fc9f78c830412e9702f617 100644 --- a/install/updates/40-vault.update +++ b/install/updates/40-vault.update @@ -3,6 +3,13 @@ default: objectClass: top default: objectClass: ipaVaultContainer default: cn: vaults default: description: Root vault container +default: aci: (target="ldap:///cn=*,cn=users,cn=vaults,$SUFFIX")(targetattr="*")(version 3.0; acl "Allow add macro dn"; allow (add) userdn = "ldap:///uid=($$attr.cn),cn=users,cn=accounts,$SUFFIX";) +default: aci: (targetfilter="(objectClass=ipaVaultContainer)")(targetattr="*")(version 3.0; acl "Container members can access the container"; allow(read, search, compare) userattr="member#USERDN";) +default: aci: (targetfilter="(objectClass=ipaVaultContainer)")(targetattr="*")(version 3.0; acl "Container owners can modify the container"; allow(read, search, compare, write) userattr="owner#USERDN";) +default: aci: (targetfilter="(objectClass=ipaVaultContainer)")(targetattr="*")(version 3.0; acl "Container owners can manage sub-containers"; allow(read, search, compare, add, delete) userattr="parent[1].owner#USERDN";) +default: aci: (targetfilter="(objectClass=ipaVault)")(targetattr="*")(version 3.0; acl "Container owners can manage vaults in the container"; allow(read, search, compare, add, delete) userattr="parent[1].owner#USERDN";) +default: aci: (targetfilter="(objectClass=ipaVault)")(targetattr="*")(version 3.0; acl "Vault members can access the vault"; allow(read, search, compare) userattr="member#USERDN";) +default: aci: (targetfilter="(objectClass=ipaVault)")(targetattr="*")(version 3.0; acl "Vault owners can modify the vault"; allow(read, search, compare, write) userattr="owner#USERDN";) dn: cn=services,cn=vaults,$SUFFIX default: objectClass: top diff --git a/ipalib/plugins/vault.py b/ipalib/plugins/vault.py index 82a14ba42d82055a0652349ccc945b8485404c6c..9ae38b472b883c21eaa3b0bdb99e22ce89205ab2 100644 --- a/ipalib/plugins/vault.py +++ b/ipalib/plugins/vault.py @@ -33,7 +33,10 @@ from ipalib.frontend import Command from ipalib import api, errors from ipalib import Str, Bytes, Flag from ipalib.plugable import Registry -from ipalib.plugins.baseldap import LDAPObject, LDAPCreate, LDAPDelete, LDAPSearch, LDAPUpdate, LDAPRetrieve +from ipalib.plugins.baseldap import LDAPObject, LDAPCreate, LDAPDelete, LDAPSearch, LDAPUpdate, LDAPRetrieve,\ + LDAPAddMember, LDAPRemoveMember +from ipalib.request import context +from ipalib.plugins.user import split_principal from ipalib import _, ngettext from ipaplatform.paths import paths from ipapython.dn import DN @@ -69,6 +72,18 @@ EXAMPLES: """) + _(""" Delete a vault: ipa vault-del MyVault +""") + _(""" + Add a vault owner: + ipa vault-add-owner MyVault --users testuser +""") + _(""" + Delete a vault owner: + ipa vault-remove-owner MyVault --users testuser +""") + _(""" + Add a vault member: + ipa vault-add-member MyVault --users testuser +""") + _(""" + Delete a vault member: + ipa vault-remove-member MyVault --users testuser """) register = Registry() @@ -87,6 +102,8 @@ class vault(LDAPObject): 'cn', 'vault_id', 'description', + 'owner', + 'member', ] search_display_attributes = [ 'cn', @@ -94,6 +111,10 @@ class vault(LDAPObject): 'description', 'ipavaulttype', ] + attribute_members = { + 'owner': ['user', 'group'], + 'member': ['user', 'group'], + } label = _('Vaults') label_singular = _('Vault') @@ -272,8 +293,13 @@ class vault_add(LDAPCreate): def pre_callback(self, ldap, dn, entry_attrs, attrs_list, *keys, **options): assert isinstance(dn, DN) + principal = getattr(context, 'principal') + (username, realm) = split_principal(principal) + owner_dn = self.api.Object['user'].get_dn(username) + entry_attrs['owner'] = owner_dn + container_dn = DN(*dn[1:]) - api.Object.vaultcontainer.create_entry(container_dn) + api.Object.vaultcontainer.create_entry(container_dn, owner=owner_dn) return dn @@ -334,10 +360,14 @@ class vault_find(LDAPSearch): def pre_callback(self, ldap, filter, attrs_list, base_dn, scope, *keys, **options): assert isinstance(base_dn, DN) + principal = getattr(context, 'principal') + (username, realm) = split_principal(principal) + owner_dn = self.api.Object['user'].get_dn(username) + container_id = self.Object.vaultcontainer.normalize_id(options.get('container')) base_dn = self.Object.vaultcontainer.get_dn(parent=container_id) - api.Object.vaultcontainer.create_entry(base_dn) + api.Object.vaultcontainer.create_entry(base_dn, owner=owner_dn) return (filter, base_dn, scope) @@ -568,6 +598,17 @@ class vault_archive(LDAPRetrieve): assert isinstance(dn, DN) vault_id = self.obj.get_id(dn) + + owners = entry_attrs.get('owner', []) + members = entry_attrs.get('member', []) + + principal = getattr(context, 'principal') + (username, realm) = split_principal(principal) + user_dn = self.api.Object['user'].get_dn(username) + + if user_dn not in owners and user_dn not in members: + raise errors.ACIError(info=_("Insufficient access to vault '%s'.") % vault_id) + entry_attrs['vault_id'] = vault_id # connect to KRA @@ -728,6 +769,17 @@ class vault_retrieve(LDAPRetrieve): assert isinstance(dn, DN) vault_id = self.obj.get_id(dn) + + owners = entry_attrs.get('owner', []) + members = entry_attrs.get('member', []) + + principal = getattr(context, 'principal') + (username, realm) = split_principal(principal) + user_dn = self.api.Object['user'].get_dn(username) + + if user_dn not in owners and user_dn not in members: + raise errors.ACIError(info=_("Insufficient access to vault '%s'.") % vault_id) + entry_attrs['vault_id'] = vault_id wrapped_session_key = base64.b64decode(options['session_key']) @@ -761,3 +813,85 @@ class vault_retrieve(LDAPRetrieve): kra_account.logout() return dn + + + at register() +class vault_add_owner(LDAPAddMember): + __doc__ = _('Add owners to a vault.') + + takes_options = LDAPAddMember.takes_options + ( + Str('container?', + cli_name='container', + doc=_('Container'), + ), + ) + + member_attributes = ['owner'] + member_count_out = ('%i owner added.', '%i owners added.') + + def post_callback(self, ldap, completed, failed, dn, entry_attrs, *keys, **options): + assert isinstance(dn, DN) + + entry_attrs['vault_id'] = self.obj.get_id(dn) + + return (completed, dn) + + + at register() +class vault_remove_owner(LDAPRemoveMember): + __doc__ = _('Remove owners from a vault.') + + takes_options = LDAPRemoveMember.takes_options + ( + Str('container?', + cli_name='container', + doc=_('Container'), + ), + ) + + member_attributes = ['owner'] + member_count_out = ('%i owner removed.', '%i owners removed.') + + def post_callback(self, ldap, completed, failed, dn, entry_attrs, *keys, **options): + assert isinstance(dn, DN) + + entry_attrs['vault_id'] = self.obj.get_id(dn) + + return (completed, dn) + + + at register() +class vault_add_member(LDAPAddMember): + __doc__ = _('Add members to a vault.') + + takes_options = LDAPAddMember.takes_options + ( + Str('container?', + cli_name='container', + doc=_('Container'), + ), + ) + + def post_callback(self, ldap, completed, failed, dn, entry_attrs, *keys, **options): + assert isinstance(dn, DN) + + entry_attrs['vault_id'] = self.obj.get_id(dn) + + return (completed, dn) + + + at register() +class vault_remove_member(LDAPRemoveMember): + __doc__ = _('Remove members from a vault.') + + takes_options = LDAPRemoveMember.takes_options + ( + Str('container?', + cli_name='container', + doc=_('Container'), + ), + ) + + def post_callback(self, ldap, completed, failed, dn, entry_attrs, *keys, **options): + assert isinstance(dn, DN) + + entry_attrs['vault_id'] = self.obj.get_id(dn) + + return (completed, dn) diff --git a/ipalib/plugins/vaultcontainer.py b/ipalib/plugins/vaultcontainer.py index 22d36eecc6a842240cbadd3ff6b6518efbfe524e..881bbea7886e06356489cd49cc32f2a6a6460a5e 100644 --- a/ipalib/plugins/vaultcontainer.py +++ b/ipalib/plugins/vaultcontainer.py @@ -22,7 +22,8 @@ import base64 from ipalib import api, errors from ipalib import Str, Flag from ipalib.plugable import Registry -from ipalib.plugins.baseldap import LDAPObject, LDAPCreate, LDAPDelete, LDAPSearch, LDAPUpdate, LDAPRetrieve +from ipalib.plugins.baseldap import LDAPObject, LDAPCreate, LDAPDelete, LDAPSearch, LDAPUpdate, LDAPRetrieve,\ + LDAPAddMember, LDAPRemoveMember from ipalib.request import context from ipalib.plugins.user import split_principal from ipalib import _, ngettext @@ -52,6 +53,18 @@ EXAMPLES: """) + _(""" Delete a vault container: ipa vaultcontainer-del MyContainer +""") + _(""" + Add a vault container owner: + ipa vaultcontainer-add-owner MyContainer --users testuser +""") + _(""" + Delete a vault container owner: + ipa vaultcontainer-remove-owner MyContainer --users testuser +""") + _(""" + Add a vault container member: + ipa vaultcontainer-add-member MyContainer --users testuser +""") + _(""" + Delete a vault container member: + ipa vaultcontainer-remove-member MyContainer --users testuser """) register = Registry() @@ -71,12 +84,18 @@ class vaultcontainer(LDAPObject): 'cn', 'container_id', 'description', + 'owner', + 'member', ] search_display_attributes = [ 'cn', 'container_id', 'description', ] + attribute_members = { + 'owner': ['user', 'group'], + 'member': ['user', 'group'], + } label = _('Vault Containers') label_singular = _('Vault Container') @@ -203,7 +222,7 @@ class vaultcontainer(LDAPObject): # return container name and parent ID return (parts[1], parts[0] + u'/') - def create_entry(self, dn): + def create_entry(self, dn, owner=None): __doc__ = _(""" Creates a container entry and its parents. """) @@ -218,7 +237,7 @@ class vaultcontainer(LDAPObject): # otherwise, create parent entry first parent_dn = DN(*dn[1:]) - self.create_entry(parent_dn) + self.create_entry(parent_dn, owner=owner) # then create the entry itself rdn = dn[0] @@ -227,6 +246,7 @@ class vaultcontainer(LDAPObject): { 'objectclass': self.object_class, 'cn': rdn['cn'], + 'owner': owner }) self.backend.add_entry(entry) @@ -240,8 +260,13 @@ class vaultcontainer_add(LDAPCreate): def pre_callback(self, ldap, dn, entry_attrs, attrs_list, *keys, **options): assert isinstance(dn, DN) + principal = getattr(context, 'principal') + (username, realm) = split_principal(principal) + owner_dn = self.api.Object['user'].get_dn(username) + entry_attrs['owner'] = owner_dn + parent_dn = DN(*dn[1:]) - self.obj.create_entry(parent_dn) + self.obj.create_entry(parent_dn, owner=owner_dn) return dn @@ -313,10 +338,14 @@ class vaultcontainer_find(LDAPSearch): def pre_callback(self, ldap, filter, attrs_list, base_dn, scope, *keys, **options): assert isinstance(base_dn, DN) + principal = getattr(context, 'principal') + (username, realm) = split_principal(principal) + owner_dn = self.api.Object['user'].get_dn(username) + parent_id = self.obj.normalize_id(options.get('parent')) base_dn = self.obj.get_dn(parent=parent_id) - self.obj.create_entry(base_dn) + self.obj.create_entry(base_dn, owner=owner_dn) return (filter, base_dn, scope) @@ -359,3 +388,86 @@ class vaultcontainer_show(LDAPRetrieve): entry_attrs['container_id'] = self.obj.get_id(dn) return dn + + + at register() +class vaultcontainer_add_owner(LDAPAddMember): + __doc__ = _('Add owners to a vault container.') + + takes_options = LDAPAddMember.takes_options + ( + Str('parent?', + cli_name='parent', + doc=_('Parent container'), + ), + ) + + member_attributes = ['owner'] + member_count_out = ('%i owner added.', '%i owners added.') + + def post_callback(self, ldap, completed, failed, dn, entry_attrs, *keys, **options): + assert isinstance(dn, DN) + + entry_attrs['container_id'] = self.obj.get_id(dn) + + return (completed, dn) + + + at register() +class vaultcontainer_remove_owner(LDAPRemoveMember): + __doc__ = _('Remove owners from a vault container.') + + takes_options = LDAPRemoveMember.takes_options + ( + Str('parent?', + cli_name='parent', + doc=_('Parent container'), + ), + ) + + member_attributes = ['owner'] + member_count_out = ('%i owner removed.', '%i owners removed.') + + def post_callback(self, ldap, completed, failed, dn, entry_attrs, *keys, **options): + assert isinstance(dn, DN) + + entry_attrs['container_id'] = self.obj.get_id(dn) + + return (completed, dn) + + + at register() +class vaultcontainer_add_member(LDAPAddMember): + __doc__ = _('Add members to a vault container.') + + takes_options = LDAPAddMember.takes_options + ( + Str('parent?', + cli_name='parent', + doc=_('Parent container'), + ), + ) + + def post_callback(self, ldap, completed, failed, dn, entry_attrs, *keys, **options): + assert isinstance(dn, DN) + + entry_attrs['container_id'] = self.obj.get_id(dn) + + return (completed, dn) + + + at register() +class vaultcontainer_remove_member(LDAPRemoveMember): + __doc__ = _('Remove members from a vault container.') + + + takes_options = LDAPRemoveMember.takes_options + ( + Str('parent?', + cli_name='parent', + doc=_('Parent container'), + ), + ) + + def post_callback(self, ldap, completed, failed, dn, entry_attrs, *keys, **options): + assert isinstance(dn, DN) + + entry_attrs['container_id'] = self.obj.get_id(dn) + + return (completed, dn) diff --git a/ipatests/test_xmlrpc/test_vault_plugin.py b/ipatests/test_xmlrpc/test_vault_plugin.py index 9857ffbda7a768ac0522989c3ccfa3fa1bf1f5a4..b7f2a14a1971d5c1f0c93bcf7dd8949bc047dd55 100644 --- a/ipatests/test_xmlrpc/test_vault_plugin.py +++ b/ipatests/test_xmlrpc/test_vault_plugin.py @@ -51,6 +51,7 @@ class test_vault(Declarative): 'objectclass': (u'ipaVault', u'top'), 'cn': [test_vault], 'vault_id': u'/users/admin/%s' % test_vault, + 'owner_user': [u'admin'], }, }, }, @@ -100,6 +101,7 @@ class test_vault(Declarative): 'dn': u'cn=%s,cn=admin,cn=users,cn=vaults,%s' % (test_vault, api.env.basedn), 'cn': [test_vault], 'vault_id': u'/users/admin/%s' % test_vault, + 'owner_user': [u'admin'], }, }, }, @@ -120,6 +122,7 @@ class test_vault(Declarative): 'cn': [test_vault], 'vault_id': u'/users/admin/%s' % test_vault, 'description': [u'Test vault'], + 'owner_user': [u'admin'], }, }, }, @@ -141,6 +144,7 @@ class test_vault(Declarative): 'cn': [test_vault], 'vault_id': u'/users/admin/%s' % test_vault, 'description': [u'Test vault'], + 'owner_user': [u'admin'], }, }, }, @@ -160,6 +164,7 @@ class test_vault(Declarative): 'cn': [test_vault], 'vault_id': u'/users/admin/%s' % test_vault, 'description': [u'Test vault'], + 'owner_user': [u'admin'], 'nonce': fuzzy_string, 'vault_data': fuzzy_string, 'data': binary_data, @@ -184,6 +189,7 @@ class test_vault(Declarative): 'cn': [test_vault], 'vault_id': u'/users/admin/%s' % test_vault, 'description': [u'Test vault'], + 'owner_user': [u'admin'], }, }, }, @@ -205,6 +211,7 @@ class test_vault(Declarative): 'cn': [test_vault], 'vault_id': u'/users/admin/%s' % test_vault, 'description': [u'Test vault'], + 'owner_user': [u'admin'], 'nonce': fuzzy_string, 'vault_data': fuzzy_string, 'text': text_data, diff --git a/ipatests/test_xmlrpc/test_vaultcontainer_plugin.py b/ipatests/test_xmlrpc/test_vaultcontainer_plugin.py index 957228b8f57623e93cda9a5295f9977dd6b84110..b99f9f68b8a480908602503d726205eeec36c2d2 100644 --- a/ipatests/test_xmlrpc/test_vaultcontainer_plugin.py +++ b/ipatests/test_xmlrpc/test_vaultcontainer_plugin.py @@ -94,6 +94,7 @@ class test_vault(Declarative): 'objectclass': (u'ipaVaultContainer', u'top'), 'cn': [private_container], 'container_id': u'/users/admin/%s/' % private_container, + 'owner_user': [u'admin'], }, }, }, @@ -143,6 +144,7 @@ class test_vault(Declarative): 'dn': u'cn=%s,cn=admin,cn=users,cn=vaults,%s' % (private_container, api.env.basedn), 'cn': [private_container], 'container_id': u'/users/admin/%s/' % private_container, + 'owner_user': [u'admin'], }, }, }, @@ -163,6 +165,7 @@ class test_vault(Declarative): 'cn': [private_container], 'container_id': u'/users/admin/%s/' % private_container, 'description': [u'Private container'], + 'owner_user': [u'admin'], }, }, }, @@ -210,6 +213,7 @@ class test_vault(Declarative): 'objectclass': (u'ipaVaultContainer', u'top'), 'cn': [shared_container], 'container_id': u'/shared/%s/' % shared_container, + 'owner_user': [u'admin'], }, }, }, @@ -253,6 +257,7 @@ class test_vault(Declarative): 'dn': u'cn=%s,cn=shared,cn=vaults,%s' % (shared_container, api.env.basedn), 'cn': [shared_container], 'container_id': u'/shared/%s/' % shared_container, + 'owner_user': [u'admin'], }, }, }, @@ -274,6 +279,7 @@ class test_vault(Declarative): 'cn': [shared_container], 'container_id': u'/shared/%s/' % shared_container, 'description': [u'shared container'], + 'owner_user': [u'admin'], }, }, }, @@ -313,6 +319,7 @@ class test_vault(Declarative): 'objectclass': (u'ipaVaultContainer', u'top'), 'cn': [service_container], 'container_id': u'/services/%s/' % service_container, + 'owner_user': [u'admin'], }, }, }, @@ -331,6 +338,7 @@ class test_vault(Declarative): 'objectclass': (u'ipaVaultContainer', u'top'), 'cn': [base_container], 'container_id': u'/users/admin/%s/' % base_container, + 'owner_user': [u'admin'], }, }, }, @@ -352,6 +360,7 @@ class test_vault(Declarative): 'objectclass': (u'ipaVaultContainer', u'top'), 'cn': [child_container], 'container_id': u'/users/admin/%s/%s/' % (base_container, child_container), + 'owner_user': [u'admin'], }, }, }, @@ -375,6 +384,7 @@ class test_vault(Declarative): 'cn': [grandchild_container], 'container_id': u'/users/admin/%s/%s/%s/' % (base_container, child_container, grandchild_container), + 'owner_user': [u'admin'], }, }, }, -- 1.9.0 -------------- next part -------------- >From 5a5dc7cb80dfa2569db2f786d9564407d759c162 Mon Sep 17 00:00:00 2001 From: "Endi S. Dewata" Date: Fri, 24 Oct 2014 19:53:16 -0400 Subject: [PATCH] Added symmetric and asymmetric vaults. The IPA vault has been modified to support symmetric and asymmetric vaults to allow client to pre-encrypt the data before it's sent to the server. The encryption functionality is implemented using the python-cryptography library. New LDAP attribute types have been added to store vault type, salt and public key. New test cases have been added for the new vault types. https://fedorahosted.org/freeipa/ticket/3872 --- API.txt | 29 +- freeipa.spec.in | 2 + install/share/60basev3.ldif | 4 +- ipalib/plugins/vault.py | 467 +++++++++++++++++++++++++++++- ipatests/test_xmlrpc/test_vault_plugin.py | 217 ++++++++++++++ 5 files changed, 710 insertions(+), 9 deletions(-) diff --git a/API.txt b/API.txt index 6babee618c1214f0678e2f063a41d0db43ed75b3..8e7e4f9615a75e4cf365d07d0ba669060a541679 100644 --- a/API.txt +++ b/API.txt @@ -4518,7 +4518,7 @@ output: Output('result', , None) output: Output('summary', (, ), None) output: PrimaryKey('value', None, None) command: vault_add -args: 1,12,3 +args: 1,18,3 arg: Str('cn', attribute=True, cli_name='vault_name', maxlength=255, multivalue=False, pattern='^[a-zA-Z0-9_.-]+$', primary_key=True, required=True) option: Str('addattr*', cli_name='addattr', exclude='webui') option: Flag('all', autofill=True, cli_name='all', default=False, exclude='webui') @@ -4526,7 +4526,13 @@ option: Str('container', attribute=False, cli_name='container', multivalue=False option: Bytes('data?', cli_name='data') option: Str('description', attribute=True, cli_name='desc', multivalue=False, required=False) option: Str('in?', cli_name='in') +option: Bytes('ipapublickey', attribute=True, cli_name='public_key', multivalue=False, required=False) +option: Str('ipavaultsalt', attribute=True, cli_name='salt', multivalue=False, required=False) +option: Str('ipavaulttype', attribute=True, autofill=True, cli_name='type', default=u'standard', multivalue=False, required=False) option: Flag('no_members', autofill=True, default=False, exclude='webui') +option: Str('password?', cli_name='password') +option: Str('password_file?', cli_name='password_file') +option: Str('public_key_file?', cli_name='public_key_file') option: Flag('raw', autofill=True, cli_name='raw', default=False, exclude='webui') option: Str('setattr*', cli_name='setattr', exclude='webui') option: Str('text?', cli_name='text') @@ -4562,14 +4568,17 @@ output: Output('completed', , None) output: Output('failed', , None) output: Entry('result', , Gettext('A dictionary representing an LDAP entry', domain='ipa', localedir=None)) command: vault_archive -args: 1,12,3 +args: 1,15,3 arg: Str('cn', attribute=True, cli_name='vault_name', maxlength=255, multivalue=False, pattern='^[a-zA-Z0-9_.-]+$', primary_key=True, query=True, required=True) option: Flag('all', autofill=True, cli_name='all', default=False, exclude='webui') option: Str('container?', cli_name='container') option: Bytes('data?', cli_name='data') +option: Bytes('encryption_key?', cli_name='encryption_key') option: Str('in?', cli_name='in') option: Flag('no_members', autofill=True, default=False, exclude='webui') option: Str('nonce?', cli_name='nonce') +option: Str('password?', cli_name='password') +option: Str('password_file?', cli_name='password_file') option: Flag('raw', autofill=True, cli_name='raw', default=False, exclude='webui') option: Flag('rights', autofill=True, default=False) option: Str('session_key?', cli_name='session_key') @@ -4589,12 +4598,15 @@ output: Output('result', , None) output: Output('summary', (, ), None) output: ListOfPrimaryKeys('value', None, None) command: vault_find -args: 1,11,4 +args: 1,14,4 arg: Str('criteria?', noextrawhitespace=False) option: Flag('all', autofill=True, cli_name='all', default=False, exclude='webui') option: Str('cn', attribute=True, autofill=False, cli_name='vault_name', maxlength=255, multivalue=False, pattern='^[a-zA-Z0-9_.-]+$', primary_key=True, query=True, required=False) option: Str('container', attribute=False, autofill=False, cli_name='container', multivalue=False, pattern='^[a-zA-Z0-9_.-/]+$', query=True, required=False) option: Str('description', attribute=True, autofill=False, cli_name='desc', multivalue=False, query=True, required=False) +option: Bytes('ipapublickey', attribute=True, autofill=False, cli_name='public_key', multivalue=False, query=True, required=False) +option: Str('ipavaultsalt', attribute=True, autofill=False, cli_name='salt', multivalue=False, query=True, required=False) +option: Str('ipavaulttype', attribute=True, autofill=False, cli_name='type', default=u'standard', multivalue=False, query=True, required=False) option: Flag('no_members', autofill=True, default=False, exclude='webui') option: Flag('pkey_only?', autofill=True, default=False) option: Flag('raw', autofill=True, cli_name='raw', default=False, exclude='webui') @@ -4607,13 +4619,16 @@ output: ListOfEntries('result', (, ), Gettext('A list output: Output('summary', (, ), None) output: Output('truncated', , None) command: vault_mod -args: 1,11,3 +args: 1,14,3 arg: Str('cn', attribute=True, cli_name='vault_name', maxlength=255, multivalue=False, pattern='^[a-zA-Z0-9_.-]+$', primary_key=True, query=True, required=True) option: Str('addattr*', cli_name='addattr', exclude='webui') option: Flag('all', autofill=True, cli_name='all', default=False, exclude='webui') option: Str('container', attribute=False, autofill=False, cli_name='container', multivalue=False, pattern='^[a-zA-Z0-9_.-/]+$', required=False) option: Str('delattr*', cli_name='delattr', exclude='webui') option: Str('description', attribute=True, autofill=False, cli_name='desc', multivalue=False, required=False) +option: Bytes('ipapublickey', attribute=True, autofill=False, cli_name='public_key', multivalue=False, required=False) +option: Str('ipavaultsalt', attribute=True, autofill=False, cli_name='salt', multivalue=False, required=False) +option: Str('ipavaulttype', attribute=True, autofill=False, cli_name='type', default=u'standard', multivalue=False, required=False) option: Flag('no_members', autofill=True, default=False, exclude='webui') option: Flag('raw', autofill=True, cli_name='raw', default=False, exclude='webui') option: Flag('rights', autofill=True, default=False) @@ -4650,12 +4665,16 @@ output: Output('completed', , None) output: Output('failed', , None) output: Entry('result', , Gettext('A dictionary representing an LDAP entry', domain='ipa', localedir=None)) command: vault_retrieve -args: 1,10,3 +args: 1,14,3 arg: Str('cn', attribute=True, cli_name='vault_name', maxlength=255, multivalue=False, pattern='^[a-zA-Z0-9_.-]+$', primary_key=True, query=True, required=True) option: Flag('all', autofill=True, cli_name='all', default=False, exclude='webui') option: Str('container?', cli_name='container') option: Flag('no_members', autofill=True, default=False, exclude='webui') option: Str('out?', cli_name='out') +option: Str('password?', cli_name='password') +option: Str('password_file?', cli_name='password_file') +option: Bytes('private_key?', cli_name='private_key') +option: Str('private_key_file?', cli_name='private_key_file') option: Flag('raw', autofill=True, cli_name='raw', default=False, exclude='webui') option: Flag('rights', autofill=True, default=False) option: Str('session_key?', cli_name='session_key') diff --git a/freeipa.spec.in b/freeipa.spec.in index b186d9fdff31118ea4d929f024f4dc16a75b1d0b..e4f92d6b23925eef6714ca53c89778efae2becb6 100644 --- a/freeipa.spec.in +++ b/freeipa.spec.in @@ -94,6 +94,7 @@ BuildRequires: p11-kit-devel BuildRequires: pki-base >= 10.2.1-0.1 BuildRequires: python-pytest-multihost >= 0.5 BuildRequires: python-pytest-sourceorder +BuildRequires: python-cryptography %description IPA is an integrated solution to provide centrally managed Identity (machine, @@ -149,6 +150,7 @@ Requires: openssl Requires: softhsm >= 2.0.0b1-3 Requires: p11-kit Requires: systemd-python +Requires: python-cryptography Conflicts: %{alt_name}-server Obsoletes: %{alt_name}-server < %{version} diff --git a/install/share/60basev3.ldif b/install/share/60basev3.ldif index cdabfac35488bda184af564a174306a681183084..9e0f70a41ef50e78d4e464bab428325dfb6568fa 100644 --- a/install/share/60basev3.ldif +++ b/install/share/60basev3.ldif @@ -54,6 +54,8 @@ attributeTypes: (2.16.840.1.113730.3.8.11.55 NAME 'ipaSecretKey' DESC 'Encrypted attributeTypes: (2.16.840.1.113730.3.8.11.61 NAME 'ipaWrappingKey' DESC 'PKCS#11 URI of the wrapping key' EQUALITY caseExactMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 SINGLE-VALUE X-ORIGIN 'IPA v4.1' ) attributeTypes: (2.16.840.1.113730.3.8.11.64 NAME 'ipaSecretKeyRef' DESC 'DN of the ipa key object' EQUALITY distinguishedNameMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.12 X-ORIGIN 'IPA v4.1' ) attributeTypes: (2.16.840.1.113730.3.8.11.65 NAME 'ipaWrappingMech' DESC 'PKCS#11 wrapping mechanism equivalent to CK_MECHANISM_TYPE' EQUALITY caseIgnoreMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 SINGLE-VALUE X-ORIGIN 'IPA v4.1') +attributeTypes: (2.16.840.1.113730.3.8.18.2.1 NAME 'ipaVaultType' DESC 'IPA vault type' EQUALITY caseExactMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 X-ORIGIN 'IPA v4.2') +attributeTypes: (2.16.840.1.113730.3.8.18.2.2 NAME 'ipaVaultSalt' DESC 'IPA vault salt' EQUALITY caseExactMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 X-ORIGIN 'IPA v4.2') objectClasses: (2.16.840.1.113730.3.8.12.1 NAME 'ipaExternalGroup' SUP top STRUCTURAL MUST ( cn ) MAY ( ipaExternalMember $ memberOf $ description $ owner) X-ORIGIN 'IPA v3' ) objectClasses: (2.16.840.1.113730.3.8.12.2 NAME 'ipaNTUserAttrs' SUP top AUXILIARY MUST ( ipaNTSecurityIdentifier ) MAY ( ipaNTHash $ ipaNTLogonScript $ ipaNTProfilePath $ ipaNTHomeDirectory $ ipaNTHomeDirectoryDrive ) X-ORIGIN 'IPA v3' ) objectClasses: (2.16.840.1.113730.3.8.12.3 NAME 'ipaNTGroupAttrs' SUP top AUXILIARY MUST ( ipaNTSecurityIdentifier ) X-ORIGIN 'IPA v3' ) @@ -77,5 +79,5 @@ objectClasses: (2.16.840.1.113730.3.8.12.24 NAME 'ipaPublicKeyObject' DESC 'Wrap objectClasses: (2.16.840.1.113730.3.8.12.25 NAME 'ipaPrivateKeyObject' DESC 'Wrapped private keys' SUP top AUXILIARY MUST ( ipaPrivateKey $ ipaWrappingKey $ ipaWrappingMech ) X-ORIGIN 'IPA v4.1' ) objectClasses: (2.16.840.1.113730.3.8.12.26 NAME 'ipaSecretKeyObject' DESC 'Wrapped secret keys' SUP top AUXILIARY MUST ( ipaSecretKey $ ipaWrappingKey $ ipaWrappingMech ) X-ORIGIN 'IPA v4.1' ) objectClasses: (2.16.840.1.113730.3.8.12.34 NAME 'ipaSecretKeyRefObject' DESC 'Indirect storage for encoded key material' SUP top AUXILIARY MUST ( ipaSecretKeyRef ) X-ORIGIN 'IPA v4.1' ) -objectClasses: (2.16.840.1.113730.3.8.18.1.1 NAME 'ipaVault' DESC 'IPA vault' SUP top STRUCTURAL MUST ( cn ) MAY ( description $ owner $ member ) X-ORIGIN 'IPA v4.2' ) +objectClasses: (2.16.840.1.113730.3.8.18.1.1 NAME 'ipaVault' DESC 'IPA vault' SUP top STRUCTURAL MUST ( cn ) MAY ( description $ owner $ member $ ipaVaultType $ ipaVaultSalt $ ipaPublicKey ) X-ORIGIN 'IPA v4.2' ) objectClasses: (2.16.840.1.113730.3.8.18.1.2 NAME 'ipaVaultContainer' DESC 'IPA vault container' SUP top STRUCTURAL MUST ( cn ) MAY ( description $ owner $ member ) X-ORIGIN 'IPA v4.2' ) diff --git a/ipalib/plugins/vault.py b/ipalib/plugins/vault.py index 9ae38b472b883c21eaa3b0bdb99e22ce89205ab2..e1a4711bf835eb3f102bbe06f29dd19681912624 100644 --- a/ipalib/plugins/vault.py +++ b/ipalib/plugins/vault.py @@ -18,11 +18,19 @@ # along with this program. If not, see . import base64 +import getpass import json import os import sys import tempfile +from cryptography.fernet import Fernet, InvalidToken +from cryptography.hazmat.backends import default_backend +from cryptography.hazmat.primitives import hashes +from cryptography.hazmat.primitives.kdf.pbkdf2 import PBKDF2HMAC +from cryptography.hazmat.primitives.asymmetric import padding +from cryptography.hazmat.primitives.serialization import load_pem_public_key, load_pem_private_key + import nss.nss as nss import pki.account @@ -58,6 +66,12 @@ EXAMPLES: Add a standard vault: ipa vault-add MyVault """) + _(""" + Add a symmetric vault: + ipa vault-add MyVault --type symmetric --password-file password.txt +""") + _(""" + Add an asymmetric vault: + ipa vault-add MyVault --type asymmetric --public-key-file public.pem +""") + _(""" Show a vault: ipa vault-show MyVault """) + _(""" @@ -70,6 +84,21 @@ EXAMPLES: Retrieve data from standard vault: ipa vault-retrieve MyVault --out data.bin """) + _(""" + Archive data into symmetric vault: + ipa vault-archive MyVault --in data.bin --password-file password.txt +""") + _(""" + Archive data into asymmetric vault: + ipa vault-archive MyVault --in data.bin +""") + _(""" + Retrieve data from standard vault: + ipa vault-retrieve MyVault --out data.bin +""") + _(""" + Retrieve data from symmetric vault: + ipa vault-retrieve MyVault --out data.bin --password-file password.txt +""") + _(""" + Retrieve data from asymmetric vault: + ipa vault-retrieve MyVault --out data.bin --private-key-file private.pem +""") + _(""" Delete a vault: ipa vault-del MyVault """) + _(""" @@ -104,6 +133,9 @@ class vault(LDAPObject): 'description', 'owner', 'member', + 'ipavaulttype', + 'ipavaultsalt', + 'ipapublickey', ] search_display_attributes = [ 'cn', @@ -147,6 +179,23 @@ class vault(LDAPObject): label=_('Description'), doc=_('Vault description'), ), + Str('ipavaulttype?', + cli_name='type', + label=_('Type'), + doc=_('Vault type'), + default=u'standard', + autofill=True, + ), + Str('ipavaultsalt?', + cli_name='salt', + label=_('Salt'), + doc=_('Vault salt in base-64'), + ), + Bytes('ipapublickey?', + cli_name='public_key', + label=_('Public key'), + doc=_('Vault public key'), + ), ) def get_dn(self, *keys, **options): @@ -216,6 +265,71 @@ class vault(LDAPObject): """) return 'ipa:' + id + def generate_symmetric_key(self, password, salt): + """ + Generates symmetric key from password and salt. + """ + kdf = PBKDF2HMAC( + algorithm=hashes.SHA256(), + length=32, + salt=salt, + iterations=100000, + backend=default_backend() + ) + + return base64.b64encode(kdf.derive(password.encode('utf-8'))) + + def encrypt(self, data, symmetric_key=None, public_key=None): + """ + Encrypts data with symmetric key or public key. + """ + if symmetric_key: + fernet = Fernet(symmetric_key) + return fernet.encrypt(data) + + elif public_key: + rsa_public_key = load_pem_public_key( + data=public_key, + backend=default_backend() + ) + return rsa_public_key.encrypt( + data, + padding.OAEP( + mgf=padding.MGF1(algorithm=hashes.SHA1()), + algorithm=hashes.SHA1(), + label=None + ) + ) + + def decrypt(self, data, symmetric_key=None, private_key=None): + """ + Decrypts data with symmetric key or public key. + """ + if symmetric_key: + try: + fernet = Fernet(symmetric_key) + return fernet.decrypt(data) + except InvalidToken: + raise errors.AuthenticationError(message=_('Invalid credentials')) + + elif private_key: + try: + rsa_private_key = load_pem_private_key( + data=private_key, + password=None, + backend=default_backend() + ) + return rsa_private_key.decrypt( + data, + padding.OAEP( + mgf=padding.MGF1(algorithm=hashes.SHA1()), + algorithm=hashes.SHA1(), + label=None + ) + ) + except AssertionError: + raise errors.AuthenticationError(message=_('Invalid credentials')) + @register() class vault_add(LDAPCreate): @@ -234,6 +348,18 @@ class vault_add(LDAPCreate): cli_name='in', doc=_('File containing data to archive'), ), + Str('password?', + cli_name='password', + doc=_('Vault password'), + ), + Str('password_file?', + cli_name='password_file', + doc=_('File containing the vault password'), + ), + Str('public_key_file?', + cli_name='public_key_file', + doc=_('File containing the vault public key'), + ), ) msg_summary = _('Added vault "%(value)s"') @@ -243,9 +369,14 @@ class vault_add(LDAPCreate): vault_name = args[0] container_id = api.Object.vaultcontainer.normalize_id(options.get('container')) + vault_type = options.get('ipavaulttype') data = options.get('data') text = options.get('text') input_file = options.get('in') + password = options.get('password') + password_file = options.get('password_file') + public_key = options.get('ipapublickey') + public_key_file = options.get('public_key_file') # don't send these parameters to server if 'data' in options: @@ -254,6 +385,12 @@ class vault_add(LDAPCreate): del options['text'] if 'in' in options: del options['in'] + if 'password' in options: + del options['password'] + if 'password_file' in options: + del options['password_file'] + if 'public_key_file' in options: + del options['public_key_file'] # get data if data: @@ -279,6 +416,96 @@ class vault_add(LDAPCreate): else: data = '' + # type-specific initialization + if vault_type == 'standard': + + if password: + raise errors.ValidationError(name='password', + error=_('Invalid parameter for %s vault' % vault_type)) + + if password_file: + raise errors.ValidationError(name='password_file', + error=_('Invalid parameter for %s vault' % vault_type)) + + if public_key: + raise errors.ValidationError(name='ipapublickey', + error=_('Invalid parameter for %s vault' % vault_type)) + + if public_key_file: + raise errors.ValidationError(name='public_key_file', + error=_('Invalid parameter for %s vault' % vault_type)) + + encryption_key = None + + elif vault_type == 'symmetric': + + if public_key: + raise errors.ValidationError(name='ipapublickey', + error=_('Invalid parameter for %s vault' % vault_type)) + + if public_key_file: + raise errors.ValidationError(name='public_key_file', + error=_('Invalid parameter for %s vault' % vault_type)) + + # get vault password + if password: + pass + + elif password_file: + with open(password_file) as f: + password = unicode(f.read().rstrip('\n')) + + else: + while True: + password = unicode(getpass.getpass('New password: ')) + password2 = unicode(getpass.getpass('Verify password: ')) + if password == password2: + break + print ' ** Passwords do not match! **' + + # generate vault salt + salt = os.urandom(16) + options['ipavaultsalt'] = unicode(base64.b64encode(salt)) + + # generate encryption key from vault password + encryption_key = self.obj.generate_symmetric_key(password, salt) + + elif vault_type == 'asymmetric': + + if password: + raise errors.ValidationError(name='password', + error=_('Invalid parameter for %s vault' % vault_type)) + + if password_file: + raise errors.ValidationError(name='password_file', + error=_('Invalid parameter for %s vault' % vault_type)) + + # generate vault salt + salt = os.urandom(16) + options['ipavaultsalt'] = unicode(base64.b64encode(salt)) + + # generate encryption key + encryption_key = base64.b64encode(os.urandom(32)) + + # get vault public key + if public_key: + pass + + elif public_key_file: + with open(public_key_file, 'rb') as f: + public_key = f.read() + + else: + raise errors.ValidationError(name='ipapublickey', + error=_('Missing vault public key')) + + # store vault public key + options['ipapublickey'] = public_key + + else: + raise errors.ValidationError(name='vault_type', + error=_('Invalid vault type')) + # create the vault response = super(vault_add, self).forward(*args, **options) @@ -286,7 +513,9 @@ class vault_add(LDAPCreate): api.Command.vault_archive( vault_name, container=container_id, - data=data) + data=data, + password=password, + encryption_key=encryption_key) return response @@ -417,7 +646,6 @@ class vault_show(LDAPRetrieve): class vault_transport_cert(Command): __doc__ = _('Retrieve vault transport certificate.') - # list of attributes we want exported to JSON json_friendly_attributes = ( 'takes_args', @@ -503,6 +731,18 @@ class vault_archive(LDAPRetrieve): cli_name='nonce', doc=_('Nonce encrypted encoded in base-64'), ), + Str('password?', + cli_name='password', + doc=_('Vault password'), + ), + Str('password_file?', + cli_name='password_file', + doc=_('File containing the vault password'), + ), + Bytes('encryption_key?', + cli_name='encryption_key', + doc=_('Base-64 encoded encryption key'), + ), ) msg_summary = _('Archived data into vault "%(value)s"') @@ -512,9 +752,29 @@ class vault_archive(LDAPRetrieve): vault_name = args[0] container_id = api.Object.vaultcontainer.normalize_id(options.get('container')) + vault_type = 'standard' + salt = None + public_key = None + + # retrieve vault info + response = api.Command.vault_show(vault_name, container=container_id) + result = response['result'] + + if result.has_key('ipavaulttype'): + vault_type = result['ipavaulttype'][0] + + if result.has_key('ipavaultsalt'): + salt = str(base64.b64decode(result['ipavaultsalt'][0])) + + if result.has_key('ipapublickey'): + public_key = str(result['ipapublickey'][0]) + data = options.get('data') text = options.get('text') input_file = options.get('in') + password = options.get('password') + password_file = options.get('password_file') + encryption_key = options.get('encryption_key') # don't send these parameters to server if 'data' in options: @@ -523,6 +783,12 @@ class vault_archive(LDAPRetrieve): del options['text'] if 'in' in options: del options['in'] + if 'password' in options: + del options['password'] + if 'password_file' in options: + del options['password_file'] + if 'encryption_key' in options: + del options['encryption_key'] # get data if data: @@ -548,6 +814,75 @@ class vault_archive(LDAPRetrieve): else: data = '' + encrypted_key = None + + if vault_type == 'standard': + + if password: + raise errors.ValidationError(name='password', + error=_('Invalid parameter for %s vault' % vault_type)) + + if password_file: + raise errors.ValidationError(name='password_file', + error=_('Invalid parameter for %s vault' % vault_type)) + + elif vault_type == 'symmetric': + + if encryption_key: + pass + + else: + # get vault password + if password: + pass + + elif password_file: + with open(password_file) as f: + password = unicode(f.read().rstrip('\n')) + else: + password = unicode(getpass.getpass('Password: ')) + + try: + api.Command.vault_retrieve( + vault_name, + container=container_id, + password=password) + + except errors.NotFound: + pass + + # generate encryption key from vault password + encryption_key = self.obj.generate_symmetric_key(password, salt) + + # encrypt data with encryption key + data = self.obj.encrypt(data, symmetric_key=encryption_key) + + elif vault_type == 'asymmetric': + + if password: + raise errors.ValidationError(name='password', + error=_('Invalid parameter for %s vault' % vault_type)) + + if password_file: + raise errors.ValidationError(name='password_file', + error=_('Invalid parameter for %s vault' % vault_type)) + + if encryption_key: + pass + + else: + encryption_key = base64.b64encode(os.urandom(32)) + + # encrypt data with encryption key + data = self.obj.encrypt(data, symmetric_key=encryption_key) + + # encrypt encryption key with public key + encrypted_key = self.obj.encrypt(encryption_key, public_key=public_key) + + else: + raise errors.ValidationError(name='vault_type', + error=_('Invalid vault type')) + # initialize NSS database crypto = pki.crypto.NSSCryptoProvider(paths.IPA_NSSDB_DIR) crypto.initialize() @@ -581,6 +916,9 @@ class vault_archive(LDAPRetrieve): vault_data = {} vault_data[u'data'] = unicode(data) + if encrypted_key: + vault_data[u'encrypted_key'] = unicode(base64.b64encode(encrypted_key)) + json_vault_data = json.dumps(vault_data) # wrap vault_data with session key @@ -674,13 +1012,29 @@ class vault_retrieve(LDAPRetrieve): cli_name='session_key', doc=_('Session key wrapped with transport certificate and encoded in base-64'), ), + Str('password?', + cli_name='password', + doc=_('Vault password'), + ), + Str('password_file?', + cli_name='password_file', + doc=_('File containing the vault password'), + ), + Bytes('private_key?', + cli_name='private_key', + doc=_('Vault private key'), + ), + Str('private_key_file?', + cli_name='private_key_file', + doc=_('File containing the vault private key'), + ), ) has_output_params = ( Bytes('data', label=_('Data'), ), - Bytes('text', + Str('text', label=_('Text'), ), ) @@ -692,9 +1046,26 @@ class vault_retrieve(LDAPRetrieve): vault_name = args[0] container_id = api.Object.vaultcontainer.normalize_id(options.get('container')) + vault_type = 'standard' + salt = None + + # retrieve vault info + response = api.Command.vault_show(vault_name, container=container_id) + result = response['result'] + + if result.has_key('ipavaulttype'): + vault_type = result['ipavaulttype'][0] + + if result.has_key('ipavaultsalt'): + salt = str(base64.b64decode(result['ipavaultsalt'][0])) + show_text = options.get('show_text') stdout = options.get('stdout') output_file = options.get('out') + password = options.get('password') + password_file = options.get('password_file') + private_key = options.get('private_key') + private_key_file = options.get('private_key_file') # don't send these parameters to server if 'show_text' in options: @@ -703,6 +1074,14 @@ class vault_retrieve(LDAPRetrieve): del options['stdout'] if 'out' in options: del options['out'] + if 'password' in options: + del options['password'] + if 'password_file' in options: + del options['password_file'] + if 'private_key' in options: + del options['private_key'] + if 'private_key_file' in options: + del options['private_key_file'] # initialize NSS database crypto = pki.crypto.NSSCryptoProvider(paths.IPA_NSSDB_DIR) @@ -748,6 +1127,88 @@ class vault_retrieve(LDAPRetrieve): vault_data = json.loads(json_vault_data) data = str(vault_data[u'data']) + encrypted_key = None + + if vault_data.has_key('encrypted_key'): + encrypted_key = base64.b64decode(str(vault_data[u'encrypted_key'])) + + if vault_type == 'standard': + + if password: + raise errors.ValidationError(name='password', + error=_('Invalid parameter for %s vault' % vault_type)) + + if password_file: + raise errors.ValidationError(name='password_file', + error=_('Invalid parameter for %s vault' % vault_type)) + + if private_key: + raise errors.ValidationError(name='private_key', + error=_('Invalid parameter for %s vault' % vault_type)) + + if private_key_file: + raise errors.ValidationError(name='private_key_file', + error=_('Invalid parameter for %s vault' % vault_type)) + + elif vault_type == 'symmetric': + + if private_key: + raise errors.ValidationError(name='private_key', + error=_('Invalid parameter for %s vault' % vault_type)) + + if private_key_file: + raise errors.ValidationError(name='private_key_file', + error=_('Invalid parameter for %s vault' % vault_type)) + + # get encryption key from vault password + if password: + pass + + elif password_file: + with open(password_file) as f: + password = unicode(f.read().rstrip('\n')) + + else: + password = unicode(getpass.getpass('Password: ')) + + # generate encryption key from password + encryption_key = self.obj.generate_symmetric_key(password, salt) + + # decrypt data with encryption key + data = self.obj.decrypt(data, symmetric_key=encryption_key) + + elif vault_type == 'asymmetric': + + if password: + raise errors.ValidationError(name='password', + error=_('Invalid parameter for %s vault' % vault_type)) + + if password_file: + raise errors.ValidationError(name='password_file', + error=_('Invalid parameter for %s vault' % vault_type)) + + # get encryption key with vault private key + if private_key: + pass + + elif private_key_file: + with open(private_key_file, 'rb') as f: + private_key = f.read() + + else: + raise errors.ValidationError(name='private_key', + error=_('Missing vault private key')) + + # decrypt encryption key with private key + encryption_key = self.obj.decrypt(encrypted_key, private_key=private_key) + + # decrypt data with encryption key + data = self.obj.decrypt(data, symmetric_key=encryption_key) + + else: + raise errors.ValidationError(name='vault_type', + error=_('Invalid vault type')) + if stdout: sys.stdout.write(data) response['result'] = {} diff --git a/ipatests/test_xmlrpc/test_vault_plugin.py b/ipatests/test_xmlrpc/test_vault_plugin.py index b7f2a14a1971d5c1f0c93bcf7dd8949bc047dd55..d86e5c2e0d6e51c74086c5c9541d1d939164cf10 100644 --- a/ipatests/test_xmlrpc/test_vault_plugin.py +++ b/ipatests/test_xmlrpc/test_vault_plugin.py @@ -28,10 +28,89 @@ test_vault = u'test_vault' binary_data = '\x01\x02\x03\x04' text_data = u'secret' +symmetric_vault = u'symmetric_vault' +asymmetric_vault = u'asymmetric_vault' + +password = u'password' + +public_key = """ +-----BEGIN PUBLIC KEY----- +MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAnT61EFxUOQgCJdM0tmw/ +pRRPDPGchTClnU1eBtiQD3ItKYf1+weMGwGOSJXPtkto7NlE7Qs8WHAr0UjyeBDe +k/zeB6nSVdk47OdaW1AHrJL+44r238Jbm/+7VO5lTu6Z4N5p0VqoWNLi0Uh/CkqB +tsxXaaAgjMp0AGq2U/aO/akeEYWQOYIdqUKVgAEKX5MmIA8tmbmoYIQ+B4Q3vX7N +otG4eR6c2o9Fyjd+M4Gai5Ce0fSrigRvxAYi8xpRkQ5yQn5gf4WVrn+UKTfOIjLO +pVThop+Xivcre3SpI0kt6oZPhBw9i8gbMnqifVmGFpVdhq+QVBqp+MVJvTbhRPG6 +3wIDAQAB +-----END PUBLIC KEY----- +""" + +private_key = """ +-----BEGIN RSA PRIVATE KEY----- +MIIEpAIBAAKCAQEAnT61EFxUOQgCJdM0tmw/pRRPDPGchTClnU1eBtiQD3ItKYf1 ++weMGwGOSJXPtkto7NlE7Qs8WHAr0UjyeBDek/zeB6nSVdk47OdaW1AHrJL+44r2 +38Jbm/+7VO5lTu6Z4N5p0VqoWNLi0Uh/CkqBtsxXaaAgjMp0AGq2U/aO/akeEYWQ +OYIdqUKVgAEKX5MmIA8tmbmoYIQ+B4Q3vX7NotG4eR6c2o9Fyjd+M4Gai5Ce0fSr +igRvxAYi8xpRkQ5yQn5gf4WVrn+UKTfOIjLOpVThop+Xivcre3SpI0kt6oZPhBw9 +i8gbMnqifVmGFpVdhq+QVBqp+MVJvTbhRPG63wIDAQABAoIBAQCD2bXnfxPcMnvi +jaPwpvoDCPF0EBBHmk/0g5ApO2Qon3uBDJFUqbJwXrCY6o2d9MOJfnGONlKmcYA8 +X+d4h+SqwGjIkjxdYeSauS+Jy6Rzr1ptH/P8EjPQrfG9uJxYQDflV3nxYwwwVrx7 +8kccMPdteRB+8Bb7FzOHufMimmayCNFETnVT5CKH2PrYoPB+fr0itCipWOenDp33 +e73OV+K9U3rclmtHaoRxGohqByKfQRUkipjw4m+T3qfZZc5eN77RGW8J+oL1GVom +fwtiH7N1HVte0Dmd13nhiASg355kjqRPcIMPsRHvXkOpgg5HRUTKG5elqAyvvm27 +Fzj1YdeRAoGBAMnE61+FYh8qCyEGe8r6RGjO8iuoyk1t+0gBWbmILLBiRnj4K8Tc +k7HBG/pg3XCNbCuRwiLg8tk3VAAXzn6o+IJr3QnKbNCGa1lKfYU4mt11sBEyuL5V +NpZcZ8IiPhMlGyDA9cFbTMKOE08RqbOIdxOmTizFt0R5sYZAwOjEvBIZAoGBAMeC +N/P0bdrScFZGeS51wEdiWme/CO0IyGoqU6saI8L0dbmMJquiaAeIEjIKLqxH1RON +axhsyk97e0PCcc5QK62Utf50UUAbL/v7CpIG+qdSRYDO4bVHSCkwF32N3pYh/iVU +EsEBEkZiJi0dWa/0asDbsACutxcHda3RI5pi7oO3AoGAcbGNs/CUHt1xEfX2UaT+ +YVSjb2iYPlNH8gYYygvqqqVl8opdF3v3mYUoP8jPXrnCBzcF/uNk1HNx2O+RQxvx +lIQ1NGwlLsdfvBvWaPhBg6LqSHadVVrs/IMrUGA9PEp/Y9B3arIIqeSnCrn4Nxsh +higDCwWKRIKSPwVD7qXVGBkCgYEAu5/CASIRIeYgEXMLSd8hKcDcJo8o1MoauIT/ +1Hyrvw9pm0qrn2QHk3WrLvYWeJzBTTcEzZ6aEG+fN9UodA8/VGnzUc6QDsrCsKWh +hj0cArlDdeSZrYLQ4TNCFCiUePqU6QQM8weP6TMqlejxTKF+t8qi1bF5rCWuzP1P +D0UU7DcCgYAUvmEGckugS+FTatop8S/rmkcQ4Bf5M/YCZfsySavucDiHcBt0QtXt +Swh0XdDsYS3W1yj2XqqsQ7R58KNaffCHjjulWFzb5IiuSvvdxzWtiXHisOpO36MJ +kUlCMj24a8XsShzYTWBIyW2ngvGe3pQ9PfjkUdm0LGZjYITCBvgOKw== +-----END RSA PRIVATE KEY----- +""" + +wrong_private_key = """ +-----BEGIN RSA PRIVATE KEY----- +MIIEpAIBAAKCAQEAuQVrFCTFzc8EQ1TPR/fB2vZqcNFn7KVFYN5UuGSLm8JwdSyX +aJHFdgmdWS5NHRQS7l4kd3/mn1pk4BuhmC1tO+tPM1gunCtFvgB5fBC6WJzytBEC +y/ikvSIyOQ33/KCLdTQ/oFTIp3f5dUO62a3gjgQ9Y0Z9krRXYiB5uxMyHrr/zuxL +qRqP7pQYIkROr5VqGUgFUjFcovLvzP2sabobEnctCKm+nIE4s31QR3Kqb4l31qmX +TkbiGkoEkwoqHDuu0eH50bJwRFjjx3h+5U0bmoff1ipi+Aan5gZKoOOlnkc9d2dZ +gPBHyth6+9N3V8jrjs+VMJ8j2GzI9pnVxD93awIDAQABAoIBAC5neny54FaHBmWw +vrApJpi5Vubmzm7e4LCz8oGwzgcJ1FS/E1ZpwSGitbEpWLPjVgAs4m6KSJhM/qHq +rDPTqOLvWJTjGAWZIMvPqKiCNYqGCqU44v+vY/n/uqLuqvTUe0WxDggzW4QNJibQ +DuwLnRdhXtgoVNRXoNb+mClgXiCwjgX8j+A2lb6H4iQsQOLjLr4ifH13CqDgJk3k +2BjI1wOeesxooNk3I7ZKdVL1iV/qaeysLvtWoEmoQvrn6Fbo4HAR04tQQXEG7GzM +kZ+RVmHyn5b4tCMqnzubM+IYf7k9cJfgZIyaVxO8NSfwaJkWbQiGwzvUebRiHwPs +cK+F4bkCgYEA9VKtHX6Kr9Ht6Ho0q5eZAzRrOqgB2Uj4duFAI40bQ1q9+p+VSoKR +IqU6I16HO7mbgjlOySVg5RVIcp+9DUoNoK2KlGFswX6Q0f1J/v0CDavQoqOcpyFh +F9Egtx+5VNhknDZ0GTNWNm6bTWZUHK6ZICXs/mZkn4rHdId2Xe0Ls38CgYEAwRLg +GXSctadY0Qowy2YLe+I2EjsdXgE82TvOorytjFhOPVB/GsCOJ3IjyHJyGdpkm/85 +U0SUoeNXQHIgiMGG+xxQ/ErJhpHxiaQKHFow48x3k8R7tlO68Mfnl+BZ9goRgxio +rFwiea2MtCl2j8szh9UJ1z57aG1L0PfUctd9QhUCgYEA3O2T0ZgANc6MvmwvushP +mD9AwhZDc/bvK8A3Ds0o3EOAC5Bj1jI3mkfKT8f1aagBkAkkFql+1U+RawjILIug +Mi+XOYFze94LddDxLp2Tl9Q/k/hcP3ckBVrkZ4Y+VVZ7ZOL1Myy0W1jIq6+X2Cy0 +4erFv2VfAP7uGNdVlcjAXOkCgYABT5x/77/Ep/89ZCFSsD2xuKZ/VzFq2v1LyFEt +37QZ+NuHJQ3H47jTYb4GdWh67nWybXg5LYUI2F9WS7AW3aGKAPY30FYv+Lu4IIoF +CUO9uDyznyjr4wOo8OKMsHRL7GOUDU3P5cxCIUCMVJ++eDXAXVz0vjLeUaerIpOp +t/bcxQKBgQC4ehPJQH8WWG1FnfXh62AJBgb+yUDmp+4bTRtr/GENF9W62GN+g7or +RQqtdO1WxoXsC2qbKT4ArwOpV877KlWuLfiJMe5JS8vU/n711DCFIIfkXpeXPUVc +MszdQuc/FTSJ2DYsIwx7qq5c8mtargOjWRgZU22IgY9PKeIcitQjqw== +-----END RSA PRIVATE KEY----- +""" + class test_vault(Declarative): cleanup_commands = [ ('vault_del', [test_vault], {'continue': True}), + ('vault_del', [symmetric_vault], {'continue': True}), + ('vault_del', [asymmetric_vault], {'continue': True}), ] tests = [ @@ -52,6 +131,7 @@ class test_vault(Declarative): 'cn': [test_vault], 'vault_id': u'/users/admin/%s' % test_vault, 'owner_user': [u'admin'], + 'ipavaulttype': [u'standard'], }, }, }, @@ -82,6 +162,7 @@ class test_vault(Declarative): 'dn': u'cn=%s,cn=admin,cn=users,cn=vaults,%s' % (test_vault, api.env.basedn), 'cn': [test_vault], 'vault_id': u'/users/admin/%s' % test_vault, + 'ipavaulttype': [u'standard'], }, ], }, @@ -102,6 +183,7 @@ class test_vault(Declarative): 'cn': [test_vault], 'vault_id': u'/users/admin/%s' % test_vault, 'owner_user': [u'admin'], + 'ipavaulttype': [u'standard'], }, }, }, @@ -123,6 +205,7 @@ class test_vault(Declarative): 'vault_id': u'/users/admin/%s' % test_vault, 'description': [u'Test vault'], 'owner_user': [u'admin'], + 'ipavaulttype': [u'standard'], }, }, }, @@ -145,6 +228,7 @@ class test_vault(Declarative): 'vault_id': u'/users/admin/%s' % test_vault, 'description': [u'Test vault'], 'owner_user': [u'admin'], + 'ipavaulttype': [u'standard'], }, }, }, @@ -165,6 +249,7 @@ class test_vault(Declarative): 'vault_id': u'/users/admin/%s' % test_vault, 'description': [u'Test vault'], 'owner_user': [u'admin'], + 'ipavaulttype': [u'standard'], 'nonce': fuzzy_string, 'vault_data': fuzzy_string, 'data': binary_data, @@ -190,6 +275,7 @@ class test_vault(Declarative): 'vault_id': u'/users/admin/%s' % test_vault, 'description': [u'Test vault'], 'owner_user': [u'admin'], + 'ipavaulttype': [u'standard'], }, }, }, @@ -212,6 +298,7 @@ class test_vault(Declarative): 'vault_id': u'/users/admin/%s' % test_vault, 'description': [u'Test vault'], 'owner_user': [u'admin'], + 'ipavaulttype': [u'standard'], 'nonce': fuzzy_string, 'vault_data': fuzzy_string, 'text': text_data, @@ -245,4 +332,134 @@ class test_vault(Declarative): 'expected': errors.NotFound(reason=u'%s: vault not found' % test_vault), }, + { + 'desc': 'Create symmetric vault', + 'command': ( + 'vault_add', + [symmetric_vault], + { + 'ipavaulttype': u'symmetric', + 'password': password, + 'data': binary_data, + }, + ), + 'expected': { + 'value': symmetric_vault, + 'summary': 'Added vault "%s"' % symmetric_vault, + 'result': { + 'dn': u'cn=%s,cn=admin,cn=users,cn=vaults,%s' % (symmetric_vault, api.env.basedn), + 'objectclass': (u'ipaVault', u'top'), + 'cn': [symmetric_vault], + 'vault_id': u'/users/admin/%s' % symmetric_vault, + 'owner_user': [u'admin'], + 'ipavaulttype': [u'symmetric'], + 'ipavaultsalt': [fuzzy_string], + }, + }, + }, + + { + 'desc': 'Retrieve symmetric vault', + 'command': ( + 'vault_retrieve', + [symmetric_vault], + { + 'password': password, + }, + ), + 'expected': { + 'value': symmetric_vault, + 'summary': u'Retrieved data from vault "%s"' % symmetric_vault, + 'result': { + 'dn': u'cn=%s,cn=admin,cn=users,cn=vaults,%s' % (symmetric_vault, api.env.basedn), + 'cn': [symmetric_vault], + 'vault_id': u'/users/admin/%s' % symmetric_vault, + 'owner_user': [u'admin'], + 'ipavaulttype': [u'symmetric'], + 'ipavaultsalt': [fuzzy_string], + 'nonce': fuzzy_string, + 'vault_data': fuzzy_string, + 'data': binary_data, + }, + }, + }, + + { + 'desc': 'Retrieve symmetric vault with wrong password', + 'command': ( + 'vault_retrieve', + [symmetric_vault], + { + 'password': u'wrong', + }, + ), + 'expected': errors.AuthenticationError(message=u'Invalid credentials'), + }, + + { + 'desc': 'Create asymmetric vault', + 'command': ( + 'vault_add', + [asymmetric_vault], + { + 'ipavaulttype': u'asymmetric', + 'ipapublickey': public_key, + 'data': binary_data, + }, + ), + 'expected': { + 'value': asymmetric_vault, + 'summary': 'Added vault "%s"' % asymmetric_vault, + 'result': { + 'dn': u'cn=%s,cn=admin,cn=users,cn=vaults,%s' % (asymmetric_vault, api.env.basedn), + 'objectclass': (u'ipaVault', u'top'), + 'cn': [asymmetric_vault], + 'vault_id': u'/users/admin/%s' % asymmetric_vault, + 'owner_user': [u'admin'], + 'ipavaulttype': [u'asymmetric'], + 'ipavaultsalt': [fuzzy_string], + 'ipapublickey': [public_key], + }, + }, + }, + + { + 'desc': 'Retrieve asymmetric vault', + 'command': ( + 'vault_retrieve', + [asymmetric_vault], + { + 'private_key': private_key, + }, + ), + 'expected': { + 'value': asymmetric_vault, + 'summary': u'Retrieved data from vault "%s"' % asymmetric_vault, + 'result': { + 'dn': u'cn=%s,cn=admin,cn=users,cn=vaults,%s' % (asymmetric_vault, api.env.basedn), + 'cn': [asymmetric_vault], + 'vault_id': u'/users/admin/%s' % asymmetric_vault, + 'owner_user': [u'admin'], + 'ipavaulttype': [u'asymmetric'], + 'ipavaultsalt': [fuzzy_string], + 'ipapublickey': [public_key], + 'nonce': fuzzy_string, + 'vault_data': fuzzy_string, + 'data': binary_data, + }, + }, + }, + + { + 'desc': 'Retrieve asymmetric vault with wrong private key', + 'command': ( + 'vault_retrieve', + [asymmetric_vault], + { + 'private_key': wrong_private_key, + }, + ), + 'expected': errors.AuthenticationError(message=u'Invalid credentials'), + }, + ] -- 1.9.0 -------------- next part -------------- >From 295f092eaffbbb35b1a86660b939dc9cce685d80 Mon Sep 17 00:00:00 2001 From: "Endi S. Dewata" Date: Sat, 8 Nov 2014 02:04:03 -0500 Subject: [PATCH] Added vault secret plugin. A new plugin has been have been added to provide the interface to manage secrets within a vault. New test cases have been added for the new plugin. https://fedorahosted.org/freeipa/ticket/3872 --- API.txt | 91 ++ ipalib/plugins/vaultsecret.py | 1118 +++++++++++++++++++++++ ipatests/test_xmlrpc/test_vaultsecret_plugin.py | 211 +++++ 3 files changed, 1420 insertions(+) create mode 100644 ipalib/plugins/vaultsecret.py create mode 100644 ipatests/test_xmlrpc/test_vaultsecret_plugin.py diff --git a/API.txt b/API.txt index 8e7e4f9615a75e4cf365d07d0ba669060a541679..4adc4db4978475883013eb6ea7dd57585e1f2ae3 100644 --- a/API.txt +++ b/API.txt @@ -4825,6 +4825,97 @@ option: Str('version?', exclude='webui') output: Entry('result', , Gettext('A dictionary representing an LDAP entry', domain='ipa', localedir=None)) output: Output('summary', (, ), None) output: PrimaryKey('value', None, None) +command: vaultsecret_add +args: 2,12,3 +arg: Str('vaultcn', cli_name='vault', maxlength=255, multivalue=False, pattern='^[a-zA-Z0-9_.-]+$', primary_key=True, query=True, required=True) +arg: Str('secret_name', attribute=True, cli_name='secret', maxlength=255, multivalue=False, pattern='^[a-zA-Z0-9_.-]+$', primary_key=True, query=True, required=True) +option: Flag('all', autofill=True, cli_name='all', default=False, exclude='webui') +option: Str('container?', cli_name='container') +option: Bytes('data?', cli_name='data') +option: Str('description?', cli_name='desc') +option: Str('in?', cli_name='in') +option: Str('password?', cli_name='password') +option: Str('password_file?', cli_name='password_file') +option: Bytes('private_key?', cli_name='private_key') +option: Str('private_key_file?', cli_name='private_key_file') +option: Flag('raw', autofill=True, cli_name='raw', default=False, exclude='webui') +option: Str('text?', cli_name='text') +option: Str('version?', exclude='webui') +output: Entry('result', , Gettext('A dictionary representing an LDAP entry', domain='ipa', localedir=None)) +output: Output('summary', (, ), None) +output: PrimaryKey('value', None, None) +command: vaultsecret_del +args: 2,8,3 +arg: Str('vaultcn', cli_name='vault', maxlength=255, multivalue=False, pattern='^[a-zA-Z0-9_.-]+$', primary_key=True, query=True, required=True) +arg: Str('secret_name', attribute=True, cli_name='secret', maxlength=255, multivalue=False, pattern='^[a-zA-Z0-9_.-]+$', primary_key=True, query=True, required=True) +option: Flag('all', autofill=True, cli_name='all', default=False, exclude='webui') +option: Str('container?', cli_name='container') +option: Str('password?', cli_name='password') +option: Str('password_file?', cli_name='password_file') +option: Bytes('private_key?', cli_name='private_key') +option: Str('private_key_file?', cli_name='private_key_file') +option: Flag('raw', autofill=True, cli_name='raw', default=False, exclude='webui') +option: Str('version?', exclude='webui') +output: Entry('result', , Gettext('A dictionary representing an LDAP entry', domain='ipa', localedir=None)) +output: Output('summary', (, ), None) +output: PrimaryKey('value', None, None) +command: vaultsecret_find +args: 2,12,4 +arg: Str('vaultcn', cli_name='vault', maxlength=255, multivalue=False, pattern='^[a-zA-Z0-9_.-]+$', primary_key=True, query=True, required=True) +arg: Str('criteria?', noextrawhitespace=False) +option: Flag('all', autofill=True, cli_name='all', default=False, exclude='webui') +option: Str('container?', cli_name='container') +option: Bytes('data', attribute=True, autofill=False, cli_name='data', multivalue=False, query=True, required=False) +option: Str('description', attribute=True, autofill=False, cli_name='desc', multivalue=False, query=True, required=False) +option: Str('password?', cli_name='password') +option: Str('password_file?', cli_name='password_file') +option: Flag('pkey_only?', autofill=True, default=False) +option: Bytes('private_key?', cli_name='private_key') +option: Str('private_key_file?', cli_name='private_key_file') +option: Flag('raw', autofill=True, cli_name='raw', default=False, exclude='webui') +option: Str('secret_name', attribute=True, autofill=False, cli_name='secret', maxlength=255, multivalue=False, pattern='^[a-zA-Z0-9_.-]+$', primary_key=True, query=True, required=False) +option: Str('version?', exclude='webui') +output: Output('count', , None) +output: ListOfEntries('result', (, ), Gettext('A list of LDAP entries', domain='ipa', localedir=None)) +output: Output('summary', (, ), None) +output: Output('truncated', , None) +command: vaultsecret_mod +args: 2,12,3 +arg: Str('vaultcn', cli_name='vault', maxlength=255, multivalue=False, pattern='^[a-zA-Z0-9_.-]+$', primary_key=True, query=True, required=True) +arg: Str('secret_name', attribute=True, cli_name='secret', maxlength=255, multivalue=False, pattern='^[a-zA-Z0-9_.-]+$', primary_key=True, query=True, required=True) +option: Flag('all', autofill=True, cli_name='all', default=False, exclude='webui') +option: Str('container?', cli_name='container') +option: Bytes('data?', cli_name='data') +option: Str('description?', cli_name='desc') +option: Str('in?', cli_name='in') +option: Str('password?', cli_name='password') +option: Str('password_file?', cli_name='password_file') +option: Bytes('private_key?', cli_name='private_key') +option: Str('private_key_file?', cli_name='private_key_file') +option: Flag('raw', autofill=True, cli_name='raw', default=False, exclude='webui') +option: Str('text?', cli_name='text') +option: Str('version?', exclude='webui') +output: Entry('result', , Gettext('A dictionary representing an LDAP entry', domain='ipa', localedir=None)) +output: Output('summary', (, ), None) +output: PrimaryKey('value', None, None) +command: vaultsecret_show +args: 2,11,3 +arg: Str('vaultcn', cli_name='vault', maxlength=255, multivalue=False, pattern='^[a-zA-Z0-9_.-]+$', primary_key=True, query=True, required=True) +arg: Str('secret_name', attribute=True, cli_name='secret', maxlength=255, multivalue=False, pattern='^[a-zA-Z0-9_.-]+$', primary_key=True, query=True, required=True) +option: Flag('all', autofill=True, cli_name='all', default=False, exclude='webui') +option: Str('container?', cli_name='container') +option: Str('out?', cli_name='out') +option: Str('password?', cli_name='password') +option: Str('password_file?', cli_name='password_file') +option: Bytes('private_key?', cli_name='private_key') +option: Str('private_key_file?', cli_name='private_key_file') +option: Flag('raw', autofill=True, cli_name='raw', default=False, exclude='webui') +option: Flag('show_text?', autofill=True, default=False) +option: Flag('stdout?', autofill=True, default=False) +option: Str('version?', exclude='webui') +output: Entry('result', , Gettext('A dictionary representing an LDAP entry', domain='ipa', localedir=None)) +output: Output('summary', (, ), None) +output: PrimaryKey('value', None, None) capability: messages 2.52 capability: optional_uid_params 2.54 capability: permissions2 2.69 diff --git a/ipalib/plugins/vaultsecret.py b/ipalib/plugins/vaultsecret.py new file mode 100644 index 0000000000000000000000000000000000000000..b0896155a5af13013f13f2b3ae586da2a8832c30 --- /dev/null +++ b/ipalib/plugins/vaultsecret.py @@ -0,0 +1,1118 @@ +# Authors: +# Endi S. Dewata +# +# Copyright (C) 2015 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 . + +import base64 +import getpass +import json +import sys + +from ipalib import api, errors +from ipalib import Str, Bytes, Flag +from ipalib.plugable import Registry +from ipalib.plugins.baseldap import LDAPObject, LDAPSearch, LDAPRetrieve +from ipalib import _, ngettext + +__doc__ = _(""" +Vault secrets +""") + _(""" +Manage vault secrets. +""") + _(""" +EXAMPLES: +""") + _(""" + List vault secrets: + ipa vaultsecret-find MyVault +""") + _(""" + Add a vault secret: + ipa vaultsecret-add MyVault MySecret --in data.bin --desc "My vault secret" +""") + _(""" + Retrieve a vault secret: + ipa vaultsecret-show MyVault MySecret --out data.bin +""") + _(""" + Modify a vault secret: + ipa vaultsecret-mod MyVault MySecret --desc "My vault secret" +""") + _(""" + Delete a vault secret: + ipa vaultsecret-del MyVault MySecret +""") + +register = Registry() + + at register() +class vaultsecret(LDAPObject): + __doc__ = _(""" + Vault secret object. + """) + + parent_object = 'vault' + object_name = _('vault secret') + object_name_plural = _('vault secrets') + + default_attributes = [ + 'cn', + 'description', + 'data', + ] + search_display_attributes = [ + 'cn', + 'description', + ] + + label = _('Vault secrets') + label_singular = _('Vault secret') + + takes_params = ( + Str('secret_name', + cli_name='secret', + label=_('Secret name'), + primary_key=True, + pattern='^[a-zA-Z0-9_.-]+$', + pattern_errmsg='may only include letters, numbers, _, ., and -', + maxlength=255, + ), + Str('description?', + cli_name='desc', + label=_('Description'), + doc=_('Secret description'), + ), + Bytes('data?', + cli_name='data', + label=_('Data'), + doc=_('Base-64 encoded binary secret data'), + ), + ) + + + at register() +class vaultsecret_add(LDAPRetrieve): + __doc__ = _('Add a new vault secret.') + + takes_options = ( + Str('container?', + cli_name='container', + doc=_('Container ID'), + ), + Str('description?', + cli_name='desc', + doc=_('Secret description'), + ), + Bytes('data?', + cli_name='data', + doc=_('Base-64 encoded binary secret data'), + ), + Str('text?', + cli_name='text', + doc=_('Text secret data'), + ), + Str('in?', + cli_name='in', + doc=_('File containing secret data'), + ), + Str('password?', + cli_name='password', + doc=_('Vault password'), + ), + Str('password_file?', + cli_name='password_file', + doc=_('File containing the vault password'), + ), + Bytes('private_key?', + cli_name='private_key', + doc=_('Vault private key'), + ), + Str('private_key_file?', + cli_name='private_key_file', + doc=_('File containing the vault private key'), + ), + ) + + msg_summary = _('Added vault secret "%(value)s"') + + def forward(self, *args, **options): + + vault_name = args[0] + secret_name = args[1] + container_id = api.Object.vaultcontainer.normalize_id(options.get('container')) + + vault_type = 'standard' + salt = None + + # retrieve vault info + response = api.Command.vault_show(vault_name, container=container_id) + result = response['result'] + + if result.has_key('ipavaulttype'): + vault_type = result['ipavaulttype'][0] + + if result.has_key('ipavaultsalt'): + salt = str(result['ipavaultsalt'][0]) + + description = options.get('description') + data = options.get('data') + text = options.get('text') + input_file = options.get('in') + password = options.get('password') + password_file = options.get('password_file') + private_key = options.get('private_key') + private_key_file = options.get('private_key_file') + + # don't send these parameters to server + if 'data' in options: + del options['data'] + if 'text' in options: + del options['text'] + if 'in' in options: + del options['in'] + if 'password' in options: + del options['password'] + if 'password_file' in options: + del options['password_file'] + if 'private_key' in options: + del options['private_key'] + if 'private_key_file' in options: + del options['private_key_file'] + if 'source_secret' in options: + del options['source_secret'] + + # type-specific initialization + if vault_type == 'standard': + + if password: + raise errors.ValidationError(name='password', + error=_('Invalid parameter for %s vault' % vault_type)) + + if password_file: + raise errors.ValidationError(name='password_file', + error=_('Invalid parameter for %s vault' % vault_type)) + + if private_key: + raise errors.ValidationError(name='private_key', + error=_('Invalid parameter for %s vault' % vault_type)) + + if private_key_file: + raise errors.ValidationError(name='private_key_file', + error=_('Invalid parameter for %s vault' % vault_type)) + + elif vault_type == 'symmetric': + + if private_key: + raise errors.ValidationError(name='private_key', + error=_('Invalid parameter for %s vault' % vault_type)) + + if private_key_file: + raise errors.ValidationError(name='private_key_file', + error=_('Invalid parameter for %s vault' % vault_type)) + + # get vault password + if password: + pass + + elif password_file: + with open(password_file) as f: + password = unicode(f.read().rstrip('\n')) + + else: + password = unicode(getpass.getpass('Password: ')) + + elif vault_type == 'asymmetric': + + if password: + raise errors.ValidationError(name='password', + error=_('Invalid parameter for %s vault' % vault_type)) + + if password_file: + raise errors.ValidationError(name='password_file', + error=_('Invalid parameter for %s vault' % vault_type)) + + # get vault public key + if private_key: + pass + + elif private_key_file: + with open(private_key_file, 'rb') as f: + private_key = f.read() + + else: + raise errors.ValidationError(name='private_key', + error=_('Missing vault private key')) + + else: + raise errors.ValidationError(name='vault_type', + error=_('Invalid vault type')) + + # retrieve secrets + response = api.Command.vault_retrieve( + vault_name, + container=container_id, + password=password, + private_key=private_key) + + vault_data = response['result']['data'] + + if not vault_data: + json_data = { + 'secrets': [] + } + + else: + json_data = json.loads(vault_data) + + secrets = json_data['secrets'] + + # get data + if data: + if text: + raise errors.ValidationError(name='text', + error=_('Input data already specified')) + + if input_file: + raise errors.ValidationError(name='input_file', + error=_('Input data already specified')) + + elif text: + if input_file: + raise errors.ValidationError(name='input_file', + error=_('Input data already specified')) + + data = text.encode('utf-8') + + elif input_file: + with open(input_file, 'rb') as f: + data = f.read() + + else: + data = '' + + # add new secret + for secret in secrets: + if secret['secret_name'] == secret_name: + raise errors.DuplicateEntry(message=_('vault secret with name "%s" already exists' % secret_name)) + + # store encoded data for storage + secret = { + 'secret_name': secret_name, + 'data': unicode(base64.b64encode(data)), + } + if description: + secret['description'] = description + + secrets.append(secret) + + # rearchive secrets + vault_data = json.dumps(json_data) + response = api.Command.vault_archive( + vault_name, + container=container_id, + data=vault_data, + password=password) + + # restore binary data for response + secret['data'] = data + + response = { + 'value': secret_name, + 'summary': u'Added vault secret "%s"' % secret_name, + 'result': secret, + } + + return response + + + at register() +class vaultsecret_del(LDAPRetrieve): + __doc__ = _('Delete a vault secret.') + + takes_options = ( + Str('container?', + cli_name='container', + doc=_('Container ID'), + ), + Str('password?', + cli_name='password', + doc=_('Vault password'), + ), + Str('password_file?', + cli_name='password_file', + doc=_('File containing the vault password'), + ), + Bytes('private_key?', + cli_name='private_key', + doc=_('Vault private key'), + ), + Str('private_key_file?', + cli_name='private_key_file', + doc=_('File containing the vault private key'), + ), + ) + + msg_summary = _('Deleted vault secret "%(value)s"') + + def forward(self, *args, **options): + + vault_name = args[0] + secret_name = args[1] + container_id = api.Object.vaultcontainer.normalize_id(options.get('container')) + + vault_type = 'standard' + salt = None + + # retrieve vault info + response = api.Command.vault_show(vault_name, container=container_id) + result = response['result'] + + if result.has_key('ipavaulttype'): + vault_type = result['ipavaulttype'][0] + + if result.has_key('ipavaultsalt'): + salt = str(result['ipavaultsalt'][0]) + + password = options.get('password') + password_file = options.get('password_file') + private_key = options.get('private_key') + private_key_file = options.get('private_key_file') + + # don't send these parameters to server + if 'password' in options: + del options['password'] + if 'password_file' in options: + del options['password_file'] + if 'private_key' in options: + del options['private_key'] + if 'private_key_file' in options: + del options['private_key_file'] + + # type-specific initialization + if vault_type == 'standard': + + if password: + raise errors.ValidationError(name='password', + error=_('Invalid parameter for %s vault' % vault_type)) + + if password_file: + raise errors.ValidationError(name='password_file', + error=_('Invalid parameter for %s vault' % vault_type)) + + if private_key: + raise errors.ValidationError(name='private_key', + error=_('Invalid parameter for %s vault' % vault_type)) + + if private_key_file: + raise errors.ValidationError(name='private_key_file', + error=_('Invalid parameter for %s vault' % vault_type)) + + elif vault_type == 'symmetric': + + if private_key: + raise errors.ValidationError(name='private_key', + error=_('Invalid parameter for %s vault' % vault_type)) + + if private_key_file: + raise errors.ValidationError(name='private_key_file', + error=_('Invalid parameter for %s vault' % vault_type)) + + # get vault password + if password: + pass + + elif password_file: + with open(password_file) as f: + password = unicode(f.read().rstrip('\n')) + + else: + password = unicode(getpass.getpass('Password: ')) + + elif vault_type == 'asymmetric': + + if password: + raise errors.ValidationError(name='password', + error=_('Invalid parameter for %s vault' % vault_type)) + + if password_file: + raise errors.ValidationError(name='password_file', + error=_('Invalid parameter for %s vault' % vault_type)) + + # get vault public key + if private_key: + pass + + elif private_key_file: + with open(private_key_file, 'rb') as f: + private_key = f.read() + + else: + raise errors.ValidationError(name='private_key', + error=_('Missing vault private key')) + + else: + raise errors.ValidationError(name='vault_type', + error=_('Invalid vault type')) + + # retrieve secrets + response = api.Command.vault_retrieve( + vault_name, + container=container_id, + password=password, + private_key=private_key) + + vault_data = response['result']['data'] + + if not vault_data: + json_data = { + 'secrets': [] + } + + else: + json_data = json.loads(vault_data) + + secrets = json_data['secrets'] + + # find the secret + secret = None + for s in secrets: + if s['secret_name'] == secret_name: + secret = s + break + + if not secret: + raise errors.NotFound(reason=_('%s: vault secret not found' % secret_name)) + + # delete secret + secrets.remove(secret) + + # rearchive secrets + vault_data = json.dumps(json_data) + response = api.Command.vault_archive( + vault_name, + container=container_id, + data=vault_data, + password=password) + + response = { + 'value': secret_name, + 'summary': u'Deleted vault secret "%s"' % secret_name, + 'result': { + 'failed': (), + }, + } + + return response + + + at register() +class vaultsecret_find(LDAPSearch): + __doc__ = _(""" + Search for vault secrets. + """) + + takes_options = ( + Str('container?', + cli_name='container', + doc=_('Container ID'), + ), + Str('password?', + cli_name='password', + doc=_('Vault password'), + ), + Str('password_file?', + cli_name='password_file', + doc=_('File containing the vault password'), + ), + Bytes('private_key?', + cli_name='private_key', + doc=_('Vault private key'), + ), + Str('private_key_file?', + cli_name='private_key_file', + doc=_('File containing the vault private key'), + ), + ) + + def forward(self, *args, **options): + + vault_name = args[0] + container_id = api.Object.vaultcontainer.normalize_id(options.get('container')) + + vault_type = 'standard' + salt = None + + # retrieve vault info + response = api.Command.vault_show(vault_name, container=container_id) + result = response['result'] + + if result.has_key('ipavaulttype'): + vault_type = result['ipavaulttype'][0] + + if result.has_key('ipavaultsalt'): + salt = str(result['ipavaultsalt'][0]) + + password = options.get('password') + password_file = options.get('password_file') + private_key = options.get('private_key') + private_key_file = options.get('private_key_file') + + # don't send these parameters to server + if 'password' in options: + del options['password'] + if 'password_file' in options: + del options['password_file'] + if 'private_key' in options: + del options['private_key'] + if 'private_key_file' in options: + del options['private_key_file'] + + # type-specific initialization + if vault_type == 'standard': + + if password: + raise errors.ValidationError(name='password', + error=_('Invalid parameter for %s vault' % vault_type)) + + if password_file: + raise errors.ValidationError(name='password_file', + error=_('Invalid parameter for %s vault' % vault_type)) + + if private_key: + raise errors.ValidationError(name='private_key', + error=_('Invalid parameter for %s vault' % vault_type)) + + if private_key_file: + raise errors.ValidationError(name='private_key_file', + error=_('Invalid parameter for %s vault' % vault_type)) + + elif vault_type == 'symmetric': + + if private_key: + raise errors.ValidationError(name='private_key', + error=_('Invalid parameter for %s vault' % vault_type)) + + if private_key_file: + raise errors.ValidationError(name='private_key_file', + error=_('Invalid parameter for %s vault' % vault_type)) + + # get vault password + if password: + pass + + elif password_file: + with open(password_file) as f: + password = unicode(f.read().rstrip('\n')) + + else: + password = unicode(getpass.getpass('Password: ')) + + elif vault_type == 'asymmetric': + + if password: + raise errors.ValidationError(name='password', + error=_('Invalid parameter for %s vault' % vault_type)) + + if password_file: + raise errors.ValidationError(name='password_file', + error=_('Invalid parameter for %s vault' % vault_type)) + + # get vault public key + if private_key: + pass + + elif private_key_file: + with open(private_key_file, 'rb') as f: + private_key = f.read() + + else: + raise errors.ValidationError(name='private_key', + error=_('Missing vault private key')) + + else: + raise errors.ValidationError(name='vault_type', + error=_('Invalid vault type')) + + response = api.Command.vault_retrieve( + vault_name, + container=container_id, + password=password, + private_key=private_key) + + vault_data = response['result']['data'] + + if not vault_data: + json_data = { + 'secrets': [] + } + + else: + json_data = json.loads(vault_data) + + secrets = json_data['secrets'] + + # decode data for response + for secret in secrets: + secret['data'] = base64.b64decode(secret['data']) + + response = { + 'count': len(secrets), + 'truncated': False, + 'summary': u'%d vault secret matched' % len(secrets), + 'result': secrets, + } + + return response + + + at register() +class vaultsecret_mod(LDAPRetrieve): + __doc__ = _('Modify a vault secret.') + + takes_options = ( + Str('container?', + cli_name='container', + doc=_('Container ID'), + ), + Str('description?', + cli_name='desc', + doc=_('Secret description'), + ), + Bytes('data?', + cli_name='data', + doc=_('Binary secret data'), + ), + Str('text?', + cli_name='text', + doc=_('Text secret data'), + ), + Str('in?', + cli_name='in', + doc=_('File containing secret data'), + ), + Str('password?', + cli_name='password', + doc=_('Vault password'), + ), + Str('password_file?', + cli_name='password_file', + doc=_('File containing the vault password'), + ), + Bytes('private_key?', + cli_name='private_key', + doc=_('Vault private key'), + ), + Str('private_key_file?', + cli_name='private_key_file', + doc=_('File containing the vault private key'), + ), + ) + + msg_summary = _('Modified vault secret "%(value)s"') + + def forward(self, *args, **options): + + vault_name = args[0] + secret_name = args[1] + container_id = api.Object.vaultcontainer.normalize_id(options.get('container')) + + vault_type = 'standard' + salt = None + + # retrieve vault info + response = api.Command.vault_show(vault_name, container=container_id) + result = response['result'] + + if result.has_key('ipavaulttype'): + vault_type = result['ipavaulttype'][0] + + if result.has_key('ipavaultsalt'): + salt = str(result['ipavaultsalt'][0]) + + description = options.get('description') + data = options.get('data') + text = options.get('text') + input_file = options.get('in') + password = options.get('password') + password_file = options.get('password_file') + private_key = options.get('private_key') + private_key_file = options.get('private_key_file') + + # don't send these parameters to server + if 'data' in options: + del options['data'] + if 'text' in options: + del options['text'] + if 'in' in options: + del options['in'] + if 'password' in options: + del options['password'] + if 'password_file' in options: + del options['password_file'] + if 'private_key' in options: + del options['private_key'] + if 'private_key_file' in options: + del options['private_key_file'] + + # type-specific initialization + if vault_type == 'standard': + + if password: + raise errors.ValidationError(name='password', + error=_('Invalid parameter for %s vault' % vault_type)) + + if password_file: + raise errors.ValidationError(name='password_file', + error=_('Invalid parameter for %s vault' % vault_type)) + + if private_key: + raise errors.ValidationError(name='private_key', + error=_('Invalid parameter for %s vault' % vault_type)) + + if private_key_file: + raise errors.ValidationError(name='private_key_file', + error=_('Invalid parameter for %s vault' % vault_type)) + + elif vault_type == 'symmetric': + + if private_key: + raise errors.ValidationError(name='private_key', + error=_('Invalid parameter for %s vault' % vault_type)) + + if private_key_file: + raise errors.ValidationError(name='private_key_file', + error=_('Invalid parameter for %s vault' % vault_type)) + + # get vault password + if password: + pass + + elif password_file: + with open(password_file) as f: + password = unicode(f.read().rstrip('\n')) + + else: + password = unicode(getpass.getpass('Password: ')) + + elif vault_type == 'asymmetric': + + if password: + raise errors.ValidationError(name='password', + error=_('Invalid parameter for %s vault' % vault_type)) + + if password_file: + raise errors.ValidationError(name='password_file', + error=_('Invalid parameter for %s vault' % vault_type)) + + # get vault public key + if private_key: + pass + + elif private_key_file: + with open(private_key_file, 'rb') as f: + private_key = f.read() + + else: + raise errors.ValidationError(name='private_key', + error=_('Missing vault private key')) + + else: + raise errors.ValidationError(name='vault_type', + error=_('Invalid vault type')) + + # get data + if data: + if text: + raise errors.ValidationError(name='text', + error=_('Input data already specified')) + + if input_file: + raise errors.ValidationError(name='input_file', + error=_('Input data already specified')) + + elif text: + if input_file: + raise errors.ValidationError(name='input_file', + error=_('Input data already specified')) + + data = text.encode('utf-8') + + elif input_file: + with open(input_file, 'rb') as f: + data = f.read() + + else: + pass + + # retrieve secrets + response = api.Command.vault_retrieve( + vault_name, + container=container_id, + password=password, + private_key=private_key) + + vault_data = response['result']['data'] + + if not vault_data: + json_data = { + 'secrets': [] + } + + else: + json_data = json.loads(vault_data) + + secrets = json_data['secrets'] + + # find the secret + secret = None + for s in secrets: + if s['secret_name'] == secret_name: + secret = s + break + + if not secret: + raise errors.NotFound(reason=_('%s: vault secret not found' % secret_name)) + + # modify the secret + if description: + secret['description'] = description + if data: + secret['data'] = unicode(base64.b64encode(data)) + + # rearchive secrets + vault_data = json.dumps(json_data) + response = api.Command.vault_archive( + vault_name, + container=container_id, + data=vault_data, + password=password) + + # decode data for response + secret['data'] = base64.b64decode(secret['data']) + + response = { + 'value': secret_name, + 'summary': u'Modified vault secret "%s"' % secret_name, + 'result': secret, + } + + return response + + + at register() +class vaultsecret_show(LDAPRetrieve): + __doc__ = _('Display information about a vault secret.') + + takes_options = ( + Str('container?', + cli_name='container', + doc=_('Container ID'), + ), + Flag('show_text?', + doc=_('Show text data'), + autofill=False, + ), + Flag('stdout?', + doc=_('Show data on standard output'), + autofill=False, + ), + Str('out?', + cli_name='out', + doc=_('File to store retrieved data'), + ), + Str('password?', + cli_name='password', + doc=_('Vault password'), + ), + Str('password_file?', + cli_name='password_file', + doc=_('File containing the vault password'), + ), + Bytes('private_key?', + cli_name='private_key', + doc=_('Vault private key'), + ), + Str('private_key_file?', + cli_name='private_key_file', + doc=_('File containing the vault private key'), + ), + ) + + has_output_params = ( + Str('text', + label=_('Text'), + ), + ) + + def forward(self, *args, **options): + + vault_name = args[0] + secret_name = args[1] + container_id = api.Object.vaultcontainer.normalize_id(options.get('container')) + + vault_type = 'standard' + salt = None + + # retrieve vault info + response = api.Command.vault_show(vault_name, container=container_id) + result = response['result'] + + if result.has_key('ipavaulttype'): + vault_type = result['ipavaulttype'][0] + + if result.has_key('ipavaultsalt'): + salt = str(result['ipavaultsalt'][0]) + + show_text = options.get('show_text') + stdout = options.get('stdout') + output_file = options.get('out') + password = options.get('password') + password_file = options.get('password_file') + private_key = options.get('private_key') + private_key_file = options.get('private_key_file') + + # don't send these parameters to server + if 'show_text' in options: + del options['show_text'] + if 'stdout' in options: + del options['stdout'] + if 'out' in options: + del options['out'] + if 'password' in options: + del options['password'] + if 'password_file' in options: + del options['password_file'] + if 'private_key' in options: + del options['private_key'] + if 'private_key_file' in options: + del options['private_key_file'] + + # type-specific initialization + if vault_type == 'standard': + + if password: + raise errors.ValidationError(name='password', + error=_('Invalid parameter for %s vault' % vault_type)) + + if password_file: + raise errors.ValidationError(name='password_file', + error=_('Invalid parameter for %s vault' % vault_type)) + + if private_key: + raise errors.ValidationError(name='private_key', + error=_('Invalid parameter for %s vault' % vault_type)) + + if private_key_file: + raise errors.ValidationError(name='private_key_file', + error=_('Invalid parameter for %s vault' % vault_type)) + + elif vault_type == 'symmetric': + + if private_key: + raise errors.ValidationError(name='private_key', + error=_('Invalid parameter for %s vault' % vault_type)) + + if private_key_file: + raise errors.ValidationError(name='private_key_file', + error=_('Invalid parameter for %s vault' % vault_type)) + + # get vault password + if password: + pass + + elif password_file: + with open(password_file) as f: + password = unicode(f.read().rstrip('\n')) + + else: + password = unicode(getpass.getpass('Password: ')) + + elif vault_type == 'asymmetric': + + if password: + raise errors.ValidationError(name='password', + error=_('Invalid parameter for %s vault' % vault_type)) + + if password_file: + raise errors.ValidationError(name='password_file', + error=_('Invalid parameter for %s vault' % vault_type)) + + # get vault public key + if private_key: + pass + + elif private_key_file: + with open(private_key_file, 'rb') as f: + private_key = f.read() + + else: + raise errors.ValidationError(name='private_key', + error=_('Missing vault private key')) + + else: + raise errors.ValidationError(name='vault_type', + error=_('Invalid vault type')) + + # retrieve secrets + response = api.Command.vault_retrieve( + vault_name, + container=container_id, + password=password, + private_key=private_key) + + vault_data = response['result']['data'] + + if not vault_data: + json_data = { + 'secrets': [] + } + + else: + json_data = json.loads(vault_data) + + secrets = json_data['secrets'] + + secret = None + + # find the secret + for s in secrets: + if s['secret_name'] == secret_name: + secret = s + break + + if not secret: + raise errors.NotFound(reason=_('%s: vault secret not found' % secret_name)) + + # decode data for response + secret['data'] = base64.b64decode(secret['data']) + + response = { + 'value': secret_name, + 'result': secret, + } + + if stdout: + sys.stdout.write(secret['data']) + response['result'] = {} + + elif output_file: + with open(output_file, 'w') as f: + f.write(secret['data']) + response['result'] = {} + + elif show_text: + response['result']['text'] = unicode(secret['data']) + del response['result']['data'] + + else: + pass + + return response diff --git a/ipatests/test_xmlrpc/test_vaultsecret_plugin.py b/ipatests/test_xmlrpc/test_vaultsecret_plugin.py new file mode 100644 index 0000000000000000000000000000000000000000..68f0fb0d7085be512939e397ea49abbcf3ca3c7b --- /dev/null +++ b/ipatests/test_xmlrpc/test_vaultsecret_plugin.py @@ -0,0 +1,211 @@ +# Authors: +# Endi S. Dewata +# +# Copyright (C) 2015 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 . + +""" +Test the `ipalib/plugins/vaultsecret.py` module. +""" + +from ipalib import api, errors +from xmlrpc_test import Declarative, fuzzy_string + +test_vault = u'test_vault' +test_vaultsecret = u'test_vaultsecret' +binary_data = '\x01\x02\x03\x04' +text_data = u'secret' + +class test_vault(Declarative): + + cleanup_commands = [ + ('vault_del', [test_vault], {'continue': True}), + ] + + tests = [ + + { + 'desc': 'Create test vault', + 'command': ( + 'vault_add', + [test_vault], + {}, + ), + 'expected': { + 'value': test_vault, + 'summary': 'Added vault "%s"' % test_vault, + 'result': { + 'dn': u'cn=%s,cn=admin,cn=users,cn=vaults,%s' % (test_vault, api.env.basedn), + 'objectclass': (u'ipaVault', u'top'), + 'cn': [test_vault], + 'vault_id': u'/users/admin/%s' % test_vault, + 'owner_user': [u'admin'], + 'ipavaulttype': [u'standard'], + }, + }, + }, + + { + 'desc': 'Create test vault secret with binary data', + 'command': ( + 'vaultsecret_add', + [test_vault, test_vaultsecret], + { + 'data': binary_data, + }, + ), + 'expected': { + 'value': test_vaultsecret, + 'summary': 'Added vault secret "%s"' % test_vaultsecret, + 'result': { + 'secret_name': test_vaultsecret, + 'data': binary_data, + }, + }, + }, + + { + 'desc': 'Create duplicate vault secret', + 'command': ( + 'vaultsecret_add', + [test_vault, test_vaultsecret], + {}, + ), + 'expected': errors.DuplicateEntry(message=u'vault secret with name "%s" already exists' % test_vaultsecret), + }, + + { + 'desc': 'Find vault secrets', + 'command': ( + 'vaultsecret_find', + [test_vault], + {}, + ), + 'expected': { + 'count': 1, + 'truncated': False, + 'summary': u'1 vault secret matched', + 'result': [ + { + 'secret_name': test_vaultsecret, + 'data': binary_data, + }, + ], + }, + }, + + { + 'desc': 'Retrieve test vault secret', + 'command': ( + 'vaultsecret_show', + [test_vault, test_vaultsecret], + {}, + ), + 'expected': { + 'value': test_vaultsecret, + 'summary': None, + 'result': { + 'secret_name': test_vaultsecret, + 'data': binary_data, + }, + }, + }, + + { + 'desc': 'Modify test vault secret', + 'command': ( + 'vaultsecret_mod', + [test_vault, test_vaultsecret], + { + 'description': u'Test vault secret', + }, + ), + 'expected': { + 'value': test_vaultsecret, + 'summary': u'Modified vault secret "%s"' % test_vaultsecret, + 'result': { + 'secret_name': test_vaultsecret, + 'description': u'Test vault secret', + 'data': binary_data, + }, + }, + }, + + { + 'desc': 'Delete test vault secret', + 'command': ( + 'vaultsecret_del', + [test_vault, test_vaultsecret], + {}, + ), + 'expected': { + 'value': test_vaultsecret, + 'summary': u'Deleted vault secret "%s"' % test_vaultsecret, + 'result': { + 'failed': (), + }, + }, + }, + + { + 'desc': 'Delete non-existent vault secret', + 'command': ( + 'vaultsecret_del', + [test_vault, test_vaultsecret], + {}, + ), + 'expected': errors.NotFound(reason=u'%s: vault secret not found' % test_vaultsecret), + }, + + { + 'desc': 'Create test vault secret with text data', + 'command': ( + 'vaultsecret_add', + [test_vault, test_vaultsecret], + { + 'text': text_data, + }, + ), + 'expected': { + 'value': test_vaultsecret, + 'summary': 'Added vault secret "%s"' % test_vaultsecret, + 'result': { + 'secret_name': test_vaultsecret, + 'data': text_data.encode('utf-8'), + }, + }, + }, + + { + 'desc': 'Retrieve test vault secret as text', + 'command': ( + 'vaultsecret_show', + [test_vault, test_vaultsecret], + { + 'show_text': True, + }, + ), + 'expected': { + 'value': test_vaultsecret, + 'summary': None, + 'result': { + 'secret_name': test_vaultsecret, + 'text': text_data, + }, + }, + }, + + ] -- 1.9.0 -------------- next part -------------- >From 3cb3643db8828b9837f52168b3474a7202bf99c6 Mon Sep 17 00:00:00 2001 From: "Endi S. Dewata" Date: Sat, 21 Feb 2015 17:17:03 -0500 Subject: [PATCH] Added vault escrow functionality. The symmetric and asymmetric vaults have been modified to support escrow for recovery. The LDAP schema has been modified to store the escrow public key. New test cases have been added for the escrow functionality. https://fedorahosted.org/freeipa/ticket/3872 --- API.txt | 14 ++- install/share/60basev3.ldif | 3 +- ipalib/plugins/vault.py | 127 ++++++++++++++++++++++-- ipatests/test_xmlrpc/test_vault_plugin.py | 159 ++++++++++++++++++++++++++++++ 4 files changed, 288 insertions(+), 15 deletions(-) diff --git a/API.txt b/API.txt index 4adc4db4978475883013eb6ea7dd57585e1f2ae3..3f4afc6c61021673ed2af7b713ad443def474db5 100644 --- a/API.txt +++ b/API.txt @@ -4518,14 +4518,16 @@ output: Output('result', , None) output: Output('summary', (, ), None) output: PrimaryKey('value', None, None) command: vault_add -args: 1,18,3 +args: 1,20,3 arg: Str('cn', attribute=True, cli_name='vault_name', maxlength=255, multivalue=False, pattern='^[a-zA-Z0-9_.-]+$', primary_key=True, required=True) option: Str('addattr*', cli_name='addattr', exclude='webui') option: Flag('all', autofill=True, cli_name='all', default=False, exclude='webui') option: Str('container', attribute=False, cli_name='container', multivalue=False, pattern='^[a-zA-Z0-9_.-/]+$', required=False) option: Bytes('data?', cli_name='data') option: Str('description', attribute=True, cli_name='desc', multivalue=False, required=False) +option: Str('escrow_public_key_file?', cli_name='escrow_public_key_file') option: Str('in?', cli_name='in') +option: Bytes('ipaescrowpublickey', attribute=True, cli_name='escrow_public_key', multivalue=False, required=False) option: Bytes('ipapublickey', attribute=True, cli_name='public_key', multivalue=False, required=False) option: Str('ipavaultsalt', attribute=True, cli_name='salt', multivalue=False, required=False) option: Str('ipavaulttype', attribute=True, autofill=True, cli_name='type', default=u'standard', multivalue=False, required=False) @@ -4598,12 +4600,13 @@ output: Output('result', , None) output: Output('summary', (, ), None) output: ListOfPrimaryKeys('value', None, None) command: vault_find -args: 1,14,4 +args: 1,15,4 arg: Str('criteria?', noextrawhitespace=False) option: Flag('all', autofill=True, cli_name='all', default=False, exclude='webui') option: Str('cn', attribute=True, autofill=False, cli_name='vault_name', maxlength=255, multivalue=False, pattern='^[a-zA-Z0-9_.-]+$', primary_key=True, query=True, required=False) option: Str('container', attribute=False, autofill=False, cli_name='container', multivalue=False, pattern='^[a-zA-Z0-9_.-/]+$', query=True, required=False) option: Str('description', attribute=True, autofill=False, cli_name='desc', multivalue=False, query=True, required=False) +option: Bytes('ipaescrowpublickey', attribute=True, autofill=False, cli_name='escrow_public_key', multivalue=False, query=True, required=False) option: Bytes('ipapublickey', attribute=True, autofill=False, cli_name='public_key', multivalue=False, query=True, required=False) option: Str('ipavaultsalt', attribute=True, autofill=False, cli_name='salt', multivalue=False, query=True, required=False) option: Str('ipavaulttype', attribute=True, autofill=False, cli_name='type', default=u'standard', multivalue=False, query=True, required=False) @@ -4619,13 +4622,14 @@ output: ListOfEntries('result', (, ), Gettext('A list output: Output('summary', (, ), None) output: Output('truncated', , None) command: vault_mod -args: 1,14,3 +args: 1,15,3 arg: Str('cn', attribute=True, cli_name='vault_name', maxlength=255, multivalue=False, pattern='^[a-zA-Z0-9_.-]+$', primary_key=True, query=True, required=True) option: Str('addattr*', cli_name='addattr', exclude='webui') option: Flag('all', autofill=True, cli_name='all', default=False, exclude='webui') option: Str('container', attribute=False, autofill=False, cli_name='container', multivalue=False, pattern='^[a-zA-Z0-9_.-/]+$', required=False) option: Str('delattr*', cli_name='delattr', exclude='webui') option: Str('description', attribute=True, autofill=False, cli_name='desc', multivalue=False, required=False) +option: Bytes('ipaescrowpublickey', attribute=True, autofill=False, cli_name='escrow_public_key', multivalue=False, required=False) option: Bytes('ipapublickey', attribute=True, autofill=False, cli_name='public_key', multivalue=False, required=False) option: Str('ipavaultsalt', attribute=True, autofill=False, cli_name='salt', multivalue=False, required=False) option: Str('ipavaulttype', attribute=True, autofill=False, cli_name='type', default=u'standard', multivalue=False, required=False) @@ -4665,10 +4669,12 @@ output: Output('completed', , None) output: Output('failed', , None) output: Entry('result', , Gettext('A dictionary representing an LDAP entry', domain='ipa', localedir=None)) command: vault_retrieve -args: 1,14,3 +args: 1,16,3 arg: Str('cn', attribute=True, cli_name='vault_name', maxlength=255, multivalue=False, pattern='^[a-zA-Z0-9_.-]+$', primary_key=True, query=True, required=True) option: Flag('all', autofill=True, cli_name='all', default=False, exclude='webui') option: Str('container?', cli_name='container') +option: Bytes('escrow_private_key?', cli_name='escrow_private_key') +option: Str('escrow_private_key_file?', cli_name='escrow_private_key_file') option: Flag('no_members', autofill=True, default=False, exclude='webui') option: Str('out?', cli_name='out') option: Str('password?', cli_name='password') diff --git a/install/share/60basev3.ldif b/install/share/60basev3.ldif index 9e0f70a41ef50e78d4e464bab428325dfb6568fa..d0ea7135b724381dc21281870c78b7b5b0f67be1 100644 --- a/install/share/60basev3.ldif +++ b/install/share/60basev3.ldif @@ -56,6 +56,7 @@ attributeTypes: (2.16.840.1.113730.3.8.11.64 NAME 'ipaSecretKeyRef' DESC 'DN of attributeTypes: (2.16.840.1.113730.3.8.11.65 NAME 'ipaWrappingMech' DESC 'PKCS#11 wrapping mechanism equivalent to CK_MECHANISM_TYPE' EQUALITY caseIgnoreMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 SINGLE-VALUE X-ORIGIN 'IPA v4.1') attributeTypes: (2.16.840.1.113730.3.8.18.2.1 NAME 'ipaVaultType' DESC 'IPA vault type' EQUALITY caseExactMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 X-ORIGIN 'IPA v4.2') attributeTypes: (2.16.840.1.113730.3.8.18.2.2 NAME 'ipaVaultSalt' DESC 'IPA vault salt' EQUALITY caseExactMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 X-ORIGIN 'IPA v4.2') +attributeTypes: (2.16.840.1.113730.3.8.18.2.3 NAME 'ipaEscrowPublicKey' DESC 'IPA escrow public key as DER-encoded SubjectPublicKeyInfo (RFC 5280)' EQUALITY octetStringMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.40 X-ORIGIN 'IPA v4.2' ) objectClasses: (2.16.840.1.113730.3.8.12.1 NAME 'ipaExternalGroup' SUP top STRUCTURAL MUST ( cn ) MAY ( ipaExternalMember $ memberOf $ description $ owner) X-ORIGIN 'IPA v3' ) objectClasses: (2.16.840.1.113730.3.8.12.2 NAME 'ipaNTUserAttrs' SUP top AUXILIARY MUST ( ipaNTSecurityIdentifier ) MAY ( ipaNTHash $ ipaNTLogonScript $ ipaNTProfilePath $ ipaNTHomeDirectory $ ipaNTHomeDirectoryDrive ) X-ORIGIN 'IPA v3' ) objectClasses: (2.16.840.1.113730.3.8.12.3 NAME 'ipaNTGroupAttrs' SUP top AUXILIARY MUST ( ipaNTSecurityIdentifier ) X-ORIGIN 'IPA v3' ) @@ -79,5 +80,5 @@ objectClasses: (2.16.840.1.113730.3.8.12.24 NAME 'ipaPublicKeyObject' DESC 'Wrap objectClasses: (2.16.840.1.113730.3.8.12.25 NAME 'ipaPrivateKeyObject' DESC 'Wrapped private keys' SUP top AUXILIARY MUST ( ipaPrivateKey $ ipaWrappingKey $ ipaWrappingMech ) X-ORIGIN 'IPA v4.1' ) objectClasses: (2.16.840.1.113730.3.8.12.26 NAME 'ipaSecretKeyObject' DESC 'Wrapped secret keys' SUP top AUXILIARY MUST ( ipaSecretKey $ ipaWrappingKey $ ipaWrappingMech ) X-ORIGIN 'IPA v4.1' ) objectClasses: (2.16.840.1.113730.3.8.12.34 NAME 'ipaSecretKeyRefObject' DESC 'Indirect storage for encoded key material' SUP top AUXILIARY MUST ( ipaSecretKeyRef ) X-ORIGIN 'IPA v4.1' ) -objectClasses: (2.16.840.1.113730.3.8.18.1.1 NAME 'ipaVault' DESC 'IPA vault' SUP top STRUCTURAL MUST ( cn ) MAY ( description $ owner $ member $ ipaVaultType $ ipaVaultSalt $ ipaPublicKey ) X-ORIGIN 'IPA v4.2' ) +objectClasses: (2.16.840.1.113730.3.8.18.1.1 NAME 'ipaVault' DESC 'IPA vault' SUP top STRUCTURAL MUST ( cn ) MAY ( description $ owner $ member $ ipaVaultType $ ipaVaultSalt $ ipaPublicKey $ ipaEscrowPublicKey ) X-ORIGIN 'IPA v4.2' ) objectClasses: (2.16.840.1.113730.3.8.18.1.2 NAME 'ipaVaultContainer' DESC 'IPA vault container' SUP top STRUCTURAL MUST ( cn ) MAY ( description $ owner $ member ) X-ORIGIN 'IPA v4.2' ) diff --git a/ipalib/plugins/vault.py b/ipalib/plugins/vault.py index e1a4711bf835eb3f102bbe06f29dd19681912624..69c12aaf1c8503a345e115fb1a660aed8f85fb17 100644 --- a/ipalib/plugins/vault.py +++ b/ipalib/plugins/vault.py @@ -72,6 +72,12 @@ EXAMPLES: Add an asymmetric vault: ipa vault-add MyVault --type asymmetric --public-key-file public.pem """) + _(""" + Add a escrowed symmetric vault: + ipa vault-add MyVault --type symmetric --password-file password.txt --escrow-public-key-file escrow-public.pem +""") + _(""" + Add an escrowed asymmetric vault: + ipa vault-add MyVault --type asymmetric --public-key-file public.pem --escrow-public-key-file escrow-public.pem +""") + _(""" Show a vault: ipa vault-show MyVault """) + _(""" @@ -99,6 +105,12 @@ EXAMPLES: Retrieve data from asymmetric vault: ipa vault-retrieve MyVault --out data.bin --private-key-file private.pem """) + _(""" + Recover data from escrowed symmetric vault: + ipa vault-retrieve MyVault --out data.bin --escrow-private-key-file escrow-private.pem +""") + _(""" + Recover data from escrowed asymmetric vault: + ipa vault-retrieve MyVault --out data.bin --escrow-private-key-file escrow-private.pem +""") + _(""" Delete a vault: ipa vault-del MyVault """) + _(""" @@ -136,6 +148,7 @@ class vault(LDAPObject): 'ipavaulttype', 'ipavaultsalt', 'ipapublickey', + 'ipaescrowpublickey', ] search_display_attributes = [ 'cn', @@ -196,6 +209,11 @@ class vault(LDAPObject): label=_('Public key'), doc=_('Vault public key'), ), + Bytes('ipaescrowpublickey?', + cli_name='escrow_public_key', + label=_('Escrow public key'), + doc=_('Escrow public key'), + ), ) def get_dn(self, *keys, **options): @@ -360,6 +378,10 @@ class vault_add(LDAPCreate): cli_name='public_key_file', doc=_('File containing the vault public key'), ), + Str('escrow_public_key_file?', + cli_name='escrow_public_key_file', + doc=_('File containing the escrow public key'), + ), ) msg_summary = _('Added vault "%(value)s"') @@ -377,6 +399,8 @@ class vault_add(LDAPCreate): password_file = options.get('password_file') public_key = options.get('ipapublickey') public_key_file = options.get('public_key_file') + escrow_public_key = options.get('ipaescrowpublickey') + escrow_public_key_file = options.get('escrow_public_key_file') # don't send these parameters to server if 'data' in options: @@ -391,6 +415,8 @@ class vault_add(LDAPCreate): del options['password_file'] if 'public_key_file' in options: del options['public_key_file'] + if 'escrow_public_key_file' in options: + del options['escrow_public_key_file'] # get data if data: @@ -435,6 +461,14 @@ class vault_add(LDAPCreate): raise errors.ValidationError(name='public_key_file', error=_('Invalid parameter for %s vault' % vault_type)) + if escrow_public_key: + raise errors.ValidationError(name='ipaescrowpublickey', + error=_('Invalid parameter for %s vault' % vault_type)) + + if escrow_public_key_file: + raise errors.ValidationError(name='escrow_public_key_file', + error=_('Invalid parameter for %s vault' % vault_type)) + encryption_key = None elif vault_type == 'symmetric': @@ -470,6 +504,16 @@ class vault_add(LDAPCreate): # generate encryption key from vault password encryption_key = self.obj.generate_symmetric_key(password, salt) + # get escrow public key + if escrow_public_key: + options['ipaescrowpublickey'] = escrow_public_key + + elif escrow_public_key_file: + with open(escrow_public_key_file, 'rb') as f: + escrow_public_key = f.read() + + options['ipaescrowpublickey'] = escrow_public_key + elif vault_type == 'asymmetric': if password: @@ -502,6 +546,16 @@ class vault_add(LDAPCreate): # store vault public key options['ipapublickey'] = public_key + # get escrow public key + if escrow_public_key: + options['ipaescrowpublickey'] = escrow_public_key + + elif escrow_public_key_file: + with open(escrow_public_key_file, 'rb') as f: + escrow_public_key = f.read() + + options['ipaescrowpublickey'] = escrow_public_key + else: raise errors.ValidationError(name='vault_type', error=_('Invalid vault type')) @@ -755,6 +809,7 @@ class vault_archive(LDAPRetrieve): vault_type = 'standard' salt = None public_key = None + escrow_public_key = None # retrieve vault info response = api.Command.vault_show(vault_name, container=container_id) @@ -769,6 +824,9 @@ class vault_archive(LDAPRetrieve): if result.has_key('ipapublickey'): public_key = str(result['ipapublickey'][0]) + if result.has_key('ipaescrowpublickey'): + escrow_public_key = str(result['ipaescrowpublickey'][0]) + data = options.get('data') text = options.get('text') input_file = options.get('in') @@ -815,6 +873,7 @@ class vault_archive(LDAPRetrieve): data = '' encrypted_key = None + escrowed_key = None if vault_type == 'standard': @@ -857,6 +916,10 @@ class vault_archive(LDAPRetrieve): # encrypt data with encryption key data = self.obj.encrypt(data, symmetric_key=encryption_key) + # encrypt encryption key with escrow public key + if escrow_public_key: + escrowed_key = self.obj.encrypt(encryption_key, public_key=escrow_public_key) + elif vault_type == 'asymmetric': if password: @@ -879,6 +942,10 @@ class vault_archive(LDAPRetrieve): # encrypt encryption key with public key encrypted_key = self.obj.encrypt(encryption_key, public_key=public_key) + # encrypt encryption key with escrow public key + if escrow_public_key: + escrowed_key = self.obj.encrypt(encryption_key, public_key=escrow_public_key) + else: raise errors.ValidationError(name='vault_type', error=_('Invalid vault type')) @@ -919,6 +986,9 @@ class vault_archive(LDAPRetrieve): if encrypted_key: vault_data[u'encrypted_key'] = unicode(base64.b64encode(encrypted_key)) + if escrowed_key: + vault_data[u'escrowed_key'] = unicode(base64.b64encode(escrowed_key)) + json_vault_data = json.dumps(vault_data) # wrap vault_data with session key @@ -1028,6 +1098,14 @@ class vault_retrieve(LDAPRetrieve): cli_name='private_key_file', doc=_('File containing the vault private key'), ), + Bytes('escrow_private_key?', + cli_name='escrow_private_key', + doc=_('Escrow vault private key'), + ), + Str('escrow_private_key_file?', + cli_name='escrow_private_key_file', + doc=_('File containing the escrow vault private key'), + ), ) has_output_params = ( @@ -1066,6 +1144,8 @@ class vault_retrieve(LDAPRetrieve): password_file = options.get('password_file') private_key = options.get('private_key') private_key_file = options.get('private_key_file') + escrow_private_key = options.get('escrow_private_key') + escrow_private_key_file = options.get('escrow_private_key_file') # don't send these parameters to server if 'show_text' in options: @@ -1082,6 +1162,10 @@ class vault_retrieve(LDAPRetrieve): del options['private_key'] if 'private_key_file' in options: del options['private_key_file'] + if 'escrow_private_key' in options: + del options['escrow_private_key'] + if 'escrow_private_key_file' in options: + del options['escrow_private_key_file'] # initialize NSS database crypto = pki.crypto.NSSCryptoProvider(paths.IPA_NSSDB_DIR) @@ -1132,6 +1216,11 @@ class vault_retrieve(LDAPRetrieve): if vault_data.has_key('encrypted_key'): encrypted_key = base64.b64decode(str(vault_data[u'encrypted_key'])) + escrowed_key = None + + if vault_data.has_key('escrowed_key'): + escrowed_key = base64.b64decode(str(vault_data['escrowed_key'])) + if vault_type == 'standard': if password: @@ -1160,19 +1249,29 @@ class vault_retrieve(LDAPRetrieve): raise errors.ValidationError(name='private_key_file', error=_('Invalid parameter for %s vault' % vault_type)) - # get encryption key from vault password + # get encryption key from vault password or escrowed private key if password: - pass + encryption_key = self.obj.generate_symmetric_key(password, salt) elif password_file: with open(password_file) as f: password = unicode(f.read().rstrip('\n')) + encryption_key = self.obj.generate_symmetric_key(password, salt) + + elif escrow_private_key: + + encryption_key = self.obj.decrypt(escrowed_key, private_key=escrow_private_key) + + elif escrow_private_key_file: + with open(escrow_private_key_file, 'rb') as f: + escrow_private_key = f.read() + + encryption_key = self.obj.decrypt(escrowed_key, private_key=escrow_private_key) + else: password = unicode(getpass.getpass('Password: ')) - - # generate encryption key from password - encryption_key = self.obj.generate_symmetric_key(password, salt) + encryption_key = self.obj.generate_symmetric_key(password, salt) # decrypt data with encryption key data = self.obj.decrypt(data, symmetric_key=encryption_key) @@ -1187,21 +1286,29 @@ class vault_retrieve(LDAPRetrieve): raise errors.ValidationError(name='password_file', error=_('Invalid parameter for %s vault' % vault_type)) - # get encryption key with vault private key + # get encryption key with vault private key or escrowed private key if private_key: - pass + encryption_key = self.obj.decrypt(encrypted_key, private_key=private_key) elif private_key_file: with open(private_key_file, 'rb') as f: private_key = f.read() + encryption_key = self.obj.decrypt(encrypted_key, private_key=private_key) + + elif escrow_private_key: + encryption_key = self.obj.decrypt(escrowed_key, private_key=escrow_private_key) + + elif escrow_private_key_file: + with open(escrow_private_key_file, 'rb') as f: + escrow_private_key = f.read() + + encryption_key = self.obj.decrypt(escrowed_key, private_key=escrow_private_key) + else: raise errors.ValidationError(name='private_key', error=_('Missing vault private key')) - # decrypt encryption key with private key - encryption_key = self.obj.decrypt(encrypted_key, private_key=private_key) - # decrypt data with encryption key data = self.obj.decrypt(data, symmetric_key=encryption_key) diff --git a/ipatests/test_xmlrpc/test_vault_plugin.py b/ipatests/test_xmlrpc/test_vault_plugin.py index d86e5c2e0d6e51c74086c5c9541d1d939164cf10..04f56ecafd3a141b39a0e32f1725258582b6b141 100644 --- a/ipatests/test_xmlrpc/test_vault_plugin.py +++ b/ipatests/test_xmlrpc/test_vault_plugin.py @@ -31,6 +31,9 @@ text_data = u'secret' symmetric_vault = u'symmetric_vault' asymmetric_vault = u'asymmetric_vault' +escrowed_symmetric_vault = u'escrowed_symmetric_vault' +escrowed_asymmetric_vault = u'escrowed_asymmetric_vault' + password = u'password' public_key = """ @@ -75,6 +78,48 @@ kUlCMj24a8XsShzYTWBIyW2ngvGe3pQ9PfjkUdm0LGZjYITCBvgOKw== -----END RSA PRIVATE KEY----- """ +escrow_public_key = """ +-----BEGIN PUBLIC KEY----- +MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAv7E/QLVyKjrgDctZ50U7 +rmtL7Ks1QLoccp9WvZJ6WI1rYd0fX5FySS4dI6QTNZc6qww8NeNuZtkoxT9m1wkk +Rl/3wK7fWNLenH/+VHOaTQc20exg7ztfsO7JIsmKmigtticdR5C4jLfjcOp+WjLH +w3zrmrO5SIZ8njxMoDcQJa2vu/t281U/I7ti8ue09FSitIECU05vgmPS+MnXR8HK +PxXqrNkjl29mXNbPiByWwlse3Prwved9I7fwgpiHJqUBFudD/0tZ4DWyLG7t9wM1 +O8gRaRg1r+ENVpmMSvXo4+8+bR3rEYddD5zU7nKXafeuthXlXplae/8uZmCiSI63 +TwIDAQAB +-----END PUBLIC KEY----- +""" + +escrow_private_key = """ +-----BEGIN RSA PRIVATE KEY----- +MIIEpgIBAAKCAQEAv7E/QLVyKjrgDctZ50U7rmtL7Ks1QLoccp9WvZJ6WI1rYd0f +X5FySS4dI6QTNZc6qww8NeNuZtkoxT9m1wkkRl/3wK7fWNLenH/+VHOaTQc20exg +7ztfsO7JIsmKmigtticdR5C4jLfjcOp+WjLHw3zrmrO5SIZ8njxMoDcQJa2vu/t2 +81U/I7ti8ue09FSitIECU05vgmPS+MnXR8HKPxXqrNkjl29mXNbPiByWwlse3Prw +ved9I7fwgpiHJqUBFudD/0tZ4DWyLG7t9wM1O8gRaRg1r+ENVpmMSvXo4+8+bR3r +EYddD5zU7nKXafeuthXlXplae/8uZmCiSI63TwIDAQABAoIBAQCA+0GFR9F+isjx +Xy+qBpKmxLl8kKKvX8r+cSpLOkEqTlW/rqqKgnI0vVuL/L2UJKKsLvpghBxoBZyC +RCvtatBGrhIlS0UrHg/9m73Ek1hylfUUAQokTn4PrkwWJSgmm/xOATmZSs5ymNTn +yFCmXl69sdNR77YvD5bQXeBtOT+bKXy7yQ1TmYPwwSjL+WSlMV6ZfE3HNVmxPTpk +CTFS638cJblWk9MUIy8HIlhu6If2P4RnHr7ZGGivhREayvs0zXcAfqhIyFHruxSE +yYnmqH9paWjv5mP3YyLoKr+NUvvxnBr/9wCTt0TKgG8G6rpkHuPDLQni9wUGnew8 +QdMgFEohAoGBAPH4vaVB5gDVfvIqwJBsBLHpPq72GvxjrM/exD0jIIpXZxz9gCql +CmC5b1RS1uy8PMoc/RO4CE7UTLaTesciP6LjTD1RhH3rLLJO8/iVC1RXgMrCLHLm +ZQnDhIQGGNQxpvBjQy5ZOWat2dFxYhHN630IFPOtrWsOmJ5HsL1JrjzxAoGBAMrO +R1zNwQ42VbJS6AFshZVjmUV2h3REGh4zG/9IqL0Hz493hyCTGoDPLLXIbtkqNqzQ +XibSZ9RMVPKKTiNQTx91DTgh4Anz8xUr84tA2iAf3ayNWKi3Y3GhmP2EWp1qYeom +kV8Uq0lt4dHZuEo3LuqvbtbzlF9qUXqKS5qy6Tg/AoGBAKCp02o2HjzxhS/QeTmr +r1ZeE7PiTzrECAuh01TwzPtuW1XhcEdgfEqK9cPcmT5pIkflBZkhOcr1pdYYiI5O +TEigeY/BX6KoE251hALLG9GtpCN82DyWhAH+oy9ySOwj5793eTT+I2HtD1LE4SQH +QVQsmJTP/fS2pVl7KnwUvy9RAoGBAKzo2qchNewsHzx+uxgbsnkABfnXaP2T4sDE +yqYJCPTB6BFl02vOf9Y6zN/gF8JH333P2bY3xhaXTgXMLXqmSg+D+NVW7HEP8Lyo +UGj1zgN9p74qdODEGqETKiFb6vYzcW/1mhP6x18/tDz658k+611kXZge7O288+MK +bhNjXrx5AoGBAMox25PcxVgOjCd9+LdUcIOG6LQ971eCH1NKL9YAekICnwMrStbK +veCYju6ok4ZWnMiH8MR1jgC39RWtjJZwynCuPXUP2/vZkoVf1tCZyz7dSm8TdS/2 +5NdOHVy7+NQcEPSm7/FmXdpcR9ZSGAuxMBfnEUibdyz5LdJGnFUN/+HS +-----END RSA PRIVATE KEY----- +""" + wrong_private_key = """ -----BEGIN RSA PRIVATE KEY----- MIIEpAIBAAKCAQEAuQVrFCTFzc8EQ1TPR/fB2vZqcNFn7KVFYN5UuGSLm8JwdSyX @@ -111,6 +156,8 @@ class test_vault(Declarative): ('vault_del', [test_vault], {'continue': True}), ('vault_del', [symmetric_vault], {'continue': True}), ('vault_del', [asymmetric_vault], {'continue': True}), + ('vault_del', [escrowed_symmetric_vault], {'continue': True}), + ('vault_del', [escrowed_asymmetric_vault], {'continue': True}), ] tests = [ @@ -462,4 +509,116 @@ class test_vault(Declarative): 'expected': errors.AuthenticationError(message=u'Invalid credentials'), }, + { + 'desc': 'Create escrowed symmetric vault', + 'command': ( + 'vault_add', + [escrowed_symmetric_vault], + { + 'ipavaulttype': u'symmetric', + 'password': password, + 'ipaescrowpublickey': escrow_public_key, + 'data': binary_data, + }, + ), + 'expected': { + 'value': escrowed_symmetric_vault, + 'summary': 'Added vault "%s"' % escrowed_symmetric_vault, + 'result': { + 'dn': u'cn=%s,cn=admin,cn=users,cn=vaults,%s' % (escrowed_symmetric_vault, api.env.basedn), + 'objectclass': (u'ipaVault', u'top'), + 'cn': [escrowed_symmetric_vault], + 'vault_id': u'/users/admin/%s' % escrowed_symmetric_vault, + 'owner_user': [u'admin'], + 'ipavaulttype': [u'symmetric'], + 'ipavaultsalt': [fuzzy_string], + 'ipaescrowpublickey': [fuzzy_string], + }, + }, + }, + + { + 'desc': 'Recover escrowed symmetric vault', + 'command': ( + 'vault_retrieve', + [escrowed_symmetric_vault], + { + 'escrow_private_key': escrow_private_key, + }, + ), + 'expected': { + 'value': escrowed_symmetric_vault, + 'summary': u'Retrieved data from vault "%s"' % escrowed_symmetric_vault, + 'result': { + 'dn': u'cn=%s,cn=admin,cn=users,cn=vaults,%s' % (escrowed_symmetric_vault, api.env.basedn), + 'cn': [escrowed_symmetric_vault], + 'vault_id': u'/users/admin/%s' % escrowed_symmetric_vault, + 'owner_user': [u'admin'], + 'ipavaulttype': [u'symmetric'], + 'ipavaultsalt': [fuzzy_string], + 'ipaescrowpublickey': [fuzzy_string], + 'nonce': fuzzy_string, + 'vault_data': fuzzy_string, + 'data': binary_data, + }, + }, + }, + + { + 'desc': 'Create escrowed asymmetric vault', + 'command': ( + 'vault_add', + [escrowed_asymmetric_vault], + { + 'ipavaulttype': u'asymmetric', + 'ipapublickey': public_key, + 'ipaescrowpublickey': escrow_public_key, + 'data': binary_data, + }, + ), + 'expected': { + 'value': escrowed_asymmetric_vault, + 'summary': 'Added vault "%s"' % escrowed_asymmetric_vault, + 'result': { + 'dn': u'cn=%s,cn=admin,cn=users,cn=vaults,%s' % (escrowed_asymmetric_vault, api.env.basedn), + 'objectclass': (u'ipaVault', u'top'), + 'cn': [escrowed_asymmetric_vault], + 'vault_id': u'/users/admin/%s' % escrowed_asymmetric_vault, + 'owner_user': [u'admin'], + 'ipavaulttype': [u'asymmetric'], + 'ipavaultsalt': [fuzzy_string], + 'ipapublickey': [public_key], + 'ipaescrowpublickey': [fuzzy_string], + }, + }, + }, + + { + 'desc': 'Recover escrowed asymmetric vault', + 'command': ( + 'vault_retrieve', + [escrowed_asymmetric_vault], + { + 'escrow_private_key': escrow_private_key, + }, + ), + 'expected': { + 'value': escrowed_asymmetric_vault, + 'summary': u'Retrieved data from vault "%s"' % escrowed_asymmetric_vault, + 'result': { + 'dn': u'cn=%s,cn=admin,cn=users,cn=vaults,%s' % (escrowed_asymmetric_vault, api.env.basedn), + 'cn': [escrowed_asymmetric_vault], + 'vault_id': u'/users/admin/%s' % escrowed_asymmetric_vault, + 'owner_user': [u'admin'], + 'ipavaulttype': [u'asymmetric'], + 'ipavaultsalt': [fuzzy_string], + 'ipapublickey': [public_key], + 'ipaescrowpublickey': [fuzzy_string], + 'nonce': fuzzy_string, + 'vault_data': fuzzy_string, + 'data': binary_data, + }, + }, + }, + ] -- 1.9.0 -------------- next part -------------- >From 0b1ef839a4abe58f328f4c1bb3db5f91f79c4f5c Mon Sep 17 00:00:00 2001 From: "Endi S. Dewata" Date: Mon, 23 Feb 2015 10:37:25 -0500 Subject: [PATCH] Updated VERSION file. The API version number has been updated for the new vault functionalities. https://fedorahosted.org/freeipa/ticket/3872 --- VERSION | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/VERSION b/VERSION index 50ce95753a133de467570a3384034614f838ec57..a367f777d12292da88ae1caa67b50d2e780e9caa 100644 --- a/VERSION +++ b/VERSION @@ -90,5 +90,5 @@ IPA_DATA_VERSION=20100614120000 # # ######################################################## IPA_API_VERSION_MAJOR=2 -IPA_API_VERSION_MINOR=114 -# Last change: Gabe - Add flag "ask_create" to ipapermright +IPA_API_VERSION_MINOR=115 +# Last change: edewata - initial vault implementation -- 1.9.0 From pspacek at redhat.com Tue Feb 24 12:42:19 2015 From: pspacek at redhat.com (Petr Spacek) Date: Tue, 24 Feb 2015 13:42:19 +0100 Subject: [Freeipa-devel] [PATCH 0319] Fix crash caused by race condition during resolver cache flushing In-Reply-To: <54CA46E9.4070605@redhat.com> References: <54B51AA7.7070801@redhat.com> <54CA46E9.4070605@redhat.com> Message-ID: <54EC71AB.3020304@redhat.com> On 29.1.2015 15:42, Tomas Hozza wrote: > On 01/13/2015 02:16 PM, Petr Spacek wrote: >> > Hello, >> > >> > This patch should be applied to v2 branch. >> > >> > Fix crash caused by race condition during resolver cache flushing. >> > >> > dns_view_flushcache() call has to be always done in single-thread mode. >> > Locking around the call was missing in forwarder reconfiguration and >> > zone deletion which could cause crash. >> > >> > https://fedorahosted.org/bind-dyndb-ldap/ticket/142 > ACK. Rebased and pushed to v2 branch: 274a3b1cd0ed58f307f4a8d0a7c2f4006fb5e35e -- Petr Spacek @ Red Hat From pspacek at redhat.com Tue Feb 24 14:01:57 2015 From: pspacek at redhat.com (Petr Spacek) Date: Tue, 24 Feb 2015 15:01:57 +0100 Subject: [Freeipa-devel] [PATCH 0316] Fix crash triggered by zone objects with unexpected DN In-Reply-To: <54E45D2A.6050907@redhat.com> References: <54905091.5010201@redhat.com> <54E45D2A.6050907@redhat.com> Message-ID: <54EC8455.3040907@redhat.com> Hello, On 18.2.2015 10:36, Tomas Hozza wrote: > On 12/16/2014 04:32 PM, Petr Spacek wrote: >> Hello, >> >> Fix crash triggered by zone objects with unexpected DN. >> >> https://fedorahosted.org/bind-dyndb-ldap/ticket/148 >> > NACK. > > The patch seems to make no difference when using the reproducer from ticket 148 > > 18-Feb-2015 10:34:09.067 running > 18-Feb-2015 10:34:09.139 ldap_helper.c:4876: INSIST(task == inst->task) failed, back trace > 18-Feb-2015 10:34:09.139 #0 0x555555587a80 in ?? > 18-Feb-2015 10:34:09.139 #1 0x7ffff620781a in ?? > 18-Feb-2015 10:34:09.139 #2 0x7ffff20b00b2 in ?? > 18-Feb-2015 10:34:09.140 #3 0x7ffff1e7ccf9 in ?? > 18-Feb-2015 10:34:09.140 #4 0x7ffff1e7d992 in ?? > 18-Feb-2015 10:34:09.140 #5 0x7ffff20a7f3b in ?? > 18-Feb-2015 10:34:09.140 #6 0x7ffff5dda52a in ?? > 18-Feb-2015 10:34:09.140 #7 0x7ffff508d79d in ?? > 18-Feb-2015 10:34:09.140 exiting (due to assertion failure) > > Program received signal SIGABRT, Aborted. > [Switching to Thread 0x7fffea7cd700 (LWP 1719)] > 0x00007ffff4fc18c7 in __GI_raise (sig=sig at entry=6) at ../sysdeps/unix/sysv/linux/raise.c:55 > 55 return INLINE_SYSCALL (tgkill, 3, pid, selftid, sig); > Missing separate debuginfos, use: debuginfo-install cyrus-sasl-gssapi-2.1.26-19.fc21.x86_64 cyrus-sasl-lib-2.1.26-19.fc21.x86_64 cyrus-sasl-md5-2.1.26-19.fc21.x86_64 cyrus-sasl-plain-2.1.26-19.fc21.x86_64 gssproxy-0.3.1-4.fc21.x86_64 keyutils-libs-1.5.9-4.fc21.x86_64 libattr-2.4.47-9.fc21.x86_64 libdb-5.3.28-9.fc21.x86_64 libgcc-4.9.2-6.fc21.x86_64 libselinux-2.3-5.fc21.x86_64 nspr-4.10.8-1.fc21.x86_64 nss-3.17.4-1.fc21.x86_64 nss-softokn-freebl-3.17.4-1.fc21.x86_64 nss-util-3.17.4-1.fc21.x86_64 pcre-8.35-8.fc21.x86_64 sssd-client-1.12.3-4.fc21.x86_64 xz-libs-5.1.2-14alpha.fc21.x86_64 > (gdb) bt > #0 0x00007ffff4fc18c7 in __GI_raise (sig=sig at entry=6) at ../sysdeps/unix/sysv/linux/raise.c:55 > #1 0x00007ffff4fc352a in __GI_abort () at abort.c:89 > #2 0x0000555555587c29 in assertion_failed (file=, line=, type=, cond=) at ./main.c:220 > #3 0x00007ffff620781a in isc_assertion_failed (file=file at entry=0x7ffff20bad2a "ldap_helper.c", line=line at entry=4876, type=type at entry=isc_assertiontype_insist, > cond=cond at entry=0x7ffff20baf04 "task == inst->task") at assertions.c:57 > #4 0x00007ffff20b00b2 in syncrepl_update (chgtype=1, entry=0x7ffff0125590, inst=0x7ffff7fa3160) at ldap_helper.c:4876 > #5 ldap_sync_search_entry (ls=, msg=, entryUUID=, phase=LDAP_SYNC_CAPI_ADD) at ldap_helper.c:5031 > #6 0x00007ffff1e7ccf9 in ldap_sync_search_entry (ls=ls at entry=0x7fffe40008c0, res=0x7fffe4003870) at ldap_sync.c:228 > #7 0x00007ffff1e7d992 in ldap_sync_init (ls=0x7fffe40008c0, mode=mode at entry=3) at ldap_sync.c:792 > #8 0x00007ffff20a7f3b in ldap_syncrepl_watcher (arg=0x7ffff7fa3160) at ldap_helper.c:5247 > #9 0x00007ffff5dda52a in start_thread (arg=0x7fffea7cd700) at pthread_create.c:310 > #10 0x00007ffff508d79d in clone () at ../sysdeps/unix/sysv/linux/x86_64/clone.S:109 Thank you for catching this! I was using slightly different test which triggered the new code but by using different code path. This new version should be more robust. Please re-test it, thank you! -- Petr^2 Spacek -------------- next part -------------- A non-text attachment was scrubbed... Name: bind-dyndb-ldap-pspacek-0316-2-Fix-crash-triggered-by-zone-objects-with-unexpected-.patch Type: text/x-patch Size: 3270 bytes Desc: not available URL: From sbose at redhat.com Tue Feb 24 17:47:57 2015 From: sbose at redhat.com (Sumit Bose) Date: Tue, 24 Feb 2015 18:47:57 +0100 Subject: [Freeipa-devel] [PATCH 133] ipa-range-check: do not treat missing objects as error Message-ID: <20150224174757.GL3271@p.redhat.com> Hi, this patch changes a return code and should fix https://fedorahosted.org/freeipa/ticket/4924 . bye, Sumit -------------- next part -------------- From e4a6c8f3cd7975bbb276585ec1ac25a7551f46d0 Mon Sep 17 00:00:00 2001 From: Sumit Bose Date: Tue, 24 Feb 2015 18:32:43 +0100 Subject: [PATCH] ipa-range-check: do not treat missing objects as error Currently the range check plugin will return a 'Range Check error' message if a ldapmodify operation tries to change a non-existing object. Since the range check plugin does not need to care about non-existing objects we can just return 0 indicating that the range check plugin has done its work. Resolves https://fedorahosted.org/freeipa/ticket/4924 --- daemons/ipa-slapi-plugins/ipa-range-check/ipa_range_check.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/daemons/ipa-slapi-plugins/ipa-range-check/ipa_range_check.c b/daemons/ipa-slapi-plugins/ipa-range-check/ipa_range_check.c index 1fd543c185cda0a7b871875f1d4ba969a7bf910c..5b53a2fe58e1ad0ad6067ea75287f023402bb1c4 100644 --- a/daemons/ipa-slapi-plugins/ipa-range-check/ipa_range_check.c +++ b/daemons/ipa-slapi-plugins/ipa-range-check/ipa_range_check.c @@ -545,8 +545,9 @@ static int ipa_range_check_pre_op(Slapi_PBlock *pb, int modtype) ret = slapi_search_internal_get_entry(dn, NULL, &entry, ctx->plugin_id); if (ret != 0 || entry == NULL) { - LOG_FATAL("Missing entry to modify.\n"); - ret = LDAP_NO_SUCH_OBJECT; + LOG("Missing entry to modify.\n"); + /* No range object, nothing to do. */ + ret = 0; goto done; } free_entry = true; -- 2.1.0 From tbabej at redhat.com Tue Feb 24 18:03:17 2015 From: tbabej at redhat.com (Tomas Babej) Date: Tue, 24 Feb 2015 19:03:17 +0100 Subject: [Freeipa-devel] [PATCH 0039] Add test case for unsupported arg for ipa-advise In-Reply-To: References: <54B69B41.9070404@redhat.com> <54B6A097.9020601@redhat.com> <54B6A1F4.8000807@redhat.com> <54B6A602.9060101@redhat.com> Message-ID: <54ECBCE5.6020204@redhat.com> Hi Gabe, sorry for the delay. Here comes the review! 1.) All the tests fail, since the IPA master is not installed at all: def test_advice(self): # Obtain the advice from the server > tasks.kinit_admin(self.master) test_integration/test_advise.py:37: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ test_integration/tasks.py:484: in kinit_admin stdin_text=host.config.admin_password) ../pytest_multihost/host.py:222: in run_command command.wait(raiseonerr=raiseonerr) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = raiseonerr = True def wait(self, raiseonerr=True): """Wait for the remote process to exit Raises an excption if the exit code is not 0, unless raiseonerr is true. """ if self._done: return self.returncode self._end_process() self._done = True if raiseonerr and self.returncode: self.log.error('Exit code: %s', self.returncode) > raise subprocess.CalledProcessError(self.returncode, self.argv) E CalledProcessError: Command '['kinit', 'admin']' returned non-zero exit status 1 Similiarly for other tests. This is caused by the fact that you did not set topology in the BaseTestAdvise class, like this: --- a/ipatests/test_integration/test_advise.py +++ b/ipatests/test_integration/test_advise.py @@ -31,6 +31,7 @@ class BaseTestAdvise(IntegrationTest, object): advice_id = None raiseerr = None advice_regex = '' + topology = 'line' 2.) BaseTestAdvise inherits from IntegrationTest and from object. Explicitly specifying object as superclass is not needed, IntegrationTest already inherits from it. 3.) I think there is no good incentive to separate the test cases into mutliple classes. Each test class adds overhead of installing and uninstalling IPA server, to guarantee a clean and sane environment. However, it seems to be an overkill for testing ipa-advise command, which should be read-only anyway. By squashing the tests into one test class, we will decrease the run time of this test more than 8-fold. 4.) The patch adds a whitespace error. The test cases themselves are looking fine, and when I fixed the missing topology, they all passed. So this is a question of fixing the above issues, and we should be ready to push. Tomas On 02/17/2015 03:29 PM, Gabe Alford wrote: > Hello, > > I was wondering if I could get a review of this patch. > > Thanks, > > Gabe > > On Thursday, January 29, 2015, Gabe Alford > wrote: > > Hello, > > Here is a patch for > https://fedorahosted.org/freeipa/ticket/4029 I added test cases > for valid and invalid advice. > > Thanks, > > Gabe > > On Wed, Jan 14, 2015 at 10:23 AM, Tomas Babej > wrote: > > > On 01/14/2015 06:13 PM, Gabe Alford wrote: >> On Wed, Jan 14, 2015 at 10:05 AM, Tomas Babej >> > > wrote: >> >> >> On 01/14/2015 06:00 PM, Tomas Babej wrote: >>> >>> On 01/14/2015 05:37 PM, Tomas Babej wrote: >>>> >>>> On 01/14/2015 02:55 PM, Gabe Alford wrote: >>>>> Hello, >>>>> >>>>> In looking into >>>>> https://fedorahosted.org/freeipa/ticket/4029 I am >>>>> wondering if there should be separate ipa-advise test, >>>>> Yes/No? Could be handy in the future to test more >>>>> ipa-advise output? Or should this test be added to the >>>>> test_legacy_clients.py? >>>>> >>>>> Thanks, >>>>> >>>>> Gabe >>>>> >>>>> On Tue, Dec 2, 2014 at 9:21 PM, Gabe Alford >>>>> >>>> > >>>>> wrote: >>>>> >>>>> Hello, >>>>> >>>>> I was going to try my hand at attempting a patch >>>>> for ipa-tests. However in wanting to test my >>>>> patch, I am not sure how to run ipa-tests to check >>>>> if it works or not. Documentation is not really >>>>> clear on what needs to be done to start a test and >>>>> run a test. This is for >>>>> https://fedorahosted.org/freeipa/ticket/4029 >>>>> >>>>> I have attached the patch that I have yet to >>>>> really test with ipa-test. Any help on how to test >>>>> the patch running ipa-tests would be great. Of >>>>> course, if one of the reviewers looks at the patch >>>>> and looks good, then I would be happy with that as >>>>> well. >>>>> >>>>> Thanks, >>>>> >>>>> Gabe >>>>> >>>>> >>>>> >>>>> >>>>> _______________________________________________ >>>>> Freeipa-devel mailing list >>>>> Freeipa-devel at redhat.com >>>>> https://www.redhat.com/mailman/listinfo/freeipa-devel >>>> >>>> Hello, >>>> >>>> TL;DR: feel free to create a separate ipa-advise test >>>> file. Test requested in this ticket really does not >>>> belong to the legacy clients feature test. >>>> >>>> As for the any new tests that might come: I think tests >>>> for ipa-advise that are specific to that particular >>>> feature should be tested with that feature, more so, if >>>> they contain parts that are supposed to work >>>> copy-pasted. If a tests, however, tests a general >>>> behaviour of ipa-advise, it should live in the >>>> ipa-advise namespace, hence separate test file. >>>> >>>> HTH, >>>> >>>> -- >>>> Tomas Babej >>>> Associate Software Engineer | Red Hat | Identity Management >>>> RHCE | Brno Site | IRC: tbabej |freeipa.org >>> >>> The attached patch looks fine, although, please also >>> test for a non-zero return code number. >>> >> >> Upon hitting send I noticed you did not include >> raiseonerr=False into the run_command call. You need to >> do that, otherwise a exception will be raised, since >> ipa-advise exited with non-zero return code. >> >> Thanks Tomas. >> >> Which do you prefer: a test_advise.py or an update to the >> existing patch? > > A new test file, as I pointed out in the second email :) sorry > for splitting. > > However, it would be the best if you could spin up a positive > test as well (maybe listing out available advices), not just > this negative one, to justify the overhead reinstalling IPA > for testing this feature. > > >>> >>> -- >>> Tomas Babej >>> Associate Software Engineer | Red Hat | Identity Management >>> RHCE | Brno Site | IRC: tbabej |freeipa.org >> >> -- >> Tomas Babej >> Associate Software Engineer | Red Hat | Identity Management >> RHCE | Brno Site | IRC: tbabej |freeipa.org >> >> > > -- > Tomas Babej > Associate Software Engineer | Red Hat | Identity Management > RHCE | Brno Site | IRC: tbabej |freeipa.org > > -------------- next part -------------- An HTML attachment was scrubbed... URL: From mbasti at redhat.com Tue Feb 24 18:10:06 2015 From: mbasti at redhat.com (Martin Basti) Date: Tue, 24 Feb 2015 19:10:06 +0100 Subject: [Freeipa-devel] IPA Server upgrade 4.2 design Message-ID: <54ECBE7E.7040108@redhat.com> Hello all, please read the design page, any objections/suggestions appreciated http://www.freeipa.org/page/V4/Server_Upgrade_Refactoring -- Martin Basti From tbabej at redhat.com Tue Feb 24 21:48:10 2015 From: tbabej at redhat.com (Tomas Babej) Date: Tue, 24 Feb 2015 22:48:10 +0100 Subject: [Freeipa-devel] [PATCH 133] ipa-range-check: do not treat missing objects as error In-Reply-To: <20150224174757.GL3271@p.redhat.com> References: <20150224174757.GL3271@p.redhat.com> Message-ID: <54ECF19A.5040109@redhat.com> On 02/24/2015 06:47 PM, Sumit Bose wrote: > Hi, > > this patch changes a return code and should fix > https://fedorahosted.org/freeipa/ticket/4924 . > > bye, > Sumit > > > _______________________________________________ > Freeipa-devel mailing list > Freeipa-devel at redhat.com > https://www.redhat.com/mailman/listinfo/freeipa-devel ACK, patch fixes the issue: modifying entry "idnsname=doesnotexist,dc=idm,dc=lab,dc=eng,dc=brq,dc=redhat,dc=com" ldap_modify: No such object (32) Pushed to: master: 1a37822c3a80ffc05e28f5b9f1bacd248f5f2375 ipa-4-1: e8b3ed3596fe1906185eb1169ecaff2cb62ff8e3 -------------- next part -------------- An HTML attachment was scrubbed... URL: From mbasti at redhat.com Wed Feb 25 13:21:11 2015 From: mbasti at redhat.com (Martin Basti) Date: Wed, 25 Feb 2015 14:21:11 +0100 Subject: [Freeipa-devel] [PATCH 0195] Fix memory leaks in ipapkcs11helper module Message-ID: <54EDCC47.40400@redhat.com> Ticket: https://fedorahosted.org/freeipa/ticket/4657 Patch attached. -- Martin Basti -------------- next part -------------- A non-text attachment was scrubbed... Name: freeipa-mbasti-0195-Fix-memory-leaks-in-ipap11helper.patch Type: text/x-patch Size: 24151 bytes Desc: not available URL: From mbasti at redhat.com Wed Feb 25 13:24:09 2015 From: mbasti at redhat.com (Martin Basti) Date: Wed, 25 Feb 2015 14:24:09 +0100 Subject: [Freeipa-devel] [PATCH 0194] Remove unused method to export secret key from ipapkcs11helper module Message-ID: <54EDCCF9.1070407@redhat.com> The method never been used, and never will be, because we do not want to export secrets. Ticket: https://fedorahosted.org/freeipa/ticket/4657 Patch attached (may require mbasti-0195, mbasti-0190) -- Martin Basti -------------- next part -------------- A non-text attachment was scrubbed... Name: freeipa-mbasti-0196-Remove-unused-method-from-ipap11pkcs-helper-module.patch Type: text/x-patch Size: 3014 bytes Desc: not available URL: From mbasti at redhat.com Wed Feb 25 13:24:49 2015 From: mbasti at redhat.com (Martin Basti) Date: Wed, 25 Feb 2015 14:24:49 +0100 Subject: [Freeipa-devel] [PATCH 0195] Fix memory leaks in ipapkcs11helper module In-Reply-To: <54EDCC47.40400@redhat.com> References: <54EDCC47.40400@redhat.com> Message-ID: <54EDCD21.4090006@redhat.com> On 25/02/15 14:21, Martin Basti wrote: > Ticket: https://fedorahosted.org/freeipa/ticket/4657 > > Patch attached. > > > > _______________________________________________ > Freeipa-devel mailing list > Freeipa-devel at redhat.com > https://www.redhat.com/mailman/listinfo/freeipa-devel I forgot to mention, requires patch mbasti-0190 -- Martin Basti -------------- next part -------------- An HTML attachment was scrubbed... URL: From mbasti at redhat.com Wed Feb 25 13:34:11 2015 From: mbasti at redhat.com (Martin Basti) Date: Wed, 25 Feb 2015 14:34:11 +0100 Subject: [Freeipa-devel] [PATCHES 0197-0198] Fix uniqueness plugins upgrade Message-ID: <54EDCF53.7050900@redhat.com> Modifications: * All plugins are migrated into new configuration style. * I left attribute uniqueness plugin disabled, cn=uid uniqueness,cn=plugins,cn=config is checking the same attribute. * POST_UPDATE plugin for uid removed, I moved it to update file. Is it okay Alexander? I haven't found reason why we need to do it in update plugin. Thierry, I touched configuration of plugins, which user lifecycle requires, can you take look if I it does not break anything? Patches attached. -- Martin Basti -------------- next part -------------- A non-text attachment was scrubbed... Name: freeipa-mbasti-0197-Migrate-uniquess-plugins-configuration-to-new-style.patch Type: text/x-patch Size: 15823 bytes Desc: not available URL: -------------- next part -------------- A non-text attachment was scrubbed... Name: freeipa-mbasti-0198-Fix-uniqueness-plugins.patch Type: text/x-patch Size: 11748 bytes Desc: not available URL: From mkosek at redhat.com Wed Feb 25 13:43:05 2015 From: mkosek at redhat.com (Martin Kosek) Date: Wed, 25 Feb 2015 14:43:05 +0100 Subject: [Freeipa-devel] [PATCH 133] ipa-range-check: do not treat missing objects as error In-Reply-To: <20150224174757.GL3271@p.redhat.com> References: <20150224174757.GL3271@p.redhat.com> Message-ID: <54EDD169.7060505@redhat.com> On 02/24/2015 06:47 PM, Sumit Bose wrote: > Hi, > > this patch changes a return code and should fix > https://fedorahosted.org/freeipa/ticket/4924 . > > bye, > Sumit I have a related question. Do I read the plugin right, that whenever any object is changed, this plugins loads the whole entry and tests some of it's attribute to see if it is ID views and then does the actual check. Is this good approach performance wise? Wouldn't it be better to decide even before that, based on DN and whether it is in the ID View Sub-Tree? CCing Thierry. Martin From mbasti at redhat.com Wed Feb 25 13:45:53 2015 From: mbasti at redhat.com (Martin Basti) Date: Wed, 25 Feb 2015 14:45:53 +0100 Subject: [Freeipa-devel] [PATCH 0199] Remove unused disable-betxn.ldif file Message-ID: <54EDD211.4080606@redhat.com> Hello, the file 'disable-betxn.ldif' is not used in code in IPA master branch. There is 10-enable-betxn.update which is used. If I'm right we can remove it. Patch attached. Please correct me if the file is needed. Martin^2 -- Martin Basti -------------- next part -------------- A non-text attachment was scrubbed... Name: freeipa-mbasti-0199-Remove-unused-disable-betxn.ldif-file.patch Type: text/x-patch Size: 2737 bytes Desc: not available URL: From sbose at redhat.com Wed Feb 25 13:57:14 2015 From: sbose at redhat.com (Sumit Bose) Date: Wed, 25 Feb 2015 14:57:14 +0100 Subject: [Freeipa-devel] [PATCH 133] ipa-range-check: do not treat missing objects as error In-Reply-To: <54EDD169.7060505@redhat.com> References: <20150224174757.GL3271@p.redhat.com> <54EDD169.7060505@redhat.com> Message-ID: <20150225135714.GQ3271@p.redhat.com> On Wed, Feb 25, 2015 at 02:43:05PM +0100, Martin Kosek wrote: > On 02/24/2015 06:47 PM, Sumit Bose wrote: > > Hi, > > > > this patch changes a return code and should fix > > https://fedorahosted.org/freeipa/ticket/4924 . > > > > bye, > > Sumit > > I have a related question. Do I read the plugin right, that whenever any object > is changed, this plugins loads the whole entry and tests some of it's attribute > to see if it is ID views and then does the actual check. > > Is this good approach performance wise? Wouldn't it be better to decide even > before that, based on DN and whether it is in the ID View Sub-Tree? CCing Thierry. I assume you meant id-ranges instead of views, but yes, as long as we add range objects only in a given sub-tree we can check the DN first. bye, Sumit > > Martin From pvoborni at redhat.com Wed Feb 25 14:22:36 2015 From: pvoborni at redhat.com (Petr Vobornik) Date: Wed, 25 Feb 2015 15:22:36 +0100 Subject: [Freeipa-devel] Announcing FreeIPA 4.1.3 Message-ID: <54EDDAAC.4080309@redhat.com> The FreeIPA team would like to announce FreeIPA v4.1.3 bug fix release! It can be downloaded from http://www.freeipa.org/page/Downloads . Fedora 21 builds are already on their way to updates-testing repository. Builds for Fedora 20 are available in the official COPR repository [https://copr.fedoraproject.org/coprs/mkosek/freeipa/]. == Highlights in 4.1.3 == === Enhancements === * ID Views support user SSH public keys * ID Views support IPA user overrides * OTP token authentication and synchronization windows are configurable * RADIUS server proxy fields added to user page in Web UI === Bug fixes === * Issues fixed in ipa-restore: ** doesn't crash if replica is unreachable ** checks if it isn't a restore on non matching host ** improved validation of input options to disallow invalid combinations ** doesn't fail if run on a system without IPA installed ** creates correct log directories * certificate renewal process is synchronized * migrate-ds: warns user if compat plugin is enabled * PassSync plugin could not update synchronized users due to too strict access control * replication agreements by Replication Administrators could not be removed due to strict access control * anonymous read of a DUA profile was not possible due to strict access control * various upgrade fixes related to DNSSEC == Upgrading == Upgrade instructions are available on upgrade page [http://www.freeipa.org/page/Upgrade]. == Feedback == Please provide comments, bugs and other feedback via the freeipa-users mailing list (http://www.redhat.com/mailman/listinfo/freeipa-users) or #freeipa channel on Freenode. == Detailed Changelog since 4.1.2 == === Alexander Bokovoy (4) === * Support Samba PASSDB 0.2.0 aka interface version 24 * ipa-cldap: support NETLOGON_NT_VERSION_5EX_WITH_IP properly * ipa-kdb: when processing transitions, hand over unknown ones to KDC * ipa-kdb: reject principals from disabled domains as a KDC policy === David Kupka (5) === * Use singular in help metavars + update man pages. * Always add /etc/hosts record when DNS is being configured. * Remove ipanttrustauthincoming/ipanttrustauthoutgoing from ipa trust-add output. * Abort backup restoration on not matching host. * idviews: Allow setting ssh public key on ipauseroverride-add === Gabe Alford (3) === * Remove dependency on subscription-manager * Typos in ipa-rmkeytab options help and man page * permission-add does not prompt for ipapermright in interactive mode === Jan Cholasta (18) === * Fix automatic CA cert renewal endless loop in dogtag-ipa-ca-renew-agent * Do not renew the IPA CA cert by serial number in dogtag-ipa-ca-renew-agent * Improve validation of --instance and --backend options in ipa-restore * Check subject name encoding in ipa-cacert-manage renew * Refer the user to freeipa.org when something goes wrong in ipa-cacert-manage * Fix ipa-restore on systems without IPA installed * Remove RUV from LDIF files before using them in ipa-restore * Fix CA certificate renewal syslog alert * Do not crash on unknown services in installutils.stopped_service * Restart dogtag when its server certificate is renewed * Make certificate renewal process synchronized * Fix validation of ipa-restore options * Do not assume certmonger is running in httpinstance * Put LDIF files to their original location in ipa-restore * Revert "Make all ipatokenTOTP attributes mandatory" * Create correct log directories during full restore in ipa-restore * Do not crash when replica is unreachable in ipa-restore * Bump 389-ds-base and pki-ca dependencies for POODLE fixes === Jan Pazdziora (1) === * No explicit zone specification. === Martin Babinsky (11) === * Moved dbus-python dependence to freeipa-python package * ipa-kdb: unexpected error code in 'ipa_kdb_audit_as_req' triggers a message * always get PAC for client principal if AS_REQ is true * ipa-kdb: more robust handling of principal addition/editing * OTP: failed search for the user of last token emits an error message * ipa-pwd-extop: added an informational comment about intentional fallthrough * ipa-uuid: emit a message when unexpected mod type is encountered * OTP: emit a log message when LDAP entry for config record is not found * ipa-client-install: put eol character after the last line of altered config file(s) * migrate-ds: exit with error message if no users/groups to migrate are found * Changing the token owner changes also the manager === Martin Ba?ti (19) === * Fix zonemgr option encoding detection * Throw zonemgr error message before installation proceeds * Upgrade fix: masking named should be executed only once * Using wget to get status of CA * Show SSHFP record containing space in fingerprint * Fix don't check certificate during getting CA status * Fix: Upgrade forwardzones zones after adding newer replica * Fix zone find during forwardzone upgrade * Fix traceback if zonemgr error contains unicode * DNS tests: separate current forward zone tests * New test cases for Forward_zones * Detect and warn about invalid DNS forward zone configuration * DNS tests: warning if forward zone is inactive * Add debug messages into client autodetection * DNSSEC catch ldap exceptions in ipa-dnskeysyncd * DNSSEC: fix root zone dns name conversion * Always return absolute idnsname in dnszone commands * Use dyndns_update instead of deprecated sssd option * Fix reference counting in pkcs11 extension === Martin Ko?ek (7) === * Bump SSSD Requires to 1.12.3 * Allow PassSync user to locate and update NT users * Allow Replication Administrators manipulate Winsync Agreements * Replication Administrators cannot remove replication agreements * Add anonymous read ACI for DUA profile * Print PublicError traceback when in debug mode * group-detach does not add correct objectclasses === Nathaniel McCallum (7) === * Catch USBError during YubiKey location * Preliminary refactoring of libotp files * Move authentication configuration cache into libotp * Enable last token deletion when password auth type is configured * Make token auth and sync windows configurable * Create an OTP help topic * Prefer TCP connections to UDP in krb5 clients === Petr Voborn?k (10) === * webui: add radius fields to user page * fix indentation in ipa-restore page * add --hosts and --hostgroup options to allow/retrieve keytab methods * webui: fix service unprovisioning * webui: increase duration of notification messages * revert removal of cn attribute from idnsRecord * migrate-ds: fix compat plugin check * rpcclient: use json_encode_binary for verbose output * Fix TOTP Synchronization Window label * Become IPA 4.1.3 === Simo Sorce (3) === * Avoid calling ldap functions without a context * Remove the removal of the ccache * Handle DAL ABI change in MIT 1.13 === Tom?? Babej (9) === * Re-initialize NSS database after otptoken plugin tests * certs: Fix incorrect flag handling in load_cacert * hosts: Display assigned ID view by default in host-find and show commands * idviews: Complain if host is already assigned the ID View in idview-apply * idviews: Ignore host or hostgroup options set to None * baseldap: Handle missing parent objects properly in *-find commands * ipatests: Add coverage for referential integrity plugin applied on ipaAssignedIDView * ipatests: Fix old command references in the ID views tests * ipatests: Fix incorrect assumptions in idviews tests -- Petr Vobornik From abokovoy at redhat.com Wed Feb 25 14:37:55 2015 From: abokovoy at redhat.com (Alexander Bokovoy) Date: Wed, 25 Feb 2015 16:37:55 +0200 Subject: [Freeipa-devel] [PATCHES 0197-0198] Fix uniqueness plugins upgrade In-Reply-To: <54EDCF53.7050900@redhat.com> References: <54EDCF53.7050900@redhat.com> Message-ID: <20150225143755.GH25455@redhat.com> On Wed, 25 Feb 2015, Martin Basti wrote: >Modifications: >* All plugins are migrated into new configuration style. >* I left attribute uniqueness plugin disabled, cn=uid >uniqueness,cn=plugins,cn=config is checking the same attribute. >* POST_UPDATE plugin for uid removed, I moved it to update file. Is it >okay Alexander? I haven't found reason why we need to do it in update >plugin. So I looked up the original thread and since there are three different ways of defining uniqueness plugin's configuration, update plugin was to me the only way to handle all different configuration types. In general we cannot rely on the fact that FreeIPA deployment only contains FreeIPA-defined plugin configurations. -- / Alexander Bokovoy -------------- next part -------------- A non-text attachment was scrubbed... Name: not available Type: application/pgp-signature Size: 473 bytes Desc: not available URL: From mbasti at redhat.com Wed Feb 25 14:49:54 2015 From: mbasti at redhat.com (Martin Basti) Date: Wed, 25 Feb 2015 15:49:54 +0100 Subject: [Freeipa-devel] [PATCHES 0197-0198] Fix uniqueness plugins upgrade In-Reply-To: <20150225143755.GH25455@redhat.com> References: <54EDCF53.7050900@redhat.com> <20150225143755.GH25455@redhat.com> Message-ID: <54EDE112.3060504@redhat.com> On 25/02/15 15:37, Alexander Bokovoy wrote: > On Wed, 25 Feb 2015, Martin Basti wrote: >> Modifications: >> * All plugins are migrated into new configuration style. >> * I left attribute uniqueness plugin disabled, cn=uid >> uniqueness,cn=plugins,cn=config is checking the same attribute. >> * POST_UPDATE plugin for uid removed, I moved it to update file. Is >> it okay Alexander? I haven't found reason why we need to do it in >> update plugin. > So I looked up the original thread and since there are three different > ways of defining uniqueness plugin's configuration, update plugin was to > me the only way to handle all different configuration types. In general > we cannot rely on the fact that FreeIPA deployment only contains > FreeIPA-defined plugin configurations. > IMO, we should care only about IPA configured plugins, we cant handle everything, what users added there. If user adds an own plugin configuration there, the one should keep responsibility to test, if the plugin configuration still works after the IPA upgrade. We can't keep what user want, and what IPA needs in all cases, we would break IPA or users expectations, or both. In this case we can add detection of conflicts and print errors during upgrade, but we cant fix plugins which user created. If we want to handle user custom configuration, we will need to add detection for lot of things during upgrade not just uid uniqueness plugin. Martin^2 -- Martin Basti From redhatrises at gmail.com Wed Feb 25 14:52:02 2015 From: redhatrises at gmail.com (Gabe Alford) Date: Wed, 25 Feb 2015 07:52:02 -0700 Subject: [Freeipa-devel] [PATCH 0039] Add test case for unsupported arg for ipa-advise In-Reply-To: <54ECBCE5.6020204@redhat.com> References: <54B69B41.9070404@redhat.com> <54B6A097.9020601@redhat.com> <54B6A1F4.8000807@redhat.com> <54B6A602.9060101@redhat.com> <54ECBCE5.6020204@redhat.com> Message-ID: No worries about the delay. Thanks for taking the time! Updated patch attached. Thanks, Gabe On Tue, Feb 24, 2015 at 11:03 AM, Tomas Babej wrote: > Hi Gabe, > > sorry for the delay. Here comes the review! > 1.) All the tests fail, since the IPA master is not installed at all: > > def test_advice(self): > # Obtain the advice from the server > > tasks.kinit_admin(self.master) > > test_integration/test_advise.py:37: > _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ > test_integration/tasks.py:484: in kinit_admin > stdin_text=host.config.admin_password) > ../pytest_multihost/host.py:222: in run_command > command.wait(raiseonerr=raiseonerr) > _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ > > self = > raiseonerr = True > > def wait(self, raiseonerr=True): > """Wait for the remote process to exit > > Raises an excption if the exit code is not 0, unless raiseonerr is > true. > """ > if self._done: > return self.returncode > > self._end_process() > > self._done = True > > if raiseonerr and self.returncode: > self.log.error('Exit code: %s', self.returncode) > > raise subprocess.CalledProcessError(self.returncode, self.argv) > E CalledProcessError: Command '['kinit', 'admin']' returned non-zero exit status 1 > > > Similiarly for other tests. This is caused by the fact that you did not > set topology in the BaseTestAdvise class, like this: > > --- a/ipatests/test_integration/test_advise.py > +++ b/ipatests/test_integration/test_advise.py > @@ -31,6 +31,7 @@ class BaseTestAdvise(IntegrationTest, object): > advice_id = None > raiseerr = None > advice_regex = '' > + topology = 'line' > > 2.) BaseTestAdvise inherits from IntegrationTest and from object. > Explicitly specifying object as superclass is not needed, IntegrationTest > already inherits from it. > > 3.) I think there is no good incentive to separate the test cases into > mutliple classes. Each test class adds overhead of installing and > uninstalling IPA server, to guarantee a clean and sane environment. > However, it seems to be an overkill for testing ipa-advise command, which > should be read-only anyway. By squashing the tests into one test class, we > will decrease the run time of this test more than 8-fold. > > 4.) The patch adds a whitespace error. > > The test cases themselves are looking fine, and when I fixed the missing > topology, they all passed. So this is a question of fixing the above > issues, and we should be ready to push. > > Tomas > -------------- next part -------------- An HTML attachment was scrubbed... URL: -------------- next part -------------- A non-text attachment was scrubbed... Name: freeipa-rga-0039-2-ipatests-Add-tests-for-valid-and-invalid-ipa-advise.patch Type: application/octet-stream Size: 5467 bytes Desc: not available URL: From abokovoy at redhat.com Wed Feb 25 15:00:51 2015 From: abokovoy at redhat.com (Alexander Bokovoy) Date: Wed, 25 Feb 2015 17:00:51 +0200 Subject: [Freeipa-devel] [PATCHES 0197-0198] Fix uniqueness plugins upgrade In-Reply-To: <54EDE112.3060504@redhat.com> References: <54EDCF53.7050900@redhat.com> <20150225143755.GH25455@redhat.com> <54EDE112.3060504@redhat.com> Message-ID: <20150225150051.GJ25455@redhat.com> On Wed, 25 Feb 2015, Martin Basti wrote: >On 25/02/15 15:37, Alexander Bokovoy wrote: >>On Wed, 25 Feb 2015, Martin Basti wrote: >>>Modifications: >>>* All plugins are migrated into new configuration style. >>>* I left attribute uniqueness plugin disabled, cn=uid >>>uniqueness,cn=plugins,cn=config is checking the same attribute. >>>* POST_UPDATE plugin for uid removed, I moved it to update file. >>>Is it okay Alexander? I haven't found reason why we need to do it >>>in update plugin. >>So I looked up the original thread and since there are three different >>ways of defining uniqueness plugin's configuration, update plugin was to >>me the only way to handle all different configuration types. In general >>we cannot rely on the fact that FreeIPA deployment only contains >>FreeIPA-defined plugin configurations. >> >IMO, we should care only about IPA configured plugins, we cant handle >everything, what users added there. > >If user adds an own plugin configuration there, the one should keep >responsibility to test, if the plugin configuration still works after >the IPA upgrade. > >We can't keep what user want, and what IPA needs in all cases, we >would break IPA or users expectations, or both. > >In this case we can add detection of conflicts and print errors during >upgrade, but we cant fix plugins which user created. If we want to >handle user custom configuration, we will need to add detection for >lot of things during upgrade not just uid uniqueness plugin. Uhm, right. Where my brain was today? :) -- / Alexander Bokovoy -------------- next part -------------- A non-text attachment was scrubbed... Name: not available Type: application/pgp-signature Size: 473 bytes Desc: not available URL: From tbordaz at redhat.com Wed Feb 25 15:16:41 2015 From: tbordaz at redhat.com (thierry bordaz) Date: Wed, 25 Feb 2015 16:16:41 +0100 Subject: [Freeipa-devel] [PATCH 133] ipa-range-check: do not treat missing objects as error In-Reply-To: <54EDD169.7060505@redhat.com> References: <20150224174757.GL3271@p.redhat.com> <54EDD169.7060505@redhat.com> Message-ID: <54EDE759.3000306@redhat.com> On 02/25/2015 02:43 PM, Martin Kosek wrote: > On 02/24/2015 06:47 PM, Sumit Bose wrote: >> Hi, >> >> this patch changes a return code and should fix >> https://fedorahosted.org/freeipa/ticket/4924 . >> >> bye, >> Sumit > I have a related question. Do I read the plugin right, that whenever any object > is changed, this plugins loads the whole entry and tests some of it's attribute > to see if it is ID views and then does the actual check. > > Is this good approach performance wise? Wouldn't it be better to decide even > before that, based on DN and whether it is in the ID View Sub-Tree? CCing Thierry. > > Martin Hello, My understanding of this preop plugins is that it checks the defined range (RangeID, RangeSize, RangeRid...) of a new/modified 'ipaBaseID' entry. It checks the range by comparing the new range against all ranges defined in objectclass=ipaIDRange entries. As far as I understand that plugin, I tend to agree with your point Martin. The DN of the new entry/modified entry is not taken into consideration. Only the fact that it contains ipaBaseID attribute. If we know that we can ignore ADD/MOD under ID View Sub-tree, then it could be tested early in the plugin and would accelerate the plugin. That would require a plugin config attibute to specify which subtrees should be ignore. thanks thierry -------------- next part -------------- An HTML attachment was scrubbed... URL: From mkosek at redhat.com Wed Feb 25 15:53:22 2015 From: mkosek at redhat.com (Martin Kosek) Date: Wed, 25 Feb 2015 16:53:22 +0100 Subject: [Freeipa-devel] [PATCHES 0197-0198] Fix uniqueness plugins upgrade In-Reply-To: <20150225150051.GJ25455@redhat.com> References: <54EDCF53.7050900@redhat.com> <20150225143755.GH25455@redhat.com> <54EDE112.3060504@redhat.com> <20150225150051.GJ25455@redhat.com> Message-ID: <54EDEFF2.2030406@redhat.com> On 02/25/2015 04:00 PM, Alexander Bokovoy wrote: > On Wed, 25 Feb 2015, Martin Basti wrote: >> On 25/02/15 15:37, Alexander Bokovoy wrote: >>> On Wed, 25 Feb 2015, Martin Basti wrote: >>>> Modifications: >>>> * All plugins are migrated into new configuration style. >>>> * I left attribute uniqueness plugin disabled, cn=uid >>>> uniqueness,cn=plugins,cn=config is checking the same attribute. >>>> * POST_UPDATE plugin for uid removed, I moved it to update file. Is it okay >>>> Alexander? I haven't found reason why we need to do it in update plugin. >>> So I looked up the original thread and since there are three different >>> ways of defining uniqueness plugin's configuration, update plugin was to >>> me the only way to handle all different configuration types. In general >>> we cannot rely on the fact that FreeIPA deployment only contains >>> FreeIPA-defined plugin configurations. >>> >> IMO, we should care only about IPA configured plugins, we cant handle >> everything, what users added there. >> >> If user adds an own plugin configuration there, the one should keep >> responsibility to test, if the plugin configuration still works after the IPA >> upgrade. >> >> We can't keep what user want, and what IPA needs in all cases, we would break >> IPA or users expectations, or both. >> >> In this case we can add detection of conflicts and print errors during >> upgrade, but we cant fix plugins which user created. If we want to handle >> user custom configuration, we will need to add detection for lot of things >> during upgrade not just uid uniqueness plugin. I tend to agree with Martin. I know that we created the special uniqueness plugin handling custom user plugins in the last release, but I am not convinced it is a good idea, it adds complexity to the upgrade, making it more difficult to debug. So if we can create the cn=uid uniqueness,cn=plugins,cn=config plugin just with simple update, I am fine with it. > Uhm, right. Where my brain was today? :) Is that an agreement with Martin's approach? I am not sure :-) From pspacek at redhat.com Wed Feb 25 16:15:57 2015 From: pspacek at redhat.com (Petr Spacek) Date: Wed, 25 Feb 2015 17:15:57 +0100 Subject: [Freeipa-devel] IPA Server upgrade 4.2 design In-Reply-To: <54ECBE7E.7040108@redhat.com> References: <54ECBE7E.7040108@redhat.com> Message-ID: <54EDF53D.8020307@redhat.com> On 24.2.2015 19:10, Martin Basti wrote: > Hello all, > > please read the design page, any objections/suggestions appreciated > http://www.freeipa.org/page/V4/Server_Upgrade_Refactoring Thank you for the design, I have only few nitpicks. > Increase update files numbers range > Update files number will be extended into 4 digits values. IMHO the dependency on particular number format should be removed altogether. It should be perfectly enough to say that updates are executed in ASCII lexicographic order and be done with it. > To resolve issues mentioned above only one command should do upgrade: ipa-server-upgrade. I very much agree with this. > ipa-server-upgrade characteristics ... > 4. LDAP data update (+ update plugins) > 5. upgrade configuration At this point I would appreciate explanatory text what is 'LDAP data update' and what is 'upgrade configuration'. Maybe some examples could be enough. > ipactl checks if installed version and version stored in LDAP are the same: ... > ipactl start|restart option --force overrides this check. I would like to see a separate option. --force currently skips rollback if some services could not start but this is fundamentally different from version/upgrade checks. > ipa-server-upgrade > --version show program's version number and exit Maybe it could print code version + data version (if available). It could be handy debugging tool. > ipa-server-upgrade > --test Note: for developing only Is it really worth the effort to keep the option and invest more time in it? -- Petr^2 Spacek From pvoborni at redhat.com Wed Feb 25 16:22:23 2015 From: pvoborni at redhat.com (Petr Vobornik) Date: Wed, 25 Feb 2015 17:22:23 +0100 Subject: [Freeipa-devel] [PATCH] 808 webui: service: add ipakrbrequirespreauth checkbox Message-ID: <54EDF6BF.9010704@redhat.com> Allow to configure missing krb ticket flag - ipakrbrequirespreauth from Web UI. -- Petr Vobornik -------------- next part -------------- A non-text attachment was scrubbed... Name: freeipa-pvoborni-0808-webui-service-add-ipakrbrequirespreauth-checkbox.patch Type: text/x-patch Size: 1135 bytes Desc: not available URL: From tbordaz at redhat.com Wed Feb 25 16:23:14 2015 From: tbordaz at redhat.com (thierry bordaz) Date: Wed, 25 Feb 2015 17:23:14 +0100 Subject: [Freeipa-devel] [PATCHES 0197-0198] Fix uniqueness plugins upgrade In-Reply-To: <54EDCF53.7050900@redhat.com> References: <54EDCF53.7050900@redhat.com> Message-ID: <54EDF6F2.8080702@redhat.com> On 02/25/2015 02:34 PM, Martin Basti wrote: > Modifications: > * All plugins are migrated into new configuration style. > * I left attribute uniqueness plugin disabled, cn=uid > uniqueness,cn=plugins,cn=config is checking the same attribute. > * POST_UPDATE plugin for uid removed, I moved it to update file. Is it > okay Alexander? I haven't found reason why we need to do it in update > plugin. > > Thierry, I touched configuration of plugins, which user lifecycle > requires, can you take look if I it does not break anything? > > Patches attached. > Hello Martin, The fix looks good. I have just one question regarding install/updates/10-uniqueness.update. For example : # uid uniqueness scopes Active/Delete containers dn: cn=attribute uniqueness,cn=plugins,cn=config -remove:nsslapd-pluginarg1:'$SUFFIX' -add:nsslapd-pluginarg1:'cn=accounts,$SUFFIX' -add:nsslapd-pluginarg2:'cn=deleted users,cn=accounts,cn=provisioning,$SUFFIX' +remove:uniqueness-subtrees:'$SUFFIX' +add:uniqueness-subtrees:'cn=accounts,$SUFFIX' +add:uniqueness-subtrees:'cn=deleted users,cn=accounts,cn=provisioning,$SUFFIX' remove:nsslapd-pluginenabled:off add:nsslapd-pluginenabled:on If we update the rpm from a version where 'nsslapd-pluginarg1' was used. It will not remove it and we will have 'nsslapd-pluginarg1' along with 'uniqueness-subtrees'. Should not we keep 'remove:nsslapd-pluginarg1:'$SUFFIX'' ? thanks thierry -------------- next part -------------- An HTML attachment was scrubbed... URL: From mbasti at redhat.com Wed Feb 25 16:28:50 2015 From: mbasti at redhat.com (Martin Basti) Date: Wed, 25 Feb 2015 17:28:50 +0100 Subject: [Freeipa-devel] [PATCHES 0197-0198] Fix uniqueness plugins upgrade In-Reply-To: <54EDF6F2.8080702@redhat.com> References: <54EDCF53.7050900@redhat.com> <54EDF6F2.8080702@redhat.com> Message-ID: <54EDF842.1050500@redhat.com> On 25/02/15 17:23, thierry bordaz wrote: > On 02/25/2015 02:34 PM, Martin Basti wrote: >> Modifications: >> * All plugins are migrated into new configuration style. >> * I left attribute uniqueness plugin disabled, cn=uid >> uniqueness,cn=plugins,cn=config is checking the same attribute. >> * POST_UPDATE plugin for uid removed, I moved it to update file. Is >> it okay Alexander? I haven't found reason why we need to do it in >> update plugin. >> >> Thierry, I touched configuration of plugins, which user lifecycle >> requires, can you take look if I it does not break anything? >> >> Patches attached. >> > Hello Martin, > > The fix looks good. I have just one question regarding > install/updates/10-uniqueness.update. > > For example : > # uid uniqueness scopes Active/Delete containers > dn: cn=attribute uniqueness,cn=plugins,cn=config > -remove:nsslapd-pluginarg1:'$SUFFIX' > -add:nsslapd-pluginarg1:'cn=accounts,$SUFFIX' > -add:nsslapd-pluginarg2:'cn=deleted users,cn=accounts,cn=provisioning,$SUFFIX' > +remove:uniqueness-subtrees:'$SUFFIX' > +add:uniqueness-subtrees:'cn=accounts,$SUFFIX' > +add:uniqueness-subtrees:'cn=deleted users,cn=accounts,cn=provisioning,$SUFFIX' > remove:nsslapd-pluginenabled:off > add:nsslapd-pluginenabled:on > If we update the rpm from a version where 'nsslapd-pluginarg1' was used. > It will not remove it and we will have 'nsslapd-pluginarg1' along with > 'uniqueness-subtrees'. > Should not we keep 'remove:nsslapd-pluginarg1:'$SUFFIX'' ? > > thanks > thierry Hello Thierry, in patch 0197 is pre-upgrade plugin, which migrate all uniqueness plugins into new syntax (this happens before the update file is applied). So no nsslapd-pluginarg* attrs will be there. and in patch 0198 I removed the cn=attribute uniquenes plugin, we already have cn=uid uniqueness that do the same thing. Martin^2 -- Martin Basti -------------- next part -------------- An HTML attachment was scrubbed... URL: From tbordaz at redhat.com Wed Feb 25 16:42:39 2015 From: tbordaz at redhat.com (thierry bordaz) Date: Wed, 25 Feb 2015 17:42:39 +0100 Subject: [Freeipa-devel] [PATCHES 0197-0198] Fix uniqueness plugins upgrade In-Reply-To: <54EDF842.1050500@redhat.com> References: <54EDCF53.7050900@redhat.com> <54EDF6F2.8080702@redhat.com> <54EDF842.1050500@redhat.com> Message-ID: <54EDFB7F.40704@redhat.com> On 02/25/2015 05:28 PM, Martin Basti wrote: > On 25/02/15 17:23, thierry bordaz wrote: >> On 02/25/2015 02:34 PM, Martin Basti wrote: >>> Modifications: >>> * All plugins are migrated into new configuration style. >>> * I left attribute uniqueness plugin disabled, cn=uid >>> uniqueness,cn=plugins,cn=config is checking the same attribute. >>> * POST_UPDATE plugin for uid removed, I moved it to update file. Is >>> it okay Alexander? I haven't found reason why we need to do it in >>> update plugin. >>> >>> Thierry, I touched configuration of plugins, which user lifecycle >>> requires, can you take look if I it does not break anything? >>> >>> Patches attached. >>> >> Hello Martin, >> >> The fix looks good. I have just one question regarding >> install/updates/10-uniqueness.update. >> >> For example : >> # uid uniqueness scopes Active/Delete containers >> dn: cn=attribute uniqueness,cn=plugins,cn=config >> -remove:nsslapd-pluginarg1:'$SUFFIX' >> -add:nsslapd-pluginarg1:'cn=accounts,$SUFFIX' >> -add:nsslapd-pluginarg2:'cn=deleted users,cn=accounts,cn=provisioning,$SUFFIX' >> +remove:uniqueness-subtrees:'$SUFFIX' >> +add:uniqueness-subtrees:'cn=accounts,$SUFFIX' >> +add:uniqueness-subtrees:'cn=deleted users,cn=accounts,cn=provisioning,$SUFFIX' >> remove:nsslapd-pluginenabled:off >> add:nsslapd-pluginenabled:on >> If we update the rpm from a version where 'nsslapd-pluginarg1' was used. >> It will not remove it and we will have 'nsslapd-pluginarg1' along >> with 'uniqueness-subtrees'. >> Should not we keep 'remove:nsslapd-pluginarg1:'$SUFFIX'' ? >> >> thanks >> thierry > Hello Thierry, > > in patch 0197 is pre-upgrade plugin, which migrate all uniqueness > plugins into new syntax (this happens before the update file is > applied). So no nsslapd-pluginarg* attrs will be there. > > and in patch 0198 I removed the cn=attribute uniquenes plugin, we > already have cn=uid uniqueness that do the same thing. > > Martin^2 > > -- > Martin Basti Ok. I understand. Thanks for the explanation. -------------- next part -------------- An HTML attachment was scrubbed... URL: From mbasti at redhat.com Wed Feb 25 16:49:08 2015 From: mbasti at redhat.com (Martin Basti) Date: Wed, 25 Feb 2015 17:49:08 +0100 Subject: [Freeipa-devel] IPA Server upgrade 4.2 design In-Reply-To: <54EDF53D.8020307@redhat.com> References: <54ECBE7E.7040108@redhat.com> <54EDF53D.8020307@redhat.com> Message-ID: <54EDFD04.5020603@redhat.com> On 25/02/15 17:15, Petr Spacek wrote: > On 24.2.2015 19:10, Martin Basti wrote: >> Hello all, >> >> please read the design page, any objections/suggestions appreciated >> http://www.freeipa.org/page/V4/Server_Upgrade_Refactoring > Thank you for the design, I have only few nitpicks. > >> Increase update files numbers range >> Update files number will be extended into 4 digits values. > IMHO the dependency on particular number format should be removed altogether. > It should be perfectly enough to say that updates are executed in ASCII > lexicographic order and be done with it. 4.1.3-2 > 4.1.3-12 in lexicographic order, this will not fit. > >> To resolve issues mentioned above only one command should do upgrade: ipa-server-upgrade. > I very much agree with this. > > >> ipa-server-upgrade characteristics > ... >> 4. LDAP data update (+ update plugins) >> 5. upgrade configuration > At this point I would appreciate explanatory text what is 'LDAP data update' > and what is 'upgrade configuration'. Maybe some examples could be enough. LDAP data update == upgrading data stored in LDAP (user data + cn=config) upgrade configuration == upgrading configuration of services in filesystem (apache, named) I will add some explanation there. > >> ipactl checks if installed version and version stored in LDAP are the same: > ... >> ipactl start|restart option --force overrides this check. > I would like to see a separate option. --force currently skips rollback if > some services could not start but this is fundamentally different from > version/upgrade checks. Ohh, good catch thank you, maybe --skip-version-check ? > >> ipa-server-upgrade >> --version show program's version number and exit > Maybe it could print code version + data version (if available). It could be > handy debugging tool. Good idea thanks >> ipa-server-upgrade >> --test Note: for developing only > Is it really worth the effort to keep the option and invest more time in it? > I do not expect any extra effort (except fixing 3 plugins - 6 lines of code approx), so if it will help to develop updates it could stay there (personally I do not use it, usually updates broke during write to server on some constraints) Thank you for you nitpicks. Martin^2 -- Martin Basti From redhatrises at gmail.com Wed Feb 25 20:26:12 2015 From: redhatrises at gmail.com (Gabe Alford) Date: Wed, 25 Feb 2015 13:26:12 -0700 Subject: [Freeipa-devel] [PATCH 0042] ipa-replica-prepare should document ipv6 options Message-ID: Hello, Fix for https://fedorahosted.org/freeipa/ticket/4877. I just took what was in the ticket. Thanks, Gabe -------------- next part -------------- An HTML attachment was scrubbed... URL: -------------- next part -------------- A non-text attachment was scrubbed... Name: freeipa-rga-0042-ipa-replica-prepare-should-document-ipv6-options.patch Type: application/octet-stream Size: 1526 bytes Desc: not available URL: From tbabej at redhat.com Wed Feb 25 22:55:55 2015 From: tbabej at redhat.com (Tomas Babej) Date: Wed, 25 Feb 2015 23:55:55 +0100 Subject: [Freeipa-devel] [PATCH 0039] Add test case for unsupported arg for ipa-advise In-Reply-To: References: <54B69B41.9070404@redhat.com> <54B6A097.9020601@redhat.com> <54B6A1F4.8000807@redhat.com> <54B6A602.9060101@redhat.com> <54ECBCE5.6020204@redhat.com> Message-ID: <54EE52FB.4040603@redhat.com> Hi Gabe, sorry for not being clear. This approach will not work: +class TestAdvice(BaseTestInvalidAdvice, + BaseTestFedoraAuthconfig, + BaseTestFreeBSDNSSPAM, + BaseTestGenericNSSPAM, + BaseTestGenericSSSDBefore19, + BaseTestRedHatNSS, + BaseTestRedHatNSSPAM, + BaseTestRedHatSSSDBefore19, + BaseTestAdvice): + pass By combining all the base classes into one, you will not get the desired effect (which is to run the test_advice method for each advice_id). Let me explain why: The test runner works in the following way: it inspects any discovered class which name begins with "Test", and executes each its method, which names begins with "test" as a test case. If the test runner inspects the TestAdvice class, the only method beggining with "test", which it will see, is the "test_advice" which was inherited back from BaseTestAdvice class. So we can safely conclude the test runner will only run 1 test case. Which one, you may ask? Well, since the test_advice behaviour is fully determined by the values of "advice_id", "advice_regex" and "raiseerr" attributes, let's look at their values in TestAdvice class. This class does not define attirbutes with such names, so we move along the inheritance chain (also called MRO) - the first class from which we inherit is BaseTestInvalidAdvice, and this class defines all three mentioned attributes. Hence the only test method will be run the test for invalid advice :) Now, how to fix this? The easiest approach would be to abandon the approach with the separate classes, and map each class to a test method in the TestAdvice class, like this (from the top of my head): +class TestAdvice(IntegrationTest): + topology = 'line' + + def test_invalid_advice(self): + advice_id = 'invalid-advise-param' + advice_regex = "invalid[\s]+\'advice\'.*" + raiseerr = False + # Obtain the advice from the server + tasks.kinit_admin(self.master) + result = self.master.run_command(['ipa-advise', self.advice_id], + raiseonerr=self.raiseerr) + + if not result.stdout_text: + advice = result.stderr_text + else: + advice = result.stdout_text + + assert re.search(self.advice_regex, advice, re.S) + + def test_advice_fedora_authconfig(self): + advice_id = 'config-fedora-authconfig' + advice_regex = "\#\!\/bin\/sh.*" \ + "authconfig[\s]+\-\-enableldap[\s]+" \ + "\-\-ldapserver\=.*[\s]+\-\-enablerfc2307bis[\s]+" \ + "\-\-enablekrb5" + raiseonerr = True + # Obtain the advice from the server + tasks.kinit_admin(self.master) + result = self.master.run_command(['ipa-advise', self.advice_id], + raiseonerr=self.raiseerr) + + if not result.stdout_text: + advice = result.stderr_text + else: + advice = result.stdout_text + + assert re.search(self.advice_regex, advice, re.S) ... the same for the remaining 6 cases Now, this pattern has lots of duplicated code which can be extracted to a helper method, I just thought it would help to be more explicit to get the idea across. In the end you can achieve the same level of conciseness than with the separate test classes. Good luck! HTH, Tomas On 02/25/2015 03:52 PM, Gabe Alford wrote: > No worries about the delay. Thanks for taking the time! Updated patch > attached. > > Thanks, > > Gabe > > On Tue, Feb 24, 2015 at 11:03 AM, Tomas Babej > wrote: > > Hi Gabe, > > sorry for the delay. Here comes the review! > > 1.) All the tests fail, since the IPA master is not installed at all: > > def test_advice(self): > # Obtain the advice from the server > > tasks.kinit_admin(self.master) > > test_integration/test_advise.py:37: > _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ > test_integration/tasks.py:484: in kinit_admin > stdin_text=host.config.admin_password) > ../pytest_multihost/host.py:222: in run_command > command.wait(raiseonerr=raiseonerr) > _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ > > self = > raiseonerr = True > > def wait(self, raiseonerr=True): > """Wait for the remote process to exit > > Raises an excption if the exit code is not 0, unless raiseonerr is > true. > """ > if self._done: > return self.returncode > > self._end_process() > > self._done = True > > if raiseonerr and self.returncode: > self.log.error('Exit code: %s', self.returncode) > > raise subprocess.CalledProcessError(self.returncode, self.argv) > E CalledProcessError: Command '['kinit', 'admin']' returned non-zero exit status 1 > > > Similiarly for other tests. This is caused by the fact that you > did not set topology in the BaseTestAdvise class, like this: > > --- a/ipatests/test_integration/test_advise.py > +++ b/ipatests/test_integration/test_advise.py > @@ -31,6 +31,7 @@ class BaseTestAdvise(IntegrationTest, object): > advice_id = None > raiseerr = None > advice_regex = '' > + topology = 'line' > > 2.) BaseTestAdvise inherits from IntegrationTest and from object. > Explicitly specifying object as superclass is not needed, > IntegrationTest already inherits from it. > > 3.) I think there is no good incentive to separate the test cases > into mutliple classes. Each test class adds overhead of installing > and uninstalling IPA server, to guarantee a clean and sane > environment. However, it seems to be an overkill for testing > ipa-advise command, which should be read-only anyway. By squashing > the tests into one test class, we will decrease the run time of > this test more than 8-fold. > > 4.) The patch adds a whitespace error. > > The test cases themselves are looking fine, and when I fixed the > missing topology, they all passed. So this is a question of fixing > the above issues, and we should be ready to push. > > Tomas > > -------------- next part -------------- An HTML attachment was scrubbed... URL: From tbabej at redhat.com Wed Feb 25 23:48:52 2015 From: tbabej at redhat.com (Tomas Babej) Date: Thu, 26 Feb 2015 00:48:52 +0100 Subject: [Freeipa-devel] [PATCH] 808 webui: service: add ipakrbrequirespreauth checkbox In-Reply-To: <54EDF6BF.9010704@redhat.com> References: <54EDF6BF.9010704@redhat.com> Message-ID: <54EE5F64.6090302@redhat.com> On 02/25/2015 05:22 PM, Petr Vobornik wrote: > Allow to configure missing krb ticket flag - ipakrbrequirespreauth > from Web UI. > > > _______________________________________________ > Freeipa-devel mailing list > Freeipa-devel at redhat.com > https://www.redhat.com/mailman/listinfo/freeipa-devel ACK, works fine. Pushed to master: 55413566caf7ba9b2f5b40d160c3a105c6599ac2 Please push to ipa-4-1 if necessary. -------------- next part -------------- An HTML attachment was scrubbed... URL: From tbabej at redhat.com Wed Feb 25 23:59:34 2015 From: tbabej at redhat.com (Tomas Babej) Date: Thu, 26 Feb 2015 00:59:34 +0100 Subject: [Freeipa-devel] [PATCH 0042] ipa-replica-prepare should document ipv6 options In-Reply-To: References: Message-ID: <54EE61E6.4020200@redhat.com> On 02/25/2015 09:26 PM, Gabe Alford wrote: > Hello, > > Fix for https://fedorahosted.org/freeipa/ticket/4877. I just took what > was in the ticket. > > Thanks, > > Gabe > > > _______________________________________________ > Freeipa-devel mailing list > Freeipa-devel at redhat.com > https://www.redhat.com/mailman/listinfo/freeipa-devel ACK (I did a very minor change to wording). Pushed to: master: c75025df8cd9b634ffe464e07ba23a2ee99b3e2d ipa-4-1: 3ab7f551f86bee75b5260901352ec6538ebda50e -------------- next part -------------- An HTML attachment was scrubbed... URL: From nkinder at redhat.com Thu Feb 26 02:28:01 2015 From: nkinder at redhat.com (Nathan Kinder) Date: Wed, 25 Feb 2015 18:28:01 -0800 Subject: [Freeipa-devel] [PATCHES 0001-0002] ipa-client-install NTP fixes Message-ID: <54EE84B1.4040206@redhat.com> Hi, The two attached patches address some issues that affect ipa-client-install when syncing time from the NTP server. Now that we use ntpd to perform the time sync, the client install can end up hanging forever when the server is not reachable (firewall issues, etc.). These patches address the issues in two different ways: 1 - Don't attempt to sync time when --no-ntp is specified. 2 - Implement a timeout capability that is used when we run ntpd to perform the time sync to prevent indefinite hanging. The one potentially contentious issue is that this introduces a new dependency on python-subprocess32 to allow us to have timeout support when using Python 2.x. This is packaged for Fedora, but I don't see it on RHEL or CentOS currently. It would need to be packaged there. https://fedorahosted.org/freeipa/ticket/4842 Thanks, -NGK -------------- next part -------------- A non-text attachment was scrubbed... Name: 0001-Skip-time-sync-during-client-install-when-using-no-n.patch Type: text/x-patch Size: 1510 bytes Desc: not available URL: -------------- next part -------------- A non-text attachment was scrubbed... Name: 0002-Timeout-when-performing-time-sync-during-client-inst.patch Type: text/x-patch Size: 5003 bytes Desc: not available URL: From mkosek at redhat.com Thu Feb 26 08:55:38 2015 From: mkosek at redhat.com (Martin Kosek) Date: Thu, 26 Feb 2015 09:55:38 +0100 Subject: [Freeipa-devel] [PATCHES 0001-0002] ipa-client-install NTP fixes In-Reply-To: <54EE84B1.4040206@redhat.com> References: <54EE84B1.4040206@redhat.com> Message-ID: <54EEDF8A.2090204@redhat.com> On 02/26/2015 03:28 AM, Nathan Kinder wrote: > Hi, > > The two attached patches address some issues that affect > ipa-client-install when syncing time from the NTP server. Now that we > use ntpd to perform the time sync, the client install can end up hanging > forever when the server is not reachable (firewall issues, etc.). These > patches address the issues in two different ways: > > 1 - Don't attempt to sync time when --no-ntp is specified. > > 2 - Implement a timeout capability that is used when we run ntpd to > perform the time sync to prevent indefinite hanging. > > The one potentially contentious issue is that this introduces a new > dependency on python-subprocess32 to allow us to have timeout support > when using Python 2.x. This is packaged for Fedora, but I don't see it > on RHEL or CentOS currently. It would need to be packaged there. > > https://fedorahosted.org/freeipa/ticket/4842 > > Thanks, > -NGK Thanks for Patches. For the second patch, I would really prefer to avoid new dependency, especially if it's not packaged in RHEL/CentOS. Maybe we could use some workaround instead, as in: http://stackoverflow.com/questions/3733270/python-subprocess-timeout ? Martin From pspacek at redhat.com Thu Feb 26 09:45:14 2015 From: pspacek at redhat.com (Petr Spacek) Date: Thu, 26 Feb 2015 10:45:14 +0100 Subject: [Freeipa-devel] IPA Server upgrade 4.2 design In-Reply-To: <54EDFD04.5020603@redhat.com> References: <54ECBE7E.7040108@redhat.com> <54EDF53D.8020307@redhat.com> <54EDFD04.5020603@redhat.com> Message-ID: <54EEEB2A.4030108@redhat.com> On 25.2.2015 17:49, Martin Basti wrote: > On 25/02/15 17:15, Petr Spacek wrote: >> On 24.2.2015 19:10, Martin Basti wrote: >>> Hello all, >>> >>> please read the design page, any objections/suggestions appreciated >>> http://www.freeipa.org/page/V4/Server_Upgrade_Refactoring >> Thank you for the design, I have only few nitpicks. >> >>> Increase update files numbers range >>> Update files number will be extended into 4 digits values. >> IMHO the dependency on particular number format should be removed altogether. >> It should be perfectly enough to say that updates are executed in ASCII >> lexicographic order and be done with it. > 4.1.3-2 > 4.1.3-12 in lexicographic order, this will not fit. Well, sure, but it allows you to use 00-a 01-b and renumber it to 001-a 002-b at will without changes to code. (Lexicographic order is what 'ls' uses by default so you can see the real ordering at any time very easily.) Also, as you pointed out, it allows you to do things like 12.345-a 12.666-bbb without changing code, again :-) Petr^2 Spacek >>> To resolve issues mentioned above only one command should do upgrade: >>> ipa-server-upgrade. >> I very much agree with this. >> >> >>> ipa-server-upgrade characteristics >> ... >>> 4. LDAP data update (+ update plugins) >>> 5. upgrade configuration >> At this point I would appreciate explanatory text what is 'LDAP data update' >> and what is 'upgrade configuration'. Maybe some examples could be enough. > LDAP data update == upgrading data stored in LDAP (user data + cn=config) > upgrade configuration == upgrading configuration of services in filesystem > (apache, named) > > I will add some explanation there. >> >>> ipactl checks if installed version and version stored in LDAP are the same: >> ... >>> ipactl start|restart option --force overrides this check. >> I would like to see a separate option. --force currently skips rollback if >> some services could not start but this is fundamentally different from >> version/upgrade checks. > Ohh, good catch thank you, maybe --skip-version-check ? Sounds fine to me. >> >>> ipa-server-upgrade >>> --version show program's version number and exit >> Maybe it could print code version + data version (if available). It could be >> handy debugging tool. > Good idea thanks >>> ipa-server-upgrade >>> --test Note: for developing only >> Is it really worth the effort to keep the option and invest more time in it? >> > I do not expect any extra effort (except fixing 3 plugins - 6 lines of code > approx), so if it will help to develop updates it could stay there (personally > I do not use it, usually updates broke during write to server on some > constraints) Okay, I thought that it is broken significantly. -- Petr^2 Spacek From dkupka at redhat.com Thu Feb 26 09:57:37 2015 From: dkupka at redhat.com (David Kupka) Date: Thu, 26 Feb 2015 10:57:37 +0100 Subject: [Freeipa-devel] [PATCH] 0039 Try continue ipa-client-automount even if nsslapd-minssf > 0. Message-ID: <54EEEE11.60305@redhat.com> https://fedorahosted.org/freeipa/ticket/4902 -- David Kupka -------------- next part -------------- A non-text attachment was scrubbed... Name: freeipa-dkupka-0039-Try-continue-ipa-client-automount-even-if-nsslapd-mi.patch Type: text/x-patch Size: 1262 bytes Desc: not available URL: From pspacek at redhat.com Thu Feb 26 11:47:59 2015 From: pspacek at redhat.com (Petr Spacek) Date: Thu, 26 Feb 2015 12:47:59 +0100 Subject: [Freeipa-devel] [PATCH 0190] DNSSEC: add support for CKM_RSA_PKCS_OAEP mechanism In-Reply-To: <54DB54DE.4030606@redhat.com> References: <54DB54DE.4030606@redhat.com> Message-ID: <54EF07EF.6060300@redhat.com> On 11.2.2015 14:10, Martin Basti wrote: > https://fedorahosted.org/freeipa/ticket/4657#comment:13 > > Patch attached. > > -- > Martin Basti > > > freeipa-mbasti-0190-DNSSEC-add-support-for-CKM_RSA_PKCS_OAEP-mechanism.patch > > > From 4d698a5adaa94eb854c75bd9bcaf3093f31a11e5 Mon Sep 17 00:00:00 2001 > From: Martin Basti > Date: Wed, 11 Feb 2015 14:05:46 +0100 > Subject: [PATCH] DNSSEC add support for CKM_RSA_PKCS_OAEP mechanism > > Ticket: https://fedorahosted.org/freeipa/ticket/4657#comment:13 > --- > ipapython/ipap11helper/p11helper.c | 72 ++++++++++++++++++++++++++++++++++++-- > 1 file changed, 69 insertions(+), 3 deletions(-) > > diff --git a/ipapython/ipap11helper/p11helper.c b/ipapython/ipap11helper/p11helper.c > index 4e0f262057b377124793f1e3091a8c9df4794164..c638bbe849f1bbddc8004bd1c4cccc1128b1c9e7 100644 > --- a/ipapython/ipap11helper/p11helper.c > +++ b/ipapython/ipap11helper/p11helper.c > @@ -53,6 +53,22 @@ > // TODO > #define CKA_COPYABLE (0x0017) > > +#define CKG_MGF1_SHA1 (0x00000001) > + > +#define CKZ_DATA_SPECIFIED (0x00000001) > + > +struct ck_rsa_pkcs_oaep_params { > + CK_MECHANISM_TYPE hash_alg; > + unsigned long mgf; > + unsigned long source; > + void *source_data; > + unsigned long source_data_len; > +}; > + > +typedef struct ck_rsa_pkcs_oaep_params CK_RSA_PKCS_OAEP_PARAMS; > +typedef struct ck_rsa_pkcs_oaep_params *CK_RSA_PKCS_OAEP_PARAMS_PTR; > + > + > CK_BBOOL true = CK_TRUE; > CK_BBOOL false = CK_FALSE; > > @@ -118,6 +134,17 @@ CK_BBOOL* bool; > } PyObj2Bool_mapping_t; > > /** > + * Constants > + */ > +static const CK_RSA_PKCS_OAEP_PARAMS CONST_RSA_PKCS_OAEP_PARAMS = { > + .hash_alg = CKM_SHA_1, > + .mgf = CKG_MGF1_SHA1, > + .source = CKZ_DATA_SPECIFIED, > + .source_data = NULL, > + .source_data_len = 0 > +}; > + > +/** > * ipap11helper Exceptions > */ > static PyObject *ipap11helperException; //parent class for all exceptions > @@ -1359,17 +1386,36 @@ P11_Helper_export_wrapped_key(P11_Helper* self, PyObject *args, PyObject *kwds) > CK_BYTE_PTR wrapped_key = NULL; > CK_ULONG wrapped_key_len = 0; > CK_MECHANISM wrapping_mech = { CKM_RSA_PKCS, NULL, 0 }; > - CK_MECHANISM_TYPE wrapping_mech_type = CKM_RSA_PKCS; > /* currently we don't support parameter in mechanism */ > > static char *kwlist[] = { "key", "wrapping_key", "wrapping_mech", NULL }; > //TODO check long overflow > //TODO export method > if (!PyArg_ParseTupleAndKeywords(args, kwds, "kkk|", kwlist, &object_key, > - &object_wrapping_key, &wrapping_mech_type)) { > + &object_wrapping_key, &wrapping_mech.mechanism)) { > return NULL; > } > - wrapping_mech.mechanism = wrapping_mech_type; > + > + // fill mech parameters > + switch(wrapping_mech.mechanism){ > + case CKM_RSA_PKCS: > + case CKM_AES_KEY_WRAP: > + case CKM_AES_KEY_WRAP_PAD: > + //default params > + break; > + > + case CKM_RSA_PKCS_OAEP: > + /* Use the same configuration as openSSL > + * https://www.openssl.org/docs/crypto/RSA_public_encrypt.html > + */ > + wrapping_mech.pParameter = (void*) &CONST_RSA_PKCS_OAEP_PARAMS; > + wrapping_mech.ulParameterLen = sizeof(CONST_RSA_PKCS_OAEP_PARAMS); > + break; > + > + default: > + PyErr_SetString(ipap11helperError, "Unsupported wrapping mechanism"); > + return NULL; > + } > > rv = self->p11->C_WrapKey(self->session, &wrapping_mech, > object_wrapping_key, object_key, NULL, &wrapped_key_len); > @@ -1452,6 +1498,26 @@ P11_Helper_import_wrapped_secret_key(P11_Helper* self, PyObject *args, > return NULL; > } > > + switch(wrapping_mech.mechanism){ > + case CKM_RSA_PKCS: > + case CKM_AES_KEY_WRAP: > + case CKM_AES_KEY_WRAP_PAD: > + //default params > + break; NACK. This switch is duplicate of the previous one. Please split it into an auxiliary function and call it twice. Thank you! -- Petr^2 Spacek From mbasti at redhat.com Thu Feb 26 11:54:08 2015 From: mbasti at redhat.com (Martin Basti) Date: Thu, 26 Feb 2015 12:54:08 +0100 Subject: [Freeipa-devel] [PATCH] 0039 Try continue ipa-client-automount even if nsslapd-minssf > 0. In-Reply-To: <54EEEE11.60305@redhat.com> References: <54EEEE11.60305@redhat.com> Message-ID: <54EF0960.4070608@redhat.com> On 26/02/15 10:57, David Kupka wrote: > https://fedorahosted.org/freeipa/ticket/4902 > > > _______________________________________________ > Freeipa-devel mailing list > Freeipa-devel at redhat.com > https://www.redhat.com/mailman/listinfo/freeipa-devel Works for me, ACK. -- Martin Basti -------------- next part -------------- An HTML attachment was scrubbed... URL: From pspacek at redhat.com Thu Feb 26 12:06:37 2015 From: pspacek at redhat.com (Petr Spacek) Date: Thu, 26 Feb 2015 13:06:37 +0100 Subject: [Freeipa-devel] [PATCH 0195] Fix memory leaks in ipapkcs11helper module In-Reply-To: <54EDCC47.40400@redhat.com> References: <54EDCC47.40400@redhat.com> Message-ID: <54EF0C4D.8010700@redhat.com> Hello Martin, thank you for patch! This NACK is only aesthetic :-) On 25.2.2015 14:21, Martin Basti wrote: > if (!check_return_value(rv, "import_wrapped_key: key unwrapping")) { > + error = 1; > + goto final; > + } This exact sequence is repeated many times in the code. I would prefer a C macro like this: #define GOTO_FAIL \ do { \ error = 1; \ goto final; \ } while(0) This allows more dense code like: if (!test) GOTO_FAIL; and does not have the risk of missing error = 1 somewhere. > +final: > if (pkey != NULL) > EVP_PKEY_free(pkey); > + if (label != NULL) PyMem_Free(label); > + if (error){ > + return NULL; > + } > return ret; > } Apparently, this is inconsistent with itself. Please pick one style and use it, e.g. if (label != NULL) PyMem_Free(label) ... and do not add curly braces when unnecessary. If you want, we can try running $ indent on current sources and committing changes separately so you do not have to make changes like this by hand. -- Petr^2 Spacek From mbasti at redhat.com Thu Feb 26 13:38:30 2015 From: mbasti at redhat.com (Martin Basti) Date: Thu, 26 Feb 2015 14:38:30 +0100 Subject: [Freeipa-devel] IPA Server upgrade 4.2 design In-Reply-To: <54EEEB2A.4030108@redhat.com> References: <54ECBE7E.7040108@redhat.com> <54EDF53D.8020307@redhat.com> <54EDFD04.5020603@redhat.com> <54EEEB2A.4030108@redhat.com> Message-ID: <54EF21D6.8000301@redhat.com> On 26/02/15 10:45, Petr Spacek wrote: > On 25.2.2015 17:49, Martin Basti wrote: >> On 25/02/15 17:15, Petr Spacek wrote: >>> On 24.2.2015 19:10, Martin Basti wrote: >>>> Hello all, >>>> >>>> please read the design page, any objections/suggestions appreciated >>>> http://www.freeipa.org/page/V4/Server_Upgrade_Refactoring >>> Thank you for the design, I have only few nitpicks. >>> >>>> Increase update files numbers range >>>> Update files number will be extended into 4 digits values. >>> IMHO the dependency on particular number format should be removed altogether. >>> It should be perfectly enough to say that updates are executed in ASCII >>> lexicographic order and be done with it. >> 4.1.3-2 > 4.1.3-12 in lexicographic order, this will not fit. > Well, sure, but it allows you to use > 00-a > 01-b > > and renumber it to > > 001-a > 002-b > > at will without changes to code. (Lexicographic order is what 'ls' uses by > default so you can see the real ordering at any time very easily.) > > Also, as you pointed out, it allows you to do things like > 12.345-a > 12.666-bbb > without changing code, again :-) Oh stupid me, I read it wrong, I replied with IPA version compare. sounds good to me, any objections anyone? > > Petr^2 Spacek > >>>> To resolve issues mentioned above only one command should do upgrade: >>>> ipa-server-upgrade. >>> I very much agree with this. >>> >>> >>>> ipa-server-upgrade characteristics >>> ... >>>> 4. LDAP data update (+ update plugins) >>>> 5. upgrade configuration >>> At this point I would appreciate explanatory text what is 'LDAP data update' >>> and what is 'upgrade configuration'. Maybe some examples could be enough. >> LDAP data update == upgrading data stored in LDAP (user data + cn=config) >> upgrade configuration == upgrading configuration of services in filesystem >> (apache, named) >> >> I will add some explanation there. >>>> ipactl checks if installed version and version stored in LDAP are the same: >>> ... >>>> ipactl start|restart option --force overrides this check. >>> I would like to see a separate option. --force currently skips rollback if >>> some services could not start but this is fundamentally different from >>> version/upgrade checks. >> Ohh, good catch thank you, maybe --skip-version-check ? > Sounds fine to me. > >>>> ipa-server-upgrade >>>> --version show program's version number and exit >>> Maybe it could print code version + data version (if available). It could be >>> handy debugging tool. >> Good idea thanks >>>> ipa-server-upgrade >>>> --test Note: for developing only >>> Is it really worth the effort to keep the option and invest more time in it? >>> >> I do not expect any extra effort (except fixing 3 plugins - 6 lines of code >> approx), so if it will help to develop updates it could stay there (personally >> I do not use it, usually updates broke during write to server on some >> constraints) > Okay, I thought that it is broken significantly. > -- Martin Basti From rcritten at redhat.com Thu Feb 26 13:55:19 2015 From: rcritten at redhat.com (Rob Crittenden) Date: Thu, 26 Feb 2015 08:55:19 -0500 Subject: [Freeipa-devel] [PATCH] 0039 Try continue ipa-client-automount even if nsslapd-minssf > 0. In-Reply-To: <54EF0960.4070608@redhat.com> References: <54EEEE11.60305@redhat.com> <54EF0960.4070608@redhat.com> Message-ID: <54EF25C7.5040001@redhat.com> Martin Basti wrote: > On 26/02/15 10:57, David Kupka wrote: >> https://fedorahosted.org/freeipa/ticket/4902 >> >> >> _______________________________________________ >> Freeipa-devel mailing list >> Freeipa-devel at redhat.com >> https://www.redhat.com/mailman/listinfo/freeipa-devel > Works for me, ACK. NACK. If you simply pass in /etc/ipa/ca.crt as the cacert path then it will use TLS. rob From dkupka at redhat.com Thu Feb 26 14:54:12 2015 From: dkupka at redhat.com (David Kupka) Date: Thu, 26 Feb 2015 15:54:12 +0100 Subject: [Freeipa-devel] [PATCH] 0039 Try continue ipa-client-automount even if nsslapd-minssf > 0. In-Reply-To: <54EF25C7.5040001@redhat.com> References: <54EEEE11.60305@redhat.com> <54EF0960.4070608@redhat.com> <54EF25C7.5040001@redhat.com> Message-ID: <54EF3394.9010502@redhat.com> On 02/26/2015 02:55 PM, Rob Crittenden wrote: > Martin Basti wrote: >> On 26/02/15 10:57, David Kupka wrote: >>> https://fedorahosted.org/freeipa/ticket/4902 >>> >>> >>> _______________________________________________ >>> Freeipa-devel mailing list >>> Freeipa-devel at redhat.com >>> https://www.redhat.com/mailman/listinfo/freeipa-devel >> Works for me, ACK. > > NACK. > > If you simply pass in /etc/ipa/ca.crt as the cacert path then it will > use TLS. > > rob > > _______________________________________________ > Freeipa-devel mailing list > Freeipa-devel at redhat.com > https://www.redhat.com/mailman/listinfo/freeipa-devel > Thanks for the catch Rob. Updated patch attached. -- David Kupka -------------- next part -------------- A non-text attachment was scrubbed... Name: freeipa-dkupka-0039-2-Use-IPA-CA-certificate-when-available-and-ignore-NO_.patch Type: text/x-patch Size: 2400 bytes Desc: not available URL: From mbasti at redhat.com Thu Feb 26 15:59:24 2015 From: mbasti at redhat.com (Martin Basti) Date: Thu, 26 Feb 2015 16:59:24 +0100 Subject: [Freeipa-devel] [PATCH 0190] DNSSEC: add support for CKM_RSA_PKCS_OAEP mechanism In-Reply-To: <54EF07EF.6060300@redhat.com> References: <54DB54DE.4030606@redhat.com> <54EF07EF.6060300@redhat.com> Message-ID: <54EF42DC.7090301@redhat.com> On 26/02/15 12:47, Petr Spacek wrote: > On 11.2.2015 14:10, Martin Basti wrote: >> https://fedorahosted.org/freeipa/ticket/4657#comment:13 >> >> Patch attached. >> >> -- >> Martin Basti >> >> >> freeipa-mbasti-0190-DNSSEC-add-support-for-CKM_RSA_PKCS_OAEP-mechanism.patch >> >> >> From 4d698a5adaa94eb854c75bd9bcaf3093f31a11e5 Mon Sep 17 00:00:00 2001 >> From: Martin Basti >> Date: Wed, 11 Feb 2015 14:05:46 +0100 >> Subject: [PATCH] DNSSEC add support for CKM_RSA_PKCS_OAEP mechanism >> >> Ticket: https://fedorahosted.org/freeipa/ticket/4657#comment:13 >> --- >> ipapython/ipap11helper/p11helper.c | 72 ++++++++++++++++++++++++++++++++++++-- >> 1 file changed, 69 insertions(+), 3 deletions(-) >> >> diff --git a/ipapython/ipap11helper/p11helper.c b/ipapython/ipap11helper/p11helper.c >> index 4e0f262057b377124793f1e3091a8c9df4794164..c638bbe849f1bbddc8004bd1c4cccc1128b1c9e7 100644 >> --- a/ipapython/ipap11helper/p11helper.c >> +++ b/ipapython/ipap11helper/p11helper.c >> @@ -53,6 +53,22 @@ >> // TODO >> #define CKA_COPYABLE (0x0017) >> >> +#define CKG_MGF1_SHA1 (0x00000001) >> + >> +#define CKZ_DATA_SPECIFIED (0x00000001) >> + >> +struct ck_rsa_pkcs_oaep_params { >> + CK_MECHANISM_TYPE hash_alg; >> + unsigned long mgf; >> + unsigned long source; >> + void *source_data; >> + unsigned long source_data_len; >> +}; >> + >> +typedef struct ck_rsa_pkcs_oaep_params CK_RSA_PKCS_OAEP_PARAMS; >> +typedef struct ck_rsa_pkcs_oaep_params *CK_RSA_PKCS_OAEP_PARAMS_PTR; >> + >> + >> CK_BBOOL true = CK_TRUE; >> CK_BBOOL false = CK_FALSE; >> >> @@ -118,6 +134,17 @@ CK_BBOOL* bool; >> } PyObj2Bool_mapping_t; >> >> /** >> + * Constants >> + */ >> +static const CK_RSA_PKCS_OAEP_PARAMS CONST_RSA_PKCS_OAEP_PARAMS = { >> + .hash_alg = CKM_SHA_1, >> + .mgf = CKG_MGF1_SHA1, >> + .source = CKZ_DATA_SPECIFIED, >> + .source_data = NULL, >> + .source_data_len = 0 >> +}; >> + >> +/** >> * ipap11helper Exceptions >> */ >> static PyObject *ipap11helperException; //parent class for all exceptions >> @@ -1359,17 +1386,36 @@ P11_Helper_export_wrapped_key(P11_Helper* self, PyObject *args, PyObject *kwds) >> CK_BYTE_PTR wrapped_key = NULL; >> CK_ULONG wrapped_key_len = 0; >> CK_MECHANISM wrapping_mech = { CKM_RSA_PKCS, NULL, 0 }; >> - CK_MECHANISM_TYPE wrapping_mech_type = CKM_RSA_PKCS; >> /* currently we don't support parameter in mechanism */ >> >> static char *kwlist[] = { "key", "wrapping_key", "wrapping_mech", NULL }; >> //TODO check long overflow >> //TODO export method >> if (!PyArg_ParseTupleAndKeywords(args, kwds, "kkk|", kwlist, &object_key, >> - &object_wrapping_key, &wrapping_mech_type)) { >> + &object_wrapping_key, &wrapping_mech.mechanism)) { >> return NULL; >> } >> - wrapping_mech.mechanism = wrapping_mech_type; >> + >> + // fill mech parameters >> + switch(wrapping_mech.mechanism){ >> + case CKM_RSA_PKCS: >> + case CKM_AES_KEY_WRAP: >> + case CKM_AES_KEY_WRAP_PAD: >> + //default params >> + break; >> + >> + case CKM_RSA_PKCS_OAEP: >> + /* Use the same configuration as openSSL >> + * https://www.openssl.org/docs/crypto/RSA_public_encrypt.html >> + */ >> + wrapping_mech.pParameter = (void*) &CONST_RSA_PKCS_OAEP_PARAMS; >> + wrapping_mech.ulParameterLen = sizeof(CONST_RSA_PKCS_OAEP_PARAMS); >> + break; >> + >> + default: >> + PyErr_SetString(ipap11helperError, "Unsupported wrapping mechanism"); >> + return NULL; >> + } >> >> rv = self->p11->C_WrapKey(self->session, &wrapping_mech, >> object_wrapping_key, object_key, NULL, &wrapped_key_len); >> @@ -1452,6 +1498,26 @@ P11_Helper_import_wrapped_secret_key(P11_Helper* self, PyObject *args, >> return NULL; >> } >> >> + switch(wrapping_mech.mechanism){ >> + case CKM_RSA_PKCS: >> + case CKM_AES_KEY_WRAP: >> + case CKM_AES_KEY_WRAP_PAD: >> + //default params >> + break; > NACK. This switch is duplicate of the previous one. Please split it into an > auxiliary function and call it twice. > > Thank you! > Thanks. Updated patch attached. -- Martin Basti -------------- next part -------------- A non-text attachment was scrubbed... Name: freeipa-mbasti-0190.2-DNSSEC-add-support-for-CKM_RSA_PKCS_OAEP-mechanism.patch Type: text/x-patch Size: 4465 bytes Desc: not available URL: From mbasti at redhat.com Thu Feb 26 16:01:34 2015 From: mbasti at redhat.com (Martin Basti) Date: Thu, 26 Feb 2015 17:01:34 +0100 Subject: [Freeipa-devel] [PATCH 0195] Fix memory leaks in ipapkcs11helper module In-Reply-To: <54EF0C4D.8010700@redhat.com> References: <54EDCC47.40400@redhat.com> <54EF0C4D.8010700@redhat.com> Message-ID: <54EF435E.5070402@redhat.com> On 26/02/15 13:06, Petr Spacek wrote: > Hello Martin, > > thank you for patch! This NACK is only aesthetic :-) > > On 25.2.2015 14:21, Martin Basti wrote: >> if (!check_return_value(rv, "import_wrapped_key: key unwrapping")) { >> + error = 1; >> + goto final; >> + } > > This exact sequence is repeated many times in the code. > > I would prefer a C macro like this: > #define GOTO_FAIL \ > do { \ > error = 1; \ > goto final; \ > } while(0) > > This allows more dense code like: > if (!test) > GOTO_FAIL; > > and does not have the risk of missing error = 1 somewhere. > >> +final: >> if (pkey != NULL) >> EVP_PKEY_free(pkey); >> + if (label != NULL) PyMem_Free(label); >> + if (error){ >> + return NULL; >> + } >> return ret; >> } > Apparently, this is inconsistent with itself. > > Please pick one style and use it, e.g. > if (label != NULL) > PyMem_Free(label) > > ... and do not add curly braces when unnecessary. > > If you want, we can try running $ indent on current sources and committing > changes separately so you do not have to make changes like this by hand. > Thanks. Updated patch attached. -- Martin Basti -------------- next part -------------- A non-text attachment was scrubbed... Name: freeipa-mbasti-0195.2-Fix-memory-leaks-in-ipap11helper.patch Type: text/x-patch Size: 26983 bytes Desc: not available URL: From redhatrises at gmail.com Thu Feb 26 16:58:31 2015 From: redhatrises at gmail.com (Gabe Alford) Date: Thu, 26 Feb 2015 09:58:31 -0700 Subject: [Freeipa-devel] [PATCH 0039] Add test case for unsupported arg for ipa-advise In-Reply-To: <54EE52FB.4040603@redhat.com> References: <54B69B41.9070404@redhat.com> <54B6A097.9020601@redhat.com> <54B6A1F4.8000807@redhat.com> <54B6A602.9060101@redhat.com> <54ECBCE5.6020204@redhat.com> <54EE52FB.4040603@redhat.com> Message-ID: Yeah. That makes more sense. Updated patch attached. Thanks, Gabe On Wed, Feb 25, 2015 at 3:55 PM, Tomas Babej wrote: > Hi Gabe, > > sorry for not being clear. This approach will not work: > > +class TestAdvice(BaseTestInvalidAdvice, > + BaseTestFedoraAuthconfig, > + BaseTestFreeBSDNSSPAM, > + BaseTestGenericNSSPAM, > + BaseTestGenericSSSDBefore19, > + BaseTestRedHatNSS, > + BaseTestRedHatNSSPAM, > + BaseTestRedHatSSSDBefore19, > + BaseTestAdvice): > + pass > > By combining all the base classes into one, you will not get the desired > effect (which is to run the test_advice method for each advice_id). Let me > explain why: > > The test runner works in the following way: it inspects any discovered > class which name begins with "Test", and executes each its method, which > names begins with "test" as a test case. > > If the test runner inspects the TestAdvice class, the only method > beggining with "test", which it will see, is the "test_advice" which was > inherited back from BaseTestAdvice class. So we can safely conclude the > test runner will only run 1 test case. > > Which one, you may ask? Well, since the test_advice behaviour is fully > determined by the values of "advice_id", "advice_regex" and "raiseerr" > attributes, let's look at their values in TestAdvice class. This class does > not define attirbutes with such names, so we move along the inheritance > chain (also called MRO) - the first class from which we inherit is > BaseTestInvalidAdvice, and this class defines all three mentioned > attributes. > > Hence the only test method will be run the test for invalid advice :) > > Now, how to fix this? The easiest approach would be to abandon the > approach with the separate classes, and map each class to a test method in > the TestAdvice class, like this (from the top of my head): > > +class TestAdvice(IntegrationTest): > + topology = 'line' > + > + def test_invalid_advice(self): > + advice_id = 'invalid-advise-param' > + advice_regex = "invalid[\s]+\'advice\'.*" > + raiseerr = False > + # Obtain the advice from the server > + tasks.kinit_admin(self.master) > + result = self.master.run_command(['ipa-advise', self.advice_id], > + raiseonerr=self.raiseerr) > + > + if not result.stdout_text: > + advice = result.stderr_text > + else: > + advice = result.stdout_text > + > + assert re.search(self.advice_regex, advice, re.S) > + > + def test_advice_fedora_authconfig(self): > + advice_id = 'config-fedora-authconfig' > + advice_regex = "\#\!\/bin\/sh.*" \ > + "authconfig[\s]+\-\-enableldap[\s]+" \ > + "\-\-ldapserver\=.*[\s]+\-\-enablerfc2307bis[\s]+" > \ > + "\-\-enablekrb5" > + raiseonerr = True > + # Obtain the advice from the server > + tasks.kinit_admin(self.master) > + result = self.master.run_command(['ipa-advise', self.advice_id], > + raiseonerr=self.raiseerr) > + > + if not result.stdout_text: > + advice = result.stderr_text > + else: > + advice = result.stdout_text > + > + assert re.search(self.advice_regex, advice, re.S) > > ... the same for the remaining 6 cases > > Now, this pattern has lots of duplicated code which can be extracted to a > helper method, I just thought it would help to be more explicit to get the > idea across. In the end you can achieve the same level of conciseness than > with the separate test classes. Good luck! > > HTH, > > Tomas > > > > > On 02/25/2015 03:52 PM, Gabe Alford wrote: > > No worries about the delay. Thanks for taking the time! Updated patch > attached. > > Thanks, > > Gabe > > On Tue, Feb 24, 2015 at 11:03 AM, Tomas Babej wrote: > >> Hi Gabe, >> >> sorry for the delay. Here comes the review! >> > 1.) All the tests fail, since the IPA master is not installed at all: >> >> def test_advice(self): >> # Obtain the advice from the server >> > tasks.kinit_admin(self.master) >> >> test_integration/test_advise.py:37: >> _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ >> test_integration/tasks.py:484: in kinit_admin >> stdin_text=host.config.admin_password) >> ../pytest_multihost/host.py:222: in run_command >> command.wait(raiseonerr=raiseonerr) >> _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ >> >> self = >> raiseonerr = True >> >> def wait(self, raiseonerr=True): >> """Wait for the remote process to exit >> >> Raises an excption if the exit code is not 0, unless raiseonerr is >> true. >> """ >> if self._done: >> return self.returncode >> >> self._end_process() >> >> self._done = True >> >> if raiseonerr and self.returncode: >> self.log.error('Exit code: %s', self.returncode) >> > raise subprocess.CalledProcessError(self.returncode, self.argv) >> E CalledProcessError: Command '['kinit', 'admin']' returned non-zero exit status 1 >> >> >> Similiarly for other tests. This is caused by the fact that you did not >> set topology in the BaseTestAdvise class, like this: >> >> --- a/ipatests/test_integration/test_advise.py >> +++ b/ipatests/test_integration/test_advise.py >> @@ -31,6 +31,7 @@ class BaseTestAdvise(IntegrationTest, object): >> advice_id = None >> raiseerr = None >> advice_regex = '' >> + topology = 'line' >> >> 2.) BaseTestAdvise inherits from IntegrationTest and from object. >> Explicitly specifying object as superclass is not needed, IntegrationTest >> already inherits from it. >> >> 3.) I think there is no good incentive to separate the test cases into >> mutliple classes. Each test class adds overhead of installing and >> uninstalling IPA server, to guarantee a clean and sane environment. >> However, it seems to be an overkill for testing ipa-advise command, which >> should be read-only anyway. By squashing the tests into one test class, we >> will decrease the run time of this test more than 8-fold. >> >> 4.) The patch adds a whitespace error. >> >> The test cases themselves are looking fine, and when I fixed the missing >> topology, they all passed. So this is a question of fixing the above >> issues, and we should be ready to push. >> >> Tomas >> > > > -------------- next part -------------- An HTML attachment was scrubbed... URL: -------------- next part -------------- A non-text attachment was scrubbed... Name: freeipa-rga-0039-3-ipatests-Add-tests-for-valid-and-invalid-ipa-advise.patch Type: application/octet-stream Size: 5855 bytes Desc: not available URL: From tbabej at redhat.com Thu Feb 26 20:18:00 2015 From: tbabej at redhat.com (Tomas Babej) Date: Thu, 26 Feb 2015 21:18:00 +0100 Subject: [Freeipa-devel] [PATCH 0039] Add test case for unsupported arg for ipa-advise In-Reply-To: References: <54B69B41.9070404@redhat.com> <54B6A097.9020601@redhat.com> <54B6A1F4.8000807@redhat.com> <54B6A602.9060101@redhat.com> <54ECBCE5.6020204@redhat.com> <54EE52FB.4040603@redhat.com> Message-ID: <54EF7F78.1090508@redhat.com> ACK. Pushed to: ipa-4-1: ddd7fb6a68fd413b1561eab9c29bac18882e5efd master: ae4ee6b53376bb7f3d1b4707c4e105c91b5cd8ab On 02/26/2015 05:58 PM, Gabe Alford wrote: > Yeah. That makes more sense. Updated patch attached. > > Thanks, > > Gabe > > On Wed, Feb 25, 2015 at 3:55 PM, Tomas Babej > wrote: > > Hi Gabe, > > sorry for not being clear. This approach will not work: > > +class TestAdvice(BaseTestInvalidAdvice, > + BaseTestFedoraAuthconfig, > + BaseTestFreeBSDNSSPAM, > + BaseTestGenericNSSPAM, > + BaseTestGenericSSSDBefore19, > + BaseTestRedHatNSS, > + BaseTestRedHatNSSPAM, > + BaseTestRedHatSSSDBefore19, > + BaseTestAdvice): > + pass > > By combining all the base classes into one, you will not get the > desired effect (which is to run the test_advice method for each > advice_id). Let me explain why: > > The test runner works in the following way: it inspects any > discovered class which name begins with "Test", and executes each > its method, which names begins with "test" as a test case. > > If the test runner inspects the TestAdvice class, the only method > beggining with "test", which it will see, is the "test_advice" > which was inherited back from BaseTestAdvice class. So we can > safely conclude the test runner will only run 1 test case. > > Which one, you may ask? Well, since the test_advice behaviour is > fully determined by the values of "advice_id", "advice_regex" and > "raiseerr" attributes, let's look at their values in TestAdvice > class. This class does not define attirbutes with such names, so > we move along the inheritance chain (also called MRO) - the first > class from which we inherit is BaseTestInvalidAdvice, and this > class defines all three mentioned attributes. > > Hence the only test method will be run the test for invalid advice :) > > Now, how to fix this? The easiest approach would be to abandon the > approach with the separate classes, and map each class to a test > method in the TestAdvice class, like this (from the top of my head): > > +class TestAdvice(IntegrationTest): > + topology = 'line' > + > + def test_invalid_advice(self): > + advice_id = 'invalid-advise-param' > + advice_regex = "invalid[\s]+\'advice\'.*" > + raiseerr = False > + # Obtain the advice from the server > + tasks.kinit_admin(self.master) > + result = self.master.run_command(['ipa-advise', > self.advice_id], > + raiseonerr=self.raiseerr) > + > + if not result.stdout_text: > + advice = result.stderr_text > + else: > + advice = result.stdout_text > + > + assert re.search(self.advice_regex, advice, re.S) > + > + def test_advice_fedora_authconfig(self): > + advice_id = 'config-fedora-authconfig' > + advice_regex = "\#\!\/bin\/sh.*" \ > + "authconfig[\s]+\-\-enableldap[\s]+" \ > + "\-\-ldapserver\=.*[\s]+\-\-enablerfc2307bis[\s]+" \ > + "\-\-enablekrb5" > + raiseonerr = True > + # Obtain the advice from the server > + tasks.kinit_admin(self.master) > + result = self.master.run_command(['ipa-advise', > self.advice_id], > + raiseonerr=self.raiseerr) > + > + if not result.stdout_text: > + advice = result.stderr_text > + else: > + advice = result.stdout_text > + > + assert re.search(self.advice_regex, advice, re.S) > > ... the same for the remaining 6 cases > > Now, this pattern has lots of duplicated code which can be > extracted to a helper method, I just thought it would help to be > more explicit to get the idea across. In the end you can achieve > the same level of conciseness than with the separate test classes. > Good luck! > > HTH, > > Tomas > > > > > On 02/25/2015 03:52 PM, Gabe Alford wrote: >> No worries about the delay. Thanks for taking the time! Updated >> patch attached. >> >> Thanks, >> >> Gabe >> >> On Tue, Feb 24, 2015 at 11:03 AM, Tomas Babej > > wrote: >> >> Hi Gabe, >> >> sorry for the delay. Here comes the review! >> >> 1.) All the tests fail, since the IPA master is not installed >> at all: >> >> def test_advice(self): >> # Obtain the advice from the server >> > tasks.kinit_admin(self.master) >> >> test_integration/test_advise.py:37: >> _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ >> test_integration/tasks.py:484: in kinit_admin >> stdin_text=host.config.admin_password) >> ../pytest_multihost/host.py:222: in run_command >> command.wait(raiseonerr=raiseonerr) >> _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ >> >> self = >> raiseonerr = True >> >> def wait(self, raiseonerr=True): >> """Wait for the remote process to exit >> >> Raises an excption if the exit code is not 0, unless raiseonerr is >> true. >> """ >> if self._done: >> return self.returncode >> >> self._end_process() >> >> self._done = True >> >> if raiseonerr and self.returncode: >> self.log.error('Exit code: %s', self.returncode) >> > raise subprocess.CalledProcessError(self.returncode, self.argv) >> E CalledProcessError: Command '['kinit', 'admin']' returned non-zero exit status 1 >> >> >> Similiarly for other tests. This is caused by the fact that >> you did not set topology in the BaseTestAdvise class, like this: >> >> --- a/ipatests/test_integration/test_advise.py >> +++ b/ipatests/test_integration/test_advise.py >> @@ -31,6 +31,7 @@ class BaseTestAdvise(IntegrationTest, object): >> advice_id = None >> raiseerr = None >> advice_regex = '' >> + topology = 'line' >> >> 2.) BaseTestAdvise inherits from IntegrationTest and from >> object. Explicitly specifying object as superclass is not >> needed, IntegrationTest already inherits from it. >> >> 3.) I think there is no good incentive to separate the test >> cases into mutliple classes. Each test class adds overhead of >> installing and uninstalling IPA server, to guarantee a clean >> and sane environment. However, it seems to be an overkill for >> testing ipa-advise command, which should be read-only anyway. >> By squashing the tests into one test class, we will decrease >> the run time of this test more than 8-fold. >> >> 4.) The patch adds a whitespace error. >> >> The test cases themselves are looking fine, and when I fixed >> the missing topology, they all passed. So this is a question >> of fixing the above issues, and we should be ready to push. >> >> Tomas >> >> > > -------------- next part -------------- An HTML attachment was scrubbed... URL: From mbasti at redhat.com Fri Feb 27 13:21:13 2015 From: mbasti at redhat.com (Martin Basti) Date: Fri, 27 Feb 2015 14:21:13 +0100 Subject: [Freeipa-devel] [PATCH] 0039 Try continue ipa-client-automount even if nsslapd-minssf > 0. In-Reply-To: <54EF3394.9010502@redhat.com> References: <54EEEE11.60305@redhat.com> <54EF0960.4070608@redhat.com> <54EF25C7.5040001@redhat.com> <54EF3394.9010502@redhat.com> Message-ID: <54F06F49.7090004@redhat.com> On 26/02/15 15:54, David Kupka wrote: > On 02/26/2015 02:55 PM, Rob Crittenden wrote: >> Martin Basti wrote: >>> On 26/02/15 10:57, David Kupka wrote: >>>> https://fedorahosted.org/freeipa/ticket/4902 >>>> >>>> >>>> _______________________________________________ >>>> Freeipa-devel mailing list >>>> Freeipa-devel at redhat.com >>>> https://www.redhat.com/mailman/listinfo/freeipa-devel >>> Works for me, ACK. >> >> NACK. >> >> If you simply pass in /etc/ipa/ca.crt as the cacert path then it will >> use TLS. >> >> rob >> >> _______________________________________________ >> Freeipa-devel mailing list >> Freeipa-devel at redhat.com >> https://www.redhat.com/mailman/listinfo/freeipa-devel >> > > Thanks for the catch Rob. Updated patch attached. > Hello, I tested it again, just nitpick: 1) Can you also update the commit message? And question: I found, if you erase /etc/ipa/ca.crt from client and use --server option pointing to different IPA server (LDAP repectively) out of realm, ipa-client-atomount returns success. Is this behavior good? This happens without this patch as well. Martin^2 -- Martin Basti From mbasti at redhat.com Fri Feb 27 13:26:58 2015 From: mbasti at redhat.com (Martin Basti) Date: Fri, 27 Feb 2015 14:26:58 +0100 Subject: [Freeipa-devel] [PATCH] 0039 Try continue ipa-client-automount even if nsslapd-minssf > 0. In-Reply-To: <54F06F49.7090004@redhat.com> References: <54EEEE11.60305@redhat.com> <54EF0960.4070608@redhat.com> <54EF25C7.5040001@redhat.com> <54EF3394.9010502@redhat.com> <54F06F49.7090004@redhat.com> Message-ID: <54F070A2.9090706@redhat.com> On 27/02/15 14:21, Martin Basti wrote: > On 26/02/15 15:54, David Kupka wrote: >> On 02/26/2015 02:55 PM, Rob Crittenden wrote: >>> Martin Basti wrote: >>>> On 26/02/15 10:57, David Kupka wrote: >>>>> https://fedorahosted.org/freeipa/ticket/4902 >>>>> >>>>> >>>>> _______________________________________________ >>>>> Freeipa-devel mailing list >>>>> Freeipa-devel at redhat.com >>>>> https://www.redhat.com/mailman/listinfo/freeipa-devel >>>> Works for me, ACK. >>> >>> NACK. >>> >>> If you simply pass in /etc/ipa/ca.crt as the cacert path then it will >>> use TLS. >>> >>> rob >>> >>> _______________________________________________ >>> Freeipa-devel mailing list >>> Freeipa-devel at redhat.com >>> https://www.redhat.com/mailman/listinfo/freeipa-devel >>> >> >> Thanks for the catch Rob. Updated patch attached. >> > Hello, I tested it again, just nitpick: > > 1) > Can you also update the commit message? Never mind, I accidentally read old commit message. sorry. > > And question: > I found, if you erase /etc/ipa/ca.crt from client and use --server > option pointing to different IPA server (LDAP repectively) out of > realm, ipa-client-atomount returns success. Is this behavior good? > This happens without this patch as well. > > Martin^2 > -- Martin Basti From dkupka at redhat.com Fri Feb 27 13:37:04 2015 From: dkupka at redhat.com (David Kupka) Date: Fri, 27 Feb 2015 14:37:04 +0100 Subject: [Freeipa-devel] [PATCH] 0039 Try continue ipa-client-automount even if nsslapd-minssf > 0. In-Reply-To: <54F070A2.9090706@redhat.com> References: <54EEEE11.60305@redhat.com> <54EF0960.4070608@redhat.com> <54EF25C7.5040001@redhat.com> <54EF3394.9010502@redhat.com> <54F06F49.7090004@redhat.com> <54F070A2.9090706@redhat.com> Message-ID: <54F07300.7070708@redhat.com> On 02/27/2015 02:26 PM, Martin Basti wrote: > On 27/02/15 14:21, Martin Basti wrote: >> On 26/02/15 15:54, David Kupka wrote: >>> On 02/26/2015 02:55 PM, Rob Crittenden wrote: >>>> Martin Basti wrote: >>>>> On 26/02/15 10:57, David Kupka wrote: >>>>>> https://fedorahosted.org/freeipa/ticket/4902 >>>>>> >>>>>> >>>>>> _______________________________________________ >>>>>> Freeipa-devel mailing list >>>>>> Freeipa-devel at redhat.com >>>>>> https://www.redhat.com/mailman/listinfo/freeipa-devel >>>>> Works for me, ACK. >>>> >>>> NACK. >>>> >>>> If you simply pass in /etc/ipa/ca.crt as the cacert path then it will >>>> use TLS. >>>> >>>> rob >>>> >>>> _______________________________________________ >>>> Freeipa-devel mailing list >>>> Freeipa-devel at redhat.com >>>> https://www.redhat.com/mailman/listinfo/freeipa-devel >>>> >>> >>> Thanks for the catch Rob. Updated patch attached. >>> >> Hello, I tested it again, just nitpick: >> >> 1) >> Can you also update the commit message? > Never mind, I accidentally read old commit message. sorry. >> >> And question: >> I found, if you erase /etc/ipa/ca.crt from client and use --server >> option pointing to different IPA server (LDAP repectively) out of >> realm, ipa-client-atomount returns success. Is this behavior good? >> This happens without this patch as well. First of all this never happens if you rely on DNS discovery so most user will never encounter this behavior, BUT it would be nice to add a check and warn the user that he is doing something unwise and will probably regret :-) Could you please file a ticket? >> >> Martin^2 >> > > -- David Kupka From rcritten at redhat.com Fri Feb 27 19:27:30 2015 From: rcritten at redhat.com (Rob Crittenden) Date: Fri, 27 Feb 2015 14:27:30 -0500 Subject: [Freeipa-devel] [PATCH] 0039 Try continue ipa-client-automount even if nsslapd-minssf > 0. In-Reply-To: <54F07300.7070708@redhat.com> References: <54EEEE11.60305@redhat.com> <54EF0960.4070608@redhat.com> <54EF25C7.5040001@redhat.com> <54EF3394.9010502@redhat.com> <54F06F49.7090004@redhat.com> <54F070A2.9090706@redhat.com> <54F07300.7070708@redhat.com> Message-ID: <54F0C522.9050209@redhat.com> David Kupka wrote: > On 02/27/2015 02:26 PM, Martin Basti wrote: >> On 27/02/15 14:21, Martin Basti wrote: >>> On 26/02/15 15:54, David Kupka wrote: >>>> On 02/26/2015 02:55 PM, Rob Crittenden wrote: >>>>> Martin Basti wrote: >>>>>> On 26/02/15 10:57, David Kupka wrote: >>>>>>> https://fedorahosted.org/freeipa/ticket/4902 >>>>>>> >>>>>>> >>>>>>> _______________________________________________ >>>>>>> Freeipa-devel mailing list >>>>>>> Freeipa-devel at redhat.com >>>>>>> https://www.redhat.com/mailman/listinfo/freeipa-devel >>>>>> Works for me, ACK. >>>>> >>>>> NACK. >>>>> >>>>> If you simply pass in /etc/ipa/ca.crt as the cacert path then it will >>>>> use TLS. >>>>> >>>>> rob >>>>> >>>>> _______________________________________________ >>>>> Freeipa-devel mailing list >>>>> Freeipa-devel at redhat.com >>>>> https://www.redhat.com/mailman/listinfo/freeipa-devel >>>>> >>>> >>>> Thanks for the catch Rob. Updated patch attached. >>>> >>> Hello, I tested it again, just nitpick: >>> >>> 1) >>> Can you also update the commit message? >> Never mind, I accidentally read old commit message. sorry. >>> >>> And question: >>> I found, if you erase /etc/ipa/ca.crt from client and use --server >>> option pointing to different IPA server (LDAP repectively) out of >>> realm, ipa-client-atomount returns success. Is this behavior good? >>> This happens without this patch as well. > > First of all this never happens if you rely on DNS discovery so most > user will never encounter this behavior, > > BUT it would be nice to add a check and warn the user that he is doing > something unwise and will probably regret :-) > Could you please file a ticket? Hmm, interesting. Yeah, I suppose trying to get a host ticket would be good defensive programming. ACK on the new patch from me too. rob From rcritten at redhat.com Fri Feb 27 19:45:51 2015 From: rcritten at redhat.com (Rob Crittenden) Date: Fri, 27 Feb 2015 14:45:51 -0500 Subject: [Freeipa-devel] IPA Server upgrade 4.2 design In-Reply-To: <54EF21D6.8000301@redhat.com> References: <54ECBE7E.7040108@redhat.com> <54EDF53D.8020307@redhat.com> <54EDFD04.5020603@redhat.com> <54EEEB2A.4030108@redhat.com> <54EF21D6.8000301@redhat.com> Message-ID: <54F0C96F.8060907@redhat.com> Martin Basti wrote: > On 26/02/15 10:45, Petr Spacek wrote: >> On 25.2.2015 17:49, Martin Basti wrote: >>> On 25/02/15 17:15, Petr Spacek wrote: >>>> On 24.2.2015 19:10, Martin Basti wrote: >>>>> Hello all, >>>>> >>>>> please read the design page, any objections/suggestions appreciated >>>>> http://www.freeipa.org/page/V4/Server_Upgrade_Refactoring >>>> Thank you for the design, I have only few nitpicks. >>>> >>>>> Increase update files numbers range >>>>> Update files number will be extended into 4 digits values. >>>> IMHO the dependency on particular number format should be removed >>>> altogether. >>>> It should be perfectly enough to say that updates are executed in ASCII >>>> lexicographic order and be done with it. >>> 4.1.3-2 > 4.1.3-12 in lexicographic order, this will not fit. >> Well, sure, but it allows you to use >> 00-a >> 01-b >> >> and renumber it to >> >> 001-a >> 002-b >> >> at will without changes to code. (Lexicographic order is what 'ls' >> uses by >> default so you can see the real ordering at any time very easily.) >> >> Also, as you pointed out, it allows you to do things like >> 12.345-a >> 12.666-bbb >> without changing code, again :-) > Oh stupid me, I read it wrong, I replied with IPA version compare. > > sounds good to me, any objections anyone? This makes sense as long as we don't abuse it. The numbers are there to apply some amount of order but flexibility is good, and will avoid the problem of having humongous update files. I'm fine with allowing DM given that it allows running as non-root (pretty much the only condition that ldapi wouldn't work), but I think a full upgrade will fail w/o root given that you are combining the two commands. On ipactl, would it be overkill if there is a tty to prompt the user to upgrade? In a non-container world it might be surprising to have an upgrade happen esp since upgrades take a while. With --skip-version-check what sorts of problems can we foresee? I assume a big warning will be added to at least the man page, if not the cli? Where does platform come from? I'm wondering how Debian will handle this. Looks really good. rob From nkinder at redhat.com Fri Feb 27 20:09:27 2015 From: nkinder at redhat.com (Nathan Kinder) Date: Fri, 27 Feb 2015 12:09:27 -0800 Subject: [Freeipa-devel] [PATCHES 0001-0002] ipa-client-install NTP fixes In-Reply-To: <54EEDF8A.2090204@redhat.com> References: <54EE84B1.4040206@redhat.com> <54EEDF8A.2090204@redhat.com> Message-ID: <54F0CEF7.8090609@redhat.com> On 02/26/2015 12:55 AM, Martin Kosek wrote: > On 02/26/2015 03:28 AM, Nathan Kinder wrote: >> Hi, >> >> The two attached patches address some issues that affect >> ipa-client-install when syncing time from the NTP server. Now that we >> use ntpd to perform the time sync, the client install can end up hanging >> forever when the server is not reachable (firewall issues, etc.). These >> patches address the issues in two different ways: >> >> 1 - Don't attempt to sync time when --no-ntp is specified. >> >> 2 - Implement a timeout capability that is used when we run ntpd to >> perform the time sync to prevent indefinite hanging. >> >> The one potentially contentious issue is that this introduces a new >> dependency on python-subprocess32 to allow us to have timeout support >> when using Python 2.x. This is packaged for Fedora, but I don't see it >> on RHEL or CentOS currently. It would need to be packaged there. >> >> https://fedorahosted.org/freeipa/ticket/4842 >> >> Thanks, >> -NGK > > Thanks for Patches. For the second patch, I would really prefer to avoid new > dependency, especially if it's not packaged in RHEL/CentOS. Maybe we could use > some workaround instead, as in: > > http://stackoverflow.com/questions/3733270/python-subprocess-timeout I don't like having to add an additional dependency either, but the alternative seems more risky. Utilizing the subprocess32 module (which is really just a backport of the normal subprocess module from Python 3.x) is not invasive for our code in ipautil.run(). Adding some sort of a thread that has to kill the spawned subprocess seems more risky (see the discussion about a race condition in the stackoverflow thread above). That said, I'm sure the thread/poll method can be made to work if you and others feel strongly that this is a better approach than adding a new dependency. -NGK > > ? > > Martin > From rcritten at redhat.com Fri Feb 27 20:20:33 2015 From: rcritten at redhat.com (Rob Crittenden) Date: Fri, 27 Feb 2015 15:20:33 -0500 Subject: [Freeipa-devel] [PATCHES 0001-0002] ipa-client-install NTP fixes In-Reply-To: <54F0CEF7.8090609@redhat.com> References: <54EE84B1.4040206@redhat.com> <54EEDF8A.2090204@redhat.com> <54F0CEF7.8090609@redhat.com> Message-ID: <54F0D191.9060908@redhat.com> Nathan Kinder wrote: > > > On 02/26/2015 12:55 AM, Martin Kosek wrote: >> On 02/26/2015 03:28 AM, Nathan Kinder wrote: >>> Hi, >>> >>> The two attached patches address some issues that affect >>> ipa-client-install when syncing time from the NTP server. Now that we >>> use ntpd to perform the time sync, the client install can end up hanging >>> forever when the server is not reachable (firewall issues, etc.). These >>> patches address the issues in two different ways: >>> >>> 1 - Don't attempt to sync time when --no-ntp is specified. >>> >>> 2 - Implement a timeout capability that is used when we run ntpd to >>> perform the time sync to prevent indefinite hanging. >>> >>> The one potentially contentious issue is that this introduces a new >>> dependency on python-subprocess32 to allow us to have timeout support >>> when using Python 2.x. This is packaged for Fedora, but I don't see it >>> on RHEL or CentOS currently. It would need to be packaged there. >>> >>> https://fedorahosted.org/freeipa/ticket/4842 >>> >>> Thanks, >>> -NGK >> >> Thanks for Patches. For the second patch, I would really prefer to avoid new >> dependency, especially if it's not packaged in RHEL/CentOS. Maybe we could use >> some workaround instead, as in: >> >> http://stackoverflow.com/questions/3733270/python-subprocess-timeout > > I don't like having to add an additional dependency either, but the > alternative seems more risky. Utilizing the subprocess32 module (which > is really just a backport of the normal subprocess module from Python > 3.x) is not invasive for our code in ipautil.run(). Adding some sort of > a thread that has to kill the spawned subprocess seems more risky (see > the discussion about a race condition in the stackoverflow thread > above). That said, I'm sure the thread/poll method can be made to work > if you and others feel strongly that this is a better approach than > adding a new dependency. Why not use /usr/bin/timeout from coreutils? rob From nkinder at redhat.com Fri Feb 27 20:49:36 2015 From: nkinder at redhat.com (Nathan Kinder) Date: Fri, 27 Feb 2015 12:49:36 -0800 Subject: [Freeipa-devel] [PATCHES 0001-0002] ipa-client-install NTP fixes In-Reply-To: <54F0D191.9060908@redhat.com> References: <54EE84B1.4040206@redhat.com> <54EEDF8A.2090204@redhat.com> <54F0CEF7.8090609@redhat.com> <54F0D191.9060908@redhat.com> Message-ID: <54F0D860.6020501@redhat.com> On 02/27/2015 12:20 PM, Rob Crittenden wrote: > Nathan Kinder wrote: >> >> >> On 02/26/2015 12:55 AM, Martin Kosek wrote: >>> On 02/26/2015 03:28 AM, Nathan Kinder wrote: >>>> Hi, >>>> >>>> The two attached patches address some issues that affect >>>> ipa-client-install when syncing time from the NTP server. Now that we >>>> use ntpd to perform the time sync, the client install can end up hanging >>>> forever when the server is not reachable (firewall issues, etc.). These >>>> patches address the issues in two different ways: >>>> >>>> 1 - Don't attempt to sync time when --no-ntp is specified. >>>> >>>> 2 - Implement a timeout capability that is used when we run ntpd to >>>> perform the time sync to prevent indefinite hanging. >>>> >>>> The one potentially contentious issue is that this introduces a new >>>> dependency on python-subprocess32 to allow us to have timeout support >>>> when using Python 2.x. This is packaged for Fedora, but I don't see it >>>> on RHEL or CentOS currently. It would need to be packaged there. >>>> >>>> https://fedorahosted.org/freeipa/ticket/4842 >>>> >>>> Thanks, >>>> -NGK >>> >>> Thanks for Patches. For the second patch, I would really prefer to avoid new >>> dependency, especially if it's not packaged in RHEL/CentOS. Maybe we could use >>> some workaround instead, as in: >>> >>> http://stackoverflow.com/questions/3733270/python-subprocess-timeout >> >> I don't like having to add an additional dependency either, but the >> alternative seems more risky. Utilizing the subprocess32 module (which >> is really just a backport of the normal subprocess module from Python >> 3.x) is not invasive for our code in ipautil.run(). Adding some sort of >> a thread that has to kill the spawned subprocess seems more risky (see >> the discussion about a race condition in the stackoverflow thread >> above). That said, I'm sure the thread/poll method can be made to work >> if you and others feel strongly that this is a better approach than >> adding a new dependency. > > Why not use /usr/bin/timeout from coreutils? That sounds like a perfectly good idea. I wasn't aware of it's existence (or it's possible that I forgot about it). Thanks for the suggestion! I'll test out a reworked version of the patch. Do you think that there is value in leaving the timeout capability centrally in ipautil.run()? We only need it for the call to 'ntpd' right now, but there might be a need for using a timeout for other commands in the future. The alternative is to just modify synconce_ntp() to use /usr/bin/timeout and leave ipautil.run() alone. -NGK > > rob > From lslebodn at redhat.com Fri Feb 27 20:50:40 2015 From: lslebodn at redhat.com (Lukas Slebodnik) Date: Fri, 27 Feb 2015 21:50:40 +0100 Subject: [Freeipa-devel] [PATCHES] SPEC: Require python2 version of sssd bindings Message-ID: <20150227205039.GB2327@mail.corp.redhat.com> ehlo, Please review attached patches and fix freeipa in fedora 22 ASAP. I think the most critical is 1st patch sh$ git grep "SSSDConfig" | grep import install/tools/ipa-upgradeconfig:import SSSDConfig ipa-client/ipa-install/ipa-client-automount:import SSSDConfig ipa-client/ipa-install/ipa-client-install: import SSSDConfig BTW package python-sssdconfig is provides since sssd-1.10.0alpha1 (2013-04-02) but it was not explicitely required. The latest python3 changes in sssd (fedora 22) is just a result of negligent packaging of freeipa. LS -------------- next part -------------- >From 09bfbd420ab83f8cb571f9dc04a5cd9c7f15d604 Mon Sep 17 00:00:00 2001 From: Lukas Slebodnik Date: Fri, 27 Feb 2015 20:40:06 +0100 Subject: [PATCH 1/3] SPEC: Explicitly requires python-sssdconfig Resolves: https://fedorahosted.org/freeipa/ticket/4929 --- freeipa.spec.in | 2 ++ 1 file changed, 2 insertions(+) diff --git a/freeipa.spec.in b/freeipa.spec.in index b186d9fdff31118ea4d929f024f4dc16a75b1d0b..9513f45c6c933a1109390393cb90d68e8c697dc7 100644 --- a/freeipa.spec.in +++ b/freeipa.spec.in @@ -122,6 +122,7 @@ Requires: mod_auth_kerb >= 5.4-16 Requires: mod_nss >= 1.0.8-26 Requires: python-ldap >= 2.4.15 Requires: python-krbV +Requires: python-sssdconfig Requires: acl Requires: python-pyasn1 Requires: memcached @@ -228,6 +229,7 @@ Requires: wget Requires: libcurl >= 7.21.7-2 Requires: xmlrpc-c >= 1.27.4 Requires: sssd >= 1.12.3 +Requires: python-sssdconfig Requires: certmonger >= 0.76.8 Requires: nss-tools Requires: bind-utils -- 2.1.0 -------------- next part -------------- >From 3efd525d72d20734d926c4e804a7080ea01cb580 Mon Sep 17 00:00:00 2001 From: Lukas Slebodnik Date: Fri, 27 Feb 2015 20:43:38 +0100 Subject: [PATCH 2/3] SPEC: Require python2 version of sssd bindings Python modules pysss and pysss_murmur was part of package sssd-common. Fedora 22 tries to get rid of python2 and therefore these modules were extracted from package sssd-common to separate packages python-sss and python-sss-murmur and python3 version of packages python3-sss python3-sss-murmur git grep "pysss" | grep import ipalib/plugins/trust.py: import pysss_murmur #pylint: disable=F0401 ipaserver/dcerpc.py:import pysss ipaserver/dcerpc.py is pacakged in freeipa-server-trust-ad palib/plugins/trust.py is packaged in freeipa-python Resolves: https://fedorahosted.org/freeipa/ticket/4929 --- freeipa.spec.in | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/freeipa.spec.in b/freeipa.spec.in index 9513f45c6c933a1109390393cb90d68e8c697dc7..7a1ff8b50ef1b462ad14fb2328149c3c2ed2fb38 100644 --- a/freeipa.spec.in +++ b/freeipa.spec.in @@ -195,6 +195,9 @@ Requires: samba >= %{samba_version} Requires: samba-winbind Requires: libsss_idmap Requires: libsss_nss_idmap-python +%if (0%{?fedora} >= 22) +Requires: python-sss +%endif # We use alternatives to divert winbind_krb5_locator.so plugin to libkrb5 # on the installes where server-trust-ad subpackage is installed because # IPA AD trusts cannot be used at the same time with the locator plugin @@ -288,6 +291,9 @@ Requires: python-qrcode-core >= 5.0.0 Requires: python-pyasn1 Requires: python-dateutil Requires: python-yubico +%if (0%{?fedora} >= 22) +Requires: python-sss-murmur +%endif Requires: wget Requires: dbus-python -- 2.1.0 -------------- next part -------------- >From 5d963dda2d6423007cea803940be5d34fdcbc377 Mon Sep 17 00:00:00 2001 From: Lukas Slebodnik Date: Fri, 27 Feb 2015 21:02:51 +0100 Subject: [PATCH 3/3] SPEC: Add missing requires for python-libsss_nss_idmap git grep "pysss_nss_idmap" | grep import ipalib/plugins/trust.py: import pysss_nss_idmap #pylint: disable=F0401 ipaserver/dcerpc.py:import pysss_nss_idmap ipaserver/dcerpc.py is packaged in freeipa-server-trust-ad palib/plugins/trust.py is packaged in freeipa-python Resolves: https://fedorahosted.org/freeipa/ticket/4929 --- freeipa.spec.in | 2 ++ 1 file changed, 2 insertions(+) diff --git a/freeipa.spec.in b/freeipa.spec.in index 7a1ff8b50ef1b462ad14fb2328149c3c2ed2fb38..fafec414849735854ee97752f20e941f01dc92ce 100644 --- a/freeipa.spec.in +++ b/freeipa.spec.in @@ -198,6 +198,7 @@ Requires: libsss_nss_idmap-python %if (0%{?fedora} >= 22) Requires: python-sss %endif +Requires: python-libsss_nss_idmap # We use alternatives to divert winbind_krb5_locator.so plugin to libkrb5 # on the installes where server-trust-ad subpackage is installed because # IPA AD trusts cannot be used at the same time with the locator plugin @@ -294,6 +295,7 @@ Requires: python-yubico %if (0%{?fedora} >= 22) Requires: python-sss-murmur %endif +Requires: python-libsss_nss_idmap Requires: wget Requires: dbus-python -- 2.1.0 From rcritten at redhat.com Fri Feb 27 21:08:19 2015 From: rcritten at redhat.com (Rob Crittenden) Date: Fri, 27 Feb 2015 16:08:19 -0500 Subject: [Freeipa-devel] [PATCHES 0001-0002] ipa-client-install NTP fixes In-Reply-To: <54F0D860.6020501@redhat.com> References: <54EE84B1.4040206@redhat.com> <54EEDF8A.2090204@redhat.com> <54F0CEF7.8090609@redhat.com> <54F0D191.9060908@redhat.com> <54F0D860.6020501@redhat.com> Message-ID: <54F0DCC3.5080405@redhat.com> Nathan Kinder wrote: > > > On 02/27/2015 12:20 PM, Rob Crittenden wrote: >> Nathan Kinder wrote: >>> >>> >>> On 02/26/2015 12:55 AM, Martin Kosek wrote: >>>> On 02/26/2015 03:28 AM, Nathan Kinder wrote: >>>>> Hi, >>>>> >>>>> The two attached patches address some issues that affect >>>>> ipa-client-install when syncing time from the NTP server. Now that we >>>>> use ntpd to perform the time sync, the client install can end up hanging >>>>> forever when the server is not reachable (firewall issues, etc.). These >>>>> patches address the issues in two different ways: >>>>> >>>>> 1 - Don't attempt to sync time when --no-ntp is specified. >>>>> >>>>> 2 - Implement a timeout capability that is used when we run ntpd to >>>>> perform the time sync to prevent indefinite hanging. >>>>> >>>>> The one potentially contentious issue is that this introduces a new >>>>> dependency on python-subprocess32 to allow us to have timeout support >>>>> when using Python 2.x. This is packaged for Fedora, but I don't see it >>>>> on RHEL or CentOS currently. It would need to be packaged there. >>>>> >>>>> https://fedorahosted.org/freeipa/ticket/4842 >>>>> >>>>> Thanks, >>>>> -NGK >>>> >>>> Thanks for Patches. For the second patch, I would really prefer to avoid new >>>> dependency, especially if it's not packaged in RHEL/CentOS. Maybe we could use >>>> some workaround instead, as in: >>>> >>>> http://stackoverflow.com/questions/3733270/python-subprocess-timeout >>> >>> I don't like having to add an additional dependency either, but the >>> alternative seems more risky. Utilizing the subprocess32 module (which >>> is really just a backport of the normal subprocess module from Python >>> 3.x) is not invasive for our code in ipautil.run(). Adding some sort of >>> a thread that has to kill the spawned subprocess seems more risky (see >>> the discussion about a race condition in the stackoverflow thread >>> above). That said, I'm sure the thread/poll method can be made to work >>> if you and others feel strongly that this is a better approach than >>> adding a new dependency. >> >> Why not use /usr/bin/timeout from coreutils? > > That sounds like a perfectly good idea. I wasn't aware of it's > existence (or it's possible that I forgot about it). Thanks for the > suggestion! I'll test out a reworked version of the patch. > > Do you think that there is value in leaving the timeout capability > centrally in ipautil.run()? We only need it for the call to 'ntpd' > right now, but there might be a need for using a timeout for other > commands in the future. The alternative is to just modify > synconce_ntp() to use /usr/bin/timeout and leave ipautil.run() alone. I think it would require a lot of research. One of the programs spawned by this is pkicreate which could take quite some time, and spawning a clone in particular. It is definitely an interesting idea but I think it is safest for now to limit it to just NTP for now. rob From nkinder at redhat.com Fri Feb 27 21:18:11 2015 From: nkinder at redhat.com (Nathan Kinder) Date: Fri, 27 Feb 2015 13:18:11 -0800 Subject: [Freeipa-devel] [PATCHES 0001-0002] ipa-client-install NTP fixes In-Reply-To: <54F0DCC3.5080405@redhat.com> References: <54EE84B1.4040206@redhat.com> <54EEDF8A.2090204@redhat.com> <54F0CEF7.8090609@redhat.com> <54F0D191.9060908@redhat.com> <54F0D860.6020501@redhat.com> <54F0DCC3.5080405@redhat.com> Message-ID: <54F0DF13.2030808@redhat.com> On 02/27/2015 01:08 PM, Rob Crittenden wrote: > Nathan Kinder wrote: >> >> >> On 02/27/2015 12:20 PM, Rob Crittenden wrote: >>> Nathan Kinder wrote: >>>> >>>> >>>> On 02/26/2015 12:55 AM, Martin Kosek wrote: >>>>> On 02/26/2015 03:28 AM, Nathan Kinder wrote: >>>>>> Hi, >>>>>> >>>>>> The two attached patches address some issues that affect >>>>>> ipa-client-install when syncing time from the NTP server. Now that we >>>>>> use ntpd to perform the time sync, the client install can end up hanging >>>>>> forever when the server is not reachable (firewall issues, etc.). These >>>>>> patches address the issues in two different ways: >>>>>> >>>>>> 1 - Don't attempt to sync time when --no-ntp is specified. >>>>>> >>>>>> 2 - Implement a timeout capability that is used when we run ntpd to >>>>>> perform the time sync to prevent indefinite hanging. >>>>>> >>>>>> The one potentially contentious issue is that this introduces a new >>>>>> dependency on python-subprocess32 to allow us to have timeout support >>>>>> when using Python 2.x. This is packaged for Fedora, but I don't see it >>>>>> on RHEL or CentOS currently. It would need to be packaged there. >>>>>> >>>>>> https://fedorahosted.org/freeipa/ticket/4842 >>>>>> >>>>>> Thanks, >>>>>> -NGK >>>>> >>>>> Thanks for Patches. For the second patch, I would really prefer to avoid new >>>>> dependency, especially if it's not packaged in RHEL/CentOS. Maybe we could use >>>>> some workaround instead, as in: >>>>> >>>>> http://stackoverflow.com/questions/3733270/python-subprocess-timeout >>>> >>>> I don't like having to add an additional dependency either, but the >>>> alternative seems more risky. Utilizing the subprocess32 module (which >>>> is really just a backport of the normal subprocess module from Python >>>> 3.x) is not invasive for our code in ipautil.run(). Adding some sort of >>>> a thread that has to kill the spawned subprocess seems more risky (see >>>> the discussion about a race condition in the stackoverflow thread >>>> above). That said, I'm sure the thread/poll method can be made to work >>>> if you and others feel strongly that this is a better approach than >>>> adding a new dependency. >>> >>> Why not use /usr/bin/timeout from coreutils? >> >> That sounds like a perfectly good idea. I wasn't aware of it's >> existence (or it's possible that I forgot about it). Thanks for the >> suggestion! I'll test out a reworked version of the patch. >> >> Do you think that there is value in leaving the timeout capability >> centrally in ipautil.run()? We only need it for the call to 'ntpd' >> right now, but there might be a need for using a timeout for other >> commands in the future. The alternative is to just modify >> synconce_ntp() to use /usr/bin/timeout and leave ipautil.run() alone. > > I think it would require a lot of research. One of the programs spawned > by this is pkicreate which could take quite some time, and spawning a > clone in particular. > > It is definitely an interesting idea but I think it is safest for now to > limit it to just NTP for now. What I meant was that we would have an optional keyword "timeout" parameter to ipautil.run() that defaults to None, just like my subprocess32 approach. If a timeout is not passed in, we would use subprocess.Popen() to run the specified command just like we do today. We would only actually pass the timeout parameter to ipautil.run() in synconce_ntp() for now, so no other commands would have a timeout in effect. The capability would be available for other commands this way though. Let me propose a patch with this implementation, and if you don't like it, we can leave ipautil.run() alone and restrict the changes to synconce_ntp(). > > rob > From nkinder at redhat.com Sat Feb 28 20:56:30 2015 From: nkinder at redhat.com (Nathan Kinder) Date: Sat, 28 Feb 2015 12:56:30 -0800 Subject: [Freeipa-devel] [PATCHES 0001-0002] ipa-client-install NTP fixes In-Reply-To: <54F0DF13.2030808@redhat.com> References: <54EE84B1.4040206@redhat.com> <54EEDF8A.2090204@redhat.com> <54F0CEF7.8090609@redhat.com> <54F0D191.9060908@redhat.com> <54F0D860.6020501@redhat.com> <54F0DCC3.5080405@redhat.com> <54F0DF13.2030808@redhat.com> Message-ID: <54F22B7E.30707@redhat.com> On 02/27/2015 01:18 PM, Nathan Kinder wrote: > > > On 02/27/2015 01:08 PM, Rob Crittenden wrote: >> Nathan Kinder wrote: >>> >>> >>> On 02/27/2015 12:20 PM, Rob Crittenden wrote: >>>> Nathan Kinder wrote: >>>>> >>>>> >>>>> On 02/26/2015 12:55 AM, Martin Kosek wrote: >>>>>> On 02/26/2015 03:28 AM, Nathan Kinder wrote: >>>>>>> Hi, >>>>>>> >>>>>>> The two attached patches address some issues that affect >>>>>>> ipa-client-install when syncing time from the NTP server. Now that we >>>>>>> use ntpd to perform the time sync, the client install can end up hanging >>>>>>> forever when the server is not reachable (firewall issues, etc.). These >>>>>>> patches address the issues in two different ways: >>>>>>> >>>>>>> 1 - Don't attempt to sync time when --no-ntp is specified. >>>>>>> >>>>>>> 2 - Implement a timeout capability that is used when we run ntpd to >>>>>>> perform the time sync to prevent indefinite hanging. >>>>>>> >>>>>>> The one potentially contentious issue is that this introduces a new >>>>>>> dependency on python-subprocess32 to allow us to have timeout support >>>>>>> when using Python 2.x. This is packaged for Fedora, but I don't see it >>>>>>> on RHEL or CentOS currently. It would need to be packaged there. >>>>>>> >>>>>>> https://fedorahosted.org/freeipa/ticket/4842 >>>>>>> >>>>>>> Thanks, >>>>>>> -NGK >>>>>> >>>>>> Thanks for Patches. For the second patch, I would really prefer to avoid new >>>>>> dependency, especially if it's not packaged in RHEL/CentOS. Maybe we could use >>>>>> some workaround instead, as in: >>>>>> >>>>>> http://stackoverflow.com/questions/3733270/python-subprocess-timeout >>>>> >>>>> I don't like having to add an additional dependency either, but the >>>>> alternative seems more risky. Utilizing the subprocess32 module (which >>>>> is really just a backport of the normal subprocess module from Python >>>>> 3.x) is not invasive for our code in ipautil.run(). Adding some sort of >>>>> a thread that has to kill the spawned subprocess seems more risky (see >>>>> the discussion about a race condition in the stackoverflow thread >>>>> above). That said, I'm sure the thread/poll method can be made to work >>>>> if you and others feel strongly that this is a better approach than >>>>> adding a new dependency. >>>> >>>> Why not use /usr/bin/timeout from coreutils? >>> >>> That sounds like a perfectly good idea. I wasn't aware of it's >>> existence (or it's possible that I forgot about it). Thanks for the >>> suggestion! I'll test out a reworked version of the patch. >>> >>> Do you think that there is value in leaving the timeout capability >>> centrally in ipautil.run()? We only need it for the call to 'ntpd' >>> right now, but there might be a need for using a timeout for other >>> commands in the future. The alternative is to just modify >>> synconce_ntp() to use /usr/bin/timeout and leave ipautil.run() alone. >> >> I think it would require a lot of research. One of the programs spawned >> by this is pkicreate which could take quite some time, and spawning a >> clone in particular. >> >> It is definitely an interesting idea but I think it is safest for now to >> limit it to just NTP for now. > > What I meant was that we would have an optional keyword "timeout" > parameter to ipautil.run() that defaults to None, just like my > subprocess32 approach. If a timeout is not passed in, we would use > subprocess.Popen() to run the specified command just like we do today. > We would only actually pass the timeout parameter to ipautil.run() in > synconce_ntp() for now, so no other commands would have a timeout in > effect. The capability would be available for other commands this way > though. > > Let me propose a patch with this implementation, and if you don't like > it, we can leave ipautil.run() alone and restrict the changes to > synconce_ntp(). An updated patch 0002 is attached that uses the approach mentioned above. Thanks, -NGK > >> >> rob >> > > _______________________________________________ > Freeipa-devel mailing list > Freeipa-devel at redhat.com > https://www.redhat.com/mailman/listinfo/freeipa-devel > -------------- next part -------------- A non-text attachment was scrubbed... Name: 0002-Timeout-when-performing-time-sync-during-client-inst.patch Type: text/x-patch Size: 3175 bytes Desc: not available URL: From rcritten at redhat.com Sat Feb 28 21:07:21 2015 From: rcritten at redhat.com (Rob Crittenden) Date: Sat, 28 Feb 2015 16:07:21 -0500 Subject: [Freeipa-devel] [PATCHES 0001-0002] ipa-client-install NTP fixes In-Reply-To: <54F22B7E.30707@redhat.com> References: <54EE84B1.4040206@redhat.com> <54EEDF8A.2090204@redhat.com> <54F0CEF7.8090609@redhat.com> <54F0D191.9060908@redhat.com> <54F0D860.6020501@redhat.com> <54F0DCC3.5080405@redhat.com> <54F0DF13.2030808@redhat.com> <54F22B7E.30707@redhat.com> Message-ID: <54F22E09.6030707@redhat.com> Nathan Kinder wrote: > > > On 02/27/2015 01:18 PM, Nathan Kinder wrote: >> >> >> On 02/27/2015 01:08 PM, Rob Crittenden wrote: >>> Nathan Kinder wrote: >>>> >>>> >>>> On 02/27/2015 12:20 PM, Rob Crittenden wrote: >>>>> Nathan Kinder wrote: >>>>>> >>>>>> >>>>>> On 02/26/2015 12:55 AM, Martin Kosek wrote: >>>>>>> On 02/26/2015 03:28 AM, Nathan Kinder wrote: >>>>>>>> Hi, >>>>>>>> >>>>>>>> The two attached patches address some issues that affect >>>>>>>> ipa-client-install when syncing time from the NTP server. Now that we >>>>>>>> use ntpd to perform the time sync, the client install can end up hanging >>>>>>>> forever when the server is not reachable (firewall issues, etc.). These >>>>>>>> patches address the issues in two different ways: >>>>>>>> >>>>>>>> 1 - Don't attempt to sync time when --no-ntp is specified. >>>>>>>> >>>>>>>> 2 - Implement a timeout capability that is used when we run ntpd to >>>>>>>> perform the time sync to prevent indefinite hanging. >>>>>>>> >>>>>>>> The one potentially contentious issue is that this introduces a new >>>>>>>> dependency on python-subprocess32 to allow us to have timeout support >>>>>>>> when using Python 2.x. This is packaged for Fedora, but I don't see it >>>>>>>> on RHEL or CentOS currently. It would need to be packaged there. >>>>>>>> >>>>>>>> https://fedorahosted.org/freeipa/ticket/4842 >>>>>>>> >>>>>>>> Thanks, >>>>>>>> -NGK >>>>>>> >>>>>>> Thanks for Patches. For the second patch, I would really prefer to avoid new >>>>>>> dependency, especially if it's not packaged in RHEL/CentOS. Maybe we could use >>>>>>> some workaround instead, as in: >>>>>>> >>>>>>> http://stackoverflow.com/questions/3733270/python-subprocess-timeout >>>>>> >>>>>> I don't like having to add an additional dependency either, but the >>>>>> alternative seems more risky. Utilizing the subprocess32 module (which >>>>>> is really just a backport of the normal subprocess module from Python >>>>>> 3.x) is not invasive for our code in ipautil.run(). Adding some sort of >>>>>> a thread that has to kill the spawned subprocess seems more risky (see >>>>>> the discussion about a race condition in the stackoverflow thread >>>>>> above). That said, I'm sure the thread/poll method can be made to work >>>>>> if you and others feel strongly that this is a better approach than >>>>>> adding a new dependency. >>>>> >>>>> Why not use /usr/bin/timeout from coreutils? >>>> >>>> That sounds like a perfectly good idea. I wasn't aware of it's >>>> existence (or it's possible that I forgot about it). Thanks for the >>>> suggestion! I'll test out a reworked version of the patch. >>>> >>>> Do you think that there is value in leaving the timeout capability >>>> centrally in ipautil.run()? We only need it for the call to 'ntpd' >>>> right now, but there might be a need for using a timeout for other >>>> commands in the future. The alternative is to just modify >>>> synconce_ntp() to use /usr/bin/timeout and leave ipautil.run() alone. >>> >>> I think it would require a lot of research. One of the programs spawned >>> by this is pkicreate which could take quite some time, and spawning a >>> clone in particular. >>> >>> It is definitely an interesting idea but I think it is safest for now to >>> limit it to just NTP for now. >> >> What I meant was that we would have an optional keyword "timeout" >> parameter to ipautil.run() that defaults to None, just like my >> subprocess32 approach. If a timeout is not passed in, we would use >> subprocess.Popen() to run the specified command just like we do today. >> We would only actually pass the timeout parameter to ipautil.run() in >> synconce_ntp() for now, so no other commands would have a timeout in >> effect. The capability would be available for other commands this way >> though. >> >> Let me propose a patch with this implementation, and if you don't like >> it, we can leave ipautil.run() alone and restrict the changes to >> synconce_ntp(). > > An updated patch 0002 is attached that uses the approach mentioned above. Looks good. Not to nitpick to death but... Can you add timeout to ipaplatform/base/paths.py as BIN_TIMEOUT = "/usr/bin/timeout" and reference that instead? It's for portability. And a question. I'm impatient. Should there be a notice that it will timeout after n seconds somewhere so people like me don't ^C after 2 seconds? Or is that just overkill and I need to learn patience? Stylistically, should we prefer p.returncode is 15 or p.returncode == 15? rob From nkinder at redhat.com Sat Feb 28 21:13:46 2015 From: nkinder at redhat.com (Nathan Kinder) Date: Sat, 28 Feb 2015 13:13:46 -0800 Subject: [Freeipa-devel] [PATCHES 0001-0002] ipa-client-install NTP fixes In-Reply-To: <54F22E09.6030707@redhat.com> References: <54EE84B1.4040206@redhat.com> <54EEDF8A.2090204@redhat.com> <54F0CEF7.8090609@redhat.com> <54F0D191.9060908@redhat.com> <54F0D860.6020501@redhat.com> <54F0DCC3.5080405@redhat.com> <54F0DF13.2030808@redhat.com> <54F22B7E.30707@redhat.com> <54F22E09.6030707@redhat.com> Message-ID: <54F22F8A.6030505@redhat.com> On 02/28/2015 01:07 PM, Rob Crittenden wrote: > Nathan Kinder wrote: >> >> >> On 02/27/2015 01:18 PM, Nathan Kinder wrote: >>> >>> >>> On 02/27/2015 01:08 PM, Rob Crittenden wrote: >>>> Nathan Kinder wrote: >>>>> >>>>> >>>>> On 02/27/2015 12:20 PM, Rob Crittenden wrote: >>>>>> Nathan Kinder wrote: >>>>>>> >>>>>>> >>>>>>> On 02/26/2015 12:55 AM, Martin Kosek wrote: >>>>>>>> On 02/26/2015 03:28 AM, Nathan Kinder wrote: >>>>>>>>> Hi, >>>>>>>>> >>>>>>>>> The two attached patches address some issues that affect >>>>>>>>> ipa-client-install when syncing time from the NTP server. Now that we >>>>>>>>> use ntpd to perform the time sync, the client install can end up hanging >>>>>>>>> forever when the server is not reachable (firewall issues, etc.). These >>>>>>>>> patches address the issues in two different ways: >>>>>>>>> >>>>>>>>> 1 - Don't attempt to sync time when --no-ntp is specified. >>>>>>>>> >>>>>>>>> 2 - Implement a timeout capability that is used when we run ntpd to >>>>>>>>> perform the time sync to prevent indefinite hanging. >>>>>>>>> >>>>>>>>> The one potentially contentious issue is that this introduces a new >>>>>>>>> dependency on python-subprocess32 to allow us to have timeout support >>>>>>>>> when using Python 2.x. This is packaged for Fedora, but I don't see it >>>>>>>>> on RHEL or CentOS currently. It would need to be packaged there. >>>>>>>>> >>>>>>>>> https://fedorahosted.org/freeipa/ticket/4842 >>>>>>>>> >>>>>>>>> Thanks, >>>>>>>>> -NGK >>>>>>>> >>>>>>>> Thanks for Patches. For the second patch, I would really prefer to avoid new >>>>>>>> dependency, especially if it's not packaged in RHEL/CentOS. Maybe we could use >>>>>>>> some workaround instead, as in: >>>>>>>> >>>>>>>> http://stackoverflow.com/questions/3733270/python-subprocess-timeout >>>>>>> >>>>>>> I don't like having to add an additional dependency either, but the >>>>>>> alternative seems more risky. Utilizing the subprocess32 module (which >>>>>>> is really just a backport of the normal subprocess module from Python >>>>>>> 3.x) is not invasive for our code in ipautil.run(). Adding some sort of >>>>>>> a thread that has to kill the spawned subprocess seems more risky (see >>>>>>> the discussion about a race condition in the stackoverflow thread >>>>>>> above). That said, I'm sure the thread/poll method can be made to work >>>>>>> if you and others feel strongly that this is a better approach than >>>>>>> adding a new dependency. >>>>>> >>>>>> Why not use /usr/bin/timeout from coreutils? >>>>> >>>>> That sounds like a perfectly good idea. I wasn't aware of it's >>>>> existence (or it's possible that I forgot about it). Thanks for the >>>>> suggestion! I'll test out a reworked version of the patch. >>>>> >>>>> Do you think that there is value in leaving the timeout capability >>>>> centrally in ipautil.run()? We only need it for the call to 'ntpd' >>>>> right now, but there might be a need for using a timeout for other >>>>> commands in the future. The alternative is to just modify >>>>> synconce_ntp() to use /usr/bin/timeout and leave ipautil.run() alone. >>>> >>>> I think it would require a lot of research. One of the programs spawned >>>> by this is pkicreate which could take quite some time, and spawning a >>>> clone in particular. >>>> >>>> It is definitely an interesting idea but I think it is safest for now to >>>> limit it to just NTP for now. >>> >>> What I meant was that we would have an optional keyword "timeout" >>> parameter to ipautil.run() that defaults to None, just like my >>> subprocess32 approach. If a timeout is not passed in, we would use >>> subprocess.Popen() to run the specified command just like we do today. >>> We would only actually pass the timeout parameter to ipautil.run() in >>> synconce_ntp() for now, so no other commands would have a timeout in >>> effect. The capability would be available for other commands this way >>> though. >>> >>> Let me propose a patch with this implementation, and if you don't like >>> it, we can leave ipautil.run() alone and restrict the changes to >>> synconce_ntp(). >> >> An updated patch 0002 is attached that uses the approach mentioned above. > > Looks good. Not to nitpick to death but... > > Can you add timeout to ipaplatform/base/paths.py as BIN_TIMEOUT = > "/usr/bin/timeout" and reference that instead? It's for portability. Sure. I was wondering if we should do something around a full path. > > And a question. I'm impatient. Should there be a notice that it will > timeout after n seconds somewhere so people like me don't ^C after 2 > seconds? Or is that just overkill and I need to learn patience? Probably both. :) There's always going to be someone out there who will do ctrl-C, so I think printing out a notice is a good idea. I'll add this. > > Stylistically, should we prefer p.returncode is 15 or p.returncode == 15? After some reading, it seems that '==' should be used. Small integers work with 'is', but '==' is the consistent way that equality of integers should be checked. I'll modify this. Thanks, -NGK > > rob > From nkinder at redhat.com Sat Feb 28 23:02:32 2015 From: nkinder at redhat.com (Nathan Kinder) Date: Sat, 28 Feb 2015 15:02:32 -0800 Subject: [Freeipa-devel] [PATCHES 0001-0002] ipa-client-install NTP fixes In-Reply-To: <54F22F8A.6030505@redhat.com> References: <54EE84B1.4040206@redhat.com> <54EEDF8A.2090204@redhat.com> <54F0CEF7.8090609@redhat.com> <54F0D191.9060908@redhat.com> <54F0D860.6020501@redhat.com> <54F0DCC3.5080405@redhat.com> <54F0DF13.2030808@redhat.com> <54F22B7E.30707@redhat.com> <54F22E09.6030707@redhat.com> <54F22F8A.6030505@redhat.com> Message-ID: <54F24908.2080308@redhat.com> On 02/28/2015 01:13 PM, Nathan Kinder wrote: > > > On 02/28/2015 01:07 PM, Rob Crittenden wrote: >> Nathan Kinder wrote: >>> >>> >>> On 02/27/2015 01:18 PM, Nathan Kinder wrote: >>>> >>>> >>>> On 02/27/2015 01:08 PM, Rob Crittenden wrote: >>>>> Nathan Kinder wrote: >>>>>> >>>>>> >>>>>> On 02/27/2015 12:20 PM, Rob Crittenden wrote: >>>>>>> Nathan Kinder wrote: >>>>>>>> >>>>>>>> >>>>>>>> On 02/26/2015 12:55 AM, Martin Kosek wrote: >>>>>>>>> On 02/26/2015 03:28 AM, Nathan Kinder wrote: >>>>>>>>>> Hi, >>>>>>>>>> >>>>>>>>>> The two attached patches address some issues that affect >>>>>>>>>> ipa-client-install when syncing time from the NTP server. Now that we >>>>>>>>>> use ntpd to perform the time sync, the client install can end up hanging >>>>>>>>>> forever when the server is not reachable (firewall issues, etc.). These >>>>>>>>>> patches address the issues in two different ways: >>>>>>>>>> >>>>>>>>>> 1 - Don't attempt to sync time when --no-ntp is specified. >>>>>>>>>> >>>>>>>>>> 2 - Implement a timeout capability that is used when we run ntpd to >>>>>>>>>> perform the time sync to prevent indefinite hanging. >>>>>>>>>> >>>>>>>>>> The one potentially contentious issue is that this introduces a new >>>>>>>>>> dependency on python-subprocess32 to allow us to have timeout support >>>>>>>>>> when using Python 2.x. This is packaged for Fedora, but I don't see it >>>>>>>>>> on RHEL or CentOS currently. It would need to be packaged there. >>>>>>>>>> >>>>>>>>>> https://fedorahosted.org/freeipa/ticket/4842 >>>>>>>>>> >>>>>>>>>> Thanks, >>>>>>>>>> -NGK >>>>>>>>> >>>>>>>>> Thanks for Patches. For the second patch, I would really prefer to avoid new >>>>>>>>> dependency, especially if it's not packaged in RHEL/CentOS. Maybe we could use >>>>>>>>> some workaround instead, as in: >>>>>>>>> >>>>>>>>> http://stackoverflow.com/questions/3733270/python-subprocess-timeout >>>>>>>> >>>>>>>> I don't like having to add an additional dependency either, but the >>>>>>>> alternative seems more risky. Utilizing the subprocess32 module (which >>>>>>>> is really just a backport of the normal subprocess module from Python >>>>>>>> 3.x) is not invasive for our code in ipautil.run(). Adding some sort of >>>>>>>> a thread that has to kill the spawned subprocess seems more risky (see >>>>>>>> the discussion about a race condition in the stackoverflow thread >>>>>>>> above). That said, I'm sure the thread/poll method can be made to work >>>>>>>> if you and others feel strongly that this is a better approach than >>>>>>>> adding a new dependency. >>>>>>> >>>>>>> Why not use /usr/bin/timeout from coreutils? >>>>>> >>>>>> That sounds like a perfectly good idea. I wasn't aware of it's >>>>>> existence (or it's possible that I forgot about it). Thanks for the >>>>>> suggestion! I'll test out a reworked version of the patch. >>>>>> >>>>>> Do you think that there is value in leaving the timeout capability >>>>>> centrally in ipautil.run()? We only need it for the call to 'ntpd' >>>>>> right now, but there might be a need for using a timeout for other >>>>>> commands in the future. The alternative is to just modify >>>>>> synconce_ntp() to use /usr/bin/timeout and leave ipautil.run() alone. >>>>> >>>>> I think it would require a lot of research. One of the programs spawned >>>>> by this is pkicreate which could take quite some time, and spawning a >>>>> clone in particular. >>>>> >>>>> It is definitely an interesting idea but I think it is safest for now to >>>>> limit it to just NTP for now. >>>> >>>> What I meant was that we would have an optional keyword "timeout" >>>> parameter to ipautil.run() that defaults to None, just like my >>>> subprocess32 approach. If a timeout is not passed in, we would use >>>> subprocess.Popen() to run the specified command just like we do today. >>>> We would only actually pass the timeout parameter to ipautil.run() in >>>> synconce_ntp() for now, so no other commands would have a timeout in >>>> effect. The capability would be available for other commands this way >>>> though. >>>> >>>> Let me propose a patch with this implementation, and if you don't like >>>> it, we can leave ipautil.run() alone and restrict the changes to >>>> synconce_ntp(). >>> >>> An updated patch 0002 is attached that uses the approach mentioned above. >> >> Looks good. Not to nitpick to death but... >> >> Can you add timeout to ipaplatform/base/paths.py as BIN_TIMEOUT = >> "/usr/bin/timeout" and reference that instead? It's for portability. > > Sure. I was wondering if we should do something around a full path. > >> >> And a question. I'm impatient. Should there be a notice that it will >> timeout after n seconds somewhere so people like me don't ^C after 2 >> seconds? Or is that just overkill and I need to learn patience? > > Probably both. :) There's always going to be someone out there who will > do ctrl-C, so I think printing out a notice is a good idea. I'll add this. > >> >> Stylistically, should we prefer p.returncode is 15 or p.returncode == 15? > > After some reading, it seems that '==' should be used. Small integers > work with 'is', but '==' is the consistent way that equality of integers > should be checked. I'll modify this. Another updated patch 0002 is attached that addresses Rob's review comments. Thanks, -NGK -------------- next part -------------- A non-text attachment was scrubbed... Name: 0002-Timeout-when-performing-time-sync-during-client-inst.patch Type: text/x-patch Size: 4001 bytes Desc: not available URL: