Starting to integrate landing page functionality (still not working).

Also did some minor cleanup.
pull/24/head
unknown 2015-02-06 20:24:10 -06:00
parent d567153d2a
commit c318424ac0
18 changed files with 413 additions and 17 deletions

View File

@ -256,6 +256,79 @@ func API_Templates_Id(w http.ResponseWriter, r *http.Request) {
} }
} }
// API_Pages handles requests for the /api/pages/ endpoint
func API_Pages(w http.ResponseWriter, r *http.Request) {
switch {
case r.Method == "GET":
ps, err := models.GetPages(ctx.Get(r, "user_id").(int64))
if checkError(err, w, "Pages not found", http.StatusNotFound) {
return
}
JSONResponse(w, ps, http.StatusOK)
//POST: Create a new page and return it as JSON
case r.Method == "POST":
p := models.Page{}
// Put the request into a page
err := json.NewDecoder(r.Body).Decode(&p)
if checkError(err, w, "Invalid Request", http.StatusBadRequest) {
return
}
_, err = models.GetPageByName(p.Name, ctx.Get(r, "user_id").(int64))
if err != gorm.RecordNotFound {
JSONResponse(w, models.Response{Success: false, Message: "Template name already in use"}, http.StatusConflict)
return
}
p.ModifiedDate = time.Now()
p.UserId = ctx.Get(r, "user_id").(int64)
err = models.PostPage(&p)
if checkError(err, w, "Error inserting page", http.StatusInternalServerError) {
return
}
JSONResponse(w, p, http.StatusCreated)
}
}
func API_Pages_Id(w http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r)
id, _ := strconv.ParseInt(vars["id"], 0, 64)
p, err := models.GetPage(id, ctx.Get(r, "user_id").(int64))
if checkError(err, w, "Page not found", http.StatusNotFound) {
Logger.Println(err)
return
}
switch {
case r.Method == "GET":
JSONResponse(w, p, http.StatusOK)
case r.Method == "DELETE":
err = models.DeletePage(id, ctx.Get(r, "user_id").(int64))
if checkError(err, w, "Error deleting page", http.StatusInternalServerError) {
return
}
JSONResponse(w, models.Response{Success: true, Message: "Page Deleted Successfully"}, http.StatusOK)
case r.Method == "PUT":
p = models.Page{}
err = json.NewDecoder(r.Body).Decode(&p)
if err != nil {
Logger.Println(err)
}
if p.Id != id {
http.Error(w, "Error: /:id and template_id mismatch", http.StatusBadRequest)
return
}
err = p.Validate()
/* if checkError(err, w, http.StatusBadRequest) {
return
}*/
p.ModifiedDate = time.Now()
p.UserId = ctx.Get(r, "user_id").(int64)
err = models.PutPage(&p)
if checkError(err, w, "Error updating group", http.StatusInternalServerError) {
return
}
JSONResponse(w, p, http.StatusOK)
}
}
func API_Import_Group(w http.ResponseWriter, r *http.Request) { func API_Import_Group(w http.ResponseWriter, r *http.Request) {
Logger.Println("Parsing CSV....") Logger.Println("Parsing CSV....")
ts, err := util.ParseCSV(r) ts, err := util.ParseCSV(r)

View File

@ -39,6 +39,8 @@ func CreateAdminRouter() http.Handler {
api.HandleFunc("/groups/{id:[0-9]+}", Use(API_Groups_Id, mid.RequireAPIKey)) api.HandleFunc("/groups/{id:[0-9]+}", Use(API_Groups_Id, mid.RequireAPIKey))
api.HandleFunc("/templates/", Use(API_Templates, mid.RequireAPIKey)) api.HandleFunc("/templates/", Use(API_Templates, mid.RequireAPIKey))
api.HandleFunc("/templates/{id:[0-9]+}", Use(API_Templates_Id, mid.RequireAPIKey)) api.HandleFunc("/templates/{id:[0-9]+}", Use(API_Templates_Id, mid.RequireAPIKey))
api.HandleFunc("/pages/", Use(API_Pages, mid.RequireAPIKey))
api.HandleFunc("/pages/{id:[0-9]+}", Use(API_Pages_Id, mid.RequireAPIKey))
api.HandleFunc("/import/group", API_Import_Group) api.HandleFunc("/import/group", API_Import_Group)
// Setup static file serving // Setup static file serving

View File

@ -8,13 +8,18 @@ import (
"github.com/coopernurse/gorp" "github.com/coopernurse/gorp"
"github.com/jinzhu/gorm" "github.com/jinzhu/gorm"
"github.com/jordan-wright/gophish/config" "github.com/jordan-wright/gophish/config"
_ "github.com/mattn/go-sqlite3" _ "github.com/mattn/go-sqlite3" // Blank import needed to import sqlite3
) )
// Conn is the connection to the SQLite database
var Conn *gorp.DbMap var Conn *gorp.DbMap
var db gorm.DB var db gorm.DB
var err error var err error
// ErrUsernameTaken is thrown when a user attempts to register a username that is taken.
var ErrUsernameTaken = errors.New("username already taken") var ErrUsernameTaken = errors.New("username already taken")
// Logger is a global logger used to show informational, warning, and error messages
var Logger = log.New(os.Stdout, " ", log.Ldate|log.Ltime|log.Lshortfile) var Logger = log.New(os.Stdout, " ", log.Ldate|log.Ltime|log.Lshortfile)
const ( const (
@ -35,6 +40,7 @@ type Flash struct {
Message string Message string
} }
// Response contains the attributes found in an API response
type Response struct { type Response struct {
Message string `json:"message"` Message string `json:"message"`
Success bool `json:"success"` Success bool `json:"success"`
@ -62,16 +68,17 @@ func Setup() error {
db.CreateTable(GroupTarget{}) db.CreateTable(GroupTarget{})
db.CreateTable(Template{}) db.CreateTable(Template{})
db.CreateTable(Attachment{}) db.CreateTable(Attachment{})
db.CreateTable(Page{})
db.CreateTable(SMTP{}) db.CreateTable(SMTP{})
db.CreateTable(Event{}) db.CreateTable(Event{})
db.CreateTable(Campaign{}) db.CreateTable(Campaign{})
//Create the default user //Create the default user
init_user := User{ initUser := User{
Username: "admin", Username: "admin",
Hash: "$2a$10$IYkPp0.QsM81lYYPrQx6W.U6oQGw7wMpozrKhKAHUBVL4mkm/EvAS", //gophish Hash: "$2a$10$IYkPp0.QsM81lYYPrQx6W.U6oQGw7wMpozrKhKAHUBVL4mkm/EvAS", //gophish
ApiKey: "12345678901234567890123456789012", ApiKey: "12345678901234567890123456789012",
} }
err = db.Save(&init_user).Error err = db.Save(&initUser).Error
if err != nil { if err != nil {
Logger.Println(err) Logger.Println(err)
} }

87
models/page.go Normal file
View File

@ -0,0 +1,87 @@
package models
import (
"errors"
"time"
)
// Page contains the fields used for a Page model
type Page struct {
Id int64 `json:"id"`
UserId int64 `json:"-"`
Name string `json:"name"`
HTML string `json:"html"`
ModifiedDate time.Time `json:"modified_date"`
}
// ErrPageNameNotSpecified is thrown if the name of the landing page is blank.
var ErrPageNameNotSpecified = errors.New("Template Name not specified")
// Validate ensures that a page contains the appropriate details
func (p *Page) Validate() error {
if p.Name == "" {
return ErrPageNameNotSpecified
}
return nil
}
// GetPages returns the pages owned by the given user.
func GetPages(uid int64) ([]Page, error) {
ps := []Page{}
err := db.Where("user_id=?", uid).Find(&ps).Error
if err != nil {
Logger.Println(err)
return ps, err
}
return ps, err
}
// GetPage returns the page, if it exists, specified by the given id and user_id.
func GetPage(id int64, uid int64) (Page, error) {
p := Page{}
err := db.Where("user_id=? and id=?", uid, id).Find(&p).Error
if err != nil {
Logger.Println(err)
}
return p, err
}
// GetPageByName returns the page, if it exists, specified by the given name and user_id.
func GetPageByName(n string, uid int64) (Page, error) {
p := Page{}
err := db.Where("user_id=? and name=?", uid, n).Find(&p).Error
if err != nil {
Logger.Println(err)
}
return p, nil
}
// PostPage creates a new page in the database.
func PostPage(p *Page) error {
// Insert into the DB
err := db.Save(p).Error
if err != nil {
Logger.Println(err)
}
return err
}
// PutPage edits an existing Page in the database.
// Per the PUT Method RFC, it presumes all data for a page is provided.
func PutPage(p *Page) error {
err := db.Debug().Where("id=?", p.Id).Save(p).Error
if err != nil {
Logger.Println(err)
}
return err
}
// DeletePage deletes an existing page in the database.
// An error is returned if a page with the given user id and page id is not found.
func DeletePage(id int64, uid int64) error {
err = db.Where("user_id=?", uid).Delete(Page{Id: id}).Error
if err != nil {
Logger.Println(err)
}
return err
}

View File

@ -29,6 +29,11 @@ app.config(function($routeProvider) {
controller: 'TemplateCtrl' controller: 'TemplateCtrl'
}) })
.when('/landing_pages', {
templateUrl: 'js/app/partials/landing_pages.html',
controller: 'LandingPageCtrl'
})
.when('/settings', { .when('/settings', {
templateUrl: 'js/app/partials/settings.html', templateUrl: 'js/app/partials/settings.html',
controller: 'SettingsCtrl' controller: 'SettingsCtrl'

View File

@ -684,6 +684,109 @@ var TemplateModalCtrl = function($scope, $upload, $modalInstance) {
} }
}; };
app.controller('LandingPageCtrl', function($scope, $modal, LandingPageService, ngTableParams) {
$scope.errorFlash = function(message) {
$scope.flashes = [];
$scope.flashes.push({
"type": "danger",
"message": message,
"icon": "fa-exclamation-circle"
})
}
$scope.successFlash = function(message) {
$scope.flashes = [];
$scope.flashes.push({
"type": "success",
"message": message,
"icon": "fa-check-circle"
})
}
$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) {
LandingPageService.query(function(pages) {
$scope.pages = pages
params.total(pages.length)
$defer.resolve(pages.slice((params.page() - 1) * params.count(), params.page() * params.count()));
})
}
});
$scope.editPage = function(page) {
if (page === 'new') {
$scope.newPage = true;
$scope.page = {
name: '',
html: '',
};
} else {
$scope.newPage = false;
$scope.page = page;
}
var modalInstance = $modal.open({
templateUrl: '/js/app/partials/modals/LandingPageModal.html',
controller: LandingPageModalCtrl,
scope: $scope
});
modalInstance.result.then(function(selectedItem) {
$scope.selected = selectedItem;
}, function() {
console.log('closed')
});
};
$scope.savePage = function(page) {
var newPage = new LandingPageService(page);
if ($scope.newPage) {
newPage.$save({}, function() {
$scope.pages.push(newPage);
$scope.mainTableParams.reload()
});
} else {
newPage.$update({
id: newPage.id
})
}
$scope.page = {
name: '',
html: '',
};
}
$scope.deletePage = function(page) {
var deletePage = new LandingPageService(page);
deletePage.$delete({
id: deletePage.id
}, function(response) {
if (response.success) {
$scope.successFlash(response.message)
} else {
$scope.errorFlash(response.message)
}
$scope.mainTableParams.reload();
});
}
});
var LandingPageModalCtrl = function($scope, $modalInstance) {
$scope.cancel = function() {
$modalInstance.dismiss('cancel');
};
$scope.ok = function(page) {
$modalInstance.dismiss('')
$scope.savePage(page)
};
};
app.controller('SettingsCtrl', function($scope, $http, $window) { app.controller('SettingsCtrl', function($scope, $http, $window) {
$scope.flashes = []; $scope.flashes = [];
$scope.user = user; $scope.user = user;

View File

@ -26,4 +26,14 @@ app.factory('TemplateService', function($resource) {
method: 'PUT' method: 'PUT'
} }
}); });
}); });
app.factory('LandingPageService', function($resource) {
return $resource('/api/pages/:id?api_key=' + user.api_key, {
id : "@id"
}, {
update: {
method: 'PUT'
}
});
});

View File

@ -8,7 +8,9 @@
</li> </li>
<li><a href="#/users">Users &amp; Groups</a> <li><a href="#/users">Users &amp; Groups</a>
</li> </li>
<li><a href="#/templates">Templates</a> <li><a href="#/templates">Email Templates</a>
</li>
<li><a href="#/landing_pages">Landing Pages</a>
</li> </li>
<li><a href="#/settings">Settings</a> <li><a href="#/settings">Settings</a>
</li> </li>
@ -25,7 +27,7 @@
<div class="row"> <div class="row">
<div class="btn-group"> <div class="btn-group">
<button type="button" class="btn btn-primary dropdown-toggle" data-toggle="dropdown"> <button type="button" class="btn btn-primary dropdown-toggle" data-toggle="dropdown">
<i class="fa fa-cogs fa-lg"></i> <i class="fa fa-cogs fa-lg"></i>
<span class="caret"></span> <span class="caret"></span>
</button> </button>
<ul class="dropdown-menu"> <ul class="dropdown-menu">

View File

@ -8,7 +8,9 @@
</li> </li>
<li><a href="#/users">Users &amp; Groups</a> <li><a href="#/users">Users &amp; Groups</a>
</li> </li>
<li><a href="#/templates">Templates</a> <li><a href="#/templates">Email Templates</a>
</li>
<li><a href="#/landing_pages">Landing Pages</a>
</li> </li>
<li><a href="#/settings">Settings</a> <li><a href="#/settings">Settings</a>
</li> </li>

View File

@ -8,7 +8,9 @@
</li> </li>
<li><a href="#/users">Users &amp; Groups</a> <li><a href="#/users">Users &amp; Groups</a>
</li> </li>
<li><a href="#/templates">Templates</a> <li><a href="#/templates">Email Templates</a>
</li>
<li><a href="#/landing_pages">Landing Pages</a>
</li> </li>
<li><a href="#/settings">Settings</a> <li><a href="#/settings">Settings</a>
</li> </li>

View File

@ -0,0 +1,59 @@
<div class="container-fluid">
<div class="row">
<div class="col-sm-3 col-md-2 sidebar">
<ul class="nav nav-sidebar">
<li><a href="#">Dashboard</a>
</li>
<li><a href="#/campaigns">Campaigns</a>
</li>
<li><a href="#/users">Users &amp; Groups</a>
</li>
<li><a href="#/templates">Email Templates</a>
</li>
<li class="active"><a href="#/landing_pages">Landing Pages</a>
</li>
<li><a href="#/settings">Settings</a>
</li>
<li><a href="/api/">API Documentation</a>
</li>
</ul>
</div>
</div>
</div>
<div class="col-sm-9 col-sm-offset-3 col-md-10 col-md-offset-2 main" ng-controller="LandingPageCtrl">
<h1 class="page-header">
Landing Pages
</h1>
<div class="row">
<div ng-repeat="flash in flashes" style="text-align:center" class="alert alert-{{flash.type}}">
<i class="fa {{flash.icon}}"></i> {{flash.message}}
</div>
</div>
<div class="row">
<button type="button" class="btn btn-primary" ng-click="editPage('new')" data-toggle="modal" data-target="#newLandingPageModal"><i class="fa fa-plus"></i> New Page</button>
</div>
&nbsp;
<div class="row">
<table ng-table="mainTableParams" class="table table-hover table-striped table-bordered">
<tbody>
<tr ng-repeat="page in $data" class="editable-row">
<td data-title="'Name'" sortable="'name'" class="col-sm-1">{{page.name}}</td>
<td data-title="'Modified Date'" class="col-sm-1">{{page.modified_date | date:'medium'}}</td>
<div class="btn-group" style="float: right;">
<button type="button" class="btn btn-primary dropdown-toggle edit-button" data-toggle="dropdown">
<span class="caret" style="border-top-color:#FFFFFF"></span>
<span class="sr-only">Toggle Dropdown</span>
</button>
<ul class="dropdown-menu" style="left:auto; right:0;" role="menu">
<li><a ng-click="editPage(page)">Edit</a>
</li>
<li class="divider"></li>
<li><a ng-click="deletePage(page)">Delete</a>
</li>
</ul>
</div>
</tr>
</tbody>
</table>
</div>
</div>

View File

@ -0,0 +1,36 @@
<!-- New Template Modal -->
<div class="modal-header">
<button type="button" class="close" ng-click="cancel()">&times;</button>
<h4 class="modal-title" ng-hide="newPage" id="pageModalLabel">Edit Page: {{page.name}}</h4>
<h4 class="modal-title" ng-show="newPage" id="pageModalLabel">New Page</h4>
</div>
<div class="modal-body">
<label class="control-label" for="name">Name:</label>
<div class="form-group">
<input type="text" class="form-control" ng-model="page.name" placeholder="Page name" id="name" autofocus/>
</div>
<!-- Nav tabs -->
<fieldset disabled>
<div class="form-group">
<button class="btn btn-danger btn-disabled"><i class="fa fa-download"></i> Clone Site (Coming Soon!)</button>
</div>
</fieldset>
<tabset>
<tab heading="HTML">
<textarea rows="10" class="form-control" ng-model="page.html" placeholder="HTML"></textarea>
</tab>
<tab heading="Preview">
<div ng-model="page.html" contenteditable></div>
</tab>
</tabset>
<br />
<fieldset disabled>
<div class="form-group">
<button class="btn btn-danger btn-disabled"><i class="fa fa-external-link-square"></i> Preview in New Window (Coming Soon!)</button>
</div>
</fieldset>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-default" ng-click="cancel()">Cancel</button>
<button type="button" class="btn btn-primary" ng-click="ok(template)" data-dismiss="modal">Save Page</button>
</div>

View File

@ -8,7 +8,9 @@
</li> </li>
<li><a href="#/users">Users &amp; Groups</a> <li><a href="#/users">Users &amp; Groups</a>
</li> </li>
<li><a href="#/templates">Templates</a> <li><a href="#/templates">Email Templates</a>
</li>
<li><a href="#/landing_pages">Landing Pages</a>
</li> </li>
<li class="active"><a href="#/settings">Settings</a> <li class="active"><a href="#/settings">Settings</a>
</li> </li>

View File

@ -8,7 +8,9 @@
</li> </li>
<li><a href="#/users">Users &amp; Groups</a> <li><a href="#/users">Users &amp; Groups</a>
</li> </li>
<li class="active"><a href="#/templates">Templates</a> <li class="active"><a href="#/templates">Email Templates</a>
</li>
<li><a href="#/landing_pages">Landing Pages</a>
</li> </li>
<li><a href="#/settings">Settings</a> <li><a href="#/settings">Settings</a>
</li> </li>
@ -20,7 +22,7 @@
</div> </div>
<div class="col-sm-9 col-sm-offset-3 col-md-10 col-md-offset-2 main" ng-controller="TemplateCtrl"> <div class="col-sm-9 col-sm-offset-3 col-md-10 col-md-offset-2 main" ng-controller="TemplateCtrl">
<h1 class="page-header"> <h1 class="page-header">
Templates Email Templates
</h1> </h1>
<div class="row"> <div class="row">
<button type="button" class="btn btn-primary" ng-click="editTemplate('new')"><i class="fa fa-plus"></i> New Template</button> <button type="button" class="btn btn-primary" ng-click="editTemplate('new')"><i class="fa fa-plus"></i> New Template</button>
@ -50,4 +52,4 @@
</tbody> </tbody>
</table> </table>
</div> </div>
</div> </div>

View File

@ -8,7 +8,9 @@
</li> </li>
<li class="active"><a href="#/users">Users &amp; Groups</a> <li class="active"><a href="#/users">Users &amp; Groups</a>
</li> </li>
<li><a href="#/templates">Templates</a> <li><a href="#/templates">Email Templates</a>
</li>
<li><a href="#/landing_pages">Landing Pages</a>
</li> </li>
<li><a href="#/settings">Settings</a> <li><a href="#/settings">Settings</a>
</li> </li>

View File

@ -55,7 +55,9 @@
</li> </li>
<li><a href="#/users">Users &amp; Groups</a> <li><a href="#/users">Users &amp; Groups</a>
</li> </li>
<li><a href="#/templates">Templates</a> <li><a href="#/templates">Email Templates</a>
</li>
<li><a href="#/landing_pages">Landing Pages</a>
</li> </li>
<li><a href="#/settings">Settings</a> <li><a href="#/settings">Settings</a>
</li> </li>
@ -91,7 +93,7 @@
<!-- Placed at the end of the document so the pages load faster --> <!-- Placed at the end of the document so the pages load faster -->
<script src="/js/jquery.js"></script> <script src="/js/jquery.js"></script>
<script src="/js/bootstrap.min.js"></script> <script src="/js/bootstrap.min.js"></script>
<script src="/js/angular-file-upload-shim.min.js"></script> <script src="/js/angular-file-upload-shim.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.10/angular.min.js"></script> <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.10/angular.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.16/angular-route.js"></script> <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.16/angular-route.js"></script>
<script src="/js/angular-file-upload.min.js"></script> <script src="/js/angular-file-upload.min.js"></script>

View File

@ -9,6 +9,8 @@
</li> </li>
<li><a href="#/templates">Templates</a> <li><a href="#/templates">Templates</a>
</li> </li>
<li><a href="#/landing_pages">Landing Pages</a>
</li>
<li><a href="#/settings">Settings</a> <li><a href="#/settings">Settings</a>
</li> </li>
<li><a href="/api/">API Documentation</a> <li><a href="/api/">API Documentation</a>

View File

@ -60,7 +60,6 @@ func processCampaign(c *models.Campaign) {
Logger.Println(err) Logger.Println(err)
} }
e.HTML = html_buff.Bytes() e.HTML = html_buff.Bytes()
//buff.Reset()
tmpl, err = template.New("text_template").Parse(c.Template.Text) tmpl, err = template.New("text_template").Parse(c.Template.Text)
if err != nil { if err != nil {
Logger.Println(err) Logger.Println(err)
@ -70,7 +69,6 @@ func processCampaign(c *models.Campaign) {
Logger.Println(err) Logger.Println(err)
} }
e.Text = text_buff.Bytes() e.Text = text_buff.Bytes()
//buff.Reset()
Logger.Println("Creating email using template") Logger.Println("Creating email using template")
e.To = []string{t.Email} e.To = []string{t.Email}
err = e.Send(c.SMTP.Host, auth) err = e.Send(c.SMTP.Host, auth)