Ejemplo n.º 1
0
def evolve(root):
    former_id = 'formeruser'

    profiles = find_profiles(root)
    search = ICatalogSearch(root)
    catalog = find_catalog(root)
    creators = catalog['creator']._fwd_index.keys()
    modifiers = catalog['modified_by']._fwd_index.keys()
    userids = set(creators) | set(modifiers)
    for userid in userids:
        if userid not in profiles:
            if former_id not in profiles:
                workflow = get_workflow(IProfile, 'security')
                former_id = make_unique_name(profiles, 'formeruser')

                print "Creating profile for former user content:", former_id
                former_profile = create_content(IProfile,
                                                firstname='Former',
                                                lastname='User')
                profiles[former_id] = former_profile
                workflow.initialize(former_profile)
                workflow.transition_to_state(former_profile, None, 'inactive')

            count, docids, resolver = search(creator=userid)
            for docid in docids:
                doc = resolver(docid)
                print "Updating 'creator' for", resource_path(doc)
                doc.creator = former_id

            count, docids, resolver = search(modified_by=userid)
            for docid in docids:
                doc = resolver(docid)
                print "Updating 'modified_by' for", resource_path(doc)
                doc.modified_by = former_id
Ejemplo n.º 2
0
Archivo: login.py Proyecto: lslaz1/karl
    def validate(self):
        email = self.request.POST.get('email', '').lower()
        self.data = {'email': email, 'date_requested': datetime.utcnow()}
        if not email or not EMAIL_RE.match(email):
            self.errors.append('Must provide valid email')
        if email in self.context.access_requests:
            self.errors.append('You have already requested access')

        search = ICatalogSearch(self.context)
        total, docids, resolver = search(email=email, interfaces=[IProfile])
        if total:
            self.errors.append('You have already have access to system')

        for field in self.fields:
            val = self.request.POST.get(field['id'], '')
            if not val:
                self.errors.append('Must provide %s' % field['label'])
            else:
                self.data[field['id']] = val

        if not verify_recaptcha(
                self.context, self.request,
                self.request.POST.get('g-recaptcha-response', '')):
            self.errors.append('Invalid recaptcha')
        return len(self.errors) == 0
Ejemplo n.º 3
0
def evolve(context):
    # add default category and layer to all calendars
    # Prevent 'set_created' event handler from being called since it will,
    # in turn, set the content_modified attribute of community which is used
    # as the "Last Activity" in the user interface.  We don't want this tweak
    # to impact a community's last activity.  This means we need to set created
    # and modified on the new layers and categories ourselves.
    registry = getSiteManager()
    registry.adapters.unsubscribe((IContent, IObjectWillBeAddedEvent), None,
                                  set_created)
    try:
        search = ICatalogSearch(context)
        default_category_name = ICalendarCategory.getTaggedValue(
            'default_name')
        default_layer_name = ICalendarLayer.getTaggedValue('default_name')
        now = datetime.now()

        cnt, docids, resolver = search(interfaces=[ICalendar])
        for docid in docids:
            calendar = resolver(docid)
            default_category = create_content(ICalendarCategory, 'Default')
            default_category.created = default_category.modified = now
            if not default_category_name in calendar:
                calendar[default_category_name] = default_category
                local_layer = create_content(ICalendarLayer,
                                             "This Calendar's Events Only",
                                             'blue',
                                             [resource_path(default_category)])
                local_layer.created = local_layer.modified = now
                if not default_layer_name in calendar:
                    calendar[default_layer_name] = local_layer
    finally:
        registry.adapters.subscribe((IContent, IObjectWillBeAddedEvent), None,
                                    set_created)
Ejemplo n.º 4
0
def evolve(root):
    catalog = find_catalog(root)
    mimetype_index = catalog['mimetype']
    search = ICatalogSearch(root)
    docid_for_addr = catalog.document_map.docid_for_address
    count, docids, resolver = search(interfaces=[ICommunityFile],
                                     mimetype={
                                         'operator':
                                         'or',
                                         'query': [
                                             'application/x-download',
                                             'application/x-application',
                                             'application/binary',
                                             'application/octet-stream',
                                         ]
                                     })

    for docid in docids:
        doc = resolver(docid)
        mimetype = mimetypes.guess_type(doc.filename)[0]
        if mimetype is not None and mimetype != doc.mimetype:
            addr = resource_path(doc)
            print "Updating mimetype for %s: %s" % (addr, mimetype)
            doc.mimetype = mimetype
            mimetype_index.reindex_doc(docid_for_addr(addr), doc)
Ejemplo n.º 5
0
def collect_profile_metrics(context, year, month):
    result = {}
    search = ICatalogSearch(context)
    begin, end = date_range(year, month)

    users = find_users(context)
    staff_set = users.users_in_group('group.KarlStaff')

    _, docids, resolver = search(
        creation_date=(None, end),
        interfaces=[IProfile],
    )

    for profile in (resolver(docid) for docid in docids):
        userid = profile.__name__
        created_count, _, _ = search(creation_date=(begin, end),
                                     creator=userid)
        total_count, _, _ = search(creation_date=(None, end), creator=userid)
        is_staff = userid in staff_set
        result[userid] = dict(
            total=total_count,
            created=created_count,
            is_staff=is_staff,
        )
    return result
Ejemplo n.º 6
0
Archivo: members.py Proyecto: zagy/karl
def member_search_json_view(context, request):
    try:
        # Query parameter shall be 'term'.
        prefix = request.params['term']
    except UnicodeDecodeError:
        # not utf8, just return empty list since tags can't have these chars
        result = JSONEncoder().encode([])
        return Response(result, content_type="application/x-json")
    # case insensitive
    prefix = prefix.lower()
    community = find_interface(context, ICommunity)
    member_names = community.member_names
    moderator_names = community.moderator_names
    community_member_names = member_names.union(moderator_names)
    query = dict(
        member_name='%s*' % prefix,
        sort_index='title',
        limit=20,
    )
    searcher = ICatalogSearch(context)
    try:
        total, docids, resolver = searcher(**query)
        profiles = filter(None, map(resolver, docids))
        records = [
            dict(
                value=profile.__name__,
                label=profile.title,
            ) for profile in profiles
            if profile.__name__ not in community_member_names
            and profile.security_state != 'inactive'
        ]
    except ParseError:
        records = []
    result = JSONEncoder().encode(records)
    return Response(result, content_type="application/x-json")
Ejemplo n.º 7
0
Archivo: login.py Proyecto: lslaz1/karl
    def create_access_request(self):
        email = self.data.get('email')
        system_name = get_setting(self.context, 'title')
        self.context.access_requests[email] = self.data
        mailer = getUtility(IMailDelivery)
        message = MIMEMultipart('alternative')
        message['Subject'] = '%s Access Request(%s)' % (
            system_name, self.data.get('fullname'))
        message['From'] = get_setting(self.context, 'admin_email')
        bodyhtml = u'''<html><body>
<p>New access request has been submitted for the site %s</p>
<p><b>Email</b>: %s <br />
%s
</p>
</body></html>''' % (self.request.application_url, email, '<br />'.join([
            _email_field_tmp % (f['label'], self.data.get(f['id'], ''))
            for f in self.fields
        ]))
        bodyplain = html2text.html2text(bodyhtml)
        htmlpart = MIMEText(bodyhtml.encode('UTF-8'), 'html', 'UTF-8')
        plainpart = MIMEText(bodyplain.encode('UTF-8'), 'plain', 'UTF-8')
        message.attach(plainpart)
        message.attach(htmlpart)

        # First, send mail to all admins
        users = find_users(self.context)
        search = ICatalogSearch(self.context)
        count, docids, resolver = search(interfaces=[IProfile])
        for docid in docids:
            profile = resolver(docid)
            if getattr(profile, 'security_state', None) == 'inactive':
                continue
            userid = profile.__name__
            if not users.member_of_group(userid, 'group.KarlAdmin'):
                continue
            copyofmsg = copy.deepcopy(message)
            fullemail = '%s <%s>' % (profile.title, profile.email)
            copyofmsg['To'] = fullemail
            mailer.send([profile.email], copyofmsg)

        # next, send to person that submitted
        message = MIMEMultipart('alternative')
        message['Subject'] = 'Access Request to %s' % system_name
        message['From'] = get_setting(self.context, 'admin_email')
        user_message = get_setting(
            self.context, 'request_access_user_message', '') % (SafeDict(
                self.data, {'system_name': system_name}))
        bodyhtml = u'<html><body>%s</body></html>' % user_message
        bodyplain = html2text.html2text(bodyhtml)
        htmlpart = MIMEText(bodyhtml.encode('UTF-8'), 'html', 'UTF-8')
        plainpart = MIMEText(bodyplain.encode('UTF-8'), 'plain', 'UTF-8')
        message.attach(plainpart)
        message.attach(htmlpart)
        copyofmsg = copy.deepcopy(message)
        fullemail = '%s <%s>' % (self.data.get('fullname', ''), email)
        copyofmsg['To'] = fullemail
        mailer.send([email], copyofmsg)

        self.submitted = True
        self.errors.append('Successfully requested access')
Ejemplo n.º 8
0
Archivo: members.py Proyecto: zagy/karl
def jquery_member_search_view(context, request):
    prefix = request.params['val'].lower()
    community = find_interface(context, ICommunity)
    member_names = community.member_names
    moderator_names = community.moderator_names
    community_member_names = member_names.union(moderator_names)
    query = dict(
        member_name='%s*' % prefix,
        sort_index='title',
        limit=20,
    )
    searcher = ICatalogSearch(context)
    try:
        total, docids, resolver = searcher(**query)
        profiles = filter(None, map(resolver, docids))
        records = [
            dict(
                id=profile.__name__,
                text=profile.title,
            ) for profile in profiles
            if profile.__name__ not in community_member_names
            and profile.security_state != 'inactive'
        ]
    except ParseError:
        records = []
    result = JSONEncoder().encode(records)
    return Response(result, content_type="application/x-json")
Ejemplo n.º 9
0
def evolve(context):
    """
    Upgrades required for new Image Drawer functionality.
    """
    # Add IImage marker to instances of ICommunityFile which are images.
    catalog = find_catalog(context)
    search = ICatalogSearch(context)
    cnt, docids, resolver = search(interfaces=[ICommunityFile])
    for docid in docids:
        obj = resolver(docid)
        if obj is None:
            continue  # Work around catalog bug
        obj._init_image()
        if obj.is_image:
            print "Image: %s" % resource_path(obj)
            catalog.reindex_doc(obj.docid, obj)

    # Convert WikiPages to Folders so they can contain attachments
    cnt, docids, resolver = search(interfaces=[IWikiPage])
    for docid in docids:
        obj = resolver(docid)
        if obj is None:
            continue  # Work around catalog bug
        print "Convert wiki page to folder: %s" % resource_path(obj)
        Folder.__init__(obj)
        catalog.reindex_doc(obj.docid, obj)
Ejemplo n.º 10
0
def get_members_batch(community, request, size=10):
    mods = list(community.moderator_names)
    any = list(community.member_names | community.moderator_names)
    principals = effective_principals(request)
    searcher = ICatalogSearch(community)
    total, docids, resolver = searcher(
        interfaces=[IProfile],
        limit=size,
        name={
            'query': any,
            'operator': 'or'
        },
        allowed={
            'query': principals,
            'operator': 'or'
        },
    )
    mod_entries = []
    other_entries = []

    for docid in docids:
        model = resolver(docid)
        if model is not None:
            if model.__name__ in mods:
                mod_entries.append(model)
            else:
                other_entries.append(model)

    return (mod_entries + other_entries)[:size]
Ejemplo n.º 11
0
def all_forums_view(context, request):
    page_title = "Message Boards"
    api = TemplateAPI(context, request, page_title)

    # Don't use the forums folder as the starting point, use its
    # parent (the community) to allow recursion
    context_path = model_path(context.__parent__)

    searcher = ICatalogSearch(context)
    total, docids, resolver = searcher(
        interfaces=[ICommunity],
        path={'query': context_path, 'include_path': True},
        allowed={'query': effective_principals(request),
                 'operator': 'or'},
        sort_index='title',
    )

    community_data = []

    for docid in docids:
        community = resolver(docid)
        if community is not None:
            forum_data = get_forum_data(community, request)
            if forum_data:
                community_data.append({'title': community.title,
                                       'forum_data': forum_data})

    return render_to_response(
        'templates/all_forums.pt',
        {'api': api,
         'community_data': community_data},
        request=request
    )
Ejemplo n.º 12
0
def evolve(root):
    former_id = None  # Create lazily, in case we don't need it

    profiles = find_profiles(root)
    search = ICatalogSearch(root)
    catalog = find_catalog(root)
    creators = catalog['creator']._fwd_index.keys()
    modifiers = catalog['modified_by']._fwd_index.keys()
    userids = set(creators) | set(modifiers)
    for userid in userids:
        if userid not in profiles:
            if former_id is None:
                former_id = make_unique_name(profiles, 'formeruser')

                print "Creating profile for former user content:", former_id
                former_profile = create_content(IProfile,
                                                firstname='Former',
                                                lastname='User')
                profiles[former_id] = former_profile

            count, docids, resolver = search(creator=userid)
            for docid in docids:
                doc = resolver(docid)
                print "Updating 'creator' for", resource_path(doc)
                doc.creator = former_id

            count, docids, resolver = search(modified_by=userid)
            for docid in docids:
                doc = resolver(docid)
                print "Updating 'modified_by' for", resource_path(doc)
                doc.modified_by = former_id
Ejemplo n.º 13
0
Archivo: people.py Proyecto: zagy/karl
def show_profiles_view(context, request):
    system_name = get_setting(context, 'system_name', 'KARL')
    page_title = '%s Profiles' % system_name
    api = TemplateAPI(context, request, page_title)

    # Grab the data for the two listings, main communities and portlet
    search = ICatalogSearch(context)

    query = dict(sort_index='title', interfaces=[IProfile], limit=5)

    titlestartswith = request.params.get('titlestartswith')
    if titlestartswith:
        query['titlestartswith'] = (titlestartswith, titlestartswith)

    num, docids, resolver = search(**query)

    profiles = []
    for docid in docids:
        model = resolver(docid)
        if model is None:
            continue
        profiles.append(model)

    mgr = ILetterManager(context)
    letter_info = mgr.get_info(request)

    return render_to_response(
        'templates/profiles.pt',
        dict(api=api,
             profiles=profiles,
             letters=letter_info),
        request=request,
        )
Ejemplo n.º 14
0
def related_communities_ajax_view(context, request):
    assert ICommunity.providedBy(context), str(type(context))

    related = []
    principals = effective_principals(request)
    searcher = ICatalogSearch(context)
    search = ' OR '.join(context.title.lower().split())
    total, docids, resolver = searcher(
        interfaces=[ICommunity],
        limit=5,
        reverse=True,
        sort_index="modified_date",
        texts=search,
        allowed={
            'query': principals,
            'operator': 'or'
        },
    )
    for docid in docids:
        model = resolver(docid)
        if model is not None:
            if model is not context:
                adapted = getMultiAdapter((model, request), IGridEntryInfo)
                related.append(adapted)

    return {'items': related}
Ejemplo n.º 15
0
def debugsearch(context, **kw):
    searcher = ICatalogSearch(context)
    kw['use_cache'] = False
    num, docids, resolver = searcher(**kw)
    L = []
    for docid in docids:
        L.append(resolver(docid))
    return num, L
Ejemplo n.º 16
0
def _iter_userids(context, request, profile_text):
    """Yield userids given a profile text search string."""
    search = ICatalogSearch(context)
    num, docids, resolver = search(interfaces=[IProfile], texts=profile_text)
    for docid in docids:
        profile = resolver(docid)
        if profile:
            yield profile.__name__
Ejemplo n.º 17
0
def number_of_comments(forum, request):
    searcher = ICatalogSearch(forum)
    total, docids, resolver = searcher(
        interfaces=[IComment],
        path={'query': resource_path(forum)},
        allowed={'query': effective_principals(request), 'operator': 'or'},
        )
    return total
Ejemplo n.º 18
0
    def _reportAddresses(self, report):
        query = report.getQuery()
        searcher = ICatalogSearch(report)
        total, docids, resolver = searcher(**query)

        for docid in docids:
            profile = resolver(docid)
            if profile is not None:
                yield profile.email
Ejemplo n.º 19
0
    def _reportAddresses(self, report):
        query = report.getQuery()
        searcher = ICatalogSearch(report)
        total, docids, resolver = searcher(**query)

        for docid in docids:
            profile = resolver(docid)
            security_state = getattr(profile, 'security_state')
            if profile is not None and security_state != 'inactive':
                yield profile.email
Ejemplo n.º 20
0
def get_catalog_batch(context, request, **kw):
    batch_start = kw.pop('batch_start', 0)
    batch_start = int(request.params.get("batch_start", batch_start))
    batch_size = kw.pop('batch_size', 20)
    batch_size = int(
        request.params.get("batch_size",
                           request.params.get('limit', batch_size)))
    sort_index = kw.pop('sort_index', None)
    sort_index = request.params.get('sort_index', sort_index)
    reverse = kw.pop('reverse', False)
    reverse = bool(int(request.params.get('reverse', reverse)))

    # XXX Asserting a default 'modified' sort order here is
    # fragrant.  It's unclear which callers depend on the behavior
    # and which don't.  Most callers probably don't even know we
    # do this.  We should make this each caller's responsibility
    # instead of embedding this policy here.
    if sort_index is None:
        sort_index = 'modified_date'
    kw['sort_index'] = sort_index
    # the reverse parameter is only useful when there's a sort index
    kw['reverse'] = reverse

    # Adding a limit will cause some indexes to use an NBEST sort
    # which is dramatically faster than sorting an entire large result set.
    kw['limit'] = batch_start + batch_size

    searcher = ICatalogSearch(context)
    numdocs, docids, resolver = searcher(**kw)

    # Use of indirection here is to avoid having to rewrite 70 tests to
    # use repoze.catalog.catalog.ResultSetSize instead of int
    total = getattr(numdocs, 'total', numdocs)
    if total < batch_start:
        batch_start = total

    # Lazily slice a lazy generator for getting models from result set
    docs = (model for model in (resolver(docid) for docid in docids)
            if model is not None)
    batch = list(islice(docs, batch_start, batch_start + batch_size))
    batch_end = batch_start + len(batch)

    info = {
        'entries': batch,
        'batch_start': batch_start,
        'batch_size': batch_size,
        'batch_end': batch_end,
        'total': getattr(numdocs, 'total', numdocs),
        'sort_index': sort_index,
        'reverse': reverse,
    }

    _add_link_data(info, context, request)
    return info
Ejemplo n.º 21
0
def evolve(root):
    search = ICatalogSearch(root)
    catalog = find_catalog(root)
    index = catalog['mimetype']
    for old_type, new_type in ie_types.items():
        cnt, docids, resolver = search(mimetype=old_type)
        for docid in docids:
            doc = resolver(docid)
            print "Adjusting mimetype for %s" % resource_path(doc)
            doc.mimetype = new_type
            index.reindex_doc(docid, doc)
Ejemplo n.º 22
0
def evolve(site):
    """Convert profile's '_pending_alerts' to an Accumulator.
    """
    catalog = find_catalog(site)
    search = ICatalogSearch(site)
    count, docids, resolver = search(
        interfaces={'query': [IProfile]})
    for docid in docids:
        doc = resolver(docid)
        alerts = doc._pending_alerts
        if not isinstance(alerts, Accumulator):
            doc._pending_alerts = Accumulator(list(alerts))
Ejemplo n.º 23
0
def evolve(site):
    print "Updating acl for /"
    update_acl(site)

    catalog = find_catalog(site)
    search = ICatalogSearch(site)
    cnt, docids, resolver = search(interfaces=[ICommunity])
    for docid in docids:
        obj = resolver(docid)
        if obj is None:
            continue  # Work around catalog bug
        print "Updating acl for %s" % resource_path(obj)
        update_acl(obj)
Ejemplo n.º 24
0
Archivo: login.py Proyecto: lslaz1/karl
def _get_valid_login(context, users, login):
    """ could be username or email """
    user = users.get(login=login)
    if user:
        return user
    # now try to see if email
    search = ICatalogSearch(context)
    count, docids, resolver = search(interfaces=[IProfile],
                                     email=login.lower())
    if count == 1:
        profile = resolver(docids[0])
        if profile.security_state != 'inactive':
            return users.get(userid=profile.__name__)
Ejemplo n.º 25
0
def evolve(root):
    search = ICatalogSearch(root)
    num, docids, resolver = search(interfaces=[IProfile])
    allowed_index = find_catalog(root)['allowed']
    for docid in docids:
        profile = resolver(docid)
        if profile.security_state != 'inactive':
            continue

        print "Updating acl for %s" % resource_path(profile)
        workflow = get_workflow(IProfile, 'security', profile)
        workflow.reset(profile)
        allowed_index.reindex_doc(docid, profile)
Ejemplo n.º 26
0
def get_forum_data(community, request):
    karldates = getUtility(IKarlDates)
    searcher = ICatalogSearch(community)
    total, docids, resolver = searcher(
        interfaces=[IForum],
        path={'query': model_path(community), 'depth': 2},
        allowed={'query': effective_principals(request),
                 'operator': 'or'},
        sort_index='title',
    )

    if not total:
        return None

    forum_data = []

    profiles = find_profiles(community)
    profiles_href = model_url(profiles, request)

    for docid in docids:
        forum = resolver(docid)
        if forum is not None:
            D = {}
            D['title'] = forum.title
            D['url'] = model_url(forum, request)
            D['number_of_topics'] = len(forum)
            D['number_of_comments'] = number_of_comments(forum, request)

            latest = latest_object(forum, request)

            _NOW = datetime.datetime.now()

            if latest:
                D['latest_activity_url'] = model_url(latest, request)
                D['latest_activity_link'] = getattr(latest, 'title', None)
                creator = getattr(latest, 'creator', None)
                D['latest_activity_byhref'] = profiles_href + creator
                profile = profiles[creator]
                D['latest_activity_byname'] = profile.title
                modified = getattr(latest, 'modified_date', _NOW)
                modified_str = karldates(modified, 'longform')
                D['latest_activity_at'] = modified_str
            else:
                D['latest_activity_url'] = None
                D['latest_activity_link'] = None
                D['latest_activity_by'] = None
                D['latest_activity_at'] = None

            forum_data.append(D)

    return forum_data
Ejemplo n.º 27
0
def latest_object(forum, request):
    searcher = ICatalogSearch(forum)
    total, docids, resolver = searcher(
        sort_index='modified_date',
        interfaces={'query': [IForumTopic, IComment], 'operator':'or'},
        path={'query': resource_path(forum)},
        allowed={'query': effective_principals(request), 'operator': 'or'},
        reverse=True)

    docids = list(docids)
    if docids:
        return resolver(docids[0])
    else:
        return None
Ejemplo n.º 28
0
def evolve(site):
    """
    Add the IPhoto marker interface to profile and news item photos.
    """
    catalog = find_catalog(site)
    search = ICatalogSearch(site)
    count, docids, resolver = search(
        interfaces={'operator': 'or', 'query': [INewsItem, IProfile]})
    for docid in docids:
        doc = resolver(docid)
        photo = doc.get('photo')
        if photo is not None and not IPhoto.providedBy(photo):
            alsoProvides(photo, IPhoto)
            catalog.reindex_doc(photo.docid, photo)
Ejemplo n.º 29
0
def archive_to_box_view(context, request):
    """
    Archive inactive communities to the Box storage service.
    """
    api = AdminTemplateAPI(context, request, 'Admin UI: Archive to Box')
    communities = None
    box = find_box(context)
    client = BoxClient(box, request.registry.settings)
    logged_in = False
    state = request.params.get('state', None)

    if state:
        if state == box.state:
            client.authorize(request.params['code'])
        else:
            raise HTTPBadRequest("Box state does not match")
        state = box.state = None

#return HTTPFound(request.path_url)

    if box.logged_in:
        logged_in = True
        # Find inactive communities
        search = ICatalogSearch(context)
        now = datetime.datetime.now()
        timeago = now - datetime.timedelta(days=425)  # ~14 months
        count, docids, resolver = search(
            interfaces=[ICommunity],
            content_modified=(None, coarse_datetime_repr(timeago)))
        communities = [{
            'title': community.title,
            'url': request.resource_url(community),
            'path': resource_path(community)
        } for community in (resolver(docid) for docid in docids)]
        communities.sort(key=itemgetter('path'))

    if not box.logged_in:
        state = box.state = str(uuid.uuid4())

    return {
        'api': api,
        'menu': _menu_macro(),
        'communities': communities,
        'logged_in': logged_in,
        'state': state,
        'client_id': client.client_id,
        'authorize_url': client.authorize_url,
        'redirect_uri': request.path_url,
    }
Ejemplo n.º 30
0
Archivo: sso.py Proyecto: hj91/karl
def verified_email_user_finder(site, context):
    email = context.profile.get('verifiedEmail')
    if not email:
        return None

    search = ICatalogSearch(site)
    count, docids, resolver = search(interfaces=[IProfile], email=email)
    if not count:
        return None
    if count > 1:
        log.warn('Unable to authenticate ambiguous email address: %s' % email)
        return None
    profile = resolver(docids[0])
    users = find_users(site)
    return users.get(profile.__name__)