Added util package for handling misc. tasks

Added basic (*not finished*) functionality for handling bulk user insert
pull/24/head
Jordan 2014-06-21 21:06:16 -05:00
parent 01901c9008
commit efec86ae56
10 changed files with 119 additions and 6 deletions

View File

@ -13,6 +13,7 @@ import (
"github.com/jinzhu/gorm" "github.com/jinzhu/gorm"
"github.com/jordan-wright/gophish/auth" "github.com/jordan-wright/gophish/auth"
"github.com/jordan-wright/gophish/models" "github.com/jordan-wright/gophish/models"
"github.com/jordan-wright/gophish/util"
"github.com/jordan-wright/gophish/worker" "github.com/jordan-wright/gophish/worker"
) )
@ -245,6 +246,15 @@ func API_Templates_Id(w http.ResponseWriter, r *http.Request) {
} }
} }
func API_Import_Group(w http.ResponseWriter, r *http.Request) {
Logger.Println("Parsing CSV....")
ts, err := util.ParseCSV(r)
if checkError(err, w, "Error deleting template", http.StatusInternalServerError) {
return
}
JSONResponse(w, ts, http.StatusOK)
}
// JSONResponse attempts to set the status code, c, and marshal the given interface, d, into a response that // JSONResponse attempts to set the status code, c, and marshal the given interface, d, into a response that
// is written to the given ResponseWriter. // is written to the given ResponseWriter.
func JSONResponse(w http.ResponseWriter, d interface{}, c int) { func JSONResponse(w http.ResponseWriter, d interface{}, c int) {

View File

@ -38,6 +38,7 @@ func CreateRouter() *nosurf.CSRFHandler {
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("/import/group", API_Import_Group)
// Setup static file serving // Setup static file serving
router.PathPrefix("/").Handler(http.FileServer(http.Dir("./static/"))) router.PathPrefix("/").Handler(http.FileServer(http.Dir("./static/")))
@ -48,6 +49,7 @@ func CreateRouter() *nosurf.CSRFHandler {
csrfHandler.ExemptGlob("/api/campaigns/*") csrfHandler.ExemptGlob("/api/campaigns/*")
csrfHandler.ExemptGlob("/api/groups/*") csrfHandler.ExemptGlob("/api/groups/*")
csrfHandler.ExemptGlob("/api/templates/*") csrfHandler.ExemptGlob("/api/templates/*")
csrfHandler.ExemptGlob("/api/import/*")
csrfHandler.ExemptGlob("/static/*") csrfHandler.ExemptGlob("/static/*")
return csrfHandler return csrfHandler
} }

View File

@ -21,8 +21,10 @@ type GroupTarget struct {
} }
type Target struct { type Target struct {
Id int64 `json:"-"` Id int64 `json:"-"`
Email string `json:"email"` FirstName string `json:"first_name"`
LastName string `json:"last_name"`
Email string `json:"email"`
} }
// GetGroups returns the groups owned by the given user. // GetGroups returns the groups owned by the given user.

File diff suppressed because one or more lines are too long

2
static/js/angular-file-upload.min.js vendored Normal file
View File

@ -0,0 +1,2 @@
/*! 1.4.0 */
!function(){var a=angular.module("angularFileUpload",[]);a.service("$upload",["$http","$timeout",function(a,b){function c(c){c.method=c.method||"POST",c.headers=c.headers||{},c.transformRequest=c.transformRequest||function(b,c){return window.ArrayBuffer&&b instanceof window.ArrayBuffer?b:a.defaults.transformRequest[0](b,c)},window.XMLHttpRequest.__isShim&&(c.headers.__setXHR_=function(){return function(a){a&&(c.__XHR=a,c.xhrFn&&c.xhrFn(a),a.upload.addEventListener("progress",function(a){c.progress&&b(function(){c.progress&&c.progress(a)})},!1),a.upload.addEventListener("load",function(a){a.lengthComputable&&c.progress&&c.progress(a)},!1))}});var d=a(c);return d.progress=function(a){return c.progress=a,d},d.abort=function(){return c.__XHR&&b(function(){c.__XHR.abort()}),d},d.xhr=function(a){return c.xhrFn=a,d},d.then=function(a,b){return function(d,e,f){c.progress=f||c.progress;var g=b.apply(a,[d,e,f]);return g.abort=a.abort,g.progress=a.progress,g.xhr=a.xhr,g.then=a.then,g}}(d,d.then),d}this.upload=function(b){b.headers=b.headers||{},b.headers["Content-Type"]=void 0,b.transformRequest=b.transformRequest||a.defaults.transformRequest;var d=new FormData,e=b.transformRequest,f=b.data;return b.transformRequest=function(a,c){if(f)if(b.formDataAppender)for(var d in f){var g=f[d];b.formDataAppender(a,d,g)}else for(var d in f){var g=f[d];if("function"==typeof e)g=e(g,c);else for(var h=0;h<e.length;h++){var i=e[h];"function"==typeof i&&(g=i(g,c))}a.append(d,g)}if(null!=b.file){var j=b.fileFormDataName||"file";if("[object Array]"===Object.prototype.toString.call(b.file))for(var k="[object String]"===Object.prototype.toString.call(j),h=0;h<b.file.length;h++)a.append(k?j+h:j[h],b.file[h],b.file[h].name);else a.append(j,b.file,b.file.name)}return a},b.data=d,c(b)},this.http=function(a){return c(a)}}]),a.directive("ngFileSelect",["$parse","$timeout",function(a,b){return function(c,d,e){var f=a(e.ngFileSelect);d.bind("change",function(a){var d,e,g=[];if(d=a.target.files,null!=d)for(e=0;e<d.length;e++)g.push(d.item(e));b(function(){f(c,{$files:g,$event:a})})}),("ontouchstart"in window||navigator.maxTouchPoints>0||navigator.msMaxTouchPoints>0)&&d.bind("touchend",function(a){a.preventDefault(),a.target.click()})}}]),a.directive("ngFileDropAvailable",["$parse","$timeout",function(a,b){return function(c,d,e){if("draggable"in document.createElement("span")){var f=a(e.ngFileDropAvailable);b(function(){f(c)})}}}]),a.directive("ngFileDrop",["$parse","$timeout",function(a,b){return function(c,d,e){function f(a,b){if(b.isDirectory){var c=b.createReader();i++,c.readEntries(function(b){for(var c=0;c<b.length;c++)f(a,b[c]);i--})}else i++,b.file(function(b){i--,a.push(b)})}if("draggable"in document.createElement("span")){var g=null,h=a(e.ngFileDrop);d[0].addEventListener("dragover",function(a){b.cancel(g),a.stopPropagation(),a.preventDefault(),d.addClass(e.ngFileDragOverClass||"dragover")},!1),d[0].addEventListener("dragleave",function(){g=b(function(){d.removeClass(e.ngFileDragOverClass||"dragover")})},!1);var i=0;d[0].addEventListener("drop",function(a){a.stopPropagation(),a.preventDefault(),d.removeClass(e.ngFileDragOverClass||"dragover");var g=[],j=a.dataTransfer.items;if(j&&j.length>0&&j[0].webkitGetAsEntry)for(var k=0;k<j.length;k++)f(g,j[k].webkitGetAsEntry());else{var l=a.dataTransfer.files;if(null!=l)for(var k=0;k<l.length;k++)g.push(l.item(k))}!function m(d){b(function(){i?m(10):h(c,{$files:g,$event:a})},d||0)}()},!1)}}}])}();

View File

@ -1,4 +1,4 @@
var app = angular.module('gophish', ['ngRoute', 'ngTable', 'ngResource', 'ui.bootstrap', 'highcharts-ng']); var app = angular.module('gophish', ['ngRoute', 'ngTable', 'ngResource', 'ui.bootstrap', 'highcharts-ng', 'angularFileUpload']);
app.config(function($routeProvider) { app.config(function($routeProvider) {
$routeProvider $routeProvider

View File

@ -193,8 +193,8 @@ app.controller('CampaignResultsCtrl', function($scope, CampaignService, GroupSer
size: { size: {
height: 300 height: 300
}, },
credits : { credits: {
enabled : false enabled: false
}, },
loading: false, loading: false,
} }
@ -324,7 +324,25 @@ app.controller('GroupCtrl', function($scope, $modal, GroupService, ngTableParams
} }
}) })
var GroupModalCtrl = function($scope, $modalInstance) { var GroupModalCtrl = function($scope, $modalInstance, $upload) {
$scope.onFileSelect = function($file) {
$scope.upload = $upload.upload({
url: '/api/import/group',
data: {},
file: $file,
}).progress(function(evt) {
console.log('percent: ' + parseInt(100.0 * evt.loaded / evt.total));
}).success(function(data, status, headers, config) {
// file is uploaded successfully
angular.forEach(data, function(record, key) {
$scope.group.targets.push({
email: record.email
});
});
$scope.editGroupTableParams.reload();
//.error(...)
});
};
$scope.cancel = function() { $scope.cancel = function() {
$modalInstance.dismiss('cancel'); $modalInstance.dismiss('cancel');
}; };

View File

@ -10,6 +10,7 @@
<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)">
<fieldset disabled> <fieldset disabled>
<div class="form-group"> <div class="form-group">
<button class="btn btn-danger"><i class="fa fa-plus"></i> Bulk Import Users (Coming Soon!)</button> <button class="btn btn-danger"><i class="fa fa-plus"></i> Bulk Import Users (Coming Soon!)</button>

View File

@ -91,8 +91,10 @@
<!-- 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="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/ui-bootstrap-0.10.0.min.js"></script> <script src="/js/ui-bootstrap-0.10.0.min.js"></script>
<script src="/js/ng-resource.min.js"></script> <script src="/js/ng-resource.min.js"></script>
<script src="/js/ng-table.min.js"></script> <script src="/js/ng-table.min.js"></script>

74
util/util.go Normal file
View File

@ -0,0 +1,74 @@
package util
import (
"encoding/csv"
"fmt"
"io"
"net/http"
"github.com/jordan-wright/gophish/models"
)
func ParseCSV(r *http.Request) ([]models.Target, error) {
mr, err := r.MultipartReader()
ts := []models.Target{}
if err != nil {
return ts, err
}
for {
part, err := mr.NextPart()
if err == io.EOF {
break
}
// Skip the "submit" part
if part.FileName() == "" {
continue
}
defer part.Close()
reader := csv.NewReader(part)
reader.TrimLeadingSpace = true
record, err := reader.Read()
if err == io.EOF {
break
}
fi := -1
li := -1
ei := -1
fn := ""
ln := ""
ea := ""
for i, v := range record {
fmt.Println(v)
switch {
case v == "First Name":
fi = i
case v == "Last Name":
li = i
case v == "Email":
ei = i
}
}
for {
record, err := reader.Read()
if err == io.EOF {
break
}
if fi != -1 {
fn = record[fi]
}
if li != -1 {
ln = record[li]
}
if ei != -1 {
ea = record[ei]
}
t := models.Target{
FirstName: fn,
LastName: ln,
Email: ea,
}
ts = append(ts, t)
}
}
return ts, nil
}