Changeset 315708 in indico


Ignore:
Timestamp:
08/23/11 15:38:51 (21 months ago)
Author:
Jose Benito <jose.benito.gonzalez@…>
Branches:
master, hello-world-walkthrough, ipv6, v0.98-series, v0.98.2, v0.98.3, v0.98b2, v0.99, 051b2622c51afb171a1dedb46a0df4fbb0cbd02e, 0da0c1403bae8e51d8229f460181c71b9e6dda72
Children:
fcdc60
Parents:
1ea4c5
git-author:
Adrian Moennich <jerome.ernst.monnich@…> (05/12/11 14:06:08)
git-committer:
Jose Benito <jose.benito.gonzalez@…> (08/23/11 15:38:51)
Message:

[IMP] Improve URL format for caching/signing

  • in both cases the query strign is sorted
  • the signature is now passed via &signature=...
  • the timestamp is included as &timestamp=...
  • the cache key now uses SHA-256 instead of hash()
Location:
indico/web/http_api
Files:
2 edited

Legend:

Unmodified
Added
Removed
  • indico/web/http_api/cache.py

    r9b51c4 r315708  
    2020from MaKaC.common.cache import MultiLevelCacheEntry, MultiLevelCache 
    2121import datetime 
     22import hashlib 
    2223import time 
    2324import os 
     
    5152        return ['requests'] 
    5253 
    53     def _generateKey(self, path, qdata): 
    54         queryHash = hash(frozenset((key, frozenset(values)) for key, values in qdata.iteritems())) 
    55         return '.'.join(map(str, (hash(path), queryHash))) 
     54    def cacheObject(self, key, obj): 
     55        key = hashlib.sha256(key).hexdigest() 
     56        return super(RequestCache, self).cacheObject(key, obj) 
    5657 
    57     def cacheObject(self, path, qdata, obj): 
    58         return super(RequestCache, self).cacheObject(self._generateKey(path, qdata), obj) 
    59  
    60     def loadObject(self, path, qdata): 
    61         return super(RequestCache, self).loadObject(self._generateKey(path, qdata)) 
     58    def loadObject(self, key): 
     59        key = hashlib.sha256(key).hexdigest() 
     60        return super(RequestCache, self).loadObject(key) 
    6261 
    6362    def isDirty(self, path, object): 
  • indico/web/http_api/handlers.py

    r1ea4c5 r315708  
    2828import re 
    2929import time 
     30import urllib 
    3031from urlparse import parse_qs 
    3132from ZODB.POSException import ConflictError 
     
    6162} 
    6263 
    63 # Compile regexps 
     64# Compile url regexps 
    6465EXPORT_URL_MAP = dict((re.compile(pathRe), handlerFunc) for pathRe, handlerFunc in EXPORT_URL_MAP.iteritems()) 
    65 RE_REMOVE_EXTENSION = re.compile(r'\.(\w+)$') 
     66# Remove the extension at the end or before the querystring 
     67RE_REMOVE_EXTENSION = re.compile(r'\.(\w+)(?:$|(?=\?))') 
     68 
     69 
     70def normalizeQuery(path, query, ts=None, remove=('timestamp', 'signature')): 
     71    """Normalize request path and query so it can be used for caching and signing 
     72 
     73    Returns a string consisting of path and sorted query string. 
     74    Dynamic arguments like signature and timestamp are removed from the query string. 
     75    """ 
     76    qdata = remove_lists(parse_qs(query)) 
     77    if remove: 
     78        for key in remove: 
     79            qdata.pop(key, None) 
     80    if ts is not None: 
     81        qdata['timestamp'] = ts 
     82    sortedQuery = sorted(qdata.items(), key=lambda x: x[0].lower()) 
     83    if sortedQuery: 
     84        return '%s?%s' % (path, urllib.urlencode(sortedQuery)) 
     85    else: 
     86        return path 
    6687 
    6788 
     
    7293    candidates = [] 
    7394    for i in xrange(-1, 2): 
    74         h = hmac.new(key, '%s?%s&%d' % (path, query, ts + i), hashlib.sha1) 
     95        h = hmac.new(key, normalizeQuery(path, query, ts + i), hashlib.sha1) 
    7596        candidates.append(h.hexdigest()) 
    7697    if signature not in candidates: 
     
    172193def handler(req, **params): 
    173194    path, query = req.URLFields['PATH_INFO'], req.URLFields['QUERY_STRING'] 
    174     # Extract HMAC signature 
    175     signature = None 
    176     m = re.search(r'&([0-9a-fA-F]{40})$', query) 
    177     if m: 
    178         signature = m.group(1).lower() 
    179         query = query[:-41] 
    180  
    181195    # Parse the actual query string 
    182196    qdata = parse_qs(query) 
    183     no_cache = get_query_parameter(qdata, ['nc', 'nocache'], 'no') == 'yes' 
    184197 
    185198    dbi = DBMgr.getInstance() 
    186199    dbi.startRequest() 
    187200 
    188     # Copy qdata for the cache key 
    189     qdata_copy = dict(qdata) 
    190201    cache = RequestCache(HelperMaKaCInfo.getMaKaCInfoInstance().getAPICacheTTL()) 
    191202 
     203    apiKey = get_query_parameter(qdata, ['ak', 'apikey'], None) 
     204    signature = get_query_parameter(qdata, ['signature']) 
     205    no_cache = get_query_parameter(qdata, ['nc', 'nocache'], 'no') == 'yes' 
    192206    pretty = get_query_parameter(qdata, ['p', 'pretty'], 'no') == 'yes' 
    193     apiKey = get_query_parameter(qdata, ['ak', 'apikey'], None) 
    194207    onlyPublic = get_query_parameter(qdata, ['op', 'onlypublic'], 'no') == 'yes' 
    195208 
     
    210223        # Get rid of API key in cache key if we did not impersonate a user 
    211224        if ak and aw.getUser() is None: 
    212             qdata_copy.pop('ak', None) 
    213             qdata_copy.pop('apikey', None) 
     225            cache_key = normalizeQuery(path, query, remove=('ak', 'apiKey', 'signature', 'timestamp')) 
     226        else: 
     227            cache_key = normalizeQuery(path, query, remove=('signature', 'timestamp')) 
    214228 
    215229        obj = None 
    216230        add_to_cache = True 
    217         cache_path = RE_REMOVE_EXTENSION.sub('', path) 
     231        cache_key = RE_REMOVE_EXTENSION.sub('', cache_key) 
    218232        if not no_cache: 
    219             obj = cache.loadObject(cache_path, qdata_copy) 
     233            obj = cache.loadObject(cache_key) 
    220234            if obj is not None: 
    221235                result, complete = obj.getContent() 
     
    226240            result, complete = func(dbi, aw, qdata, *args) 
    227241        if result is not None and add_to_cache: 
    228             cache.cacheObject(cache_path, qdata_copy, (result, complete)) 
     242            cache.cacheObject(cache_key, (result, complete)) 
    229243    except HTTPAPIError, e: 
    230244        error = e 
Note: See TracChangeset for help on using the changeset viewer.