[medium] API dashboard to display the last 10 actions

This commit is contained in:
Julien Vehent 2014-05-08 17:46:30 -04:00
Родитель 676e0cb5d6
Коммит 5e63e26a7a
3 изменённых файлов: 130 добавлений и 22 удалений

Просмотреть файл

@ -103,6 +103,7 @@ func main() {
s.HandleFunc("/command/cancel/", cancelCommand).Methods("POST")
s.HandleFunc("/agent/dashboard", getAgentsDashboard).Methods("GET")
s.HandleFunc("/agent/search", searchAgents).Methods("GET")
s.HandleFunc("/dashboard", getDashboard).Methods("GET")
// all set, start the http handler
http.Handle("/", r)
@ -138,7 +139,8 @@ func respond(code int, response *cljs.Resource, respWriter http.ResponseWriter,
func getHome(respWriter http.ResponseWriter, request *http.Request) {
var err error
opid := mig.GenID()
resource := cljs.New(request.URL.String())
loc := fmt.Sprintf("http://%s:%d%s", ctx.Server.IP, ctx.Server.Port, request.URL.String())
resource := cljs.New(loc)
defer func() {
if e := recover(); e != nil {
ctx.Channels.Log <- mig.Log{OpID: opid, Desc: fmt.Sprintf("%v", e)}.Err()
@ -152,7 +154,7 @@ func getHome(respWriter http.ResponseWriter, request *http.Request) {
err = resource.AddLink(cljs.Link{
Rel: "create action",
Href: fmt.Sprintf("%s/action/create/", ctx.Server.BaseURL),
Name: "Create an action"})
Name: "POST endpoint to create an action"})
if err != nil {
panic(err)
}
@ -160,7 +162,7 @@ func getHome(respWriter http.ResponseWriter, request *http.Request) {
err = resource.AddLink(cljs.Link{
Rel: "cancel action",
Href: fmt.Sprintf("%s/action/cancel/", ctx.Server.BaseURL),
Name: "Cancel an action"})
Name: "POST endpoint to cancel an action"})
if err != nil {
panic(err)
}
@ -168,7 +170,7 @@ func getHome(respWriter http.ResponseWriter, request *http.Request) {
err = resource.AddLink(cljs.Link{
Rel: "cancel command",
Href: fmt.Sprintf("%s/command/cancel/", ctx.Server.BaseURL),
Name: "Cancel a command"})
Name: "POST endpoint to cancel a command"})
if err != nil {
panic(err)
}
@ -177,7 +179,7 @@ func getHome(respWriter http.ResponseWriter, request *http.Request) {
err = resource.AddQuery(cljs.Query{
Rel: "Query action by ID",
Href: fmt.Sprintf("%s/action", ctx.Server.BaseURL),
Prompt: "Query action by ID",
Prompt: "GET endpoint to query an action by ID, using url parameter ?actionid=<numerical id>",
Data: []cljs.Data{
{Name: "actionid", Value: "[0-9]{1,20}", Prompt: "Action ID"},
},
@ -189,7 +191,7 @@ func getHome(respWriter http.ResponseWriter, request *http.Request) {
resource.AddQuery(cljs.Query{
Rel: "Query command by ID",
Href: fmt.Sprintf("%s/command", ctx.Server.BaseURL),
Prompt: "Query command by ID",
Prompt: "GET endpoint to query a command by ID, using url parameter ?commandid=<numerical id>",
Data: []cljs.Data{
{Name: "commandid", Value: "[0-9]{1,20}", Prompt: "Command ID"},
},
@ -201,7 +203,7 @@ func getHome(respWriter http.ResponseWriter, request *http.Request) {
resource.AddQuery(cljs.Query{
Rel: "Search agent by name",
Href: fmt.Sprintf("%s/agent/search", ctx.Server.BaseURL),
Prompt: "Search agent by name",
Prompt: "GET endpoint to search agent by name, using url parameter ?name=<string>",
Data: []cljs.Data{
{Name: "name", Value: "agent123.example.net", Prompt: "Agent Name"},
},
@ -213,7 +215,7 @@ func getHome(respWriter http.ResponseWriter, request *http.Request) {
err = resource.AddQuery(cljs.Query{
Rel: "Query MIG data",
Href: fmt.Sprintf("%s/search", ctx.Server.BaseURL),
Prompt: "Query MIG data",
Prompt: "GET endpoint to search for stuff, using the url parameters describes in the data.",
Data: []cljs.Data{
{Name: "actionid", Value: "[0-9]{1,20}", Prompt: "Action ID"},
{Name: "search", Value: "positiveresults, ...", Prompt: "Name of search query"},
@ -237,7 +239,8 @@ func getHome(respWriter http.ResponseWriter, request *http.Request) {
// search is a generic function to run queries against mongodb
func search(respWriter http.ResponseWriter, request *http.Request) {
opid := mig.GenID()
resource := cljs.New(request.URL.String())
loc := fmt.Sprintf("http://%s:%d%s", ctx.Server.IP, ctx.Server.Port, request.URL.String())
resource := cljs.New(loc)
defer func() {
if e := recover(); e != nil {
ctx.Channels.Log <- mig.Log{OpID: opid, Desc: fmt.Sprintf("%v", e)}.Err()
@ -261,7 +264,8 @@ func search(respWriter http.ResponseWriter, request *http.Request) {
// describeCreateAction returns a resource that describes how to POST new actions
func describeCreateAction(respWriter http.ResponseWriter, request *http.Request) {
opid := mig.GenID()
resource := cljs.New(request.URL.String())
loc := fmt.Sprintf("http://%s:%d%s", ctx.Server.IP, ctx.Server.Port, request.URL.String())
resource := cljs.New(loc)
defer func() {
if e := recover(); e != nil {
ctx.Channels.Log <- mig.Log{OpID: opid, Desc: fmt.Sprintf("%v", e)}.Err()
@ -288,7 +292,8 @@ func createAction(respWriter http.ResponseWriter, request *http.Request) {
var err error
opid := mig.GenID()
var action mig.Action
resource := cljs.New(request.URL.String())
loc := fmt.Sprintf("http://%s:%d%s", ctx.Server.IP, ctx.Server.Port, request.URL.String())
resource := cljs.New(loc)
defer func() {
if e := recover(); e != nil {
ctx.Channels.Log <- mig.Log{OpID: opid, ActionID: action.ID, Desc: fmt.Sprintf("%v", e)}.Err()
@ -389,7 +394,8 @@ func createAction(respWriter http.ResponseWriter, request *http.Request) {
// describeCancelAction returns a resource that describes how to cancel an action
func describeCancelAction(respWriter http.ResponseWriter, request *http.Request) {
opid := mig.GenID()
resource := cljs.New(request.URL.String())
loc := fmt.Sprintf("http://%s:%d%s", ctx.Server.IP, ctx.Server.Port, request.URL.String())
resource := cljs.New(loc)
defer func() {
if e := recover(); e != nil {
ctx.Channels.Log <- mig.Log{OpID: opid, Desc: fmt.Sprintf("%v", e)}.Err()
@ -413,7 +419,8 @@ func describeCancelAction(respWriter http.ResponseWriter, request *http.Request)
// cancelAction receives an action ID and issue a cancellation order
func cancelAction(respWriter http.ResponseWriter, request *http.Request) {
opid := mig.GenID()
resource := cljs.New(request.URL.String())
loc := fmt.Sprintf("http://%s:%d%s", ctx.Server.IP, ctx.Server.Port, request.URL.String())
resource := cljs.New(loc)
defer func() {
if e := recover(); e != nil {
ctx.Channels.Log <- mig.Log{OpID: opid, Desc: fmt.Sprintf("%v", e)}.Err()
@ -429,7 +436,8 @@ func cancelAction(respWriter http.ResponseWriter, request *http.Request) {
func getAction(respWriter http.ResponseWriter, request *http.Request) {
var err error
opid := mig.GenID()
resource := cljs.New(request.URL.String())
loc := fmt.Sprintf("http://%s:%d%s", ctx.Server.IP, ctx.Server.Port, request.URL.String())
resource := cljs.New(loc)
defer func() {
if e := recover(); e != nil {
ctx.Channels.Log <- mig.Log{OpID: opid, Desc: fmt.Sprintf("%v", e)}.Err()
@ -467,7 +475,8 @@ func getAction(respWriter http.ResponseWriter, request *http.Request) {
func getCommand(respWriter http.ResponseWriter, request *http.Request) {
var err error
opid := mig.GenID()
resource := cljs.New(request.URL.String())
loc := fmt.Sprintf("http://%s:%d%s", ctx.Server.IP, ctx.Server.Port, request.URL.String())
resource := cljs.New(loc)
defer func() {
if e := recover(); e != nil {
ctx.Channels.Log <- mig.Log{OpID: opid, Desc: fmt.Sprintf("%v", e)}.Err()
@ -507,7 +516,8 @@ func getCommand(respWriter http.ResponseWriter, request *http.Request) {
// describeCancelCommand returns a resource that describes how to cancel a command
func describeCancelCommand(respWriter http.ResponseWriter, request *http.Request) {
opid := mig.GenID()
resource := cljs.New(request.URL.String())
loc := fmt.Sprintf("http://%s:%d%s", ctx.Server.IP, ctx.Server.Port, request.URL.String())
resource := cljs.New(loc)
defer func() {
if e := recover(); e != nil {
ctx.Channels.Log <- mig.Log{OpID: opid, Desc: fmt.Sprintf("%v", e)}.Err()
@ -534,7 +544,8 @@ func cancelCommand(respWriter http.ResponseWriter, request *http.Request) {
func getAgentsDashboard(respWriter http.ResponseWriter, request *http.Request) {
opid := mig.GenID()
resource := cljs.New(request.URL.String())
loc := fmt.Sprintf("http://%s:%d%s", ctx.Server.IP, ctx.Server.Port, request.URL.String())
resource := cljs.New(loc)
defer func() {
if e := recover(); e != nil {
ctx.Channels.Log <- mig.Log{OpID: opid, Desc: fmt.Sprintf("%v", e)}.Err()
@ -548,7 +559,8 @@ func getAgentsDashboard(respWriter http.ResponseWriter, request *http.Request) {
func searchAgents(respWriter http.ResponseWriter, request *http.Request) {
opid := mig.GenID()
resource := cljs.New(request.URL.String())
loc := fmt.Sprintf("http://%s:%d%s", ctx.Server.IP, ctx.Server.Port, request.URL.String())
resource := cljs.New(loc)
defer func() {
if e := recover(); e != nil {
ctx.Channels.Log <- mig.Log{OpID: opid, Desc: fmt.Sprintf("%v", e)}.Err()
@ -560,6 +572,39 @@ func searchAgents(respWriter http.ResponseWriter, request *http.Request) {
respond(501, resource, respWriter, request, opid)
}
func getDashboard(respWriter http.ResponseWriter, request *http.Request) {
opid := mig.GenID()
loc := fmt.Sprintf("http://%s:%d%s", ctx.Server.IP, ctx.Server.Port, request.URL.String())
resource := cljs.New(loc)
defer func() {
if e := recover(); e != nil {
ctx.Channels.Log <- mig.Log{OpID: opid, Desc: fmt.Sprintf("%v", e)}.Err()
resource.SetError(cljs.Error{Code: fmt.Sprintf("%d", opid), Message: fmt.Sprintf("%v", e)})
respond(500, resource, respWriter, request, opid)
}
ctx.Channels.Log <- mig.Log{OpID: opid, Desc: "leaving getDashboard()"}.Debug()
}()
// add the last 10 actions
actions, err := ctx.DB.Last10Actions()
if err != nil {
panic(err)
}
for _, action := range actions {
// retrieve investigators
action.Investigators, err = ctx.DB.InvestigatorByActionID(action.ID)
if err != nil {
panic(err)
}
// store the results in the resource
actionItem, err := ActionToItem(action, ctx)
if err != nil {
panic(err)
}
resource.AddItem(actionItem)
}
respond(200, resource, respWriter, request, opid)
}
// safeWrite performs a two steps write:
// 1) a temp file is written
// 2) the temp file is moved into the target folder

Просмотреть файл

@ -61,6 +61,59 @@ func (db *DB) Close() {
db.c.Close()
}
// ActionByID retrieves an action from the database using its ID
func (db *DB) Last10Actions() (actions []mig.Action, err error) {
rows, err := db.c.Query(`SELECT id, name, target, description, threat, operations,
validfrom, expireafter, starttime, finishtime, lastupdatetime,
status, sentctr, returnedctr, donectr, cancelledctr, failedctr,
timeoutctr, pgpsignatures, syntaxversion
FROM actions ORDER BY starttime LIMIT 10`)
if err != nil && err != sql.ErrNoRows {
rows.Close()
err = fmt.Errorf("Error while listing actions: '%v'", err)
return
}
if err == sql.ErrNoRows {
rows.Close()
return
}
for rows.Next() {
var jDesc, jThreat, jOps, jSig []byte
var a mig.Action
err = rows.Scan(&a.ID, &a.Name, &a.Target,
&jDesc, &jThreat, &jOps, &a.ValidFrom, &a.ExpireAfter,
&a.StartTime, &a.FinishTime, &a.LastUpdateTime, &a.Status, &a.Counters.Sent,
&a.Counters.Returned, &a.Counters.Done, &a.Counters.Cancelled,
&a.Counters.Failed, &a.Counters.TimeOut, &jSig, &a.SyntaxVersion)
if err != nil {
err = fmt.Errorf("Error while retrieving action: '%v'", err)
return
}
err = json.Unmarshal(jDesc, &a.Description)
if err != nil {
err = fmt.Errorf("Failed to unmarshal action description: '%v'", err)
return
}
err = json.Unmarshal(jThreat, &a.Threat)
if err != nil {
err = fmt.Errorf("Failed to unmarshal action threat: '%v'", err)
return
}
err = json.Unmarshal(jOps, &a.Operations)
if err != nil {
err = fmt.Errorf("Failed to unmarshal action operations: '%v'", err)
return
}
err = json.Unmarshal(jSig, &a.PGPSignatures)
if err != nil {
err = fmt.Errorf("Failed to unmarshal action signatures: '%v'", err)
return
}
actions = append(actions, a)
}
return
}
// ActionByID retrieves an action from the database using its ID
func (db *DB) ActionByID(id uint64) (a mig.Action, err error) {
var jDesc, jThreat, jOps, jSig []byte
@ -83,18 +136,22 @@ func (db *DB) ActionByID(id uint64) (a mig.Action, err error) {
err = json.Unmarshal(jDesc, &a.Description)
if err != nil {
err = fmt.Errorf("Failed to unmarshal action description: '%v'", err)
return
}
err = json.Unmarshal(jThreat, &a.Threat)
if err != nil {
err = fmt.Errorf("Failed to unmarshal action threat: '%v'", err)
return
}
err = json.Unmarshal(jOps, &a.Operations)
if err != nil {
err = fmt.Errorf("Failed to unmarshal action operations: '%v'", err)
return
}
err = json.Unmarshal(jSig, &a.PGPSignatures)
if err != nil {
err = fmt.Errorf("Failed to unmarshal action signatures: '%v'", err)
return
}
return
}
@ -213,7 +270,6 @@ func (db *DB) InvestigatorByActionID(aid uint64) (ivgts []mig.Investigator, err
AND signatures.investigatorid=investigators.id`, aid)
if err != nil && err != sql.ErrNoRows {
rows.Close()
return
err = fmt.Errorf("Error while finding investigator: '%v'", err)
return
}
@ -259,14 +315,17 @@ func (db *DB) CommandByID(id uint64) (cmd mig.Command, err error) {
err = json.Unmarshal(jRes, &cmd.Results)
if err != nil {
err = fmt.Errorf("Failed to unmarshal command results: '%v'", err)
return
}
err = json.Unmarshal(jOps, &cmd.Action.Operations)
if err != nil {
err = fmt.Errorf("Failed to unmarshal action operations: '%v'", err)
return
}
err = json.Unmarshal(jSig, &cmd.Action.PGPSignatures)
if err != nil {
err = fmt.Errorf("Failed to unmarshal action signatures: '%v'", err)
return
}
return
}
@ -296,14 +355,17 @@ func (db *DB) CommandsByActionID(actionid uint64) (commands []mig.Command, err e
err = json.Unmarshal(jRes, &cmd.Results)
if err != nil {
err = fmt.Errorf("Failed to unmarshal command results: '%v'", err)
return
}
err = json.Unmarshal(jOps, &cmd.Action.Operations)
if err != nil {
err = fmt.Errorf("Failed to unmarshal action operations: '%v'", err)
return
}
err = json.Unmarshal(jSig, &cmd.Action.PGPSignatures)
if err != nil {
err = fmt.Errorf("Failed to unmarshal action signatures: '%v'", err)
return
}
commands = append(commands, cmd)
}

Просмотреть файл

@ -35,7 +35,8 @@ the terms of any one of the MPL, the GPL or the LGPL.
package mig
type Investigator struct {
ID uint64
Name, PGPFingerprint string
PublicKey []byte
ID uint64 `json:"id,omitempty"`
Name string `json:"name"`
PGPFingerprint string `json:"pgpfingerprint"`
PublicKey []byte `json:"publickey,omitempty"`
}