From 02c7c4b5b141a34824d7ae2c6f3b2a24675c4f70 Mon Sep 17 00:00:00 2001 From: Jordan Date: Tue, 1 Jul 2014 20:32:34 -0500 Subject: [PATCH] Moved models.Result into its own file Added initial dashboard template (some updates and changes will be needed) Added some documentation --- controllers/route.go | 16 ++++++ models/campaign.go | 22 +++------ models/models.go | 11 +++-- models/result.go | 40 +++++++++++++++ static/js/app/app.js | 4 +- static/js/app/controllers.js | 70 +++++++++++++++++++++++++++ static/js/app/partials/dashboard.html | 69 ++++++++++++++++++++++++++ worker/worker.go | 2 +- 8 files changed, 211 insertions(+), 23 deletions(-) create mode 100644 models/result.go create mode 100644 static/js/app/partials/dashboard.html diff --git a/controllers/route.go b/controllers/route.go index b5cac4cb..ee493004 100644 --- a/controllers/route.go +++ b/controllers/route.go @@ -18,6 +18,8 @@ import ( var templateDelims = []string{"{{%", "%}}"} var Logger = log.New(os.Stdout, " ", log.Ldate|log.Ltime|log.Lshortfile) +// CreateAdminRouter creates the routes for handling requests to the web interface. +// This function returns an http.Handler to be used in http.ListenAndServe(). func CreateAdminRouter() http.Handler { router := mux.NewRouter() // Base Front-end routes @@ -62,7 +64,21 @@ func CreatePhishingRouter() http.Handler { return router } +// PhishHandler handles incoming client connections and registers the associated actions performed +// (such as clicked link, etc.) func PhishHandler(w http.ResponseWriter, r *http.Request) { + r.ParseForm() + id := r.Form.Get("rid") + if id == "" { + http.NotFound(w, r) + return + } + rs, err := models.GetResult(id) + if err != nil { + http.NotFound(w, r) + return + } + rs.UpdateStatus("Clicked Link") w.Write([]byte("It Works!")) } diff --git a/models/campaign.go b/models/campaign.go index 9d687df3..d5bf1734 100644 --- a/models/campaign.go +++ b/models/campaign.go @@ -41,29 +41,18 @@ func (c *Campaign) UpdateStatus(s string) error { return db.Table("campaigns").Where("id=?", c.Id).Update("status", s).Error } -func (c *Campaign) AddEvent (e Event) error { +func (c *Campaign) AddEvent(e Event) error { e.CampaignId = c.Id e.Time = time.Now() return db.Debug().Save(&e).Error } -type Result struct { - Id int64 `json:"-"` - CampaignId int64 `json:"-"` - Email string `json:"email"` - Status string `json:"status" sql:"not null"` -} - -func (r *Result) UpdateStatus(s string) error { - return db.Debug().Table("results").Where("id=?", r.Id).Update("status", s).Error -} - type Event struct { Id int64 `json:"-"` CampaignId int64 `json:"-"` Email string `json:"email"` Time time.Time `json:"time"` - Message string `json:"message"` + Message string `json:"message"` } // GetCampaigns returns the campaigns owned by the given user. @@ -115,7 +104,7 @@ func PostCampaign(c *Campaign, uid int64) error { c.UserId = uid c.CreatedDate = time.Now() c.CompletedDate = time.Time{} - c.Status = QUEUED + c.Status = CAMPAIGN_QUEUED // Check to make sure all the groups already exist for i, g := range c.Groups { c.Groups[i], err = GetGroupByName(g.Name, uid) @@ -144,7 +133,7 @@ func PostCampaign(c *Campaign, uid int64) error { Logger.Println(err) return err } - err = c.AddEvent(Event{Message:"Campaign Created"}) + err = c.AddEvent(Event{Message: "Campaign Created"}) if err != nil { Logger.Println(err) } @@ -153,7 +142,8 @@ func PostCampaign(c *Campaign, uid int64) error { // Insert a result for each target in the group for _, t := range g.Targets { r := Result{Email: t.Email, Status: "Unknown", CampaignId: c.Id} - err := db.Save(&r).Error + r.GenerateId() + err = db.Save(&r).Error if err != nil { Logger.Printf("Error adding result record for target %s\n", t.Email) Logger.Println(err) diff --git a/models/models.go b/models/models.go index 8f5248a3..a54fd9ac 100644 --- a/models/models.go +++ b/models/models.go @@ -18,10 +18,13 @@ var ErrUsernameTaken = errors.New("username already taken") var Logger = log.New(os.Stdout, " ", log.Ldate|log.Ltime|log.Lshortfile) const ( - IN_PROGRESS string = "In progress" - QUEUED string = "Queued" - COMPLETE string = "Completed" - ERROR string = "Error" + CAMPAIGN_IN_PROGRESS string = "In progress" + CAMPAIGN_QUEUED string = "Queued" + CAMPAIGN_COMPLETE string = "Completed" + STATUS_SENT string = "Email Sent" + STATUS_OPENED string = "Email Opened" + STATUS_CLICKED string = "Clicked Link" + ERROR string = "Error" ) // Flash is used to hold flash information for use in templates. diff --git a/models/result.go b/models/result.go new file mode 100644 index 00000000..c29de83c --- /dev/null +++ b/models/result.go @@ -0,0 +1,40 @@ +package models + +import ( + "crypto/rand" + "fmt" + "io" + + "github.com/jinzhu/gorm" +) + +type Result struct { + Id int64 `json:"-"` + CampaignId int64 `json:"-"` + RId string `json:"id"` + Email string `json:"email"` + Status string `json:"status" sql:"not null"` +} + +func (r *Result) UpdateStatus(s string) error { + return db.Table("results").Where("id=?", r.Id).Update("status", s).Error +} + +func (r *Result) GenerateId() { + // Keep trying until we generate a unique key (shouldn't take more than one or two iterations) + k := make([]byte, 32) + for { + io.ReadFull(rand.Reader, k) + r.RId = fmt.Sprintf("%x", k) + err := db.Table("results").Where("id=?", r.RId).First(&Result{}).Error + if err == gorm.RecordNotFound { + break + } + } +} + +func GetResult(rid string) (Result, error) { + r := Result{} + err := db.Where("r_id=?", rid).First(&r).Error + return r, err +} diff --git a/static/js/app/app.js b/static/js/app/app.js index 1b03eaeb..1ec9c8b0 100644 --- a/static/js/app/app.js +++ b/static/js/app/app.js @@ -5,8 +5,8 @@ app.config(function($routeProvider) { // route for the home page .when('/', { - templateUrl: 'js/app/partials/campaigns.html', - controller: 'CampaignCtrl' + templateUrl: 'js/app/partials/dashboard.html', + controller: 'DashboardCtrl' }) .when('/campaigns', { diff --git a/static/js/app/controllers.js b/static/js/app/controllers.js index 48ef07a5..099db27c 100644 --- a/static/js/app/controllers.js +++ b/static/js/app/controllers.js @@ -1,3 +1,73 @@ +app.controller('DashboardCtrl', function($scope, CampaignService, ngTableParams, $http) { + $scope.mainTableParams = new ngTableParams({ + page: 1, // show first page + count: 10, // count per page + sorting: { + name: 'asc' // initial sorting + } + }, { + total: 0, // length of data + getData: function($defer, params) { + CampaignService.query(function(campaigns) { + $scope.campaigns = campaigns + var campaign_series = [] + angular.forEach(campaigns, function(campaign, key) { + campaign_series.push({ + y: 0 + }) + angular.forEach(campaign.results, function(result, r_key) { + if (result.status == "Clicked Link") { + campaign_series[campaign_series.length - 1].y++; + } + }) + campaign_series[campaign_series.length - 1].y = Math.floor((campaign_series[campaign_series.length - 1].y / campaign.results.length)*100) + console.log(campaign_series) + }); + $scope.overview_chart = { + options: { + chart: { + type: 'area' + }, + tooltip: { + formatter: function() { + return "Successful Phishes: " + this.point.y + "%" + }, + style: { + padding: 10, + fontWeight: 'bold' + } + }, + plotOptions: { + pie: { + allowPointSelect: true, + cursor: 'pointer', + dataLabels: { + enabled: false + }, + showInLegend: true + } + } + }, + series: [{ + data: campaign_series + }], + title: { + text: 'Phishing Success Overview' + }, + size: { + height: 300 + }, + credits: { + enabled: false + }, + loading: false, + } + params.total(Math.min(campaigns.length, 5)); + $defer.resolve(campaigns.slice(0, params.total())); + }) + } + }); +}) app.controller('CampaignCtrl', function($scope, $modal, CampaignService, GroupService, TemplateService, ngTableParams, $http) { $scope.flashes = [] $scope.mainTableParams = new ngTableParams({ diff --git a/static/js/app/partials/dashboard.html b/static/js/app/partials/dashboard.html new file mode 100644 index 00000000..6d4e8b28 --- /dev/null +++ b/static/js/app/partials/dashboard.html @@ -0,0 +1,69 @@ +
+ +
+
+

+ Dashboard +

+
+
+ {{flash.message}} +
+
+
+
+ +
+
+
+

Recent Campaigns

+
+
+ +
+    +
+ + + + + + + + +
{{campaign.created_date | date:'medium'}}{{campaign.name}} +
+ + +
+
{{campaign.status}}
+
+
diff --git a/worker/worker.go b/worker/worker.go index eb570938..84c579a4 100644 --- a/worker/worker.go +++ b/worker/worker.go @@ -35,7 +35,7 @@ func (w *Worker) Start() { func processCampaign(c *models.Campaign) { Logger.Printf("Worker received: %s", c.Name) - err := c.UpdateStatus(models.IN_PROGRESS) + err := c.UpdateStatus(models.CAMPAIGN_IN_PROGRESS) if err != nil { Logger.Println(err) }