mirror of https://github.com/gophish/gophish
Adding handling for campaign events
Cleaned up user import button Cleaning up modal interfacing Added ability to set result statuspull/24/head
parent
efec86ae56
commit
49da412538
|
@ -36,6 +36,17 @@ func (c *Campaign) Validate() (string, bool) {
|
||||||
return "", true
|
return "", true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *Campaign) UpdateStatus(s string) error {
|
||||||
|
// This could be made simpler, but I think there's a bug in gorm
|
||||||
|
return db.Table("campaigns").Where("id=?", c.Id).Update("status", s).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 {
|
type Result struct {
|
||||||
Id int64 `json:"-"`
|
Id int64 `json:"-"`
|
||||||
CampaignId int64 `json:"-"`
|
CampaignId int64 `json:"-"`
|
||||||
|
@ -43,12 +54,16 @@ type Result struct {
|
||||||
Status string `json:"status" sql:"not null"`
|
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 {
|
type Event struct {
|
||||||
Id int64 `json:"-"`
|
Id int64 `json:"-"`
|
||||||
CampaignId int64 `json:"-"`
|
CampaignId int64 `json:"-"`
|
||||||
Email string `json:"email"`
|
Email string `json:"email"`
|
||||||
Time time.Time `json:"time"`
|
Time time.Time `json:"time"`
|
||||||
Message time.Time `json:"message"`
|
Message string `json:"message"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetCampaigns returns the campaigns owned by the given user.
|
// GetCampaigns returns the campaigns owned by the given user.
|
||||||
|
@ -81,10 +96,10 @@ func GetCampaign(id int64, uid int64) (Campaign, error) {
|
||||||
// PostCampaign inserts a campaign and all associated records into the database.
|
// PostCampaign inserts a campaign and all associated records into the database.
|
||||||
func PostCampaign(c *Campaign, uid int64) error {
|
func PostCampaign(c *Campaign, uid int64) error {
|
||||||
// Fill in the details
|
// Fill in the details
|
||||||
|
c.UserId = uid
|
||||||
c.CreatedDate = time.Now()
|
c.CreatedDate = time.Now()
|
||||||
c.CompletedDate = time.Time{}
|
c.CompletedDate = time.Time{}
|
||||||
c.Status = QUEUED
|
c.Status = QUEUED
|
||||||
c.Events = append(c.Events)
|
|
||||||
// Check to make sure all the groups already exist
|
// Check to make sure all the groups already exist
|
||||||
for i, g := range c.Groups {
|
for i, g := range c.Groups {
|
||||||
c.Groups[i], err = GetGroupByName(g.Name, uid)
|
c.Groups[i], err = GetGroupByName(g.Name, uid)
|
||||||
|
@ -113,27 +128,26 @@ func PostCampaign(c *Campaign, uid int64) error {
|
||||||
Logger.Println(err)
|
Logger.Println(err)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
err = c.AddEvent(Event{Message:"Campaign Created"})
|
||||||
|
if err != nil {
|
||||||
|
Logger.Println(err)
|
||||||
|
}
|
||||||
// Insert all the results
|
// Insert all the results
|
||||||
for _, g := range c.Groups {
|
for _, g := range c.Groups {
|
||||||
// Insert a result for each target in the group
|
// Insert a result for each target in the group
|
||||||
for _, t := range g.Targets {
|
for _, t := range g.Targets {
|
||||||
r := Result{Email: t.Email, Status: "Unknown", CampaignId: c.Id}
|
r := Result{Email: t.Email, Status: "Unknown", CampaignId: c.Id}
|
||||||
c.Results = append(c.Results, r)
|
|
||||||
err := db.Save(&r).Error
|
err := db.Save(&r).Error
|
||||||
if err != nil {
|
if err != nil {
|
||||||
Logger.Printf("Error adding result record for target %s\n", t.Email)
|
Logger.Printf("Error adding result record for target %s\n", t.Email)
|
||||||
Logger.Println(err)
|
Logger.Println(err)
|
||||||
}
|
}
|
||||||
|
c.Results = append(c.Results, r)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func UpdateCampaignStatus(c *Campaign, s string) error {
|
|
||||||
// This could be made simpler, but I think there's a bug in gorm
|
|
||||||
return db.Table("campaigns").Where("id=?", c.Id).Update("status", s).Error
|
|
||||||
}
|
|
||||||
|
|
||||||
//DeleteCampaign deletes the specified campaign
|
//DeleteCampaign deletes the specified campaign
|
||||||
func DeleteCampaign(id int64) error {
|
func DeleteCampaign(id int64) error {
|
||||||
// Delete all the campaign results
|
// Delete all the campaign results
|
||||||
|
|
|
@ -57,6 +57,7 @@ func Setup() error {
|
||||||
db.CreateTable(GroupTarget{})
|
db.CreateTable(GroupTarget{})
|
||||||
db.CreateTable(Template{})
|
db.CreateTable(Template{})
|
||||||
db.CreateTable(SMTP{})
|
db.CreateTable(SMTP{})
|
||||||
|
db.CreateTable(Event{})
|
||||||
db.CreateTable(Campaign{})
|
db.CreateTable(Campaign{})
|
||||||
//Create the default user
|
//Create the default user
|
||||||
init_user := User{
|
init_user := User{
|
||||||
|
|
|
@ -128,3 +128,23 @@
|
||||||
.dropdown-menu > li > a {
|
.dropdown-menu > li > a {
|
||||||
cursor:pointer;
|
cursor:pointer;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.btn-file {
|
||||||
|
position: relative;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
.btn-file input[type=file] {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
right: 0;
|
||||||
|
min-width: 100%;
|
||||||
|
min-height: 100%;
|
||||||
|
font-size: 999px;
|
||||||
|
text-align: right;
|
||||||
|
filter: alpha(opacity=0);
|
||||||
|
opacity: 0;
|
||||||
|
outline: none;
|
||||||
|
background: white;
|
||||||
|
cursor: inherit;
|
||||||
|
display: block;
|
||||||
|
}
|
|
@ -35,6 +35,19 @@ app.config(function($routeProvider) {
|
||||||
})
|
})
|
||||||
});
|
});
|
||||||
|
|
||||||
|
app.filter('cut', function() {
|
||||||
|
return function(value, max, tail) {
|
||||||
|
if (!value) return '';
|
||||||
|
max = parseInt(max, 10);
|
||||||
|
truncd = []
|
||||||
|
for(var i=0; i < Math.min(value.length,max); i++) {
|
||||||
|
if (i == max-1) truncd.push("...")
|
||||||
|
else truncd.push(value[i].email);
|
||||||
|
}
|
||||||
|
return truncd;
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
// Example provided by http://docs.angularjs.org/api/ng/type/ngModel.NgModelController
|
// Example provided by http://docs.angularjs.org/api/ng/type/ngModel.NgModelController
|
||||||
app.directive('contenteditable', function() {
|
app.directive('contenteditable', function() {
|
||||||
return {
|
return {
|
||||||
|
@ -59,9 +72,9 @@ app.directive('contenteditable', function() {
|
||||||
var html = element.html();
|
var html = element.html();
|
||||||
// When we clear the content editable the browser leaves a <br> behind
|
// When we clear the content editable the browser leaves a <br> behind
|
||||||
// If strip-br attribute is provided then we strip this out
|
// If strip-br attribute is provided then we strip this out
|
||||||
if (attrs.stripBr && html == '<br>') {
|
//if (attrs.stripBr && html == '<br>') {
|
||||||
html = '';
|
// html = '';
|
||||||
}
|
//}
|
||||||
ngModel.$setViewValue(html);
|
ngModel.$setViewValue(html);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -124,6 +124,10 @@ var CampaignModalCtrl = function($scope, $modalInstance) {
|
||||||
$scope.cancel = function() {
|
$scope.cancel = function() {
|
||||||
$modalInstance.dismiss('cancel');
|
$modalInstance.dismiss('cancel');
|
||||||
};
|
};
|
||||||
|
$scope.ok = function(campaign) {
|
||||||
|
$modalInstance.dismiss("")
|
||||||
|
$scope.saveCampaign(campaign)
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
app.controller('CampaignResultsCtrl', function($scope, CampaignService, GroupService, ngTableParams, $http, $window) {
|
app.controller('CampaignResultsCtrl', function($scope, CampaignService, GroupService, ngTableParams, $http, $window) {
|
||||||
|
@ -333,7 +337,6 @@ var GroupModalCtrl = function($scope, $modalInstance, $upload) {
|
||||||
}).progress(function(evt) {
|
}).progress(function(evt) {
|
||||||
console.log('percent: ' + parseInt(100.0 * evt.loaded / evt.total));
|
console.log('percent: ' + parseInt(100.0 * evt.loaded / evt.total));
|
||||||
}).success(function(data, status, headers, config) {
|
}).success(function(data, status, headers, config) {
|
||||||
// file is uploaded successfully
|
|
||||||
angular.forEach(data, function(record, key) {
|
angular.forEach(data, function(record, key) {
|
||||||
$scope.group.targets.push({
|
$scope.group.targets.push({
|
||||||
email: record.email
|
email: record.email
|
||||||
|
@ -451,6 +454,10 @@ var TemplateModalCtrl = function($scope, $modalInstance) {
|
||||||
$scope.cancel = function() {
|
$scope.cancel = function() {
|
||||||
$modalInstance.dismiss('cancel');
|
$modalInstance.dismiss('cancel');
|
||||||
};
|
};
|
||||||
|
$scope.ok = function(template) {
|
||||||
|
$modalInstance.dismiss('')
|
||||||
|
$scope.saveTemplate(template)
|
||||||
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
app.controller('SettingsCtrl', function($scope, $http, $window) {
|
app.controller('SettingsCtrl', function($scope, $http, $window) {
|
||||||
|
|
|
@ -39,5 +39,5 @@
|
||||||
</div>
|
</div>
|
||||||
<div class="modal-footer">
|
<div class="modal-footer">
|
||||||
<button type="button" class="btn btn-default" ng-click="cancel()">Cancel</button>
|
<button type="button" class="btn btn-default" ng-click="cancel()">Cancel</button>
|
||||||
<button type="button" class="btn btn-primary" ng-click="saveTemplate(template)" data-dismiss="modal">Save Template</button>
|
<button type="button" class="btn btn-primary" ng-click="ok(template)" data-dismiss="modal">Save Template</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -10,12 +10,11 @@
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<input type="text" class="form-control" ng-model="group.name" placeholder="Group name" id="name" autofocus/>
|
<input type="text" class="form-control" ng-model="group.name" placeholder="Group name" id="name" autofocus/>
|
||||||
</div>
|
</div>
|
||||||
<input type="file" ng-file-select="onFileSelect($files)">
|
<div class="form-group">
|
||||||
<fieldset disabled>
|
<span class="btn btn-danger btn-file" tooltip="Supports CSV files" tooltip-placement="right"><i class="fa fa-plus"></i> Bulk Import Users
|
||||||
<div class="form-group">
|
<input type="file" ng-file-select="onFileSelect($files)">
|
||||||
<button class="btn btn-danger"><i class="fa fa-plus"></i> Bulk Import Users (Coming Soon!)</button>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</fieldset>
|
|
||||||
<label class="control-label" for="users">Users:</label>
|
<label class="control-label" for="users">Users:</label>
|
||||||
<form ng:submit="addTarget()">
|
<form ng:submit="addTarget()">
|
||||||
<div class="input-group">
|
<div class="input-group">
|
||||||
|
|
|
@ -37,8 +37,8 @@
|
||||||
<tr ng-repeat="group in $data" class="editable-row">
|
<tr ng-repeat="group in $data" class="editable-row">
|
||||||
<td data-title="'Name'" sortable="'name'" class="col-sm-1">{{group.name}}</td>
|
<td data-title="'Name'" sortable="'name'" class="col-sm-1">{{group.name}}</td>
|
||||||
<td data-title="'Members'" class="col-sm-2">
|
<td data-title="'Members'" class="col-sm-2">
|
||||||
<span ng-repeat="target in group.targets">
|
<span ng-repeat="target in group.targets | cut:5 track by $index ">
|
||||||
{{target.email}}{{$last ? '' : ', '}}
|
{{target}}{{$last ? '' : ', '}}
|
||||||
</span>
|
</span>
|
||||||
<div class="btn-group" style="float: right;">
|
<div class="btn-group" style="float: right;">
|
||||||
<button type="button" class="btn btn-primary dropdown-toggle edit-button" data-toggle="dropdown">
|
<button type="button" class="btn btn-primary dropdown-toggle edit-button" data-toggle="dropdown">
|
||||||
|
@ -49,7 +49,7 @@
|
||||||
<li><a ng-click="editGroup(group)">Edit</a>
|
<li><a ng-click="editGroup(group)">Edit</a>
|
||||||
</li>
|
</li>
|
||||||
<li class="divider"></li>
|
<li class="divider"></li>
|
||||||
<li><a ng-click="deleteGroup(group)" ng-href="#">Delete</a>
|
<li><a ng-click="deleteGroup(group)">Delete</a>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -33,7 +33,7 @@ func (w *Worker) Start() {
|
||||||
|
|
||||||
func processCampaign(c *models.Campaign) {
|
func processCampaign(c *models.Campaign) {
|
||||||
Logger.Printf("Worker received: %s", c.Name)
|
Logger.Printf("Worker received: %s", c.Name)
|
||||||
err := models.UpdateCampaignStatus(c, models.IN_PROGRESS)
|
err := c.UpdateStatus(models.IN_PROGRESS)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
Logger.Println(err)
|
Logger.Println(err)
|
||||||
}
|
}
|
||||||
|
@ -58,7 +58,17 @@ func processCampaign(c *models.Campaign) {
|
||||||
err := e.Send(c.SMTP.Host, auth)
|
err := e.Send(c.SMTP.Host, auth)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
Logger.Println(err)
|
Logger.Println(err)
|
||||||
|
err = t.UpdateStatus("Error")
|
||||||
|
if err != nil {
|
||||||
|
Logger.Println(err)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
err = t.UpdateStatus("Email Sent")
|
||||||
|
if err != nil {
|
||||||
|
Logger.Println(err)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Logger.Printf("Sending Email to %s\n", t.Email)
|
Logger.Printf("Sending Email to %s\n", t.Email)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue