예제 #1
0
    def country_blocked(self):
        if self.request.form.get('apiMethod', None) == 'request_country_exception':
            user = api.user.get(username=self.username)
            cache_key = self.auth.get_country_exception_cache_key(user.getId)
            try:
                request_data = cache.get(cache_key)
            except KeyError:
                request_data = None

            if not request_data:
                self.request_country_exception()
            else:
                if request_data.get('granted', False):
                    self.auth.set_secure_flow_state(self.auth.CHECK_CREDENTIALS)
                    return json.dumps({
                        'success': True,
                        'messsage': 'Your request has already been granted, try logging in again.'
                                    ' Requests expire 12 hours after issue.'
                    })
                else:
                    return json.dumps({
                        'success': True,
                        'message': 'Your request has been issued, but not yet granted.'
                                   ' Requests expire 12 hours after issue.'
                    })
예제 #2
0
    def add(self, text, type=u'info'):
        if self.anon:
            return super(CastleStatusMessage, self).add(text, type)

        try:
            text = translate(text)
        except Exception:
            pass

        cache_key = self.get_cache_key()
        try:
            messages = cache.get(cache_key)
        except KeyError:
            messages = []

        site_path = context_path = '/'.join(self.site_path)
        context = get_context_from_request(self.context)
        if context:
            try:
                context_path = '/'.join(context.getPhysicalPath())
            except AttributeError:
                pass

        messages.append({
            'text': text,
            'type': type,
            'timestamp': time.time(),
            'context': context_path[len(site_path):]
        })
        messages = messages[-self.max_messages:]

        # cache for 1 hour, should it be longer? shorter?
        cache.set(cache_key, messages, 1 * 60 * 60)
예제 #3
0
    def get_cachable_config_data(self):
        cache_key = '%s-config-data' % '/'.join(self.site.getPhysicalPath()[1:])
        try:
            return cache.get(cache_key)
        except Exception:
            pass

        available_tiles = self.registry.get('castle.slot_tiles')
        if not available_tiles:
            available_tiles = {
                'Structure': ['plone.app.standardtiles.rawhtml']
            }

        # otherwise, you're editing the value in the DB!!!!
        available_tiles = available_tiles.copy()

        for group_name, tile_ids in available_tiles.items():
            group = []
            for tile_id in tile_ids:
                tile = getUtility(ITileType, name=tile_id)
                group.append({
                    'id': tile_id,
                    'label': tile.title
                })
            available_tiles[group_name] = group

        data = {
            'data-available-slots': json.dumps(available_tiles),
            'data-youtube-enabled': str(youtube.get_oauth_token() is not None).lower()
        }

        cache.set(cache_key, data, 600)
        return data
예제 #4
0
    def __call__(self):
        auth = self.authenticator = getMultiAdapter(
            (self.context, self.request), IAuthenticator)

        userid = self.request.form.get('userid')
        code = self.request.form.get('code')
        if userid and code:
            exc_key = auth.get_country_exception_cache_key(userid)
            try:
                data = cache.get(exc_key)
                if not strings_differ(data['code'], code):
                    timestamp = data.get('timestamp')
                    if timestamp and (time.time() <
                                      (timestamp + (12 * 60 * 60))):
                        user = api.user.get(data['userid'])
                        self.message = 'Successfully issued country login exception for {}({}).'.format(  # noqa
                            user.getProperty('fullname') or user.getUserName(),
                            user.getUserName())
                        self.success = True
                        data['granted'] = True
                        data['timestamp'] = time.time()
                        cache.set(exc_key, data, 12 * 60 * 60)
                        self.send_email(data)
            except:
                pass
        return self.index()
예제 #5
0
def manage_pasteObjects(self, cb_copy_data=None, REQUEST=None):
    """
    override normal paste action to see if we're looking at the paste
    data being stored in cache
    """
    if cb_copy_data is not None:
        cp = cb_copy_data
    elif REQUEST is not None and '__cp' in REQUEST:
        cp = REQUEST['__cp']
    else:
        cp = None
    if cp is None:
        raise CopyError(eNoData)

    try:
        op, mdatas = _cb_decode(cp)
    except Exception:
        raise CopyError(eInvalid)

    try:
        if mdatas[0][0].startswith('cache:'):
            cache_key = mdatas[0][0].replace('cache:', '')
            new_mdatas = cache.get(cache_key)
            cdata = _cb_encode((op, new_mdatas))
            return self._old_manage_pasteObjects(cdata)
    except IndexError:
        pass
    return self._old_manage_pasteObjects(cb_copy_data, REQUEST)
예제 #6
0
    def get_popular_tags(self, limit=20):
        cache_key = '-'.join(
            self.site.getPhysicalPath()[1:]) + '-popular-tags-' + str(limit)

        try:
            result = cache.get(cache_key)
        except Exception:
            result = None
        if result is not None:
            return result

        cat = api.portal.get_tool('portal_catalog')
        index = cat._catalog.getIndex('Subject')
        tags = []
        for name in index._index.keys():
            try:
                number_of_entries = len(index._index[name])
            except TypeError:
                continue
            tags.append({'name': name, 'count': number_of_entries})

        sorted_tags = list(reversed(sorted(
            tags, key=lambda tag: tag['count'])))[:limit]

        cache.set(cache_key, sorted_tags, 60 * 5)
        return sorted_tags
예제 #7
0
def MosaicRegistry_parseRegistry(self):
    cache_key = '%s-mosaic-registry' % '/'.join(
        api.portal.get().getPhysicalPath()[1:])
    try:
        result = cache.get(cache_key)
    except KeyError:
        result = self._old_parseRegistry()
        cache.set(cache_key, result, 60 * 2)  # cache for 2 minutes

    registry = getUtility(IRegistry)
    settings = registry.forInterface(ITinyMCESchema,
                                     prefix="plone",
                                     check=False)
    if settings.libraries_spellchecker_choice != 'AtD':
        return result

    # add atd config to toolbar dynamically
    mos_settings = result['plone']['app']['mosaic']
    mos_settings['richtext_toolbar']['AtD'] = {
        'category': u'actions',
        'name': u'toolbar-AtD',
        'weight': 0,
        'favorite': False,
        'label': u'After the deadline',
        'action': u'AtD',
        'icon': False
    }
    for widget_type in _rich_text_widget_types:
        mos_settings['widget_actions'][widget_type]['actions'].append(
            'toolbar-AtD')
    mos_settings['structure_tiles']['text']['available_actions'].append(
        'toolbar-AtD')
    mos_settings['app_tiles']['plone_app_standardtiles_rawhtml'][
        'available_actions'].append('toolbar-AtD')  # noqa
    return result
예제 #8
0
def _paste_items(where, op, mdatas):
    logger.info('Copying a bunch of items')
    portal = api.portal.get()
    catalog = api.portal.get_tool('portal_catalog')
    dest = portal.restrictedTraverse(str(where.lstrip('/')))

    count = 0
    commit_count = 0
    portal._p_jar.sync()

    try:
        if mdatas[0][0].startswith('cache:'):
            cache_key = mdatas[0][0].replace('cache:', '')
            mdatas = cache.get(cache_key)
    except IndexError:
        pass

    for mdata in mdatas[:]:
        count += len(catalog(path={'query': '/'.join(mdata), 'depth': -1}))
        ob = portal.unrestrictedTraverse(str('/'.join(mdata)), None)
        if ob is None:
            continue
        if op == 0:
            # copy
            api.content.copy(ob, dest, safe_id=True)
        else:
            api.content.move(ob, dest, safe_id=True)

        if count / 50 != commit_count:
            # commit every 50 objects moved
            transaction.commit()
            commit_count = count / 50
            portal._p_jar.sync()
            # so we do not redo it
            try:
                mdatas.remove(mdata)
            except:
                pass

    # we commit here so we can trigger conflict errors before
    # trying to send email
    transaction.commit()

    user = api.user.get_current()
    email = user.getProperty('email')
    if email:
        name = user.getProperty('fullname') or user.getId()
        try:
            utils.send_email(
                recipients=email,
                subject="Paste Operation Finished(Site: %s)" %
                (api.portal.get_registry_record('plone.site_title')),
                html="""
    <p>Hi %s,</p>

    <p>The site has finished pasting items into /%s folder.</p>""" %
                (name, where.lstrip('/')))
        except:
            logger.warn('Could not send status email ', exc_info=True)
예제 #9
0
    def get_all(self):
        cache_key = self.get_cache_key()

        try:
            messages = cache.get(cache_key)
        except KeyError:
            messages = []
        return messages
예제 #10
0
def MosaicRegistry_parseRegistry(self):
    cache_key = '%s-mosaic-registry' % '/'.join(
        api.portal.get().getPhysicalPath()[1:])
    try:
        return cache.get(cache_key)
    except KeyError:
        result = self._old_parseRegistry()
        cache.set(cache_key, result, 60 * 2)  # cache for 2 minutes
        return result
예제 #11
0
    def get_secure_flow_state(self):
        key = self.get_secure_flow_key()
        try:
            state_with_timestamp = cache.get(key)
            state = state_with_timestamp['state']
        except KeyError:
            state = None

        return state
예제 #12
0
 def country_exception_granted(self, userid):
     cache_key = self.get_country_exception_cache_key(userid)
     try:
         data = cache.get(cache_key)
     except Exception:
         return False
     timestamp = data.get('timestamp')
     if (data.get('granted') and timestamp
             and (time.time() < (timestamp + (12 * 60 * 60)))):
         return True
     return True
예제 #13
0
 def get_ga_profile(self, service):
     cache_key = '%s-ga-profile' % '-'.join(
         api.portal.get().getPhysicalPath()[1:])
     try:
         profile = cache.get(cache_key)
     except Exception:
         profile = None
     if profile is None:
         profile = analytics.get_ga_profile(service)
         cache.set(cache_key, profile, 60 * 60 * 1)
     return profile
예제 #14
0
    def __init__(self, site, login):
        self.site = site
        self.login = login

        try:
            self.attempts = cache.get(self.cache_key)
        except (AttributeError, KeyError):
            self.attempts = []

        registry = getUtility(IRegistry)
        self.window = registry.get('plone.failed_login_attempt_window', 3600)
        self.max_number = registry.get('plone.max_failed_login_attempts', 15)
예제 #15
0
    def test_authorize_code_does_not_work_out_of_time(self):
        view = SecureLoginView(self.portal, self.request)
        view()  # REQUESTING_AUTH_CODE state
        self.request.form.update({'username': TEST_USER_NAME})
        self.request.REQUEST_METHOD = 'POST'
        view()  # CHECK_CREDENTIALS state
        code = cache.get(
            view.auth.get_2factor_code_key(TEST_USER_NAME))['code']
        self.request.form.update({
            'username': TEST_USER_NAME,
            'password': TEST_USER_PASSWORD,
            'code': code
        })

        # set the code back
        cache_key = view.auth.get_2factor_code_key(TEST_USER_NAME)
        code_data = cache.get(cache_key)
        code_data['timestamp'] -= (5 * 60) + 1
        cache.set(cache_key, code_data)

        self.request.REQUEST_METHOD = 'POST'
        result = json.loads(view())
        self.assertFalse(result['success'])
예제 #16
0
    def authorize_2factor(self, username, code, offset=0):
        try:
            value = cache.get(self.get_2factor_code_key(username))
        except Exception:
            return False

        # check actual code
        if strings_differ(value['code'].lower(), code.lower()):
            return False

        # then check timing
        timestamp = value.get('timestamp')
        if not timestamp or (time.time() > (timestamp + 5 * 60 + offset)):
            return False
        return True
예제 #17
0
 def test_two_factor_login_failure(self):
     view = SecureLoginView(self.portal, self.request)
     view()  # REQUESTING_AUTH_CODE state
     self.request.form.update({'username': TEST_USER_NAME})
     self.request.REQUEST_METHOD = 'POST'
     view()  # CHECK_CREDENTIALS state
     code = cache.get(
         view.auth.get_2factor_code_key(TEST_USER_NAME))['code']
     self.auth.set_secure_flow_state(self.auth.CHECK_CREDENTIALS)
     self.request.form.update({
         'username': TEST_USER_NAME,
         'password': '******',
         'code': code
     })
     self.request.REQUEST_METHOD = 'POST'
     result = json.loads(view())
     self.assertFalse(result['success'])
예제 #18
0
    def ga_api_call(self, paths):
        params = json.loads(self.request.get('params'))

        cache_key = '-'.join(api.portal.get().getPhysicalPath()[1:])
        for key, value in params.items():
            cache_key += '%s=%s' % (key, value)

        try:
            result = cache.get(cache_key)
        except Exception:
            result = None

        if result is None:
            service = analytics.get_ga_service()
            if not service:
                return {'error': 'Could not get GA Service'}

            profile = self.get_ga_profile(service)
            if not profile:
                return {'error': 'Could not get GA Profile'}

            if self.request.get('type') == 'realtime':
                ga = service.data().realtime()
                if not params.pop('global', False):
                    # need to restrict by filters
                    path_query = ','.join(
                        ['rt:pagePath==%s' % p for p in paths])
                    params['filters'] = path_query
            else:
                if not params.pop('global', False):
                    # need to restrict by filters
                    path_query = ','.join(
                        ['ga:pagePath==%s' % p for p in paths])
                    params['filters'] = path_query
                ga = service.data().ga()

            query = ga.get(ids='ga:' + profile, **params)
            result = query.execute()
            if result:
                cache_duration = self.request.get('cache_duration')
                if cache_duration:
                    cache.set(cache_key, result, int(cache_duration))
            else:
                result = {'error': 'GA query execution yielded no result.'}

        return result
예제 #19
0
    def test_authorize_code_does_not_work_out_of_time(self):
        self.request.form.update({
            'apiMethod': 'authorize_code',
            'username': TEST_USER_NAME
        })
        view = SecureLoginView(self.portal, self.request)
        code = view.auth.issue_2factor_code(TEST_USER_NAME)
        self.request.form.update({'code': code})

        # set the code back
        cache_key = view.auth.get_2factor_code_key(TEST_USER_NAME)
        code_data = cache.get(cache_key)
        code_data['timestamp'] -= (5 * 60) + 1
        cache.set(cache_key, code_data)

        result = json.loads(view())
        self.assertFalse(result['success'])
예제 #20
0
def get_paste_data(req):
    try:
        op, mdatas = _cb_decode(req['__cp'])
    except:
        raise CopyError(eInvalid)

    try:
        if mdatas[0][0].startswith('cache:'):
            cache_key = mdatas[0][0].replace('cache:', '')
            mdatas = cache.get(cache_key)
    except IndexError:
        pass

    paths = []
    for mdata in mdatas:
        paths.append('/'.join(mdata))

    catalog = api.portal.get_tool('portal_catalog')
    count = len(catalog(path={'query': paths, 'depth': -1}))

    return {'paths': paths, 'op': op, 'mdatas': mdatas, 'count': count}
예제 #21
0
    def test_authorize_code_succeeds(self):
        view = SecureLoginView(self.portal, self.request)
        view()  # REQUESTING_AUTH_CODE state
        self.request.form.update({
            'authType': 'email',
            'username': TEST_USER_NAME
        })
        self.request.REQUEST_METHOD = 'POST'
        view = SecureLoginView(self.portal, self.request)
        view()  # CHECK_CREDENTIALS state
        code = cache.get(
            view.auth.get_2factor_code_key(TEST_USER_NAME))['code']
        self.request.form.update({
            'username': TEST_USER_NAME,
            'password': TEST_USER_PASSWORD,
            'code': code
        })

        self.request.REQUEST_METHOD = 'POST'
        result = json.loads(view())
        self.assertTrue(result['success'])
예제 #22
0
    def test_two_factor_login_success(self):
        registry = getUtility(IRegistry)
        registry['plone.two_factor_enabled'] = True
        view = SecureLoginView(self.portal, self.request)
        view()  # REQUESTING_AUTH_CODE state
        self.request.form.update({
            'authType': 'email',
            'username': TEST_USER_NAME
        })
        self.request.REQUEST_METHOD = 'POST'
        view()  # CHECK_CREDENTIALS state
        code = cache.get(
            view.auth.get_2factor_code_key(TEST_USER_NAME))['code']
        self.auth.set_secure_flow_state(self.auth.CHECK_CREDENTIALS)
        self.request.form.update({
            'username': TEST_USER_NAME,
            'password': TEST_USER_PASSWORD,
            'code': code
        })

        result = json.loads(view())
        self.assertTrue(result['success'])
