bumping glance for CVE-2014-9623
authorMatt Thode <prometheanfire@gentoo.org>
Thu, 29 Jan 2015 05:39:42 +0000 (05:39 +0000)
committerMatt Thode <prometheanfire@gentoo.org>
Thu, 29 Jan 2015 05:39:42 +0000 (05:39 +0000)
Package-Manager: portage-2.2.14/cvs/Linux x86_64
Manifest-Sign-Key: 0x33ED3FD25AFC78BA

app-admin/glance/ChangeLog
app-admin/glance/Manifest
app-admin/glance/files/glance-CVE-2014-9623.patch [new file with mode: 0644]
app-admin/glance/glance-2014.2.1-r2.ebuild [moved from app-admin/glance/glance-2014.2.1-r1.ebuild with 98% similarity]

index f4e33dc5c93158d2bb27cb7805c484cbcaf0497d..053b821e95ab9fd9fbf2eda965d5ea71af97a6c2 100644 (file)
@@ -1,6 +1,13 @@
 # ChangeLog for app-admin/glance
 # Copyright 1999-2015 Gentoo Foundation; Distributed under the GPL v2
-# $Header: /var/cvsroot/gentoo-x86/app-admin/glance/ChangeLog,v 1.57 2015/01/14 02:46:07 prometheanfire Exp $
+# $Header: /var/cvsroot/gentoo-x86/app-admin/glance/ChangeLog,v 1.58 2015/01/29 05:39:38 prometheanfire Exp $
+
+*glance-2014.2.1-r2 (29 Jan 2015)
+
+  29 Jan 2015; Matthew Thode <prometheanfire@gentoo.org>
+  +files/glance-CVE-2014-9623.patch, +glance-2014.2.1-r2.ebuild,
+  -glance-2014.2.1-r1.ebuild:
+  bumping glance for CVE-2014-9623
 
 *glance-2014.2.1-r1 (14 Jan 2015)
 
index ece8b778fee678d57f855e35f54db44e4b363164..bdb548b2f2420ae638ef7c3eae737730ac27a759 100644 (file)
@@ -4,28 +4,29 @@ Hash: SHA256
 AUX 0001-Prevent-file-swift-config-and-filesystem-schemes.patch 5240 SHA256 0347ad2f28e5705df46ed90d4521263a4678b65c33f17bde52b80107eb14302c SHA512 f43c51a72534d82468f4c9aec77743b77e421644e3ac9fd64bfa60e6e05984666a05c1297178bd7fdf669c5ff5aa894837813737c6e2465ef1102bf2feaefad2 WHIRLPOOL 8fa9f3c6a6e83188444beaa49f061700dda563acc0fc5cd58f1ffd7ca91c31b23f876f1b4a5e3f1c73c53ca32ca53dfbf99ee68b8afff7106f44b21e66fd455f
 AUX 0001-To-prevent-client-use-v2-patch-api-to-handle-file-an.patch 30796 SHA256 84bf7263401f5cf0227abd83bc9d0ac9caf53163b381e20083791bd5db91dbf2 SHA512 e714a3bc586f6c968008f93e7c81a5a1a76fcde76ba842689c54f699df9bb5f71ee168f8ca5641c0a9ada14b5db0c4b9b4c124c0ee30210512de07555a131c9a WHIRLPOOL bf3ef17c2b3dc86a4c8c35908aa87e5d805fe7c94379231c3e2d573d278db379d9b2475a7675b4ad4236466d665e965ab56f4b046faf55f550fbd5bafd80e7f6
 AUX glance-2013.2-sphinx_mapping.patch 582 SHA256 043c3f7ef413cf3675920880af57943b909ec9f3376f6e86a1ae1d5948d9ad98 SHA512 d012ce5eaed00c3ba7b6219813cee503f68cdb14b8e50eedc731afc63767e1867749d6e4824611e0d024b2fdebfe5a2b3ad53b0ad7b18a39defcc17094da4a0f WHIRLPOOL 659bf94e7740be3ea0d2f130b332e694ebdcca8b90acfb479b8502eec4b867eda999ec2c6cdecc1f3dcbe3e3ddf72798c76bdf16ab4ab561ce61975a451c4585
+AUX glance-CVE-2014-9623.patch 24751 SHA256 e37eb73d863952a056493ca9439cb7a601feaf9bd60fa327578e9ae0b703e8ca SHA512 08ffa4777abe8efeb5b8e0d74a08e3f2bd10fdfd2d81bc0edcc911fdade79ae16b275b18bbab15ee44e64c0406acadf453448f66eb63f349aee5e7ac1704d392 WHIRLPOOL b76fc794a7388c27c84436ec39608c178c6abc67ffea9fc00fa161f9bcf6bb184d4c2a47dc1120c30d4216cc88f87745b5d307b2e5e50c2a967a8cee8fd6bbd8
 AUX glance.confd 25 SHA256 5a53dcf1eece81a06a2dd0856b15f8abe20eb1072361b110f752e396e86a7843 SHA512 13e671d4544e58d7397c1a87eb1048ed4bb9561587fcd63783e377b2d25e810222ca3944e0c8cf13c524e64f94c435b456a0d6f7cacfb148e275377699a11ca9 WHIRLPOOL 862a310fbdd7b68f132c45797210011b607d9b5c8937d60c9f5933a4d625bc985ad0277fea26804681b7e0a674dc9da15fbef40502c4052d6742ef0a94e88f3d
 AUX glance.initd 651 SHA256 1b2fc0e11d572bfcc121cb995ea7b3cce63ee705a05d2fb34a9f99d655546553 SHA512 c0ed1cac2038dda8882d5ce013b948debf1e5e411a062d8c9cbfdb1e1683c94bfd78a3f3468de63e0cc28930c59c0ff323a3116ce6a74ccf5d8ad1d547805bad WHIRLPOOL b0b25f4e983bf9f46cb9dd5872d14b24dc90e3c383dc458c79d065e0855bbf838afc4a13f0c92db640cd64311e22985d0b92e1ca4d428ce8b1f08ff23b89cecf
 DIST glance-2014.2.1.tar.gz 780312 SHA256 8059435c485be487d7b1e73da2f758e9871abf74cddde9b592939f05eb77cb45 SHA512 53797d09672188092d667efed6a914d9ec5a2c014687c2ec61473ec27fb9426391213622e0166c37a969128290c4cb039f07398d5f9906504794f984cc94cf91 WHIRLPOOL 41d05b55d2ac2c9310f0d3583e782c40c5dfecd256bb51219947016f3e71874eb2321c98c3a1fe2fd807b0f312aca0dbaeff8196df4e7ad20ddde662b41be5c9
-EBUILD glance-2014.2.1-r1.ebuild 6047 SHA256 b05afd18d7b49c2c798cf8eac8660a079b36951a8e8f730e5f4a45bee90386dd SHA512 7a92a9640cfa10109548e4d68c04563f9b7ba73ba76eff793648cb8f12e05166562f97d6849680fec28879000d12a15ca94bb02b81f310d70d5bb16c6c22f4d6 WHIRLPOOL 99006c6657d8dbf26f817009bf55d8d27e4a6d30d41b20bca62cb9fad30fc6782c93414757250629ddae56dd5e2f6dda86b6bd3d87b73315bc32ef8f2ec10c15
+EBUILD glance-2014.2.1-r2.ebuild 6089 SHA256 2502dc282246dffa2cabe34ce1e8c6e3f3d45104e1af7308468129ea4a47834b SHA512 1a478828631099dc446dc1c53ec166ee317f9323f37e9caf20163fbfebf155a0cbc54ef22d52aae139ed656e48c44393e8243bf5189a44cbb40402f0d48d48b2 WHIRLPOOL 8c48eb81b8d8903b02d2d79bef9fc377cf1cf608a06174c4f34bf02f06666e21cd5e14a28029e8335e931a3c94a349978d0759b91d946c04725e22f1c3a7aabb
 EBUILD glance-2014.2.9999.ebuild 5899 SHA256 ad477d40b1be82df8abe638c68292a4ea17c7fdcef28fea6a6f78ecd603e174a SHA512 a5f14e2869eafc48a471d6404a81e57994b016b08e1de9e5eb3a5717cbdcd61da0ddd18b9a6d2c9d2847bbbffa2e73ef242a9420f0b2a1589d6ad480ab7fb5d8 WHIRLPOOL 5f2d86bb43054374959bc7a905e9489295e26d7476f574cbd3544b759cc493d78d606d19ff21be6a2462adee4877268af95fa8bb3ccecc81471b1ae6fa18c5da
 EBUILD glance-9999.ebuild 4843 SHA256 cb262eac190bee1adb59ccc125809a3633adbd3448f5a15ee567b3c9a9e0b829 SHA512 e4aa1b6862ded7694eda77bf440ad71e6e6d413a7a7786f1dcd90f0084b8fdcf200486fe50cb49cb78936f0e2242e52b8122c84daa82c142836858e52d8137e7 WHIRLPOOL be80f071732df48b517f40015296c6a921afa665fece31c1efb3a12bb7bf89df20b9d113c10fa470c5ddf25da09d2e97377042ff295cdd7db2bb05e5fc04691f
-MISC ChangeLog 11502 SHA256 072009aa64dc74ca724a5f2025215f54bde4fe0c5b7adb33dc86493510e6dd50 SHA512 7e2d14d251f9cd7ba9e8fec287cb40d62a5735e591315ce074eabbd71ea4f23c9ac9e3b234905725fac7cdbbfe7e50c44cba41a595d305d8f2b25aff0a951d11 WHIRLPOOL 67a64b139224896567b7a08d82a04789430691fd3ce0307bdea288e4687cd081b896bfaaf98d7623da594a3c3b4044b88c4b99149d72fd064890b856c254caee
+MISC ChangeLog 11725 SHA256 54dbc32d5fdac1ed219ce566508758be93e24a54f0f6b43787e8535983f9d25a SHA512 bc0f774665e71ca0ced1aa455076d614a0a550b487e589909a18701c03632de38affc5f7f620111d2dd999f11f6dbbd83b600573c2a70b7113578498f86d48e1 WHIRLPOOL bc2cd572a6105f9839ec6ab6b959d8b03266cf7303df86ca98f10f71a534338cec18cf70486d9ae10d164fc1e057552aeced75632cd075d513f7b7eb7953ef7d
 MISC metadata.xml 607 SHA256 b74d960c096528dbb7c9be6b0da777e10abe32928459f045e7c58b0156e22d95 SHA512 e38e4dd740fe55d73d6d97fd9ffa3aec5466fbb5f8e6484b125560313245ae697a0dcf612fb065125c6737a8254b6218c0703f7de79437bb2799c6c67f386e45 WHIRLPOOL 445bf712fd733fa90c395cc24fd02155bed15b092713d502b54331da9237f397877c2328adbf4049ee64ae163713ce031ebe5048c8826ed1a011d4ebcdbb681d
 -----BEGIN PGP SIGNATURE-----
 Version: GnuPG v2
 
-iQIcBAEBCAAGBQJUtdh4AAoJEGSje+quGaToN+YQALAGYk6AzqLKqZFRYDlpkQ7E
-gs72ngGfzbEp7q2MKx8DEJkVGfp8PawkyK8TQcbHvpy8AYdioIXNgIGY43keL7Qx
-oOiBClqXTpOwHxHVRukZl1dH1B07TElxEUkALfgXJOJywlvZyBPSGKGuuEu4i9mv
-H0Iqz1Z7UmJibUGFLvMsI2pEH/tNbh67+mc2EZzteWsXseOTftnDrDg8X9Aseznq
-WAq+VbWJeZ8J3yN6O4r2BSN/2K0NbIoWcqu+dVKCdl+nyLyhdIUPH2TAeL6XxLFv
-x1nHJ8i3O4AYLxJZLEWTMrIfeu71t+Ic9k/ZnAjbu+Xe8HIcaCiLpboC5EqTFx2u
-nMCEVg9ZIAO/mhQmhRJtnpSpNpzUDYL3kdimFSrbg3LRV62aVzfbh4wLumLvVRcZ
-eD426trdzj+InweMW3LFM4MbgNdQz/KnRqsGKNok2TY25LujrjPikVgUz9Y1vSql
-5hgADPbmAcjswkPpkdKiFbvyQrYaGw3tcuHeZEYMjNIc2HqtxgRJfN2PLY489Xn5
-fffLs/U8pepVkxzc3knEQ8+8IghJMxDLsyqmq+yQLmxj5DQdZAfLAzlFtcB7t28I
-CSHPJT0bfvGf7kAh5Mcao8iG+DkAT6Y6mRRcFxxTpqPKnzR2723Z+L2sHyORhjBM
-6TnFEgNIqK3eV02z9xlq
-=bIIr
+iQIcBAEBCAAGBQJUyceuAAoJEGSje+quGaToiB4P/izHWx4ZwXuF4SUwT6xNnExv
+iB9E70ArbBX3T6vcarZw7+059k4Xynz9uf+9WTo9zydFafYBaBShUD9lbLqvezFp
+wwnr2efvsBPQkgXqjFx8c0Lbx8rYbVrn3FdUdDSM2fol9hrb3NbewJe2gZxFi2AZ
+NxFgBucZfQ36xe8XwoSHkuM25/2PccOuJe9nkUAK/8oNOyFMn6izPK4xH3082kUb
+9YmWZow2A2qNsxMqkTlWj6546ZzH/DiDdr9qvuQRPLnIKqK80uaR/Y5XCGF3gF5J
+GP3H7OBcbsBkpgcN8wzFUeDxeYdw13rA/G7jgBvAcH3tMPlhbJaXTqM9IIpbpzHh
+eMUkmBqa8mO2VS5AVTzQHaLHF9QZZ9ek0pN+7wGHjLeepEKgyKFWgFEIh8ioTBG6
+DxvZAPqVb0Cu8YsDDCJPNglYKjLiGBWouSPa40flUBtIJrOxBl3ispjnEhOQ1dbK
+bXvOOAoMbxHLGy7HS7GHQ6tH2jtTJ4//Cy27ttHjbD0VLD8a1TzCrnfQe277T/fe
+jTtxjfInakuRGbm6Gw2NGQr0ddfNnIT4m7AHmr0pICONopFmQXXcMQXI9EK5g4pF
++3c0FRO+XptRgt/MlLbieeHbsoYwBZSof6VZwkVGoCYPTMGxQ/Qi3nxN3EHXPEg7
+6J7f6wNQsd+l+hAXfmwe
+=RIqF
 -----END PGP SIGNATURE-----
diff --git a/app-admin/glance/files/glance-CVE-2014-9623.patch b/app-admin/glance/files/glance-CVE-2014-9623.patch
new file mode 100644 (file)
index 0000000..a9cc123
--- /dev/null
@@ -0,0 +1,542 @@
+From 7d5d8657fd70b20518610b3c6f8e41e16c72fa31 Mon Sep 17 00:00:00 2001
+From: Zhi Yan Liu <zhiyanl@cn.ibm.com>
+Date: Tue, 30 Dec 2014 22:25:50 +0800
+Subject: [PATCH] Cleanup chunks for deleted image that was 'saving'
+
+Currently image data cannot be removed synchronously for an image that
+is in saving state. And when, the upload operation for such an image is
+completed the operator configured quota can be exceeded.
+
+This patch fixes the issue of left over chunks for an image which was
+deleted from saving status. However, by the limitation of the design we
+cannot enforce a global quota check for the image in saving status.
+
+This change introduces a inconsonance between http response codes of
+v1 and v2 APIs. The status codes which we will now see after the upload
+process completes on an image which was deleted mid way are:
+
+v1: 412 Precondition Failed
+v2: 410 Gone
+
+SecurityImpact
+UpgradeImpact
+APIImpact
+
+Closes-Bug: 1383973
+Closes-Bug: 1398830
+Closes-Bug: 1188532
+
+Conflicts:
+       glance/api/v1/upload_utils.py
+       glance/api/v2/image_data.py
+       glance/tests/unit/test_domain_proxy.py
+       glance/tests/unit/v1/test_api.py
+
+Change-Id: I47229b366c25367ec1bd48aec684e0880f3dfe60
+Signed-off-by: Zhi Yan Liu <zhiyanl@cn.ibm.com>
+(cherry picked from commit 0dc8fbb3479a53c5bba8475d14f4c7206904c5ea)
+---
+ glance/api/authorization.py                      |  4 +-
+ glance/api/policy.py                             |  8 ++--
+ glance/api/v1/upload_utils.py                    | 19 +++++---
+ glance/api/v2/image_data.py                      |  8 +++-
+ glance/db/__init__.py                            |  7 +--
+ glance/domain/proxy.py                           |  4 +-
+ glance/location.py                               |  4 +-
+ glance/notifier.py                               |  4 +-
+ glance/quota/__init__.py                         |  4 +-
+ glance/tests/unit/test_domain_proxy.py           | 14 +++---
+ glance/tests/unit/test_policy.py                 |  2 +-
+ glance/tests/unit/test_quota.py                  | 17 ++++---
+ glance/tests/unit/test_store_image.py            |  2 +-
+ glance/tests/unit/v1/test_api.py                 | 57 +++++++++++-------------
+ glance/tests/unit/v2/test_image_data_resource.py | 24 +++++-----
+ 15 files changed, 98 insertions(+), 80 deletions(-)
+
+diff --git a/glance/api/authorization.py b/glance/api/authorization.py
+index 149ff55..77f7ed3 100644
+--- a/glance/api/authorization.py
++++ b/glance/api/authorization.py
+@@ -158,10 +158,10 @@ class ImageMemberRepoProxy(glance.domain.proxy.Repo):
+             raise exception.Forbidden(message
+                                       % self.image.image_id)
+ 
+-    def save(self, image_member):
++    def save(self, image_member, from_state=None):
+         if (self.context.is_admin or
+                 self.context.owner == image_member.member_id):
+-            self.member_repo.save(image_member)
++            self.member_repo.save(image_member, from_state=from_state)
+         else:
+             message = _("You cannot update image member %s")
+             raise exception.Forbidden(message % image_member.member_id)
+diff --git a/glance/api/policy.py b/glance/api/policy.py
+index 0bc8d56..e395876 100644
+--- a/glance/api/policy.py
++++ b/glance/api/policy.py
+@@ -182,9 +182,9 @@ class ImageRepoProxy(glance.domain.proxy.Repo):
+         self.policy.enforce(self.context, 'get_images', {})
+         return super(ImageRepoProxy, self).list(*args, **kwargs)
+ 
+-    def save(self, image):
++    def save(self, image, from_state=None):
+         self.policy.enforce(self.context, 'modify_image', {})
+-        return super(ImageRepoProxy, self).save(image)
++        return super(ImageRepoProxy, self).save(image, from_state=from_state)
+ 
+     def add(self, image):
+         self.policy.enforce(self.context, 'add_image', {})
+@@ -285,9 +285,9 @@ class ImageMemberRepoProxy(glance.domain.proxy.Repo):
+         self.policy.enforce(self.context, 'get_member', {})
+         return self.member_repo.get(member_id)
+ 
+-    def save(self, member):
++    def save(self, member, from_state=None):
+         self.policy.enforce(self.context, 'modify_member', {})
+-        self.member_repo.save(member)
++        self.member_repo.save(member, from_state=from_state)
+ 
+     def list(self, *args, **kwargs):
+         self.policy.enforce(self.context, 'get_members', {})
+diff --git a/glance/api/v1/upload_utils.py b/glance/api/v1/upload_utils.py
+index 8a190fc..60c3d3d 100644
+--- a/glance/api/v1/upload_utils.py
++++ b/glance/api/v1/upload_utils.py
+@@ -153,12 +153,19 @@ def upload_data_to_store(req, image_meta, image_data, store, notifier):
+         update_data = {'checksum': checksum,
+                        'size': size}
+         try:
+-            image_meta = registry.update_image_metadata(req.context,
+-                                                        image_id,
+-                                                        update_data,
+-                                                        from_state='saving')
+-
+-        except exception.NotFound as e:
++            try:
++                state = 'saving'
++                image_meta = registry.update_image_metadata(req.context,
++                                                            image_id,
++                                                            update_data,
++                                                            from_state=state)
++            except exception.Duplicate:
++                image = registry.get_image_metadata(req.context, image_id)
++                if image['status'] == 'deleted':
++                    raise exception.NotFound()
++                else:
++                    raise
++        except exception.NotFound:
+             msg = _LI("Image %s could not be found after upload. The image may"
+                       " have been deleted during the upload.") % image_id
+             LOG.info(msg)
+diff --git a/glance/api/v2/image_data.py b/glance/api/v2/image_data.py
+index 430ffc5..cdfa34b 100644
+--- a/glance/api/v2/image_data.py
++++ b/glance/api/v2/image_data.py
+@@ -73,8 +73,8 @@ class ImageDataController(object):
+             try:
+                 image_repo.save(image)
+                 image.set_data(data, size)
+-                image_repo.save(image)
+-            except exception.NotFound as e:
++                image_repo.save(image, from_state='saving')
++            except (exception.NotFound, exception.Conflict) as e:
+                 msg = (_("Image %(id)s could not be found after upload."
+                          "The image may have been deleted during the upload: "
+                          "%(error)s Cleaning up the chunks uploaded") %
+@@ -152,6 +152,10 @@ class ImageDataController(object):
+             raise webob.exc.HTTPServiceUnavailable(explanation=msg,
+                                                    request=req)
+ 
++        except webob.exc.HTTPGone as e:
++            with excutils.save_and_reraise_exception():
++                LOG.error(_LE("Failed to upload image data due to HTTP error"))
++
+         except webob.exc.HTTPError as e:
+             with excutils.save_and_reraise_exception():
+                 LOG.error(_LE("Failed to upload image data due to HTTP error"))
+diff --git a/glance/db/__init__.py b/glance/db/__init__.py
+index 05db7f8..483ba21 100644
+--- a/glance/db/__init__.py
++++ b/glance/db/__init__.py
+@@ -164,7 +164,7 @@ class ImageRepo(object):
+         image.created_at = new_values['created_at']
+         image.updated_at = new_values['updated_at']
+ 
+-    def save(self, image):
++    def save(self, image, from_state=None):
+         image_values = self._format_image_to_db(image)
+         if image_values['size'] > CONF.image_size_cap:
+             raise exception.ImageSizeLimitExceeded
+@@ -172,7 +172,8 @@ class ImageRepo(object):
+             new_values = self.db_api.image_update(self.context,
+                                                   image.image_id,
+                                                   image_values,
+-                                                  purge_props=True)
++                                                  purge_props=True,
++                                                  from_state=from_state)
+         except (exception.NotFound, exception.Forbidden):
+             msg = _("No image found with ID %s") % image.image_id
+             raise exception.NotFound(msg)
+@@ -265,7 +266,7 @@ class ImageMemberRepo(object):
+             msg = _("The specified member %s could not be found")
+             raise exception.NotFound(msg % image_member.id)
+ 
+-    def save(self, image_member):
++    def save(self, image_member, from_state=None):
+         image_member_values = self._format_image_member_to_db(image_member)
+         try:
+             new_values = self.db_api.image_member_update(self.context,
+diff --git a/glance/domain/proxy.py b/glance/domain/proxy.py
+index 5a91d34..09c0fb2 100644
+--- a/glance/domain/proxy.py
++++ b/glance/domain/proxy.py
+@@ -94,9 +94,9 @@ class Repo(object):
+         result = self.base.add(base_item)
+         return self.helper.proxy(result)
+ 
+-    def save(self, item):
++    def save(self, item, from_state=None):
+         base_item = self.helper.unproxy(item)
+-        result = self.base.save(base_item)
++        result = self.base.save(base_item, from_state=from_state)
+         return self.helper.proxy(result)
+ 
+     def remove(self, item):
+diff --git a/glance/location.py b/glance/location.py
+index f83fa7a..b49546d 100644
+--- a/glance/location.py
++++ b/glance/location.py
+@@ -60,8 +60,8 @@ class ImageRepoProxy(glance.domain.proxy.Repo):
+         self._set_acls(image)
+         return result
+ 
+-    def save(self, image):
+-        result = super(ImageRepoProxy, self).save(image)
++    def save(self, image, from_state=None):
++        result = super(ImageRepoProxy, self).save(image, from_state=from_state)
+         self._set_acls(image)
+         return result
+ 
+diff --git a/glance/notifier.py b/glance/notifier.py
+index 5ec0854..21223da 100644
+--- a/glance/notifier.py
++++ b/glance/notifier.py
+@@ -122,8 +122,8 @@ class ImageRepoProxy(glance.domain.proxy.Repo):
+                                              item_proxy_class=ImageProxy,
+                                              item_proxy_kwargs=proxy_kwargs)
+ 
+-    def save(self, image):
+-        super(ImageRepoProxy, self).save(image)
++    def save(self, image, from_state=None):
++        super(ImageRepoProxy, self).save(image, from_state=from_state)
+         self.notifier.info('image.update',
+                            format_image_notification(image))
+ 
+diff --git a/glance/quota/__init__.py b/glance/quota/__init__.py
+index 4051992..d628a8c 100644
+--- a/glance/quota/__init__.py
++++ b/glance/quota/__init__.py
+@@ -104,10 +104,10 @@ class ImageRepoProxy(glance.domain.proxy.Repo):
+             LOG.debug(six.text_type(exc))
+             raise exc
+ 
+-    def save(self, image):
++    def save(self, image, from_state=None):
+         if image.added_new_properties():
+             self._enforce_image_property_quota(len(image.extra_properties))
+-        return super(ImageRepoProxy, self).save(image)
++        return super(ImageRepoProxy, self).save(image, from_state=from_state)
+ 
+     def add(self, image):
+         self._enforce_image_property_quota(len(image.extra_properties))
+diff --git a/glance/tests/unit/test_domain_proxy.py b/glance/tests/unit/test_domain_proxy.py
+index 1bb6863..2b8f792 100644
+--- a/glance/tests/unit/test_domain_proxy.py
++++ b/glance/tests/unit/test_domain_proxy.py
+@@ -74,7 +74,7 @@ class TestProxyRepoPlain(test_utils.BaseTestCase):
+         self._test_method('add', 'snuff', 'enough')
+ 
+     def test_save(self):
+-        self._test_method('save', 'snuff', 'enough')
++        self._test_method('save', 'snuff', 'enough', from_state=None)
+ 
+     def test_remove(self):
+         self._test_method('add', None, 'flying')
+@@ -121,14 +121,14 @@ class TestProxyRepoWrapping(test_utils.BaseTestCase):
+             self.assertEqual(results[i].args, tuple())
+             self.assertEqual(results[i].kwargs, {'a': 1})
+ 
+-    def _test_method_with_proxied_argument(self, name, result):
++    def _test_method_with_proxied_argument(self, name, result, **kwargs):
+         self.fake_repo.result = result
+         item = FakeProxy('snoop')
+         method = getattr(self.proxy_repo, name)
+         proxy_result = method(item)
+ 
+-        self.assertEqual(self.fake_repo.args, ('snoop',))
+-        self.assertEqual(self.fake_repo.kwargs, {})
++        self.assertEqual(('snoop',), self.fake_repo.args)
++        self.assertEqual(kwargs, self.fake_repo.kwargs)
+ 
+         if result is None:
+             self.assertIsNone(proxy_result)
+@@ -145,10 +145,12 @@ class TestProxyRepoWrapping(test_utils.BaseTestCase):
+         self._test_method_with_proxied_argument('add', None)
+ 
+     def test_save(self):
+-        self._test_method_with_proxied_argument('save', 'dog')
++        self._test_method_with_proxied_argument('save', 'dog',
++                                                from_state=None)
+ 
+     def test_save_with_no_result(self):
+-        self._test_method_with_proxied_argument('save', None)
++        self._test_method_with_proxied_argument('save', None,
++                                                from_state=None)
+ 
+     def test_remove(self):
+         self._test_method_with_proxied_argument('remove', 'dog')
+diff --git a/glance/tests/unit/test_policy.py b/glance/tests/unit/test_policy.py
+index 5b5e870..44546a0 100644
+--- a/glance/tests/unit/test_policy.py
++++ b/glance/tests/unit/test_policy.py
+@@ -78,7 +78,7 @@ class MemberRepoStub(object):
+     def get(self, *args, **kwargs):
+         return 'member_repo_get'
+ 
+-    def save(self, image_member):
++    def save(self, image_member, from_state=None):
+         image_member.output = 'member_repo_save'
+ 
+     def list(self, *args, **kwargs):
+diff --git a/glance/tests/unit/test_quota.py b/glance/tests/unit/test_quota.py
+index c12eda2..1c40fb4 100644
+--- a/glance/tests/unit/test_quota.py
++++ b/glance/tests/unit/test_quota.py
+@@ -367,7 +367,8 @@ class TestImagePropertyQuotas(test_utils.BaseTestCase):
+         self.image.extra_properties = {'foo': 'bar'}
+         self.image_repo_proxy.save(self.image)
+ 
+-        self.image_repo_mock.save.assert_called_once_with(self.base_image)
++        self.image_repo_mock.save.assert_called_once_with(self.base_image,
++                                                          from_state=None)
+ 
+     def test_save_image_too_many_image_properties(self):
+         self.config(image_property_quota=1)
+@@ -383,7 +384,8 @@ class TestImagePropertyQuotas(test_utils.BaseTestCase):
+         self.image.extra_properties = {'foo': 'bar'}
+         self.image_repo_proxy.save(self.image)
+ 
+-        self.image_repo_mock.save.assert_called_once_with(self.base_image)
++        self.image_repo_mock.save.assert_called_once_with(self.base_image,
++                                                          from_state=None)
+ 
+     def test_add_image_with_image_property(self):
+         self.config(image_property_quota=1)
+@@ -422,7 +424,8 @@ class TestImagePropertyQuotas(test_utils.BaseTestCase):
+         self.config(image_property_quota=1)
+         self.image.extra_properties = {'foo': 'frob', 'spam': 'eggs'}
+         self.image_repo_proxy.save(self.image)
+-        self.image_repo_mock.save.assert_called_once_with(self.base_image)
++        self.image_repo_mock.save.assert_called_once_with(self.base_image,
++                                                          from_state=None)
+         self.assertEqual('frob', self.base_image.extra_properties['foo'])
+         self.assertEqual('eggs', self.base_image.extra_properties['spam'])
+ 
+@@ -431,7 +434,8 @@ class TestImagePropertyQuotas(test_utils.BaseTestCase):
+         self.config(image_property_quota=1)
+         del self.image.extra_properties['foo']
+         self.image_repo_proxy.save(self.image)
+-        self.image_repo_mock.save.assert_called_once_with(self.base_image)
++        self.image_repo_mock.save.assert_called_once_with(self.base_image,
++                                                          from_state=None)
+         self.assertNotIn('foo', self.base_image.extra_properties)
+         self.assertEqual('ham', self.base_image.extra_properties['spam'])
+ 
+@@ -447,7 +451,7 @@ class TestImagePropertyQuotas(test_utils.BaseTestCase):
+         del self.image.extra_properties['frob']
+         del self.image.extra_properties['lorem']
+         self.image_repo_proxy.save(self.image)
+-        call_args = mock.call(self.base_image)
++        call_args = mock.call(self.base_image, from_state=None)
+         self.assertEqual(call_args, self.image_repo_mock.save.call_args)
+         self.assertEqual('bar', self.base_image.extra_properties['foo'])
+         self.assertEqual('ham', self.base_image.extra_properties['spam'])
+@@ -466,7 +470,8 @@ class TestImagePropertyQuotas(test_utils.BaseTestCase):
+         self.config(image_property_quota=1)
+         del self.image.extra_properties['foo']
+         self.image_repo_proxy.save(self.image)
+-        self.image_repo_mock.save.assert_called_once_with(self.base_image)
++        self.image_repo_mock.save.assert_called_once_with(self.base_image,
++                                                          from_state=None)
+         self.assertNotIn('foo', self.base_image.extra_properties)
+         self.assertEqual('ham', self.base_image.extra_properties['spam'])
+         self.assertEqual('baz', self.base_image.extra_properties['frob'])
+diff --git a/glance/tests/unit/test_store_image.py b/glance/tests/unit/test_store_image.py
+index 8b334ab..b656454 100644
+--- a/glance/tests/unit/test_store_image.py
++++ b/glance/tests/unit/test_store_image.py
+@@ -36,7 +36,7 @@ class ImageRepoStub(object):
+     def add(self, image):
+         return image
+ 
+-    def save(self, image):
++    def save(self, image, from_state=None):
+         return image
+ 
+ 
+diff --git a/glance/tests/unit/v1/test_api.py b/glance/tests/unit/v1/test_api.py
+index 39e9a44..7dc0737 100644
+--- a/glance/tests/unit/v1/test_api.py
++++ b/glance/tests/unit/v1/test_api.py
+@@ -39,7 +39,6 @@ from glance.db.sqlalchemy import api as db_api
+ from glance.db.sqlalchemy import models as db_models
+ from glance.openstack.common import jsonutils
+ from glance.openstack.common import timeutils
+-
+ import glance.registry.client.v1.api as registry
+ from glance.tests.unit import base
+ import glance.tests.unit.utils as unit_test_utils
+@@ -1735,8 +1734,7 @@ class TestGlanceAPI(base.IsolatedUnitTest):
+ 
+         self.assertEqual(1, mock_store_add_to_backend.call_count)
+ 
+-    def test_delete_during_image_upload(self):
+-        req = unit_test_utils.get_fake_request()
++    def _check_delete_during_image_upload(self, is_admin=False):
+ 
+         fixture_headers = {'x-image-meta-store': 'file',
+                            'x-image-meta-disk-format': 'vhd',
+@@ -1744,8 +1742,8 @@ class TestGlanceAPI(base.IsolatedUnitTest):
+                            'x-image-meta-name': 'fake image #3',
+                            'x-image-meta-property-key1': 'value1'}
+ 
+-        req = webob.Request.blank("/images")
+-        req.method = 'POST'
++        req = unit_test_utils.get_fake_request(path="/images",
++                                               is_admin=is_admin)
+         for k, v in six.iteritems(fixture_headers):
+             req.headers[k] = v
+ 
+@@ -1770,31 +1768,18 @@ class TestGlanceAPI(base.IsolatedUnitTest):
+                        mock_initiate_deletion)
+ 
+         orig_update_image_metadata = registry.update_image_metadata
+-        ctlr = glance.api.v1.controller.BaseController
+-        orig_get_image_meta_or_404 = ctlr.get_image_meta_or_404
+-
+-        def mock_update_image_metadata(*args, **kwargs):
+ 
+-            if args[2].get('status', None) == 'deleted':
++        data = "somedata"
+ 
+-                # One shot.
+-                def mock_get_image_meta_or_404(*args, **kwargs):
+-                    ret = orig_get_image_meta_or_404(*args, **kwargs)
+-                    ret['status'] = 'queued'
+-                    self.stubs.Set(ctlr, 'get_image_meta_or_404',
+-                                   orig_get_image_meta_or_404)
+-                    return ret
+-
+-                self.stubs.Set(ctlr, 'get_image_meta_or_404',
+-                               mock_get_image_meta_or_404)
++        def mock_update_image_metadata(*args, **kwargs):
+ 
+-                req = webob.Request.blank("/images/%s" % image_id)
+-                req.method = 'PUT'
+-                req.headers['Content-Type'] = 'application/octet-stream'
+-                req.body = "somedata"
++            if args[2].get('size', None) == len(data):
++                path = "/images/%s" % image_id
++                req = unit_test_utils.get_fake_request(path=path,
++                                                       method='DELETE',
++                                                       is_admin=is_admin)
+                 res = req.get_response(self.api)
+-                self.assertEqual(res.status_int, 200)
+-                self.assertFalse(res.location)
++                self.assertEqual(200, res.status_int)
+ 
+                 self.stubs.Set(registry, 'update_image_metadata',
+                                orig_update_image_metadata)
+@@ -1804,20 +1789,30 @@ class TestGlanceAPI(base.IsolatedUnitTest):
+         self.stubs.Set(registry, 'update_image_metadata',
+                        mock_update_image_metadata)
+ 
+-        req = webob.Request.blank("/images/%s" % image_id)
+-        req.method = 'DELETE'
++        req = unit_test_utils.get_fake_request(path="/images/%s" % image_id,
++                                               method='PUT')
++        req.headers['Content-Type'] = 'application/octet-stream'
++        req.body = data
+         res = req.get_response(self.api)
+-        self.assertEqual(res.status_int, 200)
++        self.assertEqual(412, res.status_int)
++        self.assertFalse(res.location)
+ 
+         self.assertTrue(called['initiate_deletion'])
+ 
+-        req = webob.Request.blank("/images/%s" % image_id)
+-        req.method = 'HEAD'
++        req = unit_test_utils.get_fake_request(path="/images/%s" % image_id,
++                                               method='HEAD',
++                                               is_admin=True)
+         res = req.get_response(self.api)
+         self.assertEqual(res.status_int, 200)
+         self.assertEqual(res.headers['x-image-meta-deleted'], 'True')
+         self.assertEqual(res.headers['x-image-meta-status'], 'deleted')
+ 
++    def test_delete_during_image_upload_by_normal_user(self):
++        self._check_delete_during_image_upload(is_admin=False)
++
++    def test_delete_during_image_upload_by_admin(self):
++        self._check_delete_during_image_upload(is_admin=True)
++
+     def test_disable_purge_props(self):
+         """
+         Test the special x-glance-registry-purge-props header controls
+diff --git a/glance/tests/unit/v2/test_image_data_resource.py b/glance/tests/unit/v2/test_image_data_resource.py
+index cc8148a..a121e82 100644
+--- a/glance/tests/unit/v2/test_image_data_resource.py
++++ b/glance/tests/unit/v2/test_image_data_resource.py
+@@ -81,7 +81,7 @@ class FakeImageRepo(object):
+         else:
+             return self.result
+ 
+-    def save(self, image):
++    def save(self, image, from_state=None):
+         self.saved_image = image
+ 
+ 
+@@ -184,17 +184,21 @@ class TestImagesController(base.StoreClearingUnitTest):
+                           request, unit_test_utils.UUID1, 'YYYY', 4)
+ 
+     def test_upload_non_existent_image_during_save_initiates_deletion(self):
+-        def fake_save(self):
++        def fake_save_not_found(self):
+             raise exception.NotFound()
+ 
+-        request = unit_test_utils.get_fake_request()
+-        image = FakeImage('abcd', locations=['http://example.com/image'])
+-        self.image_repo.result = image
+-        self.image_repo.save = fake_save
+-        image.delete = mock.Mock()
+-        self.assertRaises(webob.exc.HTTPGone, self.controller.upload,
+-                          request, str(uuid.uuid4()), 'ABC', 3)
+-        self.assertTrue(image.delete.called)
++        def fake_save_conflict(self):
++            raise exception.Conflict()
++
++        for fun in [fake_save_not_found, fake_save_conflict]:
++            request = unit_test_utils.get_fake_request()
++            image = FakeImage('abcd', locations=['http://example.com/image'])
++            self.image_repo.result = image
++            self.image_repo.save = fun
++            image.delete = mock.Mock()
++            self.assertRaises(webob.exc.HTTPGone, self.controller.upload,
++                              request, str(uuid.uuid4()), 'ABC', 3)
++            self.assertTrue(image.delete.called)
+ 
+     def test_upload_non_existent_image_raises_not_found_exception(self):
+         def fake_save(self):
+-- 
+2.0.5
+
similarity index 98%
rename from app-admin/glance/glance-2014.2.1-r1.ebuild
rename to app-admin/glance/glance-2014.2.1-r2.ebuild
index 61737396fff82a6432d3c83525f507b5e49273fd..6c08a87a352da805b166a6e33c84d9a45bd008bd 100644 (file)
@@ -1,6 +1,6 @@
 # Copyright 1999-2015 Gentoo Foundation
 # Distributed under the terms of the GNU General Public License v2
-# $Header: /var/cvsroot/gentoo-x86/app-admin/glance/glance-2014.2.1-r1.ebuild,v 1.1 2015/01/14 02:46:07 prometheanfire Exp $
+# $Header: /var/cvsroot/gentoo-x86/app-admin/glance/glance-2014.2.1-r2.ebuild,v 1.1 2015/01/29 05:39:38 prometheanfire Exp $
 
 EAPI=5
 PYTHON_COMPAT=( python2_7 )
@@ -131,6 +131,7 @@ PATCHES=(
        "${FILESDIR}/${PN}-2013.2-sphinx_mapping.patch"
        "${FILESDIR}/0001-To-prevent-client-use-v2-patch-api-to-handle-file-an.patch"
        "${FILESDIR}/0001-Prevent-file-swift-config-and-filesystem-schemes.patch"
+       "${FILESDIR}/glance-CVE-2014-9623.patch"
 )
 
 pkg_setup() {