예제 #1
0
파일: handlers.py 프로젝트: lakiw/cripts
def get_aggregate_comments(atype, value, username, date=None):
    """
    Generate a list of comments for the aggregate view.

    :param atype: How to limit the comments ("bytag", "byuser", "bycomment").
    :type atype: str
    :param value: If limiting by atype, the value to limit by.
    :type value: str
    :param username: The user getting the comments.
    :type username: str
    :param date: The specific date to get comments for.
    :type date: datetime.datetime
    :returns: list of :class:`cripts.comments.comment.Comment`
    """

    results = None
    if date:
        end_date = date+datetime.timedelta(days=1)
        query = {'date':{'$gte':date, '$lte':end_date}}
    else:
        query = {}
    if atype == 'bytag':
        query['tags'] = value
    elif atype == 'byuser':
        query['$or'] = [{'users':value}, {'analyst':value}]
    elif atype == 'bycomment':
        query['comment'] = {'$regex':value}

    results = Comment.objects(__raw__=query)
    sources = user_sources(username)
    return get_user_allowed_comments(results, sources)
예제 #2
0
def get_aggregate_comments(atype, value, username, date=None):
    """
    Generate a list of comments for the aggregate view.

    :param atype: How to limit the comments ("bytag", "byuser", "bycomment").
    :type atype: str
    :param value: If limiting by atype, the value to limit by.
    :type value: str
    :param username: The user getting the comments.
    :type username: str
    :param date: The specific date to get comments for.
    :type date: datetime.datetime
    :returns: list of :class:`cripts.comments.comment.Comment`
    """

    results = None
    if date:
        end_date = date + datetime.timedelta(days=1)
        query = {'date': {'$gte': date, '$lte': end_date}}
    else:
        query = {}
    if atype == 'bytag':
        query['tags'] = value
    elif atype == 'byuser':
        query['$or'] = [{'users': value}, {'analyst': value}]
    elif atype == 'bycomment':
        query['comment'] = {'$regex': value}

    results = Comment.objects(__raw__=query)
    sources = user_sources(username)
    return get_user_allowed_comments(results, sources)
예제 #3
0
파일: handlers.py 프로젝트: lakiw/cripts
def add_results(object_type, object_id, analysis_id, result, type_, subtype,
               analyst):
    """
    Add multiple results to an analysis task.

    :param object_type: The top-level object type.
    :type object_type: str
    :param object_id: The ObjectId to search for.
    :type object_id: str
    :param analysis_id: The ID of the task to update.
    :type analysis_id: str
    :param result: The list of result to append.
    :type result: list of str
    :param type_: The list of result types.
    :type type_: list of str
    :param subtype: The list of result subtypes.
    :type subtype: list of str
    :param analyst: The user updating the results.
    :type analyst: str
    :returns: dict with keys "success" (boolean) and "message" (str) if failed.
    """

    res = {'success': False}
    if not object_type or not object_id or not analysis_id:
        res['message'] = "Must supply object id/type and analysis id."
        return res

    # Validate user can add service results to this TLO.
    klass = class_from_type(object_type)
    sources = user_sources(analyst)
    obj = klass.objects(id=object_id, source__name__in=sources).first()
    if not obj:
        res['message'] = "Could not find object to add results to."
        return res

    if not(result and type_ and subtype):
        res['message'] = "Need a result, type, and subtype to add a result."
        return res

    if not(len(result) == len(type_) == len(subtype)):
        res['message'] = "result, type, and subtype need to be the same length."
        return res

    # Update analysis results
    final_list = []
    for key, r in enumerate(result):
        final = {}
        final['subtype'] = subtype[key]
        final['result'] = r
        tmp = ast.literal_eval(type_[key])
        for k in tmp:
            final[k] = tmp[k]
        final_list.append(final)

    ar = AnalysisResult.objects(analysis_id=analysis_id).first()
    if ar:
        AnalysisResult.objects(id=ar.id).update_one(push_all__results=final_list)
        
    res['success'] = True
    return res
예제 #4
0
def refresh_services(request, cripts_type, identifier):
    """
    Refresh the Analysis tab with the latest information.
    """

    response = {}

    # Verify user can see results.
    sources = user_sources(request.user.username)
    klass = class_from_type(cripts_type)
    if not klass:
        msg = 'Could not find object to refresh!'
        response['success'] = False
        response['html'] = msg
        return HttpResponse(json.dumps(response),
                            content_type="application/json")
    if hasattr(klass, 'source'):
        obj = klass.objects(id=identifier, source__name__in=sources).first()
    else:
        obj = klass.objects(id=identifier).first()
    if not obj:
        msg = 'Could not find object to refresh!'
        response['success'] = False
        response['html'] = msg
        return HttpResponse(json.dumps(response),
                            content_type="application/json")

    # Get analysis results.
    results = AnalysisResult.objects(object_type=cripts_type,
                                     object_id=identifier)

    relationship = {'type': cripts_type, 'value': identifier}

    subscription = {'type': cripts_type, 'id': identifier}

    service_list = get_supported_services(cripts_type)

    response['success'] = True
    response['html'] = render_to_string(
        "services_analysis_listing.html", {
            'relationship': relationship,
            'subscription': subscription,
            'service_results': results,
            'cripts_type': cripts_type,
            'identifier': identifier,
            'service_list': service_list
        }, RequestContext(request))

    return HttpResponse(json.dumps(response), content_type="application/json")
예제 #5
0
파일: views.py 프로젝트: lakiw/cripts
def refresh_services(request, cripts_type, identifier):
    """
    Refresh the Analysis tab with the latest information.
    """

    response = {}

    # Verify user can see results.
    sources = user_sources(request.user.username)
    klass = class_from_type(cripts_type)
    if not klass:
        msg = 'Could not find object to refresh!'
        response['success'] = False
        response['html'] = msg
        return HttpResponse(json.dumps(response), content_type="application/json")
    if hasattr(klass, 'source'):
        obj = klass.objects(id=identifier,source__name__in=sources).first()
    else:
        obj = klass.objects(id=identifier).first()
    if not obj:
        msg = 'Could not find object to refresh!'
        response['success'] = False
        response['html'] = msg
        return HttpResponse(json.dumps(response), content_type="application/json")

    # Get analysis results.
    results = AnalysisResult.objects(object_type=cripts_type,
                                     object_id=identifier)

    relationship = {'type': cripts_type,
                    'value': identifier}

    subscription = {'type': cripts_type,
                    'id': identifier}

    service_list = get_supported_services(cripts_type)

    response['success'] = True
    response['html'] = render_to_string("services_analysis_listing.html",
                                        {'relationship': relationship,
                                         'subscription': subscription,
                                         'service_results': results,
                                         'cripts_type': cripts_type,
                                         'identifier': identifier,
                                         'service_list': service_list},
                                        RequestContext(request))

    return HttpResponse(json.dumps(response), content_type="application/json")
예제 #6
0
파일: handlers.py 프로젝트: lakiw/cripts
def add_log(object_type, object_id, analysis_id, log_message, level, analyst):
    """
    Add a log entry to an analysis task.

    :param object_type: The top-level object type.
    :type object_type: str
    :param object_id: The ObjectId to search for.
    :type object_id: str
    :param analysis_id: The ID of the task to update.
    :type analysis_id: str
    :param log_message: The log entry to append.
    :type log_message: dict
    :param level: The log level.
    :type level: str
    :param analyst: The user updating the log.
    :type analyst: str
    :returns: dict with keys "success" (boolean) and "message" (str) if failed.
    """

    results = {'success': False}
    if not object_type or not object_id or not analysis_id:
        results['message'] = "Must supply object id/type and analysis id."
        return results

    # Validate user can add service results to this TLO.
    klass = class_from_type(object_type)
    sources = user_sources(analyst)
    obj = klass.objects(id=object_id, source__name__in=sources).first()
    if not obj:
        results['message'] = "Could not find object to add results to."
        return results

    # Update analysis log
    le = EmbeddedAnalysisResultLog()
    le.message = log_message
    le.level = level
    le.datetime = str(datetime.datetime.now())
    ar = AnalysisResult.objects(analysis_id=analysis_id).first()
    if ar:
        AnalysisResult.objects(id=ar.id).update_one(push__log=le)
        results['success'] = True
    else:
        results['message'] = "Could not find task to add log to."
    return results
예제 #7
0
파일: handlers.py 프로젝트: lakiw/cripts
def finish_task(object_type, object_id, analysis_id, status, analyst):
    """
    Finish a task by setting its status to "completed" and setting the finish
    date.

    :param object_type: The top-level object type.
    :type object_type: str
    :param object_id: The ObjectId to search for.
    :type object_id: str
    :param analysis_id: The ID of the task to update.
    :type analysis_id: str
    :param status: The status of the task.
    :type status: str ("error", "completed")
    :param analyst: The user updating the log.
    :type analyst: str
    :returns: dict with keys "success" (boolean) and "message" (str) if failed.
    """

    results = {'success': False}
    if not status:
        status = "completed"
    if status not in ('error', 'completed'):
        status = "completed"
    if not object_type or not object_id or not analysis_id:
        results['message'] = "Must supply object id/type and analysis id."
        return results

    # Validate user can add service results to this TLO.
    klass = class_from_type(object_type)
    sources = user_sources(analyst)
    obj = klass.objects(id=object_id, source__name__in=sources).first()
    if not obj:
        results['message'] = "Could not find object to add results to."
        return results

    # Update analysis log
    date = str(datetime.datetime.now())
    ar = AnalysisResult.objects(analysis_id=analysis_id).first()
    if ar:
        AnalysisResult.objects(id=ar.id).update_one(set__status=status,
                                                    set__finish_date=date)
    results['success'] = True
    return results
예제 #8
0
파일: handlers.py 프로젝트: lakiw/cripts
def create_notification(obj, username, message, source_filter=None,
                        notification_type=NotificationType.ALERT):
    """
    Generate a notification -- based on mongo obj.

    :param obj: The object.
    :type obj: class which inherits from
               :class:`cripts.core.cripts_mongoengine.CriptsBaseAttributes`
    :param username: The user creating the notification.
    :type username: str
    :param message: The notification message.
    :type message: str
    :param source_filter: Filter on who can see this notification.
    :type source_filter: list(str)
    :param notification_type: The notification type (e.g. alert, error).
    :type notification_type: str
    """

    n = Notification()
    n.analyst = username
    obj_type = obj._meta['cripts_type']
    users = set()

    if notification_type not in NotificationType.ALL:
        notification_type = NotificationType.ALERT

    n.notification_type = notification_type

    if obj_type == 'Comment':
        n.obj_id = obj.obj_id
        n.obj_type = obj.obj_type
        n.notification = "%s added a comment: %s" % (username, obj.comment)
        users.update(obj.users) # notify mentioned users

        # for comments, use the sources from the object that it is linked to
        # instead of the comments's sources
        obj = class_from_id(n.obj_type, n.obj_id)
    else:
        n.notification = message
        n.obj_id = obj.id
        n.obj_type = obj_type

    if hasattr(obj, 'source'):
        sources = [s.name for s in obj.source]
        subscribed_users = get_subscribed_users(n.obj_type, n.obj_id, sources)

        # Filter on users that have access to the source of the object
        for subscribed_user in subscribed_users:
            allowed_sources = user_sources(subscribed_user)

            for allowed_source in allowed_sources:
                if allowed_source in sources:
                    if source_filter is None or allowed_source in source_filter:
                        users.add(subscribed_user)
                        break
    else:
        users.update(get_subscribed_users(n.obj_type, n.obj_id, []))

    users.discard(username) # don't notify the user creating this notification
    n.users = list(users)
    if not len(n.users):
        return
    try:
        n.save()
    except ValidationError:
        pass

    # Signal potentially waiting threads that notification information is available
    for user in n.users:
        notification_lock = NotificationLockManager.get_notification_lock(user)
        notification_lock.acquire()

        try:
            notification_lock.notifyAll()
        finally:
            notification_lock.release()
예제 #9
0
파일: handlers.py 프로젝트: lakiw/cripts
def get_email_address_details(address, analyst):
    """
    Generate the data to render the Email Address details template.

    :param address: The name of the Address to get details for.
    :type address: str
    :param analyst: The user requesting this information.
    :type analyst: str
    :returns: template (str), arguments (dict)
    """

    template = None
    allowed_sources = user_sources(analyst)
    address_object = EmailAddress.objects(address=address,
                           source__name__in=allowed_sources).first()
    if not address_object:
        error = ("Either no data exists for this email address"
                 " or you do not have permission to view it.")
        template = "error.html"
        args = {'error': error}
        return template, args

    address_object.sanitize_sources(username="******" % analyst,
                           sources=allowed_sources)

    # remove pending notifications for user
    remove_user_from_notification("%s" % analyst, address_object.id, 'EmailAddress')

    # subscription
    subscription = {
            'type': 'EmailAddress',
            'id': address_object.id,
            'subscribed': is_user_subscribed("%s" % analyst,
                                             'EmailAddress',
                                             address_object.id),
    }

    #objects
    objects = address_object.sort_objects()

    #relationships
    relationships = address_object.sort_relationships("%s" % analyst, meta=True)

    # relationship
    relationship = {
            'type': 'EmailAddress',
            'value': address_object.id
    }

    #comments
    comments = {'comments': address_object.get_comments(),
                'url_key':address_object.address}

    # favorites
    favorite = is_user_favorite("%s" % analyst, 'EmailAddress', address_object.id)

    # services
    service_list = get_supported_services('EmailAddress')

    # analysis results
    service_results = address_object.get_analysis_results()

    args = {'email_address': address_object,
            'objects': objects,
            'relationships': relationships,
            'comments': comments,
            'favorite': favorite,
            'relationship': relationship,
            'subscription': subscription,
            'address': address_object.address,
            'service_list': service_list,
            'service_results': service_results}

    return template, args
예제 #10
0
def get_event_details(event_id, analyst):
    """
    Generate the data to render the Event details template.

    :param event_id: The ObjectId of the Event to get details for.
    :type event_id: str
    :param analyst: The user requesting this information.
    :type analyst: str
    :returns: template (str), arguments (dict)
    """

    template = None
    sources = user_sources(analyst)
    event = Event.objects(id=event_id, source__name__in=sources).first()
    if not event:
        template = "error.html"
        args = {'error': "ID does not exist or insufficient privs for source"}
        return template, args

    event.sanitize("%s" % analyst)

    download_form = DownloadFileForm(initial={
        "obj_type": 'Event',
        "obj_id": event_id
    })

    # remove pending notifications for user
    remove_user_from_notification("%s" % analyst, event.id, 'Event')

    # subscription
    subscription = {
        'type': 'Event',
        'id': event.id,
        'subscribed': is_user_subscribed("%s" % analyst, 'Event', event.id),
    }

    #objects
    objects = event.sort_objects()

    #relationships
    relationships = event.sort_relationships("%s" % analyst, meta=True)

    # Get count of related Events for each related Sample
    for smp in relationships.get('Sample', []):
        count = Event.objects(relationships__object_id=smp['id'],
                              source__name__in=sources).count()
        smp['rel_smp_events'] = count

    # relationship
    relationship = {'type': 'Event', 'value': event.id}

    #comments
    comments = {'comments': event.get_comments(), 'url_key': event.id}

    # favorites
    favorite = is_user_favorite("%s" % analyst, 'Event', event.id)

    # services
    service_list = get_supported_services('Event')

    # analysis results
    service_results = event.get_analysis_results()

    args = {
        'service_list': service_list,
        'objects': objects,
        'relationships': relationships,
        'comments': comments,
        'favorite': favorite,
        'relationship': relationship,
        'subscription': subscription,
        'event': event,
        'service_results': service_results,
        'download_form': download_form
    }

    return template, args
예제 #11
0
def add_sample_for_event(event_id,
                         data,
                         analyst,
                         filedata=None,
                         filename=None,
                         md5=None,
                         email_addr=None,
                         inherit_sources=False):
    """
    Add a sample related to this Event.

    :param event_id: The ObjectId of the Event to associate with.
    :type event_id: str
    :param data: The form data.
    :type data: dict
    :param analyst: The user adding this Sample.
    :type analyst: str
    :param filedata: The sample data.
    :type filedata: file handle.
    :param filename: The name of the file.
    :type filename: str
    :param md5: The MD5 of the file.
    :type md5: str
    :param email_addr: Email address to which to email the sample
    :type email_addr: str
    :param inherit_sources: 'True' if Sample should inherit Event's Source(s)
    :type inherit_sources: bool
    :returns: dict with keys "success" (boolean) and "message" (str)
    """

    response = {
        'success': False,
        'message': 'Unknown error; unable to upload file.'
    }
    users_sources = user_sources(analyst)
    event = Event.objects(id=event_id, source__name__in=users_sources).first()
    if not event:
        return {'success': False, 'message': "No matching event found"}
    source = data['source']
    reference = data['reference']
    file_format = data['file_format']
    bucket_list = data[form_consts.Common.BUCKET_LIST_VARIABLE_NAME]
    ticket = data[form_consts.Common.TICKET_VARIABLE_NAME]
    method = data['method']
    if filename:
        filename = filename.strip()

    inherited_source = event.source if inherit_sources else None

    try:
        if filedata:
            result = handle_uploaded_file(filedata,
                                          source,
                                          method,
                                          reference,
                                          file_format,
                                          data['password'],
                                          analyst,
                                          related_id=event.id,
                                          related_type='Event',
                                          filename=filename,
                                          bucket_list=bucket_list,
                                          ticket=ticket,
                                          inherited_source=inherited_source)
        else:
            result = handle_uploaded_file(None,
                                          source,
                                          method,
                                          reference,
                                          file_format,
                                          None,
                                          analyst,
                                          related_id=event.id,
                                          related_type='Event',
                                          filename=filename,
                                          md5=md5,
                                          bucket_list=bucket_list,
                                          ticket=ticket,
                                          inherited_source=inherited_source,
                                          is_return_only_md5=False)
    except ZipFileError, zfe:
        return {'success': False, 'message': zfe.value}
예제 #12
0
파일: handlers.py 프로젝트: lakiw/cripts
def get_email_address_details(address, analyst):
    """
    Generate the data to render the Email Address details template.

    :param address: The name of the Address to get details for.
    :type address: str
    :param analyst: The user requesting this information.
    :type analyst: str
    :returns: template (str), arguments (dict)
    """

    template = None
    allowed_sources = user_sources(analyst)
    address_object = EmailAddress.objects(
        address=address, source__name__in=allowed_sources).first()
    if not address_object:
        error = ("Either no data exists for this email address"
                 " or you do not have permission to view it.")
        template = "error.html"
        args = {'error': error}
        return template, args

    address_object.sanitize_sources(username="******" % analyst,
                                    sources=allowed_sources)

    # remove pending notifications for user
    remove_user_from_notification("%s" % analyst, address_object.id,
                                  'EmailAddress')

    # subscription
    subscription = {
        'type':
        'EmailAddress',
        'id':
        address_object.id,
        'subscribed':
        is_user_subscribed("%s" % analyst, 'EmailAddress', address_object.id),
    }

    #objects
    objects = address_object.sort_objects()

    #relationships
    relationships = address_object.sort_relationships("%s" % analyst,
                                                      meta=True)

    # relationship
    relationship = {'type': 'EmailAddress', 'value': address_object.id}

    #comments
    comments = {
        'comments': address_object.get_comments(),
        'url_key': address_object.address
    }

    # favorites
    favorite = is_user_favorite("%s" % analyst, 'EmailAddress',
                                address_object.id)

    # services
    service_list = get_supported_services('EmailAddress')

    # analysis results
    service_results = address_object.get_analysis_results()

    args = {
        'email_address': address_object,
        'objects': objects,
        'relationships': relationships,
        'comments': comments,
        'favorite': favorite,
        'relationship': relationship,
        'subscription': subscription,
        'address': address_object.address,
        'service_list': service_list,
        'service_results': service_results
    }

    return template, args
예제 #13
0
파일: handlers.py 프로젝트: lakiw/cripts
def get_event_details(event_id, analyst):
    """
    Generate the data to render the Event details template.

    :param event_id: The ObjectId of the Event to get details for.
    :type event_id: str
    :param analyst: The user requesting this information.
    :type analyst: str
    :returns: template (str), arguments (dict)
    """

    template = None
    sources = user_sources(analyst)
    event = Event.objects(id=event_id, source__name__in=sources).first()
    if not event:
        template = "error.html"
        args = {'error': "ID does not exist or insufficient privs for source"}
        return template, args

    event.sanitize("%s" % analyst)

    download_form = DownloadFileForm(initial={"obj_type": 'Event',
                                              "obj_id": event_id})

    # remove pending notifications for user
    remove_user_from_notification("%s" % analyst, event.id, 'Event')

    # subscription
    subscription = {
            'type': 'Event',
            'id': event.id,
            'subscribed': is_user_subscribed("%s" % analyst,
                                             'Event', event.id),
    }

    #objects
    objects = event.sort_objects()

    #relationships
    relationships = event.sort_relationships("%s" % analyst, meta=True)

    # Get count of related Events for each related Sample
    for smp in relationships.get('Sample', []):
        count = Event.objects(relationships__object_id=smp['id'],
                              source__name__in=sources).count()
        smp['rel_smp_events'] = count

    # relationship
    relationship = {
            'type': 'Event',
            'value': event.id
    }

    #comments
    comments = {'comments': event.get_comments(), 'url_key': event.id}

    # favorites
    favorite = is_user_favorite("%s" % analyst, 'Event', event.id)

    # services
    service_list = get_supported_services('Event')

    # analysis results
    service_results = event.get_analysis_results()

    args = {'service_list': service_list,
            'objects': objects,
            'relationships': relationships,
            'comments': comments,
            'favorite': favorite,
            'relationship': relationship,
            'subscription': subscription,
            'event': event,
            'service_results': service_results,
            'download_form': download_form}

    return template, args
예제 #14
0
파일: handlers.py 프로젝트: lakiw/cripts
def add_sample_for_event(event_id, data, analyst, filedata=None, filename=None,
                         md5=None, email_addr=None, inherit_sources=False):
    """
    Add a sample related to this Event.

    :param event_id: The ObjectId of the Event to associate with.
    :type event_id: str
    :param data: The form data.
    :type data: dict
    :param analyst: The user adding this Sample.
    :type analyst: str
    :param filedata: The sample data.
    :type filedata: file handle.
    :param filename: The name of the file.
    :type filename: str
    :param md5: The MD5 of the file.
    :type md5: str
    :param email_addr: Email address to which to email the sample
    :type email_addr: str
    :param inherit_sources: 'True' if Sample should inherit Event's Source(s)
    :type inherit_sources: bool
    :returns: dict with keys "success" (boolean) and "message" (str)
    """

    response = {'success': False,
                'message': 'Unknown error; unable to upload file.'}
    users_sources = user_sources(analyst)
    event = Event.objects(id=event_id, source__name__in=users_sources).first()
    if not event:
        return {'success': False,
                'message': "No matching event found"}
    source = data['source']
    reference = data['reference']
    file_format = data['file_format']
    bucket_list = data[form_consts.Common.BUCKET_LIST_VARIABLE_NAME]
    ticket = data[form_consts.Common.TICKET_VARIABLE_NAME]
    method = data['method']
    if filename:
        filename = filename.strip()


    inherited_source = event.source if inherit_sources else None

    try:
        if filedata:
            result = handle_uploaded_file(filedata,
                                          source,
                                          method,
                                          reference,
                                          file_format,
                                          data['password'],
                                          analyst,
                                          related_id=event.id,
                                          related_type='Event',
                                          filename=filename,
                                          bucket_list=bucket_list,
                                          ticket=ticket,
                                          inherited_source=inherited_source)
        else:
            result = handle_uploaded_file(None,
                                          source,
                                          method,
                                          reference,
                                          file_format,
                                          None,
                                          analyst,
                                          related_id=event.id,
                                          related_type='Event',
                                          filename=filename,
                                          md5=md5,
                                          bucket_list=bucket_list,
                                          ticket=ticket,
                                          inherited_source=inherited_source,
                                          is_return_only_md5=False)
    except ZipFileError, zfe:
        return {'success': False, 'message': zfe.value}
예제 #15
0
파일: handlers.py 프로젝트: lakiw/cripts
def create_notification(obj,
                        username,
                        message,
                        source_filter=None,
                        notification_type=NotificationType.ALERT):
    """
    Generate a notification -- based on mongo obj.

    :param obj: The object.
    :type obj: class which inherits from
               :class:`cripts.core.cripts_mongoengine.CriptsBaseAttributes`
    :param username: The user creating the notification.
    :type username: str
    :param message: The notification message.
    :type message: str
    :param source_filter: Filter on who can see this notification.
    :type source_filter: list(str)
    :param notification_type: The notification type (e.g. alert, error).
    :type notification_type: str
    """

    n = Notification()
    n.analyst = username
    obj_type = obj._meta['cripts_type']
    users = set()

    if notification_type not in NotificationType.ALL:
        notification_type = NotificationType.ALERT

    n.notification_type = notification_type

    if obj_type == 'Comment':
        n.obj_id = obj.obj_id
        n.obj_type = obj.obj_type
        n.notification = "%s added a comment: %s" % (username, obj.comment)
        users.update(obj.users)  # notify mentioned users

        # for comments, use the sources from the object that it is linked to
        # instead of the comments's sources
        obj = class_from_id(n.obj_type, n.obj_id)
    else:
        n.notification = message
        n.obj_id = obj.id
        n.obj_type = obj_type

    if hasattr(obj, 'source'):
        sources = [s.name for s in obj.source]
        subscribed_users = get_subscribed_users(n.obj_type, n.obj_id, sources)

        # Filter on users that have access to the source of the object
        for subscribed_user in subscribed_users:
            allowed_sources = user_sources(subscribed_user)

            for allowed_source in allowed_sources:
                if allowed_source in sources:
                    if source_filter is None or allowed_source in source_filter:
                        users.add(subscribed_user)
                        break
    else:
        users.update(get_subscribed_users(n.obj_type, n.obj_id, []))

    users.discard(username)  # don't notify the user creating this notification
    n.users = list(users)
    if not len(n.users):
        return
    try:
        n.save()
    except ValidationError:
        pass

    # Signal potentially waiting threads that notification information is available
    for user in n.users:
        notification_lock = NotificationLockManager.get_notification_lock(user)
        notification_lock.acquire()

        try:
            notification_lock.notifyAll()
        finally:
            notification_lock.release()