예제 #23
0
    def get_available_slot_tiles(self):
        cache_key = '%s-slot-tiles' % '/'.join(self.site.getPhysicalPath()[1:])
        try:
            return cache.get(cache_key)
        except:
            pass

        available_tiles = self.registry.get('castle.slot_tiles')
        if not available_tiles:
            available_tiles = {
                'Structure': ['plone.app.standardtiles.rawhtml']
            }

        # otherwise, you're editing the value in the DB!!!!
        available_tiles = available_tiles.copy()

        for group_name, tile_ids in available_tiles.items():
            group = []
            for tile_id in tile_ids:
                tile = getUtility(ITileType, name=tile_id)
                group.append({'id': tile_id, 'label': tile.title})
            available_tiles[group_name] = group
        cache.set(cache_key, available_tiles, 600)
        return available_tiles
예제 #24
0
    def chunk_upload(self):
        chunk = int(self.request.form['chunk'])
        chunk_size = int(self.request.form['chunkSize'])
        total_size = int(self.request.form['totalSize'])
        total_chunks = int(math.ceil(float(total_size) / float(chunk_size)))
        _id = self.request.form.get('id')
        existing_id = self.request.form.get('content', None)
        field_name = self.request.form.get('field', None)

        if chunk > total_chunks:
            raise Exception("More chunks than what should be possible")

        cache_key_prefix = '%s-uploads-' % '/'.join(self.context.getPhysicalPath()[1:])
        if chunk == 1:
            # initializing chunk upload

            _id = utils.get_random_string(50)
            filename = self.request.form['name']
            tmp_dir = tempfile.mkdtemp()
            tmp_filename = os.path.join(tmp_dir, filename)
            info = {
                'last_chunk': 1,
                'total_size': total_size,
                'chunk_size': chunk_size,
                'tmp_file': tmp_filename,
                'name': filename
            }
        else:
            info = cache.get(cache_key_prefix + _id)
            # check things are matching up
            if info['last_chunk'] != chunk - 1:
                raise Exception('Invalid chunk sequence')
            if info['total_size'] != total_size:
                raise Exception('Invalid total size')
            if info['chunk_size'] != chunk_size:
                raise Exception('Inconsistent chunk size')
            info['last_chunk'] = chunk

        mode = 'wb'
        if chunk > 1:
            # appending to file now
            mode = 'ab+'
            if not os.path.exists(info['tmp_file']):
                raise Exception('No tmp upload file found')
        fi = open(info['tmp_file'], mode)

        while True:
            data = self.request.form['file'].read(2 << 16)
            if not data:
                break
            fi.write(data)
        fi.close()

        if chunk == total_chunks:
            # finish upload
            dup = False
            if not existing_id:
                try:
                    obj = self.create_file_content(info)
                except duplicates.DuplicateException as ex:
                    obj = ex.obj
                    dup = True
            else:
                try:
                    info['existing_id'] = existing_id
                    info['field_name'] = field_name
                    obj, success, msg = self.update_file_content(info)
                    if not success:
                        self.update_file_content(info)
                        self._clean_tmp(info)
                        return json.dumps({
                            'success': False,
                            'id': _id,
                            'reason': msg
                        })
                except Exception:
                    logger.warning(
                        'Failed to update content.', exc_info=True)
                    self._clean_tmp(info)
                    return json.dumps({
                        'success': False,
                        'id': _id
                    })
            if not info.get('field_name', '').startswith('tmp_'):
                # tmp files need to stick around and be managed later...
                self._clean_tmp(info)
            cache.delete(cache_key_prefix + _id)
            return dump_object_data(obj, dup)
        else:
            cache.set(cache_key_prefix + _id, info)
            check_put = None
            while check_put is None:
                try:
                    check_put = cache.get(cache_key_prefix + _id)
                except Exception:
                    cache.set(cache_key_prefix + _id, info)
        return json.dumps({
            'success': True,
            'id': _id
        })
예제 #25
0
    def chunk_upload(self):
        chunk = int(self.request.form['chunk'])
        chunk_size = int(self.request.form['chunkSize'])
        total_size = int(self.request.form['totalSize'])
        total_chunks = int(math.ceil(float(total_size) / float(chunk_size)))
        _id = self.request.form.get('id')

        if chunk > total_chunks:
            raise Exception("More chunks than what should be possible")

        cache_key_prefix = '%s-uploads-' % '/'.join(
            self.context.getPhysicalPath()[1:])
        if chunk == 1:
            # initializing chunk upload
            _id = utils.get_random_string(50)
            filename = self.request.form['name']
            tmp_dir = tempfile.mkdtemp()
            tmp_filename = os.path.join(tmp_dir, filename)
            info = {
                'last_chunk': 1,
                'total_size': total_size,
                'chunk_size': chunk_size,
                'tmp_file': tmp_filename,
                'name': filename
            }
        else:
            info = cache.get(cache_key_prefix + _id)
            # check things are matching up
            if info['last_chunk'] != chunk - 1:
                raise Exception('Invalid chunk sequence')
            if info['total_size'] != total_size:
                raise Exception('Invalid total size')
            if info['chunk_size'] != chunk_size:
                raise Exception('Inconsistent chunk size')
            info['last_chunk'] = chunk

        mode = 'wb'
        if chunk > 1:
            # appending to file now
            mode = 'ab+'
            if not os.path.exists(info['tmp_file']):
                raise Exception('No tmp upload file found')
        fi = open(info['tmp_file'], mode)

        while True:
            data = self.request.form['file'].read(2 << 16)
            if not data:
                break
            fi.write(data)
        fi.close()

        if chunk == total_chunks:
            # finish upload
            dup = False
            try:
                obj = self.create_file_content(info)
            except duplicates.DuplicateException as ex:
                obj = ex.obj
                dup = True
            tmp_dir = '/'.join(info['tmp_file'].split('/')[:-1])
            shutil.rmtree(tmp_dir)
            cache.delete(cache_key_prefix + _id)
            return dump_object_data(obj, dup)
        else:
            cache.set(cache_key_prefix + _id, info)
            check_put = None
            while check_put is None:
                try:
                    check_put = cache.get(cache_key_prefix + _id)
                except:
                    cache.set(cache_key_prefix + _id, info)
        return json.dumps({'success': True, 'id': _id})
예제 #26
0
    def parseRegistry(self):
        cache_key = '%s-mosaic-registry' % '/'.join(
            api.portal.get().getPhysicalPath()[1:])
        if not api.env.debug_mode():
            try:
                return cache.get(cache_key)
            except KeyError:
                result = super(CastleMosaicRegistry, self).parseRegistry()
        else:
            result = super(CastleMosaicRegistry, self).parseRegistry()

        mng = get_tile_manager()
        for tile in mng.get_tiles():
            if tile.get('hidden'):
                continue
            key = 'castle_cms_dynamic_{}'.format(tile['id'])
            category = tile.get('category') or 'advanced'
            category_id = category.replace(' ', '_').lower()
            if category_id not in result['plone']['app']['mosaic'][
                    'tiles_categories']:
                result['plone']['app']['mosaic']['tiles_categories'][
                    category_id] = {
                        'label': category,
                        'name': category_id,
                        'weight': 100
                    }
            result['plone']['app']['mosaic']['app_tiles'][key] = {
                'category': category_id,
                'default_value': None,
                'favorite': False,
                'label': tile['title'],
                'name': tile['name'],
                'tile_type_id': u'castle.cms.dynamic',
                'read_only': False,
                'rich_text': False,
                'settings': True,
                'tile_type': u'app',
                'weight': tile['weight']
            }

        registry = getUtility(IRegistry)
        settings = registry.forInterface(ITinyMCESchema,
                                         prefix="plone",
                                         check=False)
        if settings.libraries_spellchecker_choice != 'AtD':
            cache.set(cache_key, result, MOSAIC_CACHE_DURATION)
            return result

        # add atd config to toolbar dynamically
        mos_settings = result['plone']['app']['mosaic']
        mos_settings['richtext_toolbar']['AtD'] = {
            'category': u'actions',
            'name': u'toolbar-AtD',
            'weight': 0,
            'favorite': False,
            'label': u'After the deadline',
            'action': u'AtD',
            'icon': False
        }
        for widget_type in _rich_text_widget_types:
            mos_settings['widget_actions'][widget_type]['actions'].append(
                'toolbar-AtD')  # noqa
        mos_settings['structure_tiles']['text']['available_actions'].append(
            'toolbar-AtD')  # noqa
        mos_settings['app_tiles']['plone_app_standardtiles_rawhtml'][
            'available_actions'].append('toolbar-AtD')  # noqa
        cache.set(cache_key, result, MOSAIC_CACHE_DURATION)
        return result
예제 #27
0
 def get(self, cache_key=None):
     try:
         return cache.get(self.cache_key)
     except (AttributeError, KeyError):
         return None