mirror of
https://github.com/phishingclub/phishingclub.git
synced 2026-02-13 00:22:49 +00:00
598 lines
15 KiB
Go
598 lines
15 KiB
Go
package controller
|
|
|
|
import (
|
|
"archive/zip"
|
|
"bytes"
|
|
"encoding/csv"
|
|
"fmt"
|
|
|
|
"github.com/gin-gonic/gin"
|
|
"github.com/google/uuid"
|
|
"github.com/phishingclub/phishingclub/api"
|
|
"github.com/phishingclub/phishingclub/cache"
|
|
"github.com/phishingclub/phishingclub/database"
|
|
"github.com/phishingclub/phishingclub/model"
|
|
"github.com/phishingclub/phishingclub/repository"
|
|
"github.com/phishingclub/phishingclub/service"
|
|
"github.com/phishingclub/phishingclub/utils"
|
|
"github.com/phishingclub/phishingclub/vo"
|
|
)
|
|
|
|
// DomainColumnsMap is a map between the frontend and the backend
|
|
// so the frontend has user friendly names instead of direct references
|
|
// to the database schema
|
|
// this is tied to a slice in the repository package
|
|
var CompanyColumnsMap = map[string]string{
|
|
"created_at": repository.TableColumn(database.COMPANY_TABLE, "created_at"),
|
|
"updated_at": repository.TableColumn(database.COMPANY_TABLE, "updated_at"),
|
|
"name": repository.TableColumn(database.COMPANY_TABLE, "name"),
|
|
"comment": repository.TableColumn(database.COMPANY_TABLE, "comment"),
|
|
}
|
|
|
|
// Company is a Company controller
|
|
type Company struct {
|
|
Common
|
|
CompanyService *service.Company
|
|
CampaignService *service.Campaign
|
|
RecipientService *service.Recipient
|
|
}
|
|
|
|
// GetByID gets a company by id
|
|
func (c *Company) GetByID(g *gin.Context) {
|
|
session, _, ok := c.handleSession(g)
|
|
if !ok {
|
|
return
|
|
}
|
|
companyID, err := uuid.Parse(g.Param("id"))
|
|
if err != nil {
|
|
// ignore err as caused by bad user input
|
|
_ = err
|
|
c.Response.BadRequestMessage(g, api.InvalidCompanyID)
|
|
return
|
|
}
|
|
// get company
|
|
ctx := g.Request.Context()
|
|
company, err := c.CompanyService.GetByID(
|
|
ctx,
|
|
session,
|
|
&companyID,
|
|
)
|
|
// handle response
|
|
if ok := c.handleErrors(g, err); !ok {
|
|
return
|
|
}
|
|
c.Response.OK(g, company)
|
|
}
|
|
|
|
// ExportByCompanyID outputs a CSV with all events related to the recipient
|
|
func (c *Company) ExportByCompanyID(g *gin.Context) {
|
|
session, _, ok := c.handleSession(g)
|
|
if !ok {
|
|
return
|
|
}
|
|
// parse request
|
|
companyID, ok := c.handleParseIDParam(g)
|
|
if !ok {
|
|
return
|
|
}
|
|
// get the company exported
|
|
company, err := c.CompanyService.GetByID(
|
|
g,
|
|
session,
|
|
companyID,
|
|
)
|
|
// create ZIP file in memory
|
|
zipBuffer := new(bytes.Buffer)
|
|
zipWriter := zip.NewWriter(zipBuffer)
|
|
zipFileName := fmt.Sprintf("company_export_%s.zip", company.Name.MustGet().String())
|
|
|
|
// add company data to zip
|
|
{
|
|
buffer := &bytes.Buffer{}
|
|
writer := csv.NewWriter(buffer)
|
|
headers := []string{
|
|
"Created at",
|
|
"Updated at",
|
|
"Name",
|
|
"Comment",
|
|
}
|
|
err = writer.Write(headers)
|
|
if ok := c.handleErrors(g, err); !ok {
|
|
return
|
|
}
|
|
row := []string{
|
|
utils.CSVFromDate(company.CreatedAt),
|
|
utils.CSVFromDate(company.UpdatedAt),
|
|
utils.CSVRemoveFormulaStart(utils.NullableToString(company.Name)),
|
|
utils.CSVRemoveFormulaStart(utils.NullableToString(company.Comment)),
|
|
}
|
|
err = writer.Write(row)
|
|
if ok := c.handleErrors(g, err); !ok {
|
|
return
|
|
}
|
|
writer.Flush()
|
|
// add to zip
|
|
f, err := zipWriter.Create("company.csv")
|
|
if ok := c.handleErrors(g, err); !ok {
|
|
return
|
|
}
|
|
_, err = f.Write(buffer.Bytes())
|
|
if ok := c.handleErrors(g, err); !ok {
|
|
return
|
|
}
|
|
}
|
|
|
|
// add recipients to zip
|
|
{
|
|
// get the recipients
|
|
recipients, err := c.RecipientService.GetByCompanyID(
|
|
g,
|
|
session,
|
|
companyID,
|
|
&repository.RecipientOption{
|
|
WithCompany: true,
|
|
WithGroups: true,
|
|
},
|
|
)
|
|
if ok := c.handleErrors(g, err); !ok {
|
|
return
|
|
}
|
|
// write a csv buffer with all recipient and their groups
|
|
buffer := &bytes.Buffer{}
|
|
writer := csv.NewWriter(buffer)
|
|
headers := []string{
|
|
"Created at",
|
|
"Updated at",
|
|
"Email",
|
|
"Phone",
|
|
"Extra Identifier",
|
|
"Name",
|
|
"Position",
|
|
"Department",
|
|
"City",
|
|
"Country",
|
|
"Misc",
|
|
}
|
|
// find the recipient with the most groups and add that number of
|
|
// extra headers for groups
|
|
maxGroups := 0
|
|
for _, recipient := range recipients.Rows {
|
|
groups, _ := recipient.Groups.Get()
|
|
if groupLen := len(groups); groupLen > maxGroups {
|
|
maxGroups = groupLen
|
|
}
|
|
}
|
|
for i := 1; i <= maxGroups; i++ {
|
|
headers = append(headers, fmt.Sprintf("Group %d", i))
|
|
}
|
|
err = writer.Write(headers)
|
|
if ok := c.handleErrors(g, err); !ok {
|
|
return
|
|
}
|
|
for _, recipient := range recipients.Rows {
|
|
groups, _ := recipient.Groups.Get()
|
|
row := []string{
|
|
utils.CSVFromDate(recipient.CreatedAt),
|
|
utils.CSVFromDate(recipient.UpdatedAt),
|
|
utils.CSVRemoveFormulaStart(utils.NullableToString(recipient.Email)),
|
|
utils.CSVRemoveFormulaStart(utils.NullableToString(recipient.Phone)),
|
|
utils.CSVRemoveFormulaStart(utils.NullableToString(recipient.ExtraIdentifier)),
|
|
utils.CSVRemoveFormulaStart(utils.NullableToString(recipient.FirstName)),
|
|
utils.CSVRemoveFormulaStart(utils.NullableToString(recipient.LastName)),
|
|
utils.CSVRemoveFormulaStart(utils.NullableToString(recipient.Position)),
|
|
utils.CSVRemoveFormulaStart(utils.NullableToString(recipient.Department)),
|
|
utils.CSVRemoveFormulaStart(utils.NullableToString(recipient.City)),
|
|
utils.CSVRemoveFormulaStart(utils.NullableToString(recipient.Country)),
|
|
utils.CSVRemoveFormulaStart(utils.NullableToString(recipient.Misc)),
|
|
}
|
|
for _, group := range groups {
|
|
row = append(row, group.Name.MustGet().String())
|
|
}
|
|
err = writer.Write(row)
|
|
if ok := c.handleErrors(g, err); !ok {
|
|
return
|
|
}
|
|
writer.Flush()
|
|
}
|
|
// add to zip
|
|
f, err := zipWriter.Create("recipients.csv")
|
|
if ok := c.handleErrors(g, err); !ok {
|
|
return
|
|
}
|
|
_, err = f.Write(buffer.Bytes())
|
|
if ok := c.handleErrors(g, err); !ok {
|
|
return
|
|
}
|
|
}
|
|
// get all campaigns all recipient events
|
|
{
|
|
campaigns, err := c.CampaignService.GetByCompanyID(
|
|
g,
|
|
session,
|
|
companyID,
|
|
&repository.CampaignOption{},
|
|
)
|
|
for _, campaign := range campaigns.Rows {
|
|
headers := []string{
|
|
"Campaign",
|
|
"Created at",
|
|
"Recipient name",
|
|
"Recipient email",
|
|
"Event name",
|
|
"Event Details",
|
|
"User-Agent",
|
|
"IP",
|
|
}
|
|
buffer := &bytes.Buffer{}
|
|
writer := csv.NewWriter(buffer)
|
|
err = writer.Write(headers)
|
|
if ok := c.handleErrors(g, err); !ok {
|
|
return
|
|
}
|
|
queryArgs := vo.QueryArgs{}
|
|
queryArgs.OrderBy = repository.TableColumn(
|
|
database.CAMPAIGN_EVENT_TABLE,
|
|
"created_at",
|
|
)
|
|
sortOrder := g.DefaultQuery("sortOrder", "desc")
|
|
if sortOrder == "desc" {
|
|
queryArgs.Desc = true
|
|
}
|
|
// get all rows
|
|
queryArgs.Limit = 0
|
|
queryArgs.Offset = 0
|
|
// get events by campaign id
|
|
cid := campaign.ID.MustGet()
|
|
events, err := c.CampaignService.GetEventsByCampaignID(
|
|
g.Request.Context(),
|
|
session,
|
|
&cid,
|
|
&queryArgs,
|
|
nil,
|
|
nil,
|
|
)
|
|
if ok := c.handleErrors(g, err); !ok {
|
|
return
|
|
}
|
|
for _, event := range events.Rows {
|
|
firstName := "anonymized"
|
|
lastName := "anonymized"
|
|
recpEmail := "anonymized"
|
|
if event.Recipient != nil {
|
|
firstName = event.Recipient.FirstName.MustGet().String()
|
|
lastName = event.Recipient.LastName.MustGet().String()
|
|
recpEmail = event.Recipient.Email.MustGet().String()
|
|
}
|
|
row := []string{
|
|
utils.CSVRemoveFormulaStart(campaign.Name.MustGet().String()),
|
|
utils.CSVFromDate(event.CreatedAt),
|
|
utils.CSVRemoveFormulaStart(firstName),
|
|
utils.CSVRemoveFormulaStart(lastName),
|
|
utils.CSVRemoveFormulaStart(recpEmail),
|
|
utils.CSVRemoveFormulaStart(cache.EventNameByID[event.EventID.String()]),
|
|
utils.CSVRemoveFormulaStart(event.Data.String()),
|
|
utils.CSVRemoveFormulaStart(event.UserAgent.String()),
|
|
utils.CSVRemoveFormulaStart(event.IP.String()),
|
|
}
|
|
err = writer.Write(row)
|
|
if ok := c.handleErrors(g, err); !ok {
|
|
return
|
|
}
|
|
}
|
|
// add a new subdirectory wit the event file in the zip
|
|
writer.Flush()
|
|
// add to zip
|
|
filename := fmt.Sprintf("campaign_events/%s.csv", campaign.Name.MustGet().String())
|
|
f, err := zipWriter.Create(filename)
|
|
if ok := c.handleErrors(g, err); !ok {
|
|
return
|
|
}
|
|
_, err = f.Write(buffer.Bytes())
|
|
if ok := c.handleErrors(g, err); !ok {
|
|
return
|
|
}
|
|
}
|
|
}
|
|
// close zip
|
|
err = zipWriter.Close()
|
|
if ok := c.handleErrors(g, err); !ok {
|
|
return
|
|
}
|
|
|
|
c.responseWithZIP(g, zipBuffer, zipFileName)
|
|
}
|
|
|
|
// ExportShared outputs a CSV with all shared recipients and events
|
|
func (c *Company) ExportShared(g *gin.Context) {
|
|
session, _, ok := c.handleSession(g)
|
|
if !ok {
|
|
return
|
|
}
|
|
// create ZIP file in memory
|
|
zipBuffer := new(bytes.Buffer)
|
|
zipWriter := zip.NewWriter(zipBuffer)
|
|
zipFileName := "shared_export_%s.zip"
|
|
// add recipients to zip
|
|
{
|
|
// get the recipients
|
|
recipients, err := c.RecipientService.GetByCompanyID(
|
|
g,
|
|
session,
|
|
nil,
|
|
&repository.RecipientOption{
|
|
WithCompany: true,
|
|
WithGroups: true,
|
|
},
|
|
)
|
|
if ok := c.handleErrors(g, err); !ok {
|
|
return
|
|
}
|
|
// write a csv buffer with all recipient and their groups
|
|
buffer := &bytes.Buffer{}
|
|
writer := csv.NewWriter(buffer)
|
|
headers := []string{
|
|
"Created at",
|
|
"Updated at",
|
|
"Email",
|
|
"Phone",
|
|
"Extra Identifier",
|
|
"Name",
|
|
"Position",
|
|
"Department",
|
|
"City",
|
|
"Country",
|
|
"Misc",
|
|
}
|
|
// find the recipient with the most groups and add that number of
|
|
// extra headers for groups
|
|
maxGroups := 0
|
|
for _, recipient := range recipients.Rows {
|
|
groups, _ := recipient.Groups.Get()
|
|
if groupLen := len(groups); groupLen > maxGroups {
|
|
maxGroups = groupLen
|
|
}
|
|
}
|
|
for i := 1; i <= maxGroups; i++ {
|
|
headers = append(headers, fmt.Sprintf("Group %d", i))
|
|
}
|
|
err = writer.Write(headers)
|
|
if ok := c.handleErrors(g, err); !ok {
|
|
return
|
|
}
|
|
for _, recipient := range recipients.Rows {
|
|
groups, _ := recipient.Groups.Get()
|
|
row := []string{
|
|
utils.CSVFromDate(recipient.CreatedAt),
|
|
utils.CSVFromDate(recipient.UpdatedAt),
|
|
utils.CSVRemoveFormulaStart(utils.NullableToString(recipient.Email)),
|
|
utils.CSVRemoveFormulaStart(utils.NullableToString(recipient.Phone)),
|
|
utils.CSVRemoveFormulaStart(utils.NullableToString(recipient.ExtraIdentifier)),
|
|
utils.CSVRemoveFormulaStart(utils.NullableToString(recipient.FirstName)),
|
|
utils.CSVRemoveFormulaStart(utils.NullableToString(recipient.LastName)),
|
|
utils.CSVRemoveFormulaStart(utils.NullableToString(recipient.Position)),
|
|
utils.CSVRemoveFormulaStart(utils.NullableToString(recipient.Department)),
|
|
utils.CSVRemoveFormulaStart(utils.NullableToString(recipient.City)),
|
|
utils.CSVRemoveFormulaStart(utils.NullableToString(recipient.Country)),
|
|
utils.CSVRemoveFormulaStart(utils.NullableToString(recipient.Misc)),
|
|
}
|
|
for _, group := range groups {
|
|
row = append(row, group.Name.MustGet().String())
|
|
}
|
|
err = writer.Write(row)
|
|
if ok := c.handleErrors(g, err); !ok {
|
|
return
|
|
}
|
|
writer.Flush()
|
|
}
|
|
// add to zip
|
|
f, err := zipWriter.Create("recipients.csv")
|
|
if ok := c.handleErrors(g, err); !ok {
|
|
return
|
|
}
|
|
_, err = f.Write(buffer.Bytes())
|
|
if ok := c.handleErrors(g, err); !ok {
|
|
return
|
|
}
|
|
}
|
|
// get all campaigns all recipient events
|
|
{
|
|
campaigns, err := c.CampaignService.GetByCompanyID(
|
|
g,
|
|
session,
|
|
nil,
|
|
&repository.CampaignOption{},
|
|
)
|
|
for _, campaign := range campaigns.Rows {
|
|
headers := []string{
|
|
"Campaign",
|
|
"Created at",
|
|
"Recipient name",
|
|
"Recipient email",
|
|
"Event name",
|
|
"Event Details",
|
|
"User-Agent",
|
|
"IP",
|
|
}
|
|
buffer := &bytes.Buffer{}
|
|
writer := csv.NewWriter(buffer)
|
|
err = writer.Write(headers)
|
|
if ok := c.handleErrors(g, err); !ok {
|
|
return
|
|
}
|
|
queryArgs := vo.QueryArgs{}
|
|
queryArgs.OrderBy = repository.TableColumn(
|
|
database.CAMPAIGN_EVENT_TABLE,
|
|
"created_at",
|
|
)
|
|
sortOrder := g.DefaultQuery("sortOrder", "desc")
|
|
if sortOrder == "desc" {
|
|
queryArgs.Desc = true
|
|
}
|
|
// get all rows
|
|
queryArgs.Limit = 0
|
|
queryArgs.Offset = 0
|
|
// get events by campaign id
|
|
cid := campaign.ID.MustGet()
|
|
events, err := c.CampaignService.GetEventsByCampaignID(
|
|
g.Request.Context(),
|
|
session,
|
|
&cid,
|
|
&queryArgs,
|
|
nil,
|
|
nil,
|
|
)
|
|
if ok := c.handleErrors(g, err); !ok {
|
|
return
|
|
}
|
|
for _, event := range events.Rows {
|
|
firstName := "anonymized"
|
|
lastName := "anonymized"
|
|
recpEmail := "anonymized"
|
|
if event.Recipient != nil {
|
|
firstName = event.Recipient.FirstName.MustGet().String()
|
|
lastName = event.Recipient.LastName.MustGet().String()
|
|
recpEmail = event.Recipient.Email.MustGet().String()
|
|
}
|
|
row := []string{
|
|
utils.CSVRemoveFormulaStart(campaign.Name.MustGet().String()),
|
|
utils.CSVFromDate(event.CreatedAt),
|
|
utils.CSVRemoveFormulaStart(firstName),
|
|
utils.CSVRemoveFormulaStart(lastName),
|
|
utils.CSVRemoveFormulaStart(recpEmail),
|
|
utils.CSVRemoveFormulaStart(cache.EventNameByID[event.EventID.String()]),
|
|
utils.CSVRemoveFormulaStart(event.Data.String()),
|
|
utils.CSVRemoveFormulaStart(event.UserAgent.String()),
|
|
utils.CSVRemoveFormulaStart(event.IP.String()),
|
|
}
|
|
err = writer.Write(row)
|
|
if ok := c.handleErrors(g, err); !ok {
|
|
return
|
|
}
|
|
}
|
|
// add a new subdirectory wit the event file in the zip
|
|
writer.Flush()
|
|
// add to zip
|
|
filename := fmt.Sprintf("campaign_events/%s.csv", campaign.Name.MustGet().String())
|
|
f, err := zipWriter.Create(filename)
|
|
if ok := c.handleErrors(g, err); !ok {
|
|
return
|
|
}
|
|
_, err = f.Write(buffer.Bytes())
|
|
if ok := c.handleErrors(g, err); !ok {
|
|
return
|
|
}
|
|
}
|
|
}
|
|
// close zip
|
|
err := zipWriter.Close()
|
|
if ok := c.handleErrors(g, err); !ok {
|
|
return
|
|
}
|
|
|
|
c.responseWithZIP(g, zipBuffer, zipFileName)
|
|
}
|
|
|
|
// ChangeName changes a company name
|
|
func (c *Company) ChangeName(g *gin.Context) {
|
|
session, _, ok := c.handleSession(g)
|
|
if !ok {
|
|
return
|
|
}
|
|
id, ok := c.handleParseIDParam(g)
|
|
if !ok {
|
|
return
|
|
}
|
|
var req model.Company
|
|
if ok := c.handleParseRequest(g, &req); !ok {
|
|
return
|
|
}
|
|
// change company name
|
|
err := c.CompanyService.UpdateByID(
|
|
g,
|
|
session,
|
|
id,
|
|
&req,
|
|
)
|
|
// handle response
|
|
if ok := c.handleErrors(g, err); !ok {
|
|
return
|
|
}
|
|
c.Response.OK(g, nil)
|
|
}
|
|
|
|
// SoftDelete soft deletes a company
|
|
func (c *Company) DeleteByID(g *gin.Context) {
|
|
session, _, ok := c.handleSession(g)
|
|
if !ok {
|
|
return
|
|
}
|
|
// parse request
|
|
id, ok := c.handleParseIDParam(g)
|
|
if !ok {
|
|
return
|
|
}
|
|
// TODO company delete should FAIL if it has any relations to anything
|
|
// delete company
|
|
_, err := c.CompanyService.DeleteByID(g, session, id)
|
|
// handle response
|
|
if ok := c.handleErrors(g, err); !ok {
|
|
return
|
|
}
|
|
c.Response.OK(g, gin.H{})
|
|
}
|
|
|
|
// Create creates a company
|
|
func (c *Company) Create(g *gin.Context) {
|
|
// handle session
|
|
session, _, ok := c.handleSession(g)
|
|
if !ok {
|
|
return
|
|
}
|
|
// parse req
|
|
var req model.Company
|
|
if ok := c.handleParseRequest(g, &req); !ok {
|
|
return
|
|
}
|
|
// save company
|
|
ctx := g.Request.Context()
|
|
company, err := c.CompanyService.Create(
|
|
ctx,
|
|
session,
|
|
&req,
|
|
)
|
|
// handle response
|
|
if ok := c.handleErrors(g, err); !ok {
|
|
return
|
|
}
|
|
c.Response.OK(g, gin.H{
|
|
"id": company.ID,
|
|
})
|
|
}
|
|
|
|
// GetAll gets all companies with pagination
|
|
func (c *Company) GetAll(g *gin.Context) {
|
|
session, _, ok := c.handleSession(g)
|
|
if !ok {
|
|
return
|
|
}
|
|
// parse request
|
|
queryArgs, ok := c.handleQueryArgs(g)
|
|
if !ok {
|
|
return
|
|
}
|
|
queryArgs.DefaultSortByUpdatedAt()
|
|
queryArgs.RemapOrderBy(CompanyColumnsMap)
|
|
// get companies
|
|
ctx := g.Request.Context()
|
|
companies, err := c.CompanyService.GetAll(
|
|
ctx,
|
|
session,
|
|
queryArgs,
|
|
)
|
|
// handle response
|
|
if ok := c.handleErrors(g, err); !ok {
|
|
return
|
|
}
|
|
c.Response.OK(g, companies)
|
|
}
|