Fixing SSRF by requiring an API key for all import endpoints. Fixes #1026

pull/986/head^2
Jordan Wright 2018-03-26 21:04:22 -05:00
parent 9ba3f04d1e
commit 2131c17c33
No known key found for this signature in database
GPG Key ID: 138D5AD2331B3C11
9 changed files with 134 additions and 114 deletions

View File

@ -102,6 +102,13 @@ func (s *ControllersSuite) SetupTest() {
c.UpdateStatus(models.CAMPAIGN_EMAILS_SENT) c.UpdateStatus(models.CAMPAIGN_EMAILS_SENT)
} }
func (s *ControllersSuite) TestRequireAPIKey() {
resp, err := http.Post(fmt.Sprintf("%s/api/import/site", as.URL), "application/json", nil)
s.Nil(err)
defer resp.Body.Close()
s.Equal(resp.StatusCode, http.StatusBadRequest)
}
func (s *ControllersSuite) TestSiteImportBaseHref() { func (s *ControllersSuite) TestSiteImportBaseHref() {
h := "<html><head></head><body><img src=\"/test.png\"/></body></html>" h := "<html><head></head><body><img src=\"/test.png\"/></body></html>"
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {

View File

@ -59,9 +59,9 @@ func CreateAdminRouter() http.Handler {
api.HandleFunc("/smtp/", Use(API_SMTP, mid.RequireAPIKey)) api.HandleFunc("/smtp/", Use(API_SMTP, mid.RequireAPIKey))
api.HandleFunc("/smtp/{id:[0-9]+}", Use(API_SMTP_Id, mid.RequireAPIKey)) api.HandleFunc("/smtp/{id:[0-9]+}", Use(API_SMTP_Id, mid.RequireAPIKey))
api.HandleFunc("/util/send_test_email", Use(API_Send_Test_Email, mid.RequireAPIKey)) api.HandleFunc("/util/send_test_email", Use(API_Send_Test_Email, mid.RequireAPIKey))
api.HandleFunc("/import/group", API_Import_Group) api.HandleFunc("/import/group", Use(API_Import_Group, mid.RequireAPIKey))
api.HandleFunc("/import/email", API_Import_Email) api.HandleFunc("/import/email", Use(API_Import_Email, mid.RequireAPIKey))
api.HandleFunc("/import/site", API_Import_Site) api.HandleFunc("/import/site", Use(API_Import_Site, mid.RequireAPIKey))
// Setup static file serving // Setup static file serving
router.PathPrefix("/").Handler(http.FileServer(http.Dir("./static/"))) router.PathPrefix("/").Handler(http.FileServer(http.Dir("./static/")))

View File

@ -1 +1 @@
function errorFlash(e){$("#flashes").empty(),$("#flashes").append('<div style="text-align:center" class="alert alert-danger"> <i class="fa fa-exclamation-circle"></i> '+e+"</div>")}function successFlash(e){$("#flashes").empty(),$("#flashes").append('<div style="text-align:center" class="alert alert-success"> <i class="fa fa-check-circle"></i> '+e+"</div>")}function modalError(e){$("#modal\\.flashes").empty().append('<div style="text-align:center" class="alert alert-danger"> <i class="fa fa-exclamation-circle"></i> '+e+"</div>")}function query(e,t,n,r){return $.ajax({url:"/api"+e+"?api_key="+user.api_key,async:r,method:t,data:JSON.stringify(n),dataType:"json",contentType:"application/json"})}function escapeHtml(e){return $("<div/>").text(e).html()}function unescapeHtml(e){return $("<div/>").html(e).text()}var capitalize=function(e){return e.charAt(0).toUpperCase()+e.slice(1)},api={campaigns:{get:function(){return query("/campaigns/","GET",{},!1)},post:function(e){return query("/campaigns/","POST",e,!1)},summary:function(){return query("/campaigns/summary","GET",{},!1)}},campaignId:{get:function(e){return query("/campaigns/"+e,"GET",{},!0)},delete:function(e){return query("/campaigns/"+e,"DELETE",{},!1)},results:function(e){return query("/campaigns/"+e+"/results","GET",{},!0)},complete:function(e){return query("/campaigns/"+e+"/complete","GET",{},!0)},summary:function(e){return query("/campaigns/"+e+"/summary","GET",{},!0)}},groups:{get:function(){return query("/groups/","GET",{},!1)},post:function(e){return query("/groups/","POST",e,!1)},summary:function(){return query("/groups/summary","GET",{},!0)}},groupId:{get:function(e){return query("/groups/"+e,"GET",{},!1)},put:function(e){return query("/groups/"+e.id,"PUT",e,!1)},delete:function(e){return query("/groups/"+e,"DELETE",{},!1)}},templates:{get:function(){return query("/templates/","GET",{},!1)},post:function(e){return query("/templates/","POST",e,!1)}},templateId:{get:function(e){return query("/templates/"+e,"GET",{},!1)},put:function(e){return query("/templates/"+e.id,"PUT",e,!1)},delete:function(e){return query("/templates/"+e,"DELETE",{},!1)}},pages:{get:function(){return query("/pages/","GET",{},!1)},post:function(e){return query("/pages/","POST",e,!1)}},pageId:{get:function(e){return query("/pages/"+e,"GET",{},!1)},put:function(e){return query("/pages/"+e.id,"PUT",e,!1)},delete:function(e){return query("/pages/"+e,"DELETE",{},!1)}},SMTP:{get:function(){return query("/smtp/","GET",{},!1)},post:function(e){return query("/smtp/","POST",e,!1)}},SMTPId:{get:function(e){return query("/smtp/"+e,"GET",{},!1)},put:function(e){return query("/smtp/"+e.id,"PUT",e,!1)},delete:function(e){return query("/smtp/"+e,"DELETE",{},!1)}},import_email:function(e){return query("/import/email","POST",{},!1)},clone_site:function(e){return query("/import/site","POST",e,!1)},send_test_email:function(e){return query("/util/send_test_email","POST",e,!0)}};$(document).ready(function(){$.fn.dataTable.moment("MMMM Do YYYY, h:mm:ss a"),$('[data-toggle="tooltip"]').tooltip()}); function errorFlash(e){$("#flashes").empty(),$("#flashes").append('<div style="text-align:center" class="alert alert-danger"> <i class="fa fa-exclamation-circle"></i> '+e+"</div>")}function successFlash(e){$("#flashes").empty(),$("#flashes").append('<div style="text-align:center" class="alert alert-success"> <i class="fa fa-check-circle"></i> '+e+"</div>")}function modalError(e){$("#modal\\.flashes").empty().append('<div style="text-align:center" class="alert alert-danger"> <i class="fa fa-exclamation-circle"></i> '+e+"</div>")}function query(e,t,n,r){return $.ajax({url:"/api"+e+"?api_key="+user.api_key,async:r,method:t,data:JSON.stringify(n),dataType:"json",contentType:"application/json"})}function escapeHtml(e){return $("<div/>").text(e).html()}function unescapeHtml(e){return $("<div/>").html(e).text()}var capitalize=function(e){return e.charAt(0).toUpperCase()+e.slice(1)},api={campaigns:{get:function(){return query("/campaigns/","GET",{},!1)},post:function(e){return query("/campaigns/","POST",e,!1)},summary:function(){return query("/campaigns/summary","GET",{},!1)}},campaignId:{get:function(e){return query("/campaigns/"+e,"GET",{},!0)},delete:function(e){return query("/campaigns/"+e,"DELETE",{},!1)},results:function(e){return query("/campaigns/"+e+"/results","GET",{},!0)},complete:function(e){return query("/campaigns/"+e+"/complete","GET",{},!0)},summary:function(e){return query("/campaigns/"+e+"/summary","GET",{},!0)}},groups:{get:function(){return query("/groups/","GET",{},!1)},post:function(e){return query("/groups/","POST",e,!1)},summary:function(){return query("/groups/summary","GET",{},!0)}},groupId:{get:function(e){return query("/groups/"+e,"GET",{},!1)},put:function(e){return query("/groups/"+e.id,"PUT",e,!1)},delete:function(e){return query("/groups/"+e,"DELETE",{},!1)}},templates:{get:function(){return query("/templates/","GET",{},!1)},post:function(e){return query("/templates/","POST",e,!1)}},templateId:{get:function(e){return query("/templates/"+e,"GET",{},!1)},put:function(e){return query("/templates/"+e.id,"PUT",e,!1)},delete:function(e){return query("/templates/"+e,"DELETE",{},!1)}},pages:{get:function(){return query("/pages/","GET",{},!1)},post:function(e){return query("/pages/","POST",e,!1)}},pageId:{get:function(e){return query("/pages/"+e,"GET",{},!1)},put:function(e){return query("/pages/"+e.id,"PUT",e,!1)},delete:function(e){return query("/pages/"+e,"DELETE",{},!1)}},SMTP:{get:function(){return query("/smtp/","GET",{},!1)},post:function(e){return query("/smtp/","POST",e,!1)}},SMTPId:{get:function(e){return query("/smtp/"+e,"GET",{},!1)},put:function(e){return query("/smtp/"+e.id,"PUT",e,!1)},delete:function(e){return query("/smtp/"+e,"DELETE",{},!1)}},import_email:function(e){return query("/import/email","POST",e,!1)},clone_site:function(e){return query("/import/site","POST",e,!1)},send_test_email:function(e){return query("/util/send_test_email","POST",e,!0)}};$(document).ready(function(){$.fn.dataTable.moment("MMMM Do YYYY, h:mm:ss a"),$('[data-toggle="tooltip"]').tooltip()});

File diff suppressed because one or more lines are too long

View File

@ -1 +1 @@
function save(a){var e=[];$.each($("#targetsTable").DataTable().rows().data(),function(a,s){e.push({first_name:unescapeHtml(s[0]),last_name:unescapeHtml(s[1]),email:unescapeHtml(s[2]),position:unescapeHtml(s[3])})});var s={name:$("#name").val(),targets:e};a!=-1?(s.id=a,api.groupId.put(s).success(function(a){successFlash("Group updated successfully!"),load(),dismiss(),$("#modal").modal("hide")}).error(function(a){modalError(a.responseJSON.message)})):api.groups.post(s).success(function(a){successFlash("Group added successfully!"),load(),dismiss(),$("#modal").modal("hide")}).error(function(a){modalError(a.responseJSON.message)})}function dismiss(){$("#targetsTable").dataTable().DataTable().clear().draw(),$("#name").val(""),$("#modal\\.flashes").empty()}function edit(a){if(targets=$("#targetsTable").dataTable({destroy:!0,columnDefs:[{orderable:!1,targets:"no-sort"}]}),$("#modalSubmit").unbind("click").click(function(){save(a)}),a==-1);else api.groupId.get(a).success(function(a){$("#name").val(a.name),$.each(a.targets,function(a,e){targets.DataTable().row.add([escapeHtml(e.first_name),escapeHtml(e.last_name),escapeHtml(e.email),escapeHtml(e.position),'<span style="cursor:pointer;"><i class="fa fa-trash-o"></i></span>']).draw()})}).error(function(){errorFlash("Error fetching group")});$("#csvupload").fileupload({dataType:"json",add:function(a,e){$("#modal\\.flashes").empty();var s=/(csv|txt)$/i,t=e.originalFiles[0].name;return t&&!s.test(t.split(".").pop())?(modalError("Unsupported file extension (use .csv or .txt)"),!1):void e.submit()},done:function(a,e){$.each(e.result,function(a,e){addTarget(e.first_name,e.last_name,e.email,e.position)}),targets.DataTable().draw()}})}function deleteGroup(a){var e=groups.find(function(e){return e.id===a});return e?void(confirm("Delete "+e.name+"?")&&api.groupId.delete(a).success(function(a){successFlash(a.message),load()})):void console.log("wat")}function addTarget(a,e,s,t){var o=escapeHtml(s).toLowerCase(),r=[escapeHtml(a),escapeHtml(e),o,escapeHtml(t),'<span style="cursor:pointer;"><i class="fa fa-trash-o"></i></span>'],n=targets.DataTable(),i=n.column(2,{order:"index"}).data().indexOf(o);i>=0?n.row(i,{order:"index"}).data(r):n.row.add(r)}function load(){$("#groupTable").hide(),$("#emptyMessage").hide(),$("#loading").show(),api.groups.summary().success(function(a){if($("#loading").hide(),a.total>0){groups=a.groups,$("#emptyMessage").hide(),$("#groupTable").show();var e=$("#groupTable").DataTable({destroy:!0,columnDefs:[{orderable:!1,targets:"no-sort"}]});e.clear(),$.each(groups,function(a,s){e.row.add([escapeHtml(s.name),escapeHtml(s.num_targets),moment(s.modified_date).format("MMMM Do YYYY, h:mm:ss a"),"<div class='pull-right'><button class='btn btn-primary' data-toggle='modal' data-target='#modal' onclick='edit("+s.id+")'> <i class='fa fa-pencil'></i> </button> <button class='btn btn-danger' onclick='deleteGroup("+s.id+")'> <i class='fa fa-trash-o'></i> </button></div>"]).draw()})}else $("#emptyMessage").show()}).error(function(){errorFlash("Error fetching groups")})}var groups=[];$(document).ready(function(){load(),$("#targetForm").submit(function(){return addTarget($("#firstName").val(),$("#lastName").val(),$("#email").val(),$("#position").val()),targets.DataTable().draw(),$("#targetForm>div>input").val(""),$("#firstName").focus(),!1}),$("#targetsTable").on("click","span>i.fa-trash-o",function(){targets.DataTable().row($(this).parents("tr")).remove().draw()}),$("#modal").on("hide.bs.modal",function(){dismiss()})}); function save(a){var e=[];$.each($("#targetsTable").DataTable().rows().data(),function(a,s){e.push({first_name:unescapeHtml(s[0]),last_name:unescapeHtml(s[1]),email:unescapeHtml(s[2]),position:unescapeHtml(s[3])})});var s={name:$("#name").val(),targets:e};a!=-1?(s.id=a,api.groupId.put(s).success(function(a){successFlash("Group updated successfully!"),load(),dismiss(),$("#modal").modal("hide")}).error(function(a){modalError(a.responseJSON.message)})):api.groups.post(s).success(function(a){successFlash("Group added successfully!"),load(),dismiss(),$("#modal").modal("hide")}).error(function(a){modalError(a.responseJSON.message)})}function dismiss(){$("#targetsTable").dataTable().DataTable().clear().draw(),$("#name").val(""),$("#modal\\.flashes").empty()}function edit(a){if(targets=$("#targetsTable").dataTable({destroy:!0,columnDefs:[{orderable:!1,targets:"no-sort"}]}),$("#modalSubmit").unbind("click").click(function(){save(a)}),a==-1);else api.groupId.get(a).success(function(a){$("#name").val(a.name),$.each(a.targets,function(a,e){targets.DataTable().row.add([escapeHtml(e.first_name),escapeHtml(e.last_name),escapeHtml(e.email),escapeHtml(e.position),'<span style="cursor:pointer;"><i class="fa fa-trash-o"></i></span>']).draw()})}).error(function(){errorFlash("Error fetching group")});$("#csvupload").fileupload({url:"/api/import/group?api_key="+user.api_key,dataType:"json",add:function(a,e){$("#modal\\.flashes").empty();var s=/(csv|txt)$/i,t=e.originalFiles[0].name;return t&&!s.test(t.split(".").pop())?(modalError("Unsupported file extension (use .csv or .txt)"),!1):void e.submit()},done:function(a,e){$.each(e.result,function(a,e){addTarget(e.first_name,e.last_name,e.email,e.position)}),targets.DataTable().draw()}})}function deleteGroup(a){var e=groups.find(function(e){return e.id===a});return e?void(confirm("Delete "+e.name+"?")&&api.groupId.delete(a).success(function(a){successFlash(a.message),load()})):void console.log("wat")}function addTarget(a,e,s,t){var o=escapeHtml(s).toLowerCase(),r=[escapeHtml(a),escapeHtml(e),o,escapeHtml(t),'<span style="cursor:pointer;"><i class="fa fa-trash-o"></i></span>'],n=targets.DataTable(),i=n.column(2,{order:"index"}).data().indexOf(o);i>=0?n.row(i,{order:"index"}).data(r):n.row.add(r)}function load(){$("#groupTable").hide(),$("#emptyMessage").hide(),$("#loading").show(),api.groups.summary().success(function(a){if($("#loading").hide(),a.total>0){groups=a.groups,$("#emptyMessage").hide(),$("#groupTable").show();var e=$("#groupTable").DataTable({destroy:!0,columnDefs:[{orderable:!1,targets:"no-sort"}]});e.clear(),$.each(groups,function(a,s){e.row.add([escapeHtml(s.name),escapeHtml(s.num_targets),moment(s.modified_date).format("MMMM Do YYYY, h:mm:ss a"),"<div class='pull-right'><button class='btn btn-primary' data-toggle='modal' data-target='#modal' onclick='edit("+s.id+")'> <i class='fa fa-pencil'></i> </button> <button class='btn btn-danger' onclick='deleteGroup("+s.id+")'> <i class='fa fa-trash-o'></i> </button></div>"]).draw()})}else $("#emptyMessage").show()}).error(function(){errorFlash("Error fetching groups")})}var groups=[];$(document).ready(function(){load(),$("#targetForm").submit(function(){return addTarget($("#firstName").val(),$("#lastName").val(),$("#email").val(),$("#position").val()),targets.DataTable().draw(),$("#targetForm>div>input").val(""),$("#firstName").focus(),!1}),$("#targetsTable").on("click","span>i.fa-trash-o",function(){targets.DataTable().row($(this).parents("tr")).remove().draw()}),$("#modal").on("hide.bs.modal",function(){dismiss()})});

View File

@ -194,8 +194,8 @@ var api = {
} }
}, },
// import handles all of the "import" functions in the api // import handles all of the "import" functions in the api
import_email: function (raw) { import_email: function (req) {
return query("/import/email", "POST", {}, false) return query("/import/email", "POST", req, false)
}, },
// clone_site handles importing a site by url // clone_site handles importing a site by url
clone_site: function (req) { clone_site: function (req) {

View File

@ -240,15 +240,9 @@ function importEmail() {
if (!raw) { if (!raw) {
modalError("No Content Specified!") modalError("No Content Specified!")
} else { } else {
$.ajax({ api.import_email({
method: "POST",
url: "/api/import/email",
data: JSON.stringify({
content: raw, content: raw,
convert_links: convert_links convert_links: convert_links
}),
dataType: "json",
contentType: "application/json"
}) })
.success(function (data) { .success(function (data) {
$("#text_editor").val(data.text) $("#text_editor").val(data.text)
@ -337,7 +331,8 @@ $(document).ready(function () {
if ( if (
this.$element[0] !== e.target && !this.$element.has(e.target).length this.$element[0] !== e.target && !this.$element.has(e.target).length
// CKEditor compatibility fix start. // CKEditor compatibility fix start.
&& !$(e.target).closest('.cke_dialog, .cke').length &&
!$(e.target).closest('.cke_dialog, .cke').length
// CKEditor compatibility fix end. // CKEditor compatibility fix end.
) { ) {
this.$element.trigger('focus'); this.$element.trigger('focus');

View File

@ -3,7 +3,7 @@ var groups = []
// Save attempts to POST or PUT to /groups/ // Save attempts to POST or PUT to /groups/
function save(id) { function save(id) {
var targets = [] var targets = []
$.each($("#targetsTable").DataTable().rows().data(), function(i, target) { $.each($("#targetsTable").DataTable().rows().data(), function (i, target) {
targets.push({ targets.push({
first_name: unescapeHtml(target[0]), first_name: unescapeHtml(target[0]),
last_name: unescapeHtml(target[1]), last_name: unescapeHtml(target[1]),
@ -21,26 +21,26 @@ function save(id) {
// we need to PUT /groups/:id // we need to PUT /groups/:id
group.id = id group.id = id
api.groupId.put(group) api.groupId.put(group)
.success(function(data) { .success(function (data) {
successFlash("Group updated successfully!") successFlash("Group updated successfully!")
load() load()
dismiss() dismiss()
$("#modal").modal('hide') $("#modal").modal('hide')
}) })
.error(function(data) { .error(function (data) {
modalError(data.responseJSON.message) modalError(data.responseJSON.message)
}) })
} else { } else {
// Else, if this is a new group, POST it // Else, if this is a new group, POST it
// to /groups // to /groups
api.groups.post(group) api.groups.post(group)
.success(function(data) { .success(function (data) {
successFlash("Group added successfully!") successFlash("Group added successfully!")
load() load()
dismiss() dismiss()
$("#modal").modal('hide') $("#modal").modal('hide')
}) })
.error(function(data) { .error(function (data) {
modalError(data.responseJSON.message) modalError(data.responseJSON.message)
}) })
} }
@ -60,16 +60,16 @@ function edit(id) {
targets: "no-sort" targets: "no-sort"
}] }]
}) })
$("#modalSubmit").unbind('click').click(function() { $("#modalSubmit").unbind('click').click(function () {
save(id) save(id)
}) })
if (id == -1) { if (id == -1) {
var group = {} var group = {}
} else { } else {
api.groupId.get(id) api.groupId.get(id)
.success(function(group) { .success(function (group) {
$("#name").val(group.name) $("#name").val(group.name)
$.each(group.targets, function(i, record) { $.each(group.targets, function (i, record) {
targets.DataTable() targets.DataTable()
.row.add([ .row.add([
escapeHtml(record.first_name), escapeHtml(record.first_name),
@ -81,14 +81,15 @@ function edit(id) {
}); });
}) })
.error(function() { .error(function () {
errorFlash("Error fetching group") errorFlash("Error fetching group")
}) })
} }
// Handle file uploads // Handle file uploads
$("#csvupload").fileupload({ $("#csvupload").fileupload({
url: "/api/import/group?api_key=" + user.api_key,
dataType: "json", dataType: "json",
add: function(e, data) { add: function (e, data) {
$("#modal\\.flashes").empty() $("#modal\\.flashes").empty()
var acceptFileTypes = /(csv|txt)$/i; var acceptFileTypes = /(csv|txt)$/i;
var filename = data.originalFiles[0]['name'] var filename = data.originalFiles[0]['name']
@ -98,8 +99,8 @@ function edit(id) {
} }
data.submit(); data.submit();
}, },
done: function(e, data) { done: function (e, data) {
$.each(data.result, function(i, record) { $.each(data.result, function (i, record) {
addTarget( addTarget(
record.first_name, record.first_name,
record.last_name, record.last_name,
@ -112,14 +113,16 @@ function edit(id) {
} }
function deleteGroup(id) { function deleteGroup(id) {
var group = groups.find(function(x){return x.id === id}) var group = groups.find(function (x) {
return x.id === id
})
if (!group) { if (!group) {
console.log('wat'); console.log('wat');
return return
} }
if (confirm("Delete " + group.name + "?")) { if (confirm("Delete " + group.name + "?")) {
api.groupId.delete(id) api.groupId.delete(id)
.success(function(data) { .success(function (data) {
successFlash(data.message) successFlash(data.message)
load() load()
}) })
@ -162,7 +165,7 @@ function load() {
$("#emptyMessage").hide() $("#emptyMessage").hide()
$("#loading").show() $("#loading").show()
api.groups.summary() api.groups.summary()
.success(function(response) { .success(function (response) {
$("#loading").hide() $("#loading").hide()
if (response.total > 0) { if (response.total > 0) {
groups = response.groups groups = response.groups
@ -176,7 +179,7 @@ function load() {
}] }]
}); });
groupTable.clear(); groupTable.clear();
$.each(groups, function(i, group) { $.each(groups, function (i, group) {
groupTable.row.add([ groupTable.row.add([
escapeHtml(group.name), escapeHtml(group.name),
escapeHtml(group.num_targets), escapeHtml(group.num_targets),
@ -193,16 +196,16 @@ function load() {
$("#emptyMessage").show() $("#emptyMessage").show()
} }
}) })
.error(function() { .error(function () {
errorFlash("Error fetching groups") errorFlash("Error fetching groups")
}) })
} }
$(document).ready(function() { $(document).ready(function () {
load() load()
// Setup the event listeners // Setup the event listeners
// Handle manual additions // Handle manual additions
$("#targetForm").submit(function() { $("#targetForm").submit(function () {
addTarget( addTarget(
$("#firstName").val(), $("#firstName").val(),
$("#lastName").val(), $("#lastName").val(),
@ -216,13 +219,13 @@ $(document).ready(function() {
return false; return false;
}); });
// Handle Deletion // Handle Deletion
$("#targetsTable").on("click", "span>i.fa-trash-o", function() { $("#targetsTable").on("click", "span>i.fa-trash-o", function () {
targets.DataTable() targets.DataTable()
.row($(this).parents('tr')) .row($(this).parents('tr'))
.remove() .remove()
.draw(); .draw();
}); });
$("#modal").on("hide.bs.modal", function() { $("#modal").on("hide.bs.modal", function () {
dismiss(); dismiss();
}); });
}); });

View File

@ -3,24 +3,35 @@
<div class="row"> <div class="row">
<div class="col-sm-3 col-md-2 sidebar"> <div class="col-sm-3 col-md-2 sidebar">
<ul class="nav nav-sidebar"> <ul class="nav nav-sidebar">
<li><a href="/">Dashboard</a> <li>
<a href="/">Dashboard</a>
</li> </li>
<li><a href="/campaigns">Campaigns</a> <li>
<a href="/campaigns">Campaigns</a>
</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">Email Templates</a> <li>
<a href="/templates">Email Templates</a>
</li> </li>
<li><a href="/landing_pages">Landing Pages</a> <li>
<a href="/landing_pages">Landing Pages</a>
</li> </li>
<li><a href="/sending_profiles">Sending Profiles</a> <li>
<a href="/sending_profiles">Sending Profiles</a>
</li> </li>
<li><a href="/settings">Settings</a> <li>
<a href="/settings">Settings</a>
</li> </li>
<li><hr></li> <li>
<li><a href="https://gophish.gitbooks.io/user-guide/content/">User Guide</a> <hr>
</li> </li>
<li><a href="/api/">API Documentation</a> <li>
<a href="https://gophish.gitbooks.io/user-guide/content/">User Guide</a>
</li>
<li>
<a href="/api/">API Documentation</a>
</li> </li>
</ul> </ul>
</div> </div>
@ -34,7 +45,8 @@
</div> </div>
<div id="flashes" class="row"></div> <div id="flashes" class="row"></div>
<div class="row"> <div class="row">
<button type="button" class="btn btn-primary" onclick="edit(-1)" data-toggle="modal" data-target="#modal"><i class="fa fa-plus"></i> New Group</button> <button type="button" class="btn btn-primary" onclick="edit(-1)" data-toggle="modal" data-target="#modal">
<i class="fa fa-plus"></i> New Group</button>
</div> </div>
&nbsp; &nbsp;
<div id="loading"> <div id="loading">
@ -65,7 +77,9 @@
<div class="modal-dialog" role="document"> <div class="modal-dialog" role="document">
<div class="modal-content"> <div class="modal-content">
<div class="modal-header"> <div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">&times;</span></button> <button type="button" class="close" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">&times;</span>
</button>
<h4 class="modal-title" id="groupModalLabel">New Group</h4> <h4 class="modal-title" id="groupModalLabel">New Group</h4>
</div> </div>
<div class="modal-body"> <div class="modal-body">
@ -75,8 +89,9 @@
<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>
<div class="form-group"> <div class="form-group">
<span class="btn btn-danger btn-file" data-toggle="tooltip" data-placement="right" title="Supports CSV files" id="fileUpload"><i class="fa fa-plus"></i> Bulk Import Users <span class="btn btn-danger btn-file" data-toggle="tooltip" data-placement="right" title="Supports CSV files" id="fileUpload">
<input type="file" id="csvupload" data-url="/api/import/group" multiple> <i class="fa fa-plus"></i> Bulk Import Users
<input type="file" id="csvupload" multiple>
</span> </span>
</div> </div>
<div class="row"> <div class="row">
@ -94,7 +109,8 @@
<input type="text" class="form-control" placeholder="Position" id="position"> <input type="text" class="form-control" placeholder="Position" id="position">
</div> </div>
<div class="col-sm-1"> <div class="col-sm-1">
<button type="submit" class="btn btn-danger btn-lg"><i class="fa fa-plus"></i> Add</button> <button type="submit" class="btn btn-danger btn-lg">
<i class="fa fa-plus"></i> Add</button>
</div> </div>
</form> </form>
</div> </div>
@ -118,7 +134,6 @@
</div> </div>
</div> </div>
</div> </div>
{{end}} {{end}} {{define "scripts"}}
{{define "scripts"}}
<script src="/js/dist/app/users.min.js"></script> <script src="/js/dist/app/users.min.js"></script>
{{end}} {{end}}