Skip to content

Commit

Permalink
Merge pull request #6 from sasha-kantoriz/ds_support
Browse files Browse the repository at this point in the history
Asset document
  • Loading branch information
leits committed Aug 17, 2017
2 parents 1104f05 + f8f7f1d commit 270bf39
Show file tree
Hide file tree
Showing 4 changed files with 125 additions and 5 deletions.
1 change: 1 addition & 0 deletions openregistry/api/models/ocds.py
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,7 @@ class Options:

id = MD5Type(required=True, default=lambda: uuid4().hex)
hash = HashType()
documentOf = StringType(choices=['asset', 'lot'])
documentType = StringType(choices=DOCUMENT_TYPES)
title = StringType(required=True) # A title of the document.
title_en = StringType()
Expand Down
2 changes: 1 addition & 1 deletion openregistry/api/models/roles.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
draft_role = whitelist('status')

document_create_role = blacklist('id', 'datePublished', 'dateModified', 'author', 'download_url')
document_edit_role = blacklist('id', 'url', 'datePublished', 'dateModified', 'author', 'hash', 'download_url'),
document_edit_role = blacklist('id', 'url', 'datePublished', 'dateModified', 'author', 'hash', 'download_url')
document_embedded_role = (blacklist('url', 'download_url') + schematics_embedded_role)
document_view_role = (blacklist('revisions') + schematics_default_role)
document_revisions_role = whitelist('url', 'dateModified')
Expand Down
84 changes: 81 additions & 3 deletions openregistry/api/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,12 @@
from cornice.resource import view
from webob.multidict import NestedMultiDict
from pkg_resources import iter_entry_points
from urlparse import urlparse, parse_qs, urlunsplit
from urlparse import urlparse, parse_qs, urlunsplit, parse_qsl
from time import time as ttime
from urllib import quote, urlencode
from base64 import b64encode
from urllib import quote, unquote, urlencode
from base64 import b64encode, b64decode
from hashlib import sha512
from rfc6266 import build_header

from schematics.types import StringType
from jsonpatch import make_patch, apply_patch as _apply_patch
Expand Down Expand Up @@ -198,6 +199,83 @@ def generate_docservice_url(request, doc_id, temporary=True, prefix=None):
return urlunsplit((parsed_url.scheme, parsed_url.netloc, '/get/{}'.format(doc_id), urlencode(query), ''))


def update_file_content_type(request):
pass # TODO


def get_file(request):
db_doc_id = request.validated['db_doc'].id
document = request.validated['document']
key = request.params.get('download')
if not any([key in i.url for i in request.validated['documents']]):
request.errors.add('url', 'download', 'Not Found')
request.errors.status = 404
return
document = [i for i in request.validated['documents'] if key in i.url][-1]
if 'Signature=' in document.url and 'KeyID' in document.url:
url = document.url
else:
if 'download=' not in document.url:
key = urlparse(document.url).path.replace('/get/', '')
if not document.hash:
url = generate_docservice_url(request, key, prefix='{}/{}'.format(db_doc_id, document.id))
else:
url = generate_docservice_url(request, key)
request.response.content_type = document.format.encode('utf-8')
request.response.content_disposition = build_header(document.title, filename_compat=quote(document.title.encode('utf-8')))
request.response.status = '302 Moved Temporarily'
request.response.location = url
return url


def check_document(request, document, document_container):
url = document.url
parsed_url = urlparse(url)
parsed_query = dict(parse_qsl(parsed_url.query))
if not url.startswith(request.registry.docservice_url) or \
len(parsed_url.path.split('/')) != 3 or \
set(['Signature', 'KeyID']) != set(parsed_query):
request.errors.add(document_container, 'url', "Can add document only from document service.")
request.errors.status = 403
raise error_handler(request)
if not document.hash:
request.errors.add(document_container, 'hash', "This field is required.")
request.errors.status = 422
raise error_handler(request)
keyid = parsed_query['KeyID']
if keyid not in request.registry.keyring:
request.errors.add(document_container, 'url', "Document url expired.")
request.errors.status = 422
raise error_handler(request)
dockey = request.registry.keyring[keyid]
signature = parsed_query['Signature']
key = urlparse(url).path.split('/')[-1]
try:
signature = b64decode(unquote(signature))
except TypeError:
request.errors.add(document_container, 'url', "Document url signature invalid.")
request.errors.status = 422
raise error_handler(request)
mess = "{}\0{}".format(key, document.hash.split(':', 1)[-1])
try:
if mess != dockey.verify(signature + mess.encode("utf-8")):
raise ValueError
except ValueError:
request.errors.add(document_container, 'url', "Document url invalid.")
request.errors.status = 422
raise error_handler(request)


def update_document_url(request, document, document_route, route_kwargs):
key = urlparse(document.url).path.split('/')[-1]
route_kwargs.update({'_route_name': document_route,
'document_id': document.id,
'_query': {'download': key}})
document_path = request.current_route_path(**route_kwargs)
document.url = '/' + '/'.join(document_path.split('/')[3:])
return document


def forbidden(request):
request.errors.add('url', 'permission', 'Forbidden')
request.errors.status = 403
Expand Down
43 changes: 42 additions & 1 deletion openregistry/api/validation.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,13 @@
from schematics.exceptions import ModelValidationError, ModelConversionError
from openregistry.api.utils import apply_data_patch, update_logging_context, error_handler, raise_operation_error
from openregistry.api.constants import (
DOCUMENT_BLACKLISTED_FIELDS,
DOCUMENT_WHITELISTED_FIELDS
)
from openregistry.api.utils import (
apply_data_patch, update_logging_context,
check_document, update_document_url,
error_handler, raise_operation_error
)


def validate_json_data(request):
Expand Down Expand Up @@ -87,3 +95,36 @@ def validate_change_status(request, error_handler, **kwargs):
request.authenticated_role not in statuses.get(new_status, {}):
raise_operation_error(request, error_handler, msg)


# Document validators

def validate_patch_document_data(request, error_handler, **kwargs):
model = type(request.context)
return validate_data(request, model, True)


def validate_document_data(request, error_handler, **kwargs):
context = request.context if 'documents' in request.context else request.context.__parent__
model = type(context).documents.model_class
validate_data(request, model)

first_document = request.validated['documents'][-1] if 'documents' in request.validated and request.validated['documents'] else None
document = request.validated['document']
check_document(request, document, 'body')

if first_document:
for attr_name in type(first_document)._fields:
if attr_name in DOCUMENT_WHITELISTED_FIELDS:
setattr(document, attr_name, getattr(first_document, attr_name))
elif attr_name not in DOCUMENT_BLACKLISTED_FIELDS and attr_name not in request.validated['json_data']:
setattr(document, attr_name, getattr(first_document, attr_name))

document.documentOf = type(context).__name__.lower()
document_route = request.matched_route.name.replace("collection_", "")
document = update_document_url(request, document, document_route, {})
request.validated['document'] = document


def validate_file_upload(request, error_handler, **kwargs):
update_logging_context(request, {'document_id': '__new__'})
validate_document_data(request, error_handler, **kwargs)

0 comments on commit 270bf39

Please sign in to comment.