Mercurial > p > roundup > code
diff roundup/rest.py @ 5674:6dc4dba1c225
REST: Use If-Match header for incoming requests
| author | Ralf Schlatterbeck <rsc@runtux.com> |
|---|---|
| date | Mon, 25 Mar 2019 21:23:52 +0100 |
| parents | 1e8f17090a33 |
| children | 1fa59181ce58 |
line wrap: on
line diff
--- a/roundup/rest.py Mon Mar 25 19:47:29 2019 +0100 +++ b/roundup/rest.py Mon Mar 25 21:23:52 2019 +0100 @@ -135,7 +135,8 @@ etag = md5(bs2b(repr(sorted(items)))).hexdigest() logger.debug("object=%s%s; tag=%s; repr=%s", classname, id, etag, repr(node.items(protected=True))) - return etag + # Quotes are part of ETag spec, normal headers don't have quotes + return '"%s"' % etag def check_etag (node, etags, classname="Missing", id="0"): '''Take a list of etags and compare to the etag for the given node. @@ -151,7 +152,7 @@ node_etag = calculate_etag(node, classname, id) for etag in etags: - if etag != None: + if etag is not None: if etag != node_etag: return False have_etag_match=True @@ -166,7 +167,7 @@ etags = [] if '@etag' in input: etags.append(input['@etag'].value); - etags.append(headers.get("ETag", None)) + etags.append(headers.get("If-Match", None)) return etags def parse_accept_header(accept): @@ -459,6 +460,16 @@ return result + def raise_if_no_etag(self, class_name, item_id, input): + class_obj = self.db.getclass(class_name) + if not check_etag(class_obj.getnode(item_id), + obtain_etags(self.client.request.headers, input), + class_name, + item_id): + raise PreconditionFailed( + "If-Match is missing or does not match." + " Retrieve asset and retry modification if valid.") + @Routing.route("/data/<:class_name>", 'GET') @_data_decorator def get_collection(self, class_name, input): @@ -669,7 +680,7 @@ '@etag': etag } - self.client.setHeader("ETag", '"%s"'%etag) + self.client.setHeader("ETag", etag) return 200, result @Routing.route("/data/<:class_name>/<:item_id>/<:attr_name>", 'GET') @@ -717,7 +728,7 @@ '@etag': etag } - self.client.setHeader("ETag", '"%s"'%etag ) + self.client.setHeader("ETag", etag) return 200, result @Routing.route("/data/<:class_name>", 'POST') @@ -818,12 +829,7 @@ (p, class_name, item_id) ) try: - if not check_etag(class_obj.getnode(item_id), - obtain_etags(self.client.request.headers, input), - class_name, - item_id): - raise PreconditionFailed("Etag is missing or does not match." - " Retrieve asset and retry modification if valid.") + self.raise_if_no_etag(class_name, item_id, input) result = class_obj.set(item_id, **props) self.db.commit() except (TypeError, IndexError, ValueError) as message: @@ -874,11 +880,7 @@ } try: - if not check_etag(class_obj.getnode(item_id), - obtain_etags(self.client.request.headers, input), - class_name, item_id): - raise PreconditionFailed("Etag is missing or does not match." - " Retrieve asset and retry modification if valid.") + self.raise_if_no_etag(class_name, item_id, input) result = class_obj.set(item_id, **props) self.db.commit() except (TypeError, IndexError, ValueError) as message: @@ -964,13 +966,7 @@ 'Permission to retire %s %s denied' % (class_name, item_id) ) - if not check_etag(class_obj.getnode(item_id), - obtain_etags(self.client.request.headers, input), - class_name, - item_id): - raise PreconditionFailed("Etag is missing or does not match." - " Retrieve asset and retry modification if valid.") - + self.raise_if_no_etag(class_name, item_id, input) class_obj.retire (item_id) self.db.commit() result = { @@ -1014,13 +1010,7 @@ props[attr_name] = None try: - if not check_etag(class_obj.getnode(item_id), - obtain_etags(self.client.request.headers, input), - class_name, - item_id): - raise PreconditionFailed("Etag is missing or does not match." - " Retrieve asset and retry modification if valid.") - + self.raise_if_no_etag(class_name, item_id, input) class_obj.set(item_id, **props) self.db.commit() except (TypeError, IndexError, ValueError) as message: @@ -1064,12 +1054,7 @@ op = self.__default_patch_op class_obj = self.db.getclass(class_name) - if not check_etag(class_obj.getnode(item_id), - obtain_etags(self.client.request.headers, input), - class_name, - item_id): - raise PreconditionFailed("Etag is missing or does not match." - " Retrieve asset and retry modification if valid.") + self.raise_if_no_etag(class_name, item_id, input) # if patch operation is action, call the action handler action_args = [class_name + item_id] @@ -1173,12 +1158,7 @@ prop = attr_name class_obj = self.db.getclass(class_name) - if not check_etag(class_obj.getnode(item_id), - obtain_etags(self.client.request.headers, input), - class_name, - item_id): - raise PreconditionFailed("Etag is missing or does not match." - " Retrieve asset and retry modification if valid.") + self.raise_if_no_etag(class_name, item_id, input) props = { prop: self.prop_from_arg(
