Owen Nelson avatar Owen Nelson committed eca5d4a

moving the package to repo root because it sucks having a src dir

Comments (0)

Files changed (20)

MANIFEST.in

-include LICENSE
-include README
-recursive-include src/trawler/templates *.html
-
 from setuptools import setup, find_packages
-import sys
-
-sys.path.insert(0, 'src')
-
-import trawler
-
+from trawler import __version__
 setup(
     name="django-trawler",
-    version=trawler.__version__,
+    version=__version__,
     url="https://bitbucket.org/onelson/django-trawler/",
     author="Owen Nelson",
     author_email="onelson@gmail.com",
     license="MIT",
     description="Django App for running phishing campaigns (for staff security"
     " awareness training)",
-    keywords="phishing,security,django",
-    package_dir={'':'src'},
-    packages=find_packages('src'),
-    setup_requires=('setuptools'),
-    install_requires=('setuptools', 'django>=1.3'),
+    keywords="phishing, security, django",
+#    packages=find_packages(exclude=['test_proj*', 'test_proj.*', 'test_proj']),
+    packages=['trawler'],
+    package_data = {
+        'trawler': ['*.html'],
+    },
+    setup_requires=['setuptools'],
+    install_requires=['setuptools', 'django>=1.3'],
+    classifiers=[
+        "Development Status :: 3 - Alpha",
+        "Environment :: Web Environment",
+        "Framework :: Django",
+        "Intended Audience :: Developers",
+        "License :: OSI Approved :: MIT License",
+        "Operating System :: OS Independent",
+        "Programming Language :: Python",
+        "Topic :: Internet :: WWW/HTTP",
+        "Topic :: Internet :: WWW/HTTP :: Dynamic Content",
+        "Topic :: Internet :: WWW/HTTP :: WSGI",
+        "Topic :: Software Development :: Libraries :: "
+                              "Application Frameworks",
+        "Topic :: Software Development :: Libraries :: Python Modules"],
+
 )
 

src/trawler/__init__.py

-__version__ = '0.1a'
-

src/trawler/admin.py

-from django.contrib import admin
-from trawler.models import Campaign, Target
-
-class TargetInline(admin.TabularInline):
-    model = Target
-
-class CampaignAdmin(admin.ModelAdmin):
-
-    list_display = ['title', 'created', 'updated']
-    date_hierarchy = 'created'
-    actions = ['send_test_email', 'launch_campaign']
-
-    inlines = [TargetInline, ]
-
-    def send_test_email(self, request, queryset):
-        # target with pk=0 means we're testing. Hits should not be counted.
-        tmp_target = Target(email=request.user.email, pk=0)
-        # TODO: figure out a way to pass in extra_context values for this test 
-        for campaign in queryset:
-            tmp_target.campaign = campaign
-            tmp_target.dispatch()
-
-        stuff = 'email'
-        count = queryset.count()
-        if count > 1:
-            stuff = 'emails'
-
-        self.message_user(request, "%d test %s sent to %s" % \
-                          (count, stuff, request.user.email))
-
-    def launch_campaign(self, request, queryset):
-        [campaign.bulk_dispatch() for campaign in queryset]
-
-        stuff = 'campaign'
-        count = queryset.count()
-        if count > 1:
-            stuff = 'campaigns'
-
-        self.message_user(request, "%d %s launched. Game on!" % (count, stuff))
-
-admin.site.register(Campaign, CampaignAdmin)

src/trawler/models.py

-from django.db import models
-from django.core.mail import EmailMessage, EmailMultiAlternatives
-from datetime import datetime
-from django.template import Context, Template
-
-class Campaign(models.Model):
-    title = models.CharField(max_length=45)
-    sender_email = models.EmailField()
-    sender_name = models.CharField(max_length=60, blank=True,
-                                   help_text='Name to display for the sender'
-                                   ' (can be used to primitavely obfuscate the'
-                                   ' email address)')
-    subject = models.CharField(max_length=255)
-
-    email_plain = models.TextField()
-    email_html = models.TextField(blank=True)
-    img = models.FileField(upload_to='uploads/%Y/%m/%d', null=True, blank=True)
-
-    created = models.DateTimeField(auto_now_add=True, editable=False)
-    updated = models.DateTimeField(auto_now=True, editable=False)
-
-
-    def __unicode__(self):
-        return self.title
-
-    def bulk_dispatch(self):
-        [target.dispatch() for target in self.targets.all()]
-        self.save() # to update the self.updated value 
-
-    def get_percents(self):
-        """
-        Primitive % calculator for pass/fail.
-        """
-        total = self.targets.count()
-        # FIXME: there's totally a divide by zero problem here.
-        return dict(link=(float(self.targets.filter(link_followed=True
-                                                    ).count()) / total) * 100,
-                    img=(float(self.targets.filter(image_viewed=True
-                                                     ).count()) / total) * 100,
-                    good=(float(self.targets.filter(image_viewed=False,
-                                                    link_followed=False,
-                                                     ).count()) / total) * 100)
-
-
-    @models.permalink
-    def get_absolute_url(self):
-        return ('campaign_detail', (), {'pk': self.pk})
-
-class Target(models.Model):
-    email = models.EmailField()
-    campaign = models.ForeignKey(Campaign, related_name='targets')
-    link_followed = models.BooleanField(default=False, editable=False)
-    image_viewed = models.BooleanField(default=False, editable=False)
-    updated = models.DateTimeField(auto_now=True, editable=False)
-    extra_context = models.TextField(blank=True, help_text="Key=value (comma separated). Keep it "
-                            "simple since anything complex might be silently "
-                            "dropped.")
-
-    def __unicode__(self):
-        return self.email
-
-    @property
-    def extra(self):
-        """
-        Parses k,v from self.extra_context and returns as dict and drops errors 
-        silently.
-        """
-        context = {}
-        for i in self.extra_context.split(','):
-            # FIXME: need to explore which exceptions to catch, and which to raise
-            try:
-                (k, v) = i.split('=')
-                context[k.strip()] = v.strip()
-            except:
-                pass
-        return context
-
-    @models.permalink
-    def img_uri(self):
-        return ('img_hits', (), {'campaign_id': self.campaign.pk,
-                                 'target_id': self.pk})
-    @models.permalink
-    def link_uri(self):
-        return ('link_hits', (), {'campaign_id': self.campaign.pk,
-                                 'target_id': self.pk})
-
-    def dispatch(self):
-
-        if self.campaign.sender_name:
-            sender = '"%s" <%s>' % (self.campaign.sender_name,
-                                    self.campaign.sender_email)
-        else:
-            sender = self.campaign.sender_email
-
-        context = Context(dict(target=self, campaign=self.campaign))
-
-        text_content = Template(self.campaign.email_plain).render(context)
-
-        if self.campaign.email_html:
-            # plain text and html content
-            html_content = Template(self.campaign.email_html).render(context)
-
-            msg = EmailMultiAlternatives(self.campaign.subject, text_content,
-                                         sender, [self.email])
-            msg.attach_alternative(html_content, "text/html")
-        else:
-            # plain text only
-            msg = EmailMessage(self.campaign.subject, text_content, sender,
-                               [self.email])
-        msg.send()
-
Add a comment to this file

src/trawler/templates/404.html

Empty file removed.

Add a comment to this file

src/trawler/templates/500.html

Empty file removed.

src/trawler/templates/trawler/campaign_detail.html

-<!DOCTYPE html>
-<html>
-	
-	<head></head>
-	<style type="text/css">
-		table { border-collapse: collapse; }
-		td, th { 
-			border: solid 1px; 
-			padding: 5px 2em; }
-		td.true { background: #C22; }
-		td.false { background: #2C2; }
-	</style>
-	<body>
-		<table>
-			<tr>
-				<th>who</th>
-				<th>link followed</th>
-				<th>image viewed</th>
-				<th>updated</th>
-			</tr>
-			{% for target in campaign.targets.all %}
-			<tr>
-				<td>{{target.email}}</td>
-				<td class="{{target.link_followed|lower}}">{{target.link_followed}}</td>
-				<td class="{{target.image_viewed|lower}}">{{target.image_viewed}}</td>
-				<td>{{target.updated}}</td>
-			</tr>
-			{% endfor %}
-		</table>		
-	</body>
-</html>

src/trawler/tests.py

-from django.test import TestCase
-from django.contrib.auth.models import User
-from trawler.models import Target, Campaign
-
-from django.test import Client
-from django.core.urlresolvers import reverse
-
-class HitTests(TestCase):
-
-    def setUp(self):
-        self.c = Client()
-        self.user = User.objects.create(first_name='foo', last_name='bar',
-                                     email='foo@bar.com', password='foo')
-        self.c.post('/admin/', {'username': 'foo', 'password': 'bar'})
-
-        self.campaign = Campaign.objects.create(title='test', email_plain='')
-        self.target_pk = Target.objects.create(email='pat@example.com',
-                                            campaign=self.campaign, pk=123).pk
-
-    def test_image_hit(self):
-        self.assertFalse(Target.objects.get(pk=self.target_pk).image_viewed)
-        # note: we pass the 2nd arg to satisfy the open-ended regex
-        self.c.get(reverse('img_hits', kwargs={'target_id': self.target_pk,
-                                               'campaign_id': self.campaign.pk}))
-        self.assertTrue(Target.objects.get(pk=self.target_pk).image_viewed)
-
-    def test_link_hit(self):
-        self.assertFalse(Target.objects.get(pk=self.target_pk).link_followed)
-        # note: we pass the 2nd arg to satisfy the open-ended regex
-        self.c.get(reverse('link_hits', kwargs={'target_id': self.target_pk,
-                                                'campaign_id': self.campaign.pk}))
-        self.assertTrue(Target.objects.get(pk=self.target_pk).link_followed)
-
-class EmailTests(TestCase):
-
-    def setUp(self):
-        pass
-
-    def tearDown(self):
-        pass

src/trawler/urls.py

-from django.conf.urls.defaults import patterns, include, url
-from trawler.views import CampaignDetailView
-
-
-urlpatterns = patterns('trawler.views',
-    url(r'campaign/(?P<pk>\d+)/$',
-        CampaignDetailView.as_view(), name='campaign_detail'),
-    # note that these two patterns match anything following the last slash... 
-    url(r'l/(?P<campaign_id>\d+)/(?P<target_id>\d+)/', 'link_hits',
-        name='link_hits'),
-    url(r'i/(?P<campaign_id>\d+)/(?P<target_id>\d+)/', 'img_hits',
-        name='img_hits'),
-)

src/trawler/views.py

-from django.http import HttpResponse, Http404
-from django.views.generic.detail import DetailView
-from django.contrib.auth.decorators import login_required
-from django.utils.decorators import method_decorator
-from trawler.models import Target, Campaign
-import mimetypes
-
-def link_hits(request, campaign_id, target_id=0):
-    target_id = int(target_id)
-    campaign_id = int(campaign_id)
-    if target_id != 0:
-        mark = Target.objects.get(pk=target_id)
-        mark.link_followed = True
-        mark.save()
-    raise Http404()
-
-def img_hits(request, campaign_id, target_id=0):
-    target_id = int(target_id)
-    campaign_id = int(campaign_id)
-    if target_id != 0:
-        mark = Target.objects.get(pk=target_id)
-        mark.image_viewed = True
-        mark.save()
-        img = mark.campaign.img
-    else:
-        img = Campaign.objects.get(pk=campaign_id).img
-    try:
-        return HttpResponse(open(img.path, 'rb'),
-                            mimetype=mimetypes.guess_type(img.path))
-    except:
-        raise Http404()
-
-
-class CampaignDetailView(DetailView):
-    model = Campaign
-    context_object_name = 'campaign'
-
-    @method_decorator(login_required)
-    def dispatch(self, *args, **kwargs):
-        return super(CampaignDetailView, self).dispatch(*args, **kwargs)

trawler/__init__.py

+__version__ = '0.1a'
+
+from django.contrib import admin
+from trawler.models import Campaign, Target
+
+class TargetInline(admin.TabularInline):
+    model = Target
+
+class CampaignAdmin(admin.ModelAdmin):
+
+    list_display = ['title', 'created', 'updated']
+    date_hierarchy = 'created'
+    actions = ['send_test_email', 'launch_campaign']
+
+    inlines = [TargetInline, ]
+
+    def send_test_email(self, request, queryset):
+        # target with pk=0 means we're testing. Hits should not be counted.
+        tmp_target = Target(email=request.user.email, pk=0)
+        # TODO: figure out a way to pass in extra_context values for this test 
+        for campaign in queryset:
+            tmp_target.campaign = campaign
+            tmp_target.dispatch()
+
+        stuff = 'email'
+        count = queryset.count()
+        if count > 1:
+            stuff = 'emails'
+
+        self.message_user(request, "%d test %s sent to %s" % \
+                          (count, stuff, request.user.email))
+
+    def launch_campaign(self, request, queryset):
+        [campaign.bulk_dispatch() for campaign in queryset]
+
+        stuff = 'campaign'
+        count = queryset.count()
+        if count > 1:
+            stuff = 'campaigns'
+
+        self.message_user(request, "%d %s launched. Game on!" % (count, stuff))
+
+admin.site.register(Campaign, CampaignAdmin)

trawler/models.py

+from django.db import models
+from django.core.mail import EmailMessage, EmailMultiAlternatives
+from datetime import datetime
+from django.template import Context, Template
+
+class Campaign(models.Model):
+    title = models.CharField(max_length=45)
+    sender_email = models.EmailField()
+    sender_name = models.CharField(max_length=60, blank=True,
+                                   help_text='Name to display for the sender'
+                                   ' (can be used to primitavely obfuscate the'
+                                   ' email address)')
+    subject = models.CharField(max_length=255)
+
+    email_plain = models.TextField()
+    email_html = models.TextField(blank=True)
+    img = models.FileField(upload_to='uploads/%Y/%m/%d', null=True, blank=True)
+
+    created = models.DateTimeField(auto_now_add=True, editable=False)
+    updated = models.DateTimeField(auto_now=True, editable=False)
+
+
+    def __unicode__(self):
+        return self.title
+
+    def bulk_dispatch(self):
+        [target.dispatch() for target in self.targets.all()]
+        self.save() # to update the self.updated value 
+
+    def get_percents(self):
+        """
+        Primitive % calculator for pass/fail.
+        """
+        total = self.targets.count()
+        # FIXME: there's totally a divide by zero problem here.
+        return dict(link=(float(self.targets.filter(link_followed=True
+                                                    ).count()) / total) * 100,
+                    img=(float(self.targets.filter(image_viewed=True
+                                                     ).count()) / total) * 100,
+                    good=(float(self.targets.filter(image_viewed=False,
+                                                    link_followed=False,
+                                                     ).count()) / total) * 100)
+
+
+    @models.permalink
+    def get_absolute_url(self):
+        return ('campaign_detail', (), {'pk': self.pk})
+
+class Target(models.Model):
+    email = models.EmailField()
+    campaign = models.ForeignKey(Campaign, related_name='targets')
+    link_followed = models.BooleanField(default=False, editable=False)
+    image_viewed = models.BooleanField(default=False, editable=False)
+    updated = models.DateTimeField(auto_now=True, editable=False)
+    extra_context = models.TextField(blank=True, help_text="Key=value (comma separated). Keep it "
+                            "simple since anything complex might be silently "
+                            "dropped.")
+
+    def __unicode__(self):
+        return self.email
+
+    @property
+    def extra(self):
+        """
+        Parses k,v from self.extra_context and returns as dict and drops errors 
+        silently.
+        """
+        context = {}
+        for i in self.extra_context.split(','):
+            # FIXME: need to explore which exceptions to catch, and which to raise
+            try:
+                (k, v) = i.split('=')
+                context[k.strip()] = v.strip()
+            except:
+                pass
+        return context
+
+    @models.permalink
+    def img_uri(self):
+        return ('img_hits', (), {'campaign_id': self.campaign.pk,
+                                 'target_id': self.pk})
+    @models.permalink
+    def link_uri(self):
+        return ('link_hits', (), {'campaign_id': self.campaign.pk,
+                                 'target_id': self.pk})
+
+    def dispatch(self):
+
+        if self.campaign.sender_name:
+            sender = '"%s" <%s>' % (self.campaign.sender_name,
+                                    self.campaign.sender_email)
+        else:
+            sender = self.campaign.sender_email
+
+        context = Context(dict(target=self, campaign=self.campaign))
+
+        text_content = Template(self.campaign.email_plain).render(context)
+
+        if self.campaign.email_html:
+            # plain text and html content
+            html_content = Template(self.campaign.email_html).render(context)
+
+            msg = EmailMultiAlternatives(self.campaign.subject, text_content,
+                                         sender, [self.email])
+            msg.attach_alternative(html_content, "text/html")
+        else:
+            # plain text only
+            msg = EmailMessage(self.campaign.subject, text_content, sender,
+                               [self.email])
+        msg.send()
+
Add a comment to this file

trawler/templates/404.html

Empty file added.

Add a comment to this file

trawler/templates/500.html

Empty file added.

trawler/templates/trawler/campaign_detail.html

+<!DOCTYPE html>
+<html>
+	
+	<head></head>
+	<style type="text/css">
+		table { border-collapse: collapse; }
+		td, th { 
+			border: solid 1px; 
+			padding: 5px 2em; }
+		td.true { background: #C22; }
+		td.false { background: #2C2; }
+	</style>
+	<body>
+		<table>
+			<tr>
+				<th>who</th>
+				<th>link followed</th>
+				<th>image viewed</th>
+				<th>updated</th>
+			</tr>
+			{% for target in campaign.targets.all %}
+			<tr>
+				<td>{{target.email}}</td>
+				<td class="{{target.link_followed|lower}}">{{target.link_followed}}</td>
+				<td class="{{target.image_viewed|lower}}">{{target.image_viewed}}</td>
+				<td>{{target.updated}}</td>
+			</tr>
+			{% endfor %}
+		</table>		
+	</body>
+</html>
+from django.test import TestCase
+from django.contrib.auth.models import User
+from trawler.models import Target, Campaign
+
+from django.test import Client
+from django.core.urlresolvers import reverse
+
+class HitTests(TestCase):
+
+    def setUp(self):
+        self.c = Client()
+        self.user = User.objects.create(first_name='foo', last_name='bar',
+                                     email='foo@bar.com', password='foo')
+        self.c.post('/admin/', {'username': 'foo', 'password': 'bar'})
+
+        self.campaign = Campaign.objects.create(title='test', email_plain='')
+        self.target_pk = Target.objects.create(email='pat@example.com',
+                                            campaign=self.campaign, pk=123).pk
+
+    def test_image_hit(self):
+        self.assertFalse(Target.objects.get(pk=self.target_pk).image_viewed)
+        # note: we pass the 2nd arg to satisfy the open-ended regex
+        self.c.get(reverse('img_hits', kwargs={'target_id': self.target_pk,
+                                               'campaign_id': self.campaign.pk}))
+        self.assertTrue(Target.objects.get(pk=self.target_pk).image_viewed)
+
+    def test_link_hit(self):
+        self.assertFalse(Target.objects.get(pk=self.target_pk).link_followed)
+        # note: we pass the 2nd arg to satisfy the open-ended regex
+        self.c.get(reverse('link_hits', kwargs={'target_id': self.target_pk,
+                                                'campaign_id': self.campaign.pk}))
+        self.assertTrue(Target.objects.get(pk=self.target_pk).link_followed)
+
+class EmailTests(TestCase):
+
+    def setUp(self):
+        pass
+
+    def tearDown(self):
+        pass
+from django.conf.urls.defaults import patterns, include, url
+from trawler.views import CampaignDetailView
+
+
+urlpatterns = patterns('trawler.views',
+    url(r'campaign/(?P<pk>\d+)/$',
+        CampaignDetailView.as_view(), name='campaign_detail'),
+    # note that these two patterns match anything following the last slash... 
+    url(r'l/(?P<campaign_id>\d+)/(?P<target_id>\d+)/', 'link_hits',
+        name='link_hits'),
+    url(r'i/(?P<campaign_id>\d+)/(?P<target_id>\d+)/', 'img_hits',
+        name='img_hits'),
+)
+from django.http import HttpResponse, Http404
+from django.views.generic.detail import DetailView
+from django.contrib.auth.decorators import login_required
+from django.utils.decorators import method_decorator
+from trawler.models import Target, Campaign
+import mimetypes
+
+def link_hits(request, campaign_id, target_id=0):
+    target_id = int(target_id)
+    campaign_id = int(campaign_id)
+    if target_id != 0:
+        mark = Target.objects.get(pk=target_id)
+        mark.link_followed = True
+        mark.save()
+    raise Http404()
+
+def img_hits(request, campaign_id, target_id=0):
+    target_id = int(target_id)
+    campaign_id = int(campaign_id)
+    if target_id != 0:
+        mark = Target.objects.get(pk=target_id)
+        mark.image_viewed = True
+        mark.save()
+        img = mark.campaign.img
+    else:
+        img = Campaign.objects.get(pk=campaign_id).img
+    try:
+        return HttpResponse(open(img.path, 'rb'),
+                            mimetype=mimetypes.guess_type(img.path))
+    except:
+        raise Http404()
+
+
+class CampaignDetailView(DetailView):
+    model = Campaign
+    context_object_name = 'campaign'
+
+    @method_decorator(login_required)
+    def dispatch(self, *args, **kwargs):
+        return super(CampaignDetailView, self).dispatch(*args, **kwargs)
Tip: Filter by directory path e.g. /media app.js to search for public/media/app.js.
Tip: Use camelCasing e.g. ProjME to search for ProjectModifiedEvent.java.
Tip: Filter by extension type e.g. /repo .js to search for all .js files in the /repo directory.
Tip: Separate your search with spaces e.g. /ssh pom.xml to search for src/ssh/pom.xml.
Tip: Use ↑ and ↓ arrow keys to navigate and return to view the file.
Tip: You can also navigate files with Ctrl+j (next) and Ctrl+k (previous) and view the file with Ctrl+o.
Tip: You can also navigate files with Alt+j (next) and Alt+k (previous) and view the file with Alt+o.