Hello community, here is the log from the commit of package openstack-keystone for openSUSE:Factory checked in at 2012-11-20 20:16:31 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/openstack-keystone (Old) and /work/SRC/openSUSE:Factory/.openstack-keystone.new (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "openstack-keystone", Maintainer is "cth...@suse.com" Changes: -------- --- /work/SRC/openSUSE:Factory/openstack-keystone/openstack-keystone.changes 2012-01-25 15:02:32.000000000 +0100 +++ /work/SRC/openSUSE:Factory/.openstack-keystone.new/openstack-keystone.changes 2012-11-20 20:16:34.000000000 +0100 @@ -2 +2 @@ -Mon Jan 23 11:24:03 UTC 2012 - prus...@opensuse.org +Thu Nov 8 13:50:26 UTC 2012 - sasc...@suse.de @@ -4 +4,284 @@ -- fix ifdef around Source0 +- Fix malformed changes file entries + +------------------------------------------------------------------- +Thu Nov 8 13:30:05 UTC 2012 - sasc...@suse.de + +- Drop from_vcs build flag + +------------------------------------------------------------------- +Tue Oct 16 11:08:47 CEST 2012 - iartar...@suse.cz + +- patch sql backend's from_dict method to not modify the content of the + passed in dict (lp:1066851) + +------------------------------------------------------------------- +Wed Oct 10 14:56:49 CEST 2012 - iartar...@suse.cz + + - add hybrid backend test configuration file + +------------------------------------------------------------------- +Wed Oct 10 14:10:43 CEST 2012 - iartar...@suse.cz + + - make user search ldap SCOPE configurable in the hybrid backend + +------------------------------------------------------------------- +Mon Oct 8 14:38:58 CEST 2012 - iartar...@suse.cz + + - fix LDAP bind with dinamically found user DN + +------------------------------------------------------------------- +Fri Oct 5 12:46:20 CEST 2012 - iartar...@suse.cz + + - hybrid backend fixes: + + use the DN for the user we just signed in to check for password + + fix invalid user error (bnc#783200) + +------------------------------------------------------------------- +Tue Oct 2 13:57:41 CEST 2012 - iartar...@suse.cz + + - fix checking for SQL user passwords in the hybrid backend bnc#783036 + +------------------------------------------------------------------- +Mon Oct 1 09:26:15 UTC 2012 - jenk...@suse.de + +- Update to latest git (0e1f05e): + + utf-8 encode user keys in memcache (bug 1056373) + +------------------------------------------------------------------- +Wed Sep 26 09:57:47 UTC 2012 - j...@suse.de + +- make init script start after database (bnc#781798) + +------------------------------------------------------------------- +Sun Sep 16 18:24:07 UTC 2012 - jenk...@suse.de + +- Update to latest git (176ee9b): + + Require authz to update user's tenant (bug 1040626) + + List tokens for memcached backend + + Delete user tokens after role grant/revoke + + Limit token revocation to tenant (bug 1050025) + +------------------------------------------------------------------- +Wed Sep 12 11:07:31 UTC 2012 - vu...@suse.com + +- Do not use a sed to modify /usr/bin/gensslcert in %post: this + file belongs to another package, and we actually don't do any + change with the sed... Fix bnc#779747. + +------------------------------------------------------------------- +Mon Sep 10 08:26:18 UTC 2012 - vu...@suse.com + +- Add keystone-fix-revoke.patch: fix revoking of roles to also + invalidate already existing tokens. Fix bnc#779477, + CVE-2012-4413. + +------------------------------------------------------------------- +Tue Aug 28 21:40:12 UTC 2012 - cth...@suse.com + +- Require authz to update user's tenant (lp#1040626, bnc#777664) + +------------------------------------------------------------------- +Fri Aug 24 13:44:39 UTC 2012 - bwiedem...@suse.com + +- mark hybrid_config.py as config + +------------------------------------------------------------------- +Thu Aug 23 09:08:38 UTC 2012 - jenk...@suse.de + +- Update to latest git (a16a0ab): + + Allow overloading of username and tenant name in the config files. + + Returning roles from authenticate in ldap backend + +------------------------------------------------------------------- +Tue Aug 14 17:40:08 CEST 2012 - iartar...@suse.cz + +- Add hybrid identity backend + +------------------------------------------------------------------- +Tue Aug 14 14:22:19 CEST 2012 - iartar...@suse.cz + +- Add patch to log all Unauthorized exceptions (authentication + failures). Discussed in bnc#753329. + +------------------------------------------------------------------- +Fri Aug 10 22:08:44 UTC 2012 - jenk...@suse.de + +- Update to latest git (359c426): + + Open 2012.1.3 development + +------------------------------------------------------------------- +Wed Aug 8 14:12:01 UTC 2012 - jenk...@suse.de + +- Update to latest git (afc37ae): + + Open 2012.1.2 development + +------------------------------------------------------------------- +Tue Aug 7 11:53:19 UTC 2012 - bwiedem...@suse.com + +- drop executable bit from config file + +------------------------------------------------------------------- +Tue Jul 31 22:12:53 UTC 2012 - jenk...@suse.de + +- Update to latest git (f65604d): + + fix variable names to coincide with the ones in common.ldap + + Import ec2 credentials from old keystone db + + Raise unauthorized if tenant disabled (bug 988920) + +------------------------------------------------------------------- +Tue Jul 31 15:56:43 CEST 2012 - iartar...@suse.cz + +- Remove fix-ldap-varnames patch after being accepted upstream + +------------------------------------------------------------------- +Wed Jul 25 11:23:57 UTC 2012 - sasc...@suse.de + +- Secure file permissions for Apache SSL certificate files + +------------------------------------------------------------------- +Thu Jul 19 20:20:59 UTC 2012 - cth...@suse.com + +- drop keystone-cleanup-user-tenant-deletion.patch, which has been merged + upstream: https://review.openstack.org/#/c/7482/ + +------------------------------------------------------------------- +Tue Jul 17 13:02:33 UTC 2012 - sasc...@suse.de + +- Fix WSGI app names, use the 'composite' apps to get the correct + routes mapping + +------------------------------------------------------------------- +Tue Jul 17 12:18:32 UTC 2012 - sasc...@suse.de + +- Forward keystone WSGI log events to mod_wsgi + +------------------------------------------------------------------- +Tue Jul 17 11:05:49 CEST 2012 - iartar...@suse.cz + +- Fix some variable names in the LDAP backend which were causing + NameErrors +- Don't require authentication for LDAP + +------------------------------------------------------------------- +Mon Jul 16 14:22:53 CEST 2012 - iartar...@suse.cz + +- Fix bnc#755426 cleanup dependent data upon user/tenant deletion + +------------------------------------------------------------------- +Mon Jul 9 14:50:53 UTC 2012 - sasc...@suse.de + +- Provide Apache2 SSL-proxy example configuration based on mod_wsgi +- Provide self-signed SSL certificates to be used for non-production setups + (like openstack-quickstart) +- Fix /var/lib/keystone permissions to 0755 + +------------------------------------------------------------------- +Mon Jul 2 12:33:42 UTC 2012 - sasc...@suse.de + +- Drop runtime requirement on openstack-glance +- Change requirement for openstack-swift to python-swift, keystone + only seems to use it in the S3-compatibility code +- Change requirement for openstack-nova to python-nova, keystone + only seems to use it in the EC2-compatibility code + +------------------------------------------------------------------- +Wed Jun 27 10:29:24 UTC 2012 - sasc...@suse.de + +- Change versioning scheme to $release+git.$AUTHORDATE.$COMMITREV +- Simplify from_vcs macros + +------------------------------------------------------------------- +Wed Jun 27 10:35:56 CEST 2012 - vu...@suse.com + +- Really drop unused disable-tests.patch: not needed anymore. ++++ 91 more lines (skipped) ++++ between /work/SRC/openSUSE:Factory/openstack-keystone/openstack-keystone.changes ++++ and /work/SRC/openSUSE:Factory/.openstack-keystone.new/openstack-keystone.changes Old: ---- openstack-keystone-2011.3-0-g61148fb.tar.gz openstack-keystone-auth-access.diff openstack-keystone.patch New: ---- _service backend_hybrid.conf default_catalog.templates hybrid-backend.py hybrid-config.py keystone-2012.1+git.1348675554.0e1f05e.tar.gz keystone-hybrid-conf-scope.patch keystone-ldap-no-authentication.patch keystone-log-warn-auth-errors.patch keystone-sql-backend-from_dict.patch logging.conf openstack-keystone.conf.sample openstack-keystone.wsgi rpmlintrc test_backend_hybrid.py ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ openstack-keystone.spec ++++++ --- /var/tmp/diff_new_pack.at2Md4/_old 2012-11-20 20:16:36.000000000 +0100 +++ /var/tmp/diff_new_pack.at2Md4/_new 2012-11-20 20:16:36.000000000 +0100 @@ -1,7 +1,7 @@ # # spec file for package openstack-keystone # -# Copyright (c) 2011 SUSE LINUX Products GmbH, Nuernberg, Germany. +# Copyright (c) 2012 SUSE LINUX Products GmbH, Nuernberg, Germany. # Copyright (c) 2011 B1 Systems GmbH, Vohburg, Germany. # # All modifications and additions to the file contributed by third parties @@ -15,157 +15,182 @@ # Please submit bugfixes or comments via http://bugs.opensuse.org/ # -# options to forcefully build with/without vcs -%bcond_with from_vcs -%bcond_with no_from_vcs -%if %{with from_vcs} -%define BUILD_FROM_VCS 1 -%endif - -%if %{with no_from_vcs} -%define BUILD_FROM_VCS 0 -%endif - -# disable doc build on 12.1+ because of sphinx segfault -%if 0%{?suse_version} >= 1210 -%global with_doc 0 -%else -%global with_doc 1 -%endif - -%{!?python_sitelib: %global python_sitelib %(python -c "from distutils.sysconfig import get_python_lib; print(get_python_lib())")} -# fix broken macro in older distros -%{!?_initddir: %global _initddir %{_initrddir}} - -%if 0%{?BUILD_FROM_VCS} -%define vcs_summary_warn -- Git Snapshot -%else -%define vcs_summary_warn %null -%endif -%define shortname keystone -# for special number in current tarball source version -%define rversion 2011.3-0-g61148fb - -Name: openstack-keystone -Version: 2011.3.1 -%if 0%{?BUILD_FROM_VCS} -%define _version git-master -%else -%define _version %{rversion} -%endif -Release: 1.0 +%define component keystone +%define groupname openstack-%{component} +%define username openstack-%{component} + +Name: openstack-%{component} +Version: 2012.1+git.1348675554.0e1f05e +Release: 0 +Summary: OpenStack Identity Service (Keystone) License: Apache-2.0 -Summary: OpenStack Identity Service %{name} %{vcs_summary_warn} -Url: http://github.com/rackspace/keystone Group: Development/Languages/Python -%if 0%{?BUILD_FROM_VCS} -%define srcname keystone -%else -%define srcname openstack-keystone -%endif -Source0: %{srcname}-%{_version}.tar.gz +Url: https://github.com/openstack/keystone +Source: %{component}-%{version}.tar.gz Source1: %{name}.init -Patch0: %{name}.patch -Patch2: openstack-keystone-auth-access.diff +Source2: logging.conf +Source3: default_catalog.templates +# Apache2 SSL proxy example configuration: +Source4: openstack-keystone.conf.sample +# WSGI application skeleton for public and admin API apps (for the SSL proxy): +Source5: openstack-keystone.wsgi +# Hybrid identity backend - uses the existing LDAP backend for users and +# the SQL backend for tenants/roles +Source6: hybrid-backend.py +Source7: hybrid-config.py +Source8: test_backend_hybrid.py +Source9: backend_hybrid.conf +# Don't require authentication for LDAP, since some LDAP servers don't need it +# Accepted upstream in master +# https://review.openstack.org/#q,I67f64e6eb19e7a96c0e50f99e57c7fee613df909,n,z +Patch3: keystone-ldap-no-authentication.patch +# LOG.warn all exception.Unauthorized authentication failures +# Upstream Folsom review (probably not important enough for an essex backport) +# https://review.openstack.org/#q,I45cf27cb6e702a0470243efdb310eb0a65cfd2b0,n,z +Patch4: keystone-log-warn-auth-errors.patch +# Add a configuration option for reading the ldap user_scope (part of +# the hybrid backend) +Patch5: keystone-hybrid-conf-scope.patch +# Don't modify the passed in dict to the sql backend's from_dict method +# (fixes our testsuite) https://review.openstack.org/14472 +Patch6: keystone-sql-backend-from_dict.patch +BuildRequires: apache2 BuildRequires: fdupes -%if 0%{?suse_version} +BuildRequires: python-PasteScript +BuildRequires: python-Sphinx +BuildRequires: python-argparse +BuildRequires: python-dateutil BuildRequires: python-devel BuildRequires: python-distribute -%endif -%if 0%{?fedora} -BuildRequires: python2-devel -%endif -BuildRoot: %{_tmppath}/%{name}-%{version}-build - +BuildRequires: python-eventlet +BuildRequires: python-ldap +BuildRequires: python-lxml +BuildRequires: python-mox +BuildRequires: python-passlib +BuildRequires: python-prettytable +BuildRequires: python-routes +BuildRequires: python-sqlalchemy-migrate +BuildRequires: python-unittest2 +BuildRequires: python-webob +# all the packages below are only BuildRequired for the documentation +BuildRequires: openstack-glance +BuildRequires: openstack-nova +BuildRequires: openstack-swift +BuildRequires: python-crypto +# for memcache backend (this is optional?) +BuildRequires: memcached +BuildRequires: python-python-memcached +Requires: python >= 2.6.8 +Requires: python-nova +Requires: python-swift +# for memcache backend (this is optional?) +Requires: memcached +Requires: python-crypto Requires: python-keystone = %{version} - +Requires: python-python-memcached +BuildRoot: %{_tmppath}/%{name}-%{version}-build +# To generate a self-signed certificate to be used in demo setups: +Requires(post): apache2-utils +Requires(post): openssl +Requires(post): sysconfig %if 0%{?suse_version} > 1110 Requires(pre): pwdutils %else Requires(pre): shadow-utils %endif - %if 0%{?fedora} Requires(pre): shadow-utils %endif - -%define sharedstatedir %{_localstatedir}/lib +%if 0%{?suse_version} && 0%{?suse_version} <= 1110 +%{!?python_sitelib: %global python_sitelib %(python -c "from distutils.sysconfig import get_python_lib; print get_python_lib()")} +%else +BuildArch: noarch +%endif %description -Keystone is a proposed independent authentication service for OpenStack. +Keystone is an OpenStack project that provides Identity, Token, Catalog +and Policy services for use specifically by projects in the OpenStack +family. -This initial proof of concept aims to address the current use cases in Swift and Nova which are: - -REST-based, token auth for Swift -many-to-many relationship between identity and tenant for Nova. - -# ----- python-keystone %package -n python-keystone -Summary: OpenStack python-keystone %{vcs_summary_warn} -Group: Development/Languages/Python +Summary: OpenStack Identity Service (Keystone) - Python module +Group: Development/Languages/Python +Requires: curl +Requires: python >= 2.6.8 +Requires: python-Paste +Requires: python-PasteScript +Requires: python-argparse +Requires: python-dateutil Requires: python-eventlet Requires: python-httplib2 -Requires: python-passlib -Requires: python-lxml Requires: python-ldap -Requires: python-memcached +Requires: python-lxml +Requires: python-passlib +Requires: python-pastedeploy +Requires: python-prettytable Requires: python-routes -Requires: python-sqlalchemy +Requires: python-sqlalchemy-migrate Requires: python-webob -%if 0%{?suse_version} -Requires: curl -Requires: python-boto -Requires: python-Paste -Requires: python-pastedeploy -Requires: python-PasteScript -Requires: python-pysqlite -%endif -%if 0%{?fedora} -Requires: python-paste -Requires: python-paste-deploy -Requires: python-paste-script -Requires: python-sqlite2 -%endif %description -n python-keystone -This package contains the python files for keystone for OpenStack. +Keystone is an OpenStack project that provides Identity, Token, Catalog +and Policy services for use specifically by projects in the OpenStack +family. -# ----- doc -%if 0%{?with_doc} +This package contains the core Python module of OpenStack Keystone. %package doc -Summary: Documentation for %{name} %{vcs_summary_warn} -Group: Documentation/HTML +Summary: OpenStack Identity Service (Keystone) - Documentation +Group: Documentation/HTML %if 0%{?suse_version} -BuildRequires: python-Sphinx +BuildRequires: python-sphinx %endif %if 0%{?fedora} BuildRequires: python-sphinx >= 1.0 %endif %description doc -Keystone is a proposed independent authentication service for OpenStack. +Keystone is an OpenStack project that provides Identity, Token, Catalog +and Policy services for use specifically by projects in the OpenStack +family. -This initial proof of concept aims to address the current use cases in Swift and Nova which are: +This package contains documentation files for openstack-keystone. -REST-based, token auth for Swift -many-to-many relationship between identity and tenant for Nova. +%package test -This package contains documentation files for %{name}. -%endif +Summary: Testsuite for the OpenStack Keystone +Group: Development/Languages/Python +Requires: %{name} = %{version} +Requires: git-core >= 1.6.4 +Requires: python-ldap +Requires: python-lxml +Requires: python-nose +Requires: python-passlib +Requires: python-python-memcached +Requires: python-unittest2 + +%description test +The OpenStack Keystone testsuite. It is used to verify the +functionality of OpenStack Keystone. %prep -%if 0%{?BUILD_FROM_VCS} -%setup -q -n keystone-%{_version} -%else -%setup -q -n openstack-keystone-61148fb/ -%patch0 -%patch2 -%endif +%setup -q -n %{component}-%{version} + +%patch3 -p1 +%patch4 -p1 +%patch5 -p0 +%patch6 -p1 + +# Fix non-executable scripts in Python site-arch: +sed -i "s|!/usr/bin/env python||" keystone/common/sql/migrate_repo/manage.py +# set the sqlite3 path to /var/lib/keystone +sed -i 's%^connection =.*%connection = sqlite:////var/lib/keystone/keystone.db%' etc/keystone.conf +# Avoid usage of 'git' to generate timestamps: +sed -i "s|html_last_updated_fmt = .*|html_last_updated_fmt = '%{version}'|" doc/source/conf.py %build python setup.py build @@ -173,85 +198,125 @@ %install python setup.py install --prefix=%{_prefix} --root=%{buildroot} -%if 0%{?with_doc} -export PYTHONPATH="$( pwd ):$PYTHONPATH" +# Build documentation +#export PYTHONPATH="$( pwd ):$PYTHONPATH" pushd doc -sphinx-build -b html source build/html +make autodoc +make html +make man popd -%endif - -# Fix hidden-file-or-dir warning -find %{buildroot} -name "*.placeholder" | xargs rm -%if 0%{?with_doc} -rm -rf doc/build/html/.doctrees doc/build/html/.buildinfo -%endif +rm -rf doc/build/html/{.buildinfo,.doctrees} +install -d %{buildroot}%{_mandir}/man1 +install -m 644 doc/build/man/keystone.1 %{buildroot}%{_mandir}/man1 install -d -m 0755 %{buildroot}%{_sysconfdir}/keystone -cp etc/* %{buildroot}%{_sysconfdir}/keystone/ +cp %{SOURCE2} %{buildroot}%{_sysconfdir}/keystone/ +cp %{SOURCE3} %{buildroot}%{_sysconfdir}/keystone/default_catalog.templates.sample +sed -i -e 's,^template_file .*,template_file = /etc/keystone/default_catalog.templates,' etc/keystone.conf +cp etc/keystone.conf %{buildroot}%{_sysconfdir}/keystone/ +cp etc/policy.json %{buildroot}%{_sysconfdir}/keystone/ -install -p -D -m 755 %{SOURCE1} %{buildroot}%{_initrddir}/%{name} +install -p -D -m 755 %{SOURCE1} %{buildroot}%{_initddir}/%{name} mkdir -p %{buildroot}%{_sbindir} -ln -s ../..%{_initrddir}/%{name} %{buildroot}%{_sbindir}/rc%{name} +ln -s ../..%{_initddir}/%{name} %{buildroot}%{_sbindir}/rc%{name} install -d -m 755 %{buildroot}%{_localstatedir}/lib/keystone install -d -m 755 %{buildroot}%{_localstatedir}/log/keystone +# test subpackage +install -d -m 755 %{buildroot}%{_localstatedir}/lib/openstack-keystone-test +cp -av run_tests* tests etc %{buildroot}%{_localstatedir}/lib/openstack-keystone-test +rm -rf %{buildroot}%{_localstatedir}/lib/openstack-keystone-test/keystone +ln -s %{python_sitelib}/keystone %{buildroot}%{_localstatedir}/lib/openstack-keystone-test/keystone +# we would need git to test this, but we don't want care if all the +# authors are in the AUTHORS file anyway so we just disable it +rm -rf %{buildroot}%{_localstatedir}/lib/openstack-keystone-test/tests/test_authors.py +%fdupes %{buildroot}%{_localstatedir}/lib/openstack-keystone-test + # Fix files-duplicate warning %if 0%{?suse_version} %fdupes %{buildroot}%{python_sitelib} -%if 0%{?with_doc} %fdupes -s doc/build/html/ %endif -%endif + +# Apache2 SSL certificate stubs (generated in %%post) +install -d %{buildroot}%{_sysconfdir}/apache2/ssl.{crt,csr,key} +install -d %{buildroot}/srv/www/htdocs +touch %{buildroot}%{_sysconfdir}/apache2/ssl.key/openstack-keystone-{ca,server}.key +touch %{buildroot}%{_sysconfdir}/apache2/ssl.csr/openstack-keystone-server.csr +touch %{buildroot}%{_sysconfdir}/apache2/ssl.crt/openstack-keystone-{ca,server}.crt + +# Apache2 sample configuration +install -m 644 -D %{SOURCE4} %{buildroot}%{_sysconfdir}/apache2/conf.d/openstack-keystone.conf.sample + +# Apache2 WSGI apps +install -D %{SOURCE5} %{buildroot}%{_localstatedir}/lib/keystone/wsgi/admin.wsgi +install -D %{SOURCE5} %{buildroot}%{_localstatedir}/lib/keystone/wsgi/main.wsgi + +#TODO: Maybe move Apache2-related stuff + certs into sub-package? + +# Keystone hybrid identity backend +install -D -m 644 %{SOURCE6} %{buildroot}%{python_sitelib}/keystone/identity/backends/hybrid.py +install -m 644 %{SOURCE7} %{buildroot}%{python_sitelib}/keystone/identity/backends/hybrid_config.py +install -D -m 644 %{SOURCE8} %{buildroot}%{_localstatedir}/lib/openstack-keystone-test/tests/test_backend_hybrid.py +install -D -m 644 %{SOURCE9} %{buildroot}%{_localstatedir}/lib/openstack-keystone-test/tests/backend_hybrid.conf %pre -getent group keystone >/dev/null || groupadd -r keystone -getent passwd keystone >/dev/null || useradd -r -g keystone -d %{sharedstatedir}/keystone -s /sbin/nologin -c "OpenStack keystone Daemon" keystone +getent group %{groupname} >/dev/null || groupadd -r %{groupname} +getent passwd %{username} >/dev/null || useradd -r -g %{groupname} -d %{_localstatedir}/lib/keystone -s /sbin/nologin -c "OpenStack keystone Daemon" %{username} exit 0 %post +if [ ! -s %{_sysconfdir}/apache2/ssl.csr/openstack-keystone-server.csr ] ; then + # Generate a self-signed certificate to be used in non-production Dashboard setups: + (umask 377 ; /usr/bin/gensslcert -C openstack-keystone -n keystone.example.com) +fi %fillup_and_insserv %{name} %restart_on_update %{name} +#TODO/FIXME: These fixups can be removed once we release 1.0, we don't support updates from Betas anyway: +chown %{username}:%{groupname} /var/lib/keystone /var/log/keystone +chmod 0755 /var/lib/keystone +chown -R root:%{groupname} %{_sysconfdir}/keystone/*.conf # Migrating from group 'keystone' %preun %stop_on_removal %{name} %postun +%restart_on_update openstack-keystone %insserv_cleanup -%clean -rm -rf %{buildroot} - %files %defattr(-,root,root) -%dir %{_sysconfdir}/keystone -%dir %attr(0755, keystone, root) %{_localstatedir}/lib/keystone -%dir %attr(0755, keystone, root) %{_localstatedir}/log/keystone -%config(noreplace) %{_sysconfdir}/keystone/keystone.conf -%config(noreplace) %{_sysconfdir}/keystone/logging.cnf -%if 0%{?BUILD_FROM_VCS} -%config(noreplace) %{_sysconfdir}/keystone/memcache.conf -%config(noreplace) %{_sysconfdir}/keystone/ssl.conf -%endif -%{_initrddir}/%{name} +%dir %attr(0755, %{username}, %{groupname}) %{_localstatedir}/lib/keystone +%dir %attr(0750, %{username}, %{groupname}) %{_localstatedir}/log/keystone +%dir %attr(0750, root, %{groupname}) %{_sysconfdir}/keystone +%config(noreplace) %attr(0640, root, %{groupname}) %{_sysconfdir}/keystone/keystone.conf +%config(noreplace) %attr(0640, root, %{groupname}) %{_sysconfdir}/keystone/logging.conf +%config(noreplace) %{_sysconfdir}/keystone/policy.json +%attr(0640, %{username}, %{groupname}) %{_sysconfdir}/keystone/default_catalog.templates.sample +%{_initddir}/%{name} %{_sbindir}/rc%{name} -%{_bindir}/keystone -%{_bindir}/keystone-admin -%{_bindir}/keystone-auth +%{_bindir}/keystone-all %{_bindir}/keystone-manage -%if 0%{?BUILD_FROM_VCS} -%{_bindir}/keystone-control -%{_bindir}/keystone-import -%endif +%{_mandir}/man1/keystone.* +%ghost %{_sysconfdir}/apache2/ssl.key/openstack-keystone-*.key +%ghost %{_sysconfdir}/apache2/ssl.csr/openstack-keystone-server.csr +%ghost %{_sysconfdir}/apache2/ssl.crt/openstack-keystone-*.crt +%{_sysconfdir}/apache2/conf.d/openstack-keystone.conf.sample +%dir %attr(0755, root, root) %{_localstatedir}/lib/keystone/wsgi +%attr(0644, root, root) %{_localstatedir}/lib/keystone/wsgi/*.wsgi %files -n python-keystone %defattr(-,root,root,-) %{python_sitelib} +%config %{python_sitelib}/keystone/identity/backends/hybrid_config.py %doc LICENSE -%if 0%{?with_doc} %files doc %defattr(-,root,root,-) %doc LICENSE doc/build/html -%endif + +%files test +%defattr(-,root,root) +%{_localstatedir}/lib/openstack-keystone-test %changelog ++++++ _service ++++++ <services> <service name="tar_scm" mode="disabled"> <param name="url">git://github.com/openstack/keystone.git</param> <param name="scm">git</param> <param name="exclude">.git</param> <param name="exclude">.gitreview</param> <param name="version">git-stable</param> <param name="versionformat">2012.1+git.%ct.%h</param> <!-- Use this if you want a specific revision. --> <param name="revision">stable/essex</param> </service> <service name="recompress" mode="disabled"> <param name="file">keystone-*git*.tar</param> <param name="compression">gz</param> </service> <service name="set_version" mode="disabled"> <param name="basename">keystone</param> </service> </services> ++++++ backend_hybrid.conf ++++++ [sql] connection = sqlite:///test.db idle_timeout = 200 [ldap] url = fake://memory user = cn=Admin password = password backend_entities = ['Tenant', 'User', 'UserRoleAssociation', 'Role'] tree_dn = cn=example,cn=com [identity] driver = keystone.identity.backends.hybrid.Identity ++++++ default_catalog.templates ++++++ # config for TemplatedCatalog, using camelCase because I don't want to do # translations for legacy compat catalog.RegionOne.identity.publicURL = http://%SERVICE_HOST%:$(public_port)s/v2.0 catalog.RegionOne.identity.adminURL = http://%SERVICE_HOST%:$(admin_port)s/v2.0 catalog.RegionOne.identity.internalURL = http://%SERVICE_HOST%:$(public_port)s/v2.0 catalog.RegionOne.identity.name = Identity Service catalog.RegionOne.compute.publicURL = http://%SERVICE_HOST%:8774/v2/$(tenant_id)s catalog.RegionOne.compute.adminURL = http://%SERVICE_HOST%:8774/v2/$(tenant_id)s catalog.RegionOne.compute.internalURL = http://%SERVICE_HOST%:8774/v2/$(tenant_id)s catalog.RegionOne.compute.name = Compute Service catalog.RegionOne.volume.publicURL = http://%SERVICE_HOST%:8776/v1/$(tenant_id)s catalog.RegionOne.volume.adminURL = http://%SERVICE_HOST%:8776/v1/$(tenant_id)s catalog.RegionOne.volume.internalURL = http://%SERVICE_HOST%:8776/v1/$(tenant_id)s catalog.RegionOne.volume.name = Volume Service catalog.RegionOne.ec2.publicURL = http://%SERVICE_HOST%:8773/services/Cloud catalog.RegionOne.ec2.adminURL = http://%SERVICE_HOST%:8773/services/Admin catalog.RegionOne.ec2.internalURL = http://%SERVICE_HOST%:8773/services/Cloud catalog.RegionOne.ec2.name = EC2 Service catalog.RegionOne.s3.publicURL = http://%SERVICE_HOST%:%S3_SERVICE_PORT% catalog.RegionOne.s3.adminURL = http://%SERVICE_HOST%:%S3_SERVICE_PORT% catalog.RegionOne.s3.internalURL = http://%SERVICE_HOST%:%S3_SERVICE_PORT% catalog.RegionOne.s3.name = S3 Service catalog.RegionOne.image.publicURL = http://%SERVICE_HOST%:9292/v1 catalog.RegionOne.image.adminURL = http://%SERVICE_HOST%:9292/v1 catalog.RegionOne.image.internalURL = http://%SERVICE_HOST%:9292/v1 catalog.RegionOne.image.name = Image Service ++++++ hybrid-backend.py ++++++ # vim: tabstop=4 shiftwidth=4 softtabstop=4 # Copyright 2012 SUSE Linux Products GmbH # Copyright 2012 OpenStack LLC # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. """Hybrid Identity backend for Keystone on top of the LDAP and SQL backends""" from __future__ import absolute_import import ldap from ldap import filter as ldap_filter from keystone import config from keystone import exception from keystone.common import sql from keystone.common import utils from keystone.identity.backends.ldap import UserApi from keystone.identity.backends.sql import User from keystone.identity.backends.sql import Identity as SQLIdentity from keystone.identity.backends.hybrid_config import tenants_for_user def _filter_user(user_ref): if user_ref: user_ref.pop('password', None) return user_ref class Identity(SQLIdentity): def __init__(self, *args, **kwargs): super(Identity, self).__init__(*args, **kwargs) self.user = UserApi(config.CONF) # Identity interface def authenticate(self, user_id=None, tenant_id=None, password=None): """Authenticate based on a user, tenant and password. Expects the user object to have a password field and the tenant to be in the list of tenants on the user. """ user_ref = self._get_user(user_id) # if the user_ref has a password, it's from the SQL backend and # we can just check if it coincides with the one we got try: assert utils.check_password(password, user_ref['password']) except TypeError: raise AssertionError('Invalid user / password') except KeyError: # if it doesn't have a password, it must be LDAP try: # get_connection does a bind for us which checks the password assert self.user.get_connection(self.user_dn, password) except Exception: raise AssertionError('Invalid user / password') tenants = self.get_tenants_for_user(user_id) if tenant_id and tenant_id not in tenants: raise AssertionError('Invalid tenant') tenant_ref = self.get_tenant(tenant_id) if tenant_ref: metadata_ref = self.get_metadata(user_id, tenant_id) else: metadata_ref = {} return (_filter_user(user_ref), tenant_ref, metadata_ref) def _get_user(self, user_id): # try SQL first session = self.get_session() user_ref = session.query(User).filter_by(id=user_id).first() if user_ref: return user_ref.to_dict() # then try LDAP conn = self.user.get_connection() query = '(objectClass=%s)' % self.user.object_class try: users = conn.search_s(self.user_dn, ldap.SCOPE_BASE, query) except (AttributeError, ldap.NO_SUCH_OBJECT): return None if users: return self.user._ldap_res_to_model(users[0]) def get_user(self, user_id): user_ref = self._get_user(user_id) if not user_ref: return None return _filter_user(user_ref) def get_user_by_name(self, user_name): # try SQL first session = self.get_session() user_ref = session.query(User).filter_by(name=user_name).first() if user_ref: return _filter_user(user_ref.to_dict()) # then try LDAP conn = self.user.get_connection() query = '(&(objectClass=%s)(%s=%s))' % ( self.user.object_class, self.user.attribute_mapping['name'], ldap_filter.escape_filter_chars(user_name)) try: users = conn.search_s(self.user.tree_dn, config.CONF.ldap.user_search_scope, query) except ldap.NO_SUCH_OBJECT: return None if not users: return None user_ref = self.user._ldap_res_to_model(users[0]) # the DN is the first element in the returned user tuple self.user_dn = users[0][0] return _filter_user(user_ref) def get_tenants_for_user(self, user_id): session = self.get_session() return tenants_for_user(session, user_id) ++++++ hybrid-config.py ++++++ # vim: tabstop=4 shiftwidth=4 softtabstop=4 # Copyright 2012 SUSE Linux Products GmbH # Copyright 2012 OpenStack LLC # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from keystone.identity.backends.sql import UserTenantMembership, Tenant DEFAULT_TENANT = 'admin' def tenants_for_user(session, user_id): """Return a list of tenant ids associated with a user :param session: SQLAlchemy database session object (from the SQL backend) :param user_id: a user_id string which identifies a User in the LDAP backend """ # get the normal tenants from the database membership_refs = session.query(UserTenantMembership)\ .filter_by(user_id=user_id)\ .all() tenant_ids = [x.tenant_id for x in membership_refs] # all users are also automatically part of this tenant try: tenant_ids.append(session.query(Tenant)\ .filter_by(name=DEFAULT_TENANT).first().id) except AttributeError: pass return tenant_ids ++++++ keystone-hybrid-conf-scope.patch ++++++ --- keystone/config.py.orig 2012-10-10 13:35:55.000000000 +0200 +++ keystone/config.py 2012-10-10 11:28:00.000000000 +0200 @@ -168,6 +168,7 @@ register_str('user_objectclass', group='ldap') register_str('user_id_attribute', group='ldap') register_str('user_name_attribute', group='ldap', default='sn') +register_int('user_search_scope', group='ldap', default=1) register_str('tenant_tree_dn', group='ldap') register_str('tenant_objectclass', group='ldap') ++++++ keystone-ldap-no-authentication.patch ++++++ >From 33104d2cc27011f2e36104c21f721df36cdfd21b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ionu=C8=9B=20Ar=C8=9B=C4=83ri=C8=99i?= <iartar...@suse.cz> Date: Fri, 29 Jun 2012 13:57:42 +0200 Subject: [PATCH] don't assume that the LDAP server require authentication Change-Id: I67f64e6eb19e7a96c0e50f99e57c7fee613df909 --- keystone/common/ldap/core.py | 6 +++++- 1 files changed, 5 insertions(+), 1 deletions(-) diff --git a/keystone/common/ldap/core.py b/keystone/common/ldap/core.py index 2734ce4..b450dac 100644 --- a/keystone/common/ldap/core.py +++ b/keystone/common/ldap/core.py @@ -107,7 +107,11 @@ class BaseLdap(object): if password is None: password = self.LDAP_PASSWORD - conn.simple_bind_s(user, password) + # not all LDAP servers require authentication, so we don't bind + # if we don't have any user/pass + if user and password: + conn.simple_bind_s(user, password) + return conn def _id_to_dn(self, id): -- 1.7.3.4 ++++++ keystone-log-warn-auth-errors.patch ++++++ diff -rupN keystone-2012.1+git.1344578088.359c426.orig//keystone/common/wsgi.py keystone-2012.1+git.1344578088.359c426/keystone/common/wsgi.py --- keystone-2012.1+git.1344578088.359c426.orig//keystone/common/wsgi.py 2012-08-14 14:52:54.000000000 +0200 +++ keystone-2012.1+git.1344578088.359c426/keystone/common/wsgi.py 2012-08-14 14:51:17.000000000 +0200 @@ -182,6 +182,10 @@ class Application(BaseApplication): try: result = method(context, **params) + except exception.Unauthorized as e: + LOG.warning("Authorization failed. %s from %s" + % (e, req.environ['REMOTE_ADDR'])) + return render_exception(e) except exception.Error as e: LOG.warning(e) return render_exception(e) diff -rupN keystone-2012.1+git.1344578088.359c426.orig//keystone/identity/core.py keystone-2012.1+git.1344578088.359c426/keystone/identity/core.py --- keystone-2012.1+git.1344578088.359c426.orig//keystone/identity/core.py 2012-08-14 14:52:54.000000000 +0200 +++ keystone-2012.1+git.1344578088.359c426/keystone/identity/core.py 2012-08-14 14:49:39.000000000 +0200 @@ -267,7 +267,8 @@ class TenantController(wsgi.Application) token_ref = self.token_api.get_token(context=context, token_id=context['token_id']) except exception.NotFound: - raise exception.Unauthorized() + raise exception.Unauthorized("Could not find token "+ + str(context['token_id'])) user_ref = token_ref['user'] tenant_ids = self.identity_api.get_tenants_for_user( diff -rupN keystone-2012.1+git.1344578088.359c426.orig//keystone/service.py keystone-2012.1+git.1344578088.359c426/keystone/service.py --- keystone-2012.1+git.1344578088.359c426.orig//keystone/service.py 2012-08-14 14:52:54.000000000 +0200 +++ keystone-2012.1+git.1344578088.359c426/keystone/service.py 2012-08-14 14:48:24.000000000 +0200 @@ -278,13 +278,13 @@ class TokenController(wsgi.Application): # If the user is disabled don't allow them to authenticate if not user_ref.get('enabled', True): - LOG.warning('User %s is disabled' % user_id) - raise exception.Unauthorized() + raise exception.Unauthorized( + 'User %s is disabled' % user_id) # If the tenant is disabled don't allow them to authenticate if tenant_ref and not tenant_ref.get('enabled', True): - LOG.warning('Tenant %s is disabled' % tenant_id) - raise exception.Unauthorized() + raise exception.Unauthorized( + 'Tenant %s is disabled' % tenant_id) except AssertionError as e: raise exception.Unauthorized(e.message) @@ -319,7 +319,7 @@ class TokenController(wsgi.Application): old_token_ref = self.token_api.get_token(context=context, token_id=token) except exception.NotFound: - raise exception.Unauthorized() + raise exception.Unauthorized("Token not found.") user_ref = old_token_ref['user'] ++++++ keystone-sql-backend-from_dict.patch ++++++ >From 55e228c1fae3ec28d74e84ec7b9cbddc62ee8ed8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ionu=C8=9B=20Ar=C8=9B=C4=83ri=C8=99i?= <iartar...@suse.cz> Date: Tue, 16 Oct 2012 10:58:50 +0200 Subject: [PATCH] don't modify the passed in dict to from_dict Fixes bug 1066851 Change-Id: Ic1f44ba1e319b9cd7e3f1da535f9d29ae7dc4030 --- keystone/identity/backends/sql.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/keystone/identity/backends/sql.py b/keystone/identity/backends/sql.py index f8d836a..353f6d4 100644 --- a/keystone/identity/backends/sql.py +++ b/keystone/identity/backends/sql.py @@ -67,8 +67,7 @@ class User(sql.ModelBase, sql.DictBase): if k not in ['id', 'name', 'extra']: extra[k] = user_dict.pop(k) - user_dict['extra'] = extra - return cls(**user_dict) + return cls(extra=extra, **user_dict) def to_dict(self): extra_copy = self.extra.copy() @@ -92,8 +91,7 @@ class Tenant(sql.ModelBase, sql.DictBase): if k not in ['id', 'name', 'extra']: extra[k] = tenant_dict.pop(k) - tenant_dict['extra'] = extra - return cls(**tenant_dict) + return cls(extra=extra, **tenant_dict) def to_dict(self): extra_copy = copy.deepcopy(self.extra) -- 1.7.10.4 ++++++ logging.conf ++++++ [loggers] keys=root,api,registry,combined [formatters] keys=normal,normal_with_name,debug [handlers] keys=production,file,devel [logger_root] level=NOTSET handlers=devel [logger_api] level=DEBUG handlers=devel qualname=glance-api [logger_registry] level=DEBUG handlers=devel qualname=glance-registry [logger_combined] level=DEBUG handlers=devel qualname=glance-combined [handler_production] class=handlers.SysLogHandler level=ERROR formatter=normal_with_name args=(('localhost', handlers.SYSLOG_UDP_PORT), handlers.SysLogHandler.LOG_USER) [handler_file] class=FileHandler level=DEBUG formatter=normal_with_name args=('glance.log', 'w') [handler_devel] class=StreamHandler level=NOTSET formatter=debug args=(sys.stdout,) [formatter_normal] format=%(asctime)s %(levelname)s %(message)s [formatter_normal_with_name] format=(%(name)s): %(asctime)s %(levelname)s %(message)s [formatter_debug] format=(%(name)s): %(asctime)s %(levelname)s %(module)s %(funcName)s %(message)s ++++++ openstack-keystone.conf.sample ++++++ # # OpenStack Identity (keystone) Apache2 SSL proxy example configuration. # # Required Apache2 modules: # - mod_ssl # - mod_wsgi # <IfDefine SSL> <IfDefine !NOSSL> # Proxy Keystone public API: Listen 5000 <VirtualHost *:5000> ServerName openstack-keystone.example.com ServerAdmin webmas...@openstack-keystone.example.com ErrorLog /var/log/apache2/openstack-keystone-error_log TransferLog /var/log/apache2/openstack-keystone-access_log LogLevel debug SSLEngine on SSLCertificateFile /etc/apache2/ssl.crt/openstack-keystone-server.crt SSLCertificateKeyFile /etc/apache2/ssl.key/openstack-keystone-server.key # Need to run as user 'openstack-keystone' to gain access to '/etc/keystone/keystone.conf' WSGIDaemonProcess keystone_main user=openstack-keystone group=openstack-keystone processes=2 WSGIProcessGroup keystone_main WSGIScriptAlias / /var/lib/keystone/wsgi/main.wsgi <Directory /var/lib/keystone/wsgi/> Order allow,deny Allow from all </Directory> </VirtualHost> # Proxy Keystone admin API: Listen 35357 <VirtualHost *:35357> ServerName openstack-keystone.example.com ServerAdmin webmas...@openstack-keystone.example.com ErrorLog /var/log/apache2/openstack-keystone-error_log TransferLog /var/log/apache2/openstack-keystone-access_log SSLEngine on SSLCertificateFile /etc/apache2/ssl.crt/openstack-keystone-server.crt SSLCertificateKeyFile /etc/apache2/ssl.key/openstack-keystone-server.key # Need to run as user 'openstack-keystone' to gain access to '/etc/keystone/keystone.conf' WSGIDaemonProcess keystone_admin user=openstack-keystone group=openstack-keystone processes=2 WSGIProcessGroup keystone_admin WSGIScriptAlias / /var/lib/keystone/wsgi/admin.wsgi <Directory /var/lib/keystone/wsgi/> Order allow,deny Allow from all </Directory> </VirtualHost> </IfDefine> </IfDefine> ++++++ openstack-keystone.init ++++++ --- /var/tmp/diff_new_pack.at2Md4/_old 2012-11-20 20:16:36.000000000 +0100 +++ /var/tmp/diff_new_pack.at2Md4/_new 2012-11-20 20:16:36.000000000 +0100 @@ -4,19 +4,20 @@ # Provides: openstack-keystone # Required-Start: $remote_fs $syslog # Required-Stop: $remote_fs $syslog -# Should-Start: $network +# Should-Start: $network mysql postgresql +# Should-Stop: $network mysql postgresql # Default-Start: 3 5 # Default-Stop: 0 1 2 6 # Short-Description: OpenStack keystone # Description: OpenStack keystone. ### END INIT INFO -name="keystone" -USER="keystone" -GROUP="keystone" +USER="openstack-keystone" +GROUP="openstack-keystone" +DAEMON="/usr/bin/keystone-all" CONFFILE="/etc/keystone/keystone.conf" -DAEMON_OPTIONS="--config-file=$CONFFILE" +DAEMON_OPTIONS="--config-file=$CONFFILE --log-file=/var/log/keystone/keystone.log" OPTIONS="${OPTIONS} $DAEMON_OPTIONS" # Shell functions sourced from /etc/rc.status: @@ -39,12 +40,12 @@ start) echo -n "Starting $FULLNAME" cd /var/lib/keystone - startproc -t ${STARTUP_TIMEOUT:-5} -q /usr/bin/keystone $OPTIONS + startproc -s -u $USER -t ${STARTUP_TIMEOUT:-5} -q $DAEMON $OPTIONS rc_status -v ;; stop) echo -n "Shutting down $FULLNAME" - killproc /usr/bin/keystone + killproc $DAEMON rc_status -v ;; restart) @@ -56,11 +57,18 @@ ;; status) echo -n "Checking $FULLNAME" - /sbin/checkproc /usr/bin/$name + /sbin/checkproc $DAEMON rc_status -v ;; condrestart|try-restart) - $0 restart + $0 status + if test $? = 0; then + $0 restart + else + rc_reset # Not running is not a failure. + fi + # Remember status and be quiet + rc_status ;; *) echo $"Usage: $0 {start|stop|status|restart|condrestart|try-restart}" ++++++ openstack-keystone.wsgi ++++++ # # OpenStack Identity (Keystone) WSGI app skeleton # import os from paste import deploy from keystone import config from keystone.common import logging CONF = config.CONF LOG = logging.getLogger(__name__) config_files = ['/etc/keystone/keystone.conf'] CONF(config_files=config_files) config.setup_logging(CONF) app_name = os.path.basename(__file__).rsplit('.')[0] if CONF.debug: CONF.log_opt_values(logging.getLogger(CONF.prog), logging.DEBUG) options = deploy.appconfig('config:%s' % CONF.config_file[0]) application = deploy.loadapp('config:%s' % CONF.config_file[0], name=app_name) ++++++ rpmlintrc ++++++ #TODO: Fix this later on (i.e. SLE-12), Python on SLE-11 is way beyond broken: addFilter("no-binary") # This symling is for the -test package and can be ignored: addFilter("dangling-symlink /var/lib/openstack-keystone-test/keystone") ++++++ test_backend_hybrid.py ++++++ # vim: tabstop=4 shiftwidth=4 softtabstop=4 # Copyright 2012 SUSE Linux Products GmbH # Copyright 2012 OpenStack LLC # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import nose.exc from keystone import config from keystone import test from keystone.common.ldap import fakeldap from keystone.common.sql import util as sql_util from keystone.identity.backends import hybrid as identity_hybrid import default_fixtures import test_backend CONF = config.CONF def clear_database(): db = fakeldap.FakeShelve().get_instance() db.clear() class HybridIdentity(test.TestCase, test_backend.IdentityTests): def setUp(self): super(HybridIdentity, self).setUp() CONF(config_files=[test.etcdir('keystone.conf'), test.testsdir('test_overrides.conf'), test.testsdir('backend_hybrid.conf')]) sql_util.setup_test_database() self.identity_api = identity_hybrid.Identity() self.load_fixtures(default_fixtures) def tearDown(self): clear_database() test.TestCase.tearDown(self) -- To unsubscribe, e-mail: opensuse-commit+unsubscr...@opensuse.org For additional commands, e-mail: opensuse-commit+h...@opensuse.org