// authenticateLoader is used to authenticate requests that are made to the // loader API endpoints. Rather than operate on GPG signatures, the // authentication instead uses the submitted loader key func authenticateLoader(pass handler) handler { return func(w http.ResponseWriter, r *http.Request) { var ( loaderid float64 err error ) opid := getOpID(r) context.Set(r, opID, opid) lkey := r.Header.Get("X-LOADERKEY") if lkey == "" { resource := cljs.New(fmt.Sprintf("%s%s", ctx.Server.Host, r.URL.String())) resource.SetError(cljs.Error{Code: fmt.Sprintf("%.0f", opid), Message: "X-LOADERKEY header not found"}) respond(http.StatusUnauthorized, resource, w, r) return } // Do a sanity check here on the submitted loader string before // we attempt the authentication err = mig.ValidateLoaderKey(lkey) if err == nil { loaderid, err = ctx.DB.GetLoaderEntryID(lkey) } if err != nil { resource := cljs.New(fmt.Sprintf("%s%s", ctx.Server.Host, r.URL.String())) resource.SetError(cljs.Error{Code: fmt.Sprintf("%.0f", opid), Message: fmt.Sprintf("Loader authorization failed")}) respond(http.StatusUnauthorized, resource, w, r) return } context.Set(r, loaderID, loaderid) // accept request pass(w, r) } }
// authenticate is called prior to processing incoming requests. it implements the client // authentication logic, which mostly consist of validating GPG signed tokens and setting the // identity of the signer in the request context func authenticate(pass handler, adminRequired bool) handler { return func(w http.ResponseWriter, r *http.Request) { var ( err error inv mig.Investigator ) opid := getOpID(r) context.Set(r, opID, opid) context.Set(r, apiRequestCategory, RequestCategoryInvestigator) if !ctx.Authentication.Enabled { inv.Name = "authdisabled" inv.ID = 0 inv.IsAdmin = true goto authorized } if r.Header.Get("X-PGPAUTHORIZATION") == "" { inv.Name = "authmissing" inv.ID = -1 resource := cljs.New(fmt.Sprintf("%s%s", ctx.Server.Host, r.URL.String())) resource.SetError(cljs.Error{Code: fmt.Sprintf("%.0f", opid), Message: "X-PGPAUTHORIZATION header not found"}) respond(http.StatusUnauthorized, resource, w, r) return } inv, err = verifySignedToken(r.Header.Get("X-PGPAUTHORIZATION")) if err != nil { inv.Name = "authfailed" inv.ID = -1 resource := cljs.New(fmt.Sprintf("%s%s", ctx.Server.Host, r.URL.String())) resource.SetError(cljs.Error{Code: fmt.Sprintf("%.0f", opid), Message: fmt.Sprintf("Authorization verification failed with error '%v'", err)}) respond(http.StatusUnauthorized, resource, w, r) return } authorized: // store investigator identity in request context context.Set(r, authenticatedInvName, inv.Name) context.Set(r, authenticatedInvID, inv.ID) context.Set(r, authenticatedInvIsAdmin, inv.IsAdmin) // Validate investigator is an administrator if required if adminRequired { if !inv.IsAdmin { inv.Name = "authfailed" inv.ID = -1 ctx.Channels.Log <- mig.Log{ OpID: getOpID(r), Desc: fmt.Sprintf("Investigator '%v' %v has insufficient privileges to access API function", getInvName(r), getInvID(r)), }.Info() resource := cljs.New(fmt.Sprintf("%s%s", ctx.Server.Host, r.URL.String())) resource.SetError(cljs.Error{Code: fmt.Sprintf("%.0f", opid), Message: "Insufficient privileges"}) respond(http.StatusUnauthorized, resource, w, r) return } } // accept request pass(w, r) } }
// describeCreateInvestigator returns a resource that describes how to create an investigator func describeCreateInvestigator(respWriter http.ResponseWriter, request *http.Request) { var err error opid := getOpID(request) loc := fmt.Sprintf("%s%s", ctx.Server.Host, request.URL.String()) resource := cljs.New(loc) defer func() { if e := recover(); e != nil { emsg := fmt.Sprintf("%v", e) ctx.Channels.Log <- mig.Log{OpID: opid, Desc: emsg}.Err() resource.SetError(cljs.Error{Code: fmt.Sprintf("%.0f", opid), Message: emsg}) respond(500, resource, respWriter, request) } ctx.Channels.Log <- mig.Log{OpID: opid, Desc: "leaving describeCreateInvestigator()"}.Debug() }() err = resource.SetTemplate(cljs.Template{ Data: []cljs.Data{ {Name: "name", Value: "investigator's full name", Prompt: "Investigator Name"}, {Name: "publickey", Value: "armored GPG public key", Prompt: "Public Key"}, }, }) if err != nil { panic(err) } respond(200, resource, respWriter, request) }
// getHeartbeat returns a 200 func getHeartbeat(respWriter http.ResponseWriter, request *http.Request) { opid := mig.GenID() loc := fmt.Sprintf("%s%s", ctx.Server.Host, 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("%.0f", opid), Message: fmt.Sprintf("%v", e)}) respond(500, resource, respWriter, request) } ctx.Channels.Log <- mig.Log{OpID: opid, Desc: "leaving getHeartbeat()"}.Debug() }() err := resource.AddItem(cljs.Item{ Href: request.URL.String(), Data: []cljs.Data{ { Name: "heartbeat", Value: "gatorz say hi", }, }}) if err != nil { panic(err) } respond(200, resource, respWriter, request) }
// getCommand takes an actionid and a commandid and returns a command func getCommand(respWriter http.ResponseWriter, request *http.Request) { var err error opid := mig.GenID() resource := cljs.New(request.URL.String()) 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 getCommand()"}.Debug() }() var actionID, commandID int aid := request.URL.Query()["actionid"][0] if aid != "" { actionID, err = strconv.Atoi(aid) if err != nil { panic(err) } } cmdid := request.URL.Query()["commandid"][0] if cmdid != "" { commandID, err = strconv.Atoi(cmdid) if err != nil { panic(err) } } // retrieve the action cmds := []mig.Command{} var iter *mgo.Iter if commandID > 0 { if actionID > 0 { iter = ctx.DB.Col.Cmd.Find(bson.M{"id": commandID, "action.id": actionID}).Iter() } else { iter = ctx.DB.Col.Cmd.Find(bson.M{"id": commandID}).Iter() } } else { // nothing to search for, return empty resource respond(200, resource, respWriter, request, opid) } err = iter.All(&cmds) if err != nil { panic(err) } if len(cmds) == 0 { resource.SetError(cljs.Error{Code: fmt.Sprintf("%d", opid), Message: "No command found"}) respond(404, resource, respWriter, request, opid) } // store the results in the resource for _, cmd := range cmds { commandItem, err := commandToItem(cmd) if err != nil { panic(err) } resource.AddItem(commandItem) } respond(200, resource, respWriter, request, opid) }
// getAction queries the database and retrieves the detail of an action func getAction(respWriter http.ResponseWriter, request *http.Request) { var err error opid := getOpID(request) loc := fmt.Sprintf("%s%s", ctx.Server.Host, request.URL.String()) resource := cljs.New(loc) defer func() { if e := recover(); e != nil { emsg := fmt.Sprintf("%v", e) ctx.Channels.Log <- mig.Log{OpID: opid, Desc: emsg}.Err() resource.SetError(cljs.Error{Code: fmt.Sprintf("%.0f", opid), Message: emsg}) respond(http.StatusInternalServerError, resource, respWriter, request) } ctx.Channels.Log <- mig.Log{OpID: opid, Desc: "leaving getAction()"}.Debug() }() actionID, err := strconv.ParseFloat(request.URL.Query()["actionid"][0], 64) if err != nil { err = fmt.Errorf("Wrong parameters 'actionid': '%v'", err) panic(err) } // retrieve the action var a mig.Action if actionID > 0 { a, err = ctx.DB.ActionByID(actionID) if err != nil { if a.ID == -1 { // not found, return 404 resource.SetError(cljs.Error{ Code: fmt.Sprintf("%.0f", opid), Message: fmt.Sprintf("Action ID '%.0f' not found", actionID)}) respond(http.StatusNotFound, resource, respWriter, request) return } else { panic(err) } } } else { // bad request, return 400 resource.SetError(cljs.Error{ Code: fmt.Sprintf("%.0f", opid), Message: fmt.Sprintf("Invalid Action ID '%.0f'", actionID)}) respond(http.StatusBadRequest, resource, respWriter, request) return } // retrieve investigators a.Investigators, err = ctx.DB.InvestigatorByActionID(a.ID) if err != nil { panic(err) } // store the results in the resource actionItem, err := actionToItem(a, true, ctx) if err != nil { panic(err) } resource.AddItem(actionItem) respond(http.StatusOK, resource, respWriter, request) }
// createInvestigator creates an investigator into the database func createInvestigator(respWriter http.ResponseWriter, request *http.Request) { var err error opid := getOpID(request) loc := fmt.Sprintf("%s%s", ctx.Server.Host, request.URL.String()) resource := cljs.New(loc) defer func() { if e := recover(); e != nil { emsg := fmt.Sprintf("%v", e) ctx.Channels.Log <- mig.Log{OpID: opid, Desc: emsg}.Err() resource.SetError(cljs.Error{Code: fmt.Sprintf("%.0f", opid), Message: emsg}) respond(500, resource, respWriter, request) } ctx.Channels.Log <- mig.Log{OpID: opid, Desc: "leaving createInvestigator()"}.Debug() }() var inv mig.Investigator err = request.ParseMultipartForm(20480) if err != nil { panic(err) } inv.Name = request.FormValue("name") if inv.Name == "" { panic("Investigator name must not be empty") } // publickey is stored in a multipart post form, extract it _, keyHeader, err := request.FormFile("publickey") if err != nil { panic(err) } keyReader, err := keyHeader.Open() if err != nil { panic(err) } inv.PublicKey, err = ioutil.ReadAll(keyReader) if err != nil { panic(err) } if len(inv.PublicKey) == 0 { panic("Investigator Public Key must not be empty") } // validate the public key and obtain a fingerprint from it inv.PGPFingerprint, err = pgp.LoadArmoredPubKey(inv.PublicKey) if err != nil { panic(err) } // create the investigator in database inv.ID, err = ctx.DB.InsertInvestigator(inv) if err != nil { panic(err) } ctx.Channels.Log <- mig.Log{OpID: opid, Desc: "Investigator created in database"} err = resource.AddItem(cljs.Item{ Href: fmt.Sprintf("%s/investigator?investigatorid=%.0f", ctx.Server.BaseURL, inv.ID), Data: []cljs.Data{{Name: "Investigator ID " + fmt.Sprintf("%.0f", inv.ID), Value: inv}}, }) respond(201, resource, respWriter, request) }
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("%.0f", opid), Message: fmt.Sprintf("%v", e)}) respond(500, resource, respWriter, request, opid) } ctx.Channels.Log <- mig.Log{OpID: opid, Desc: "leaving getDashboard()"}.Debug() }() // get summary of agents active in the last 5 minutes sum, err := ctx.DB.SumAgentsByVersion(time.Now().Add(-5 * time.Minute)) if err != nil { panic(err) } count, err := ctx.DB.CountNewAgents(time.Now().Add(-24 * time.Hour)) if err != nil { panic(err) } double, err := ctx.DB.CountDoubleAgents(time.Now().Add(-5 * time.Minute)) if err != nil { panic(err) } disappeared, err := ctx.DB.CountDisappearedAgents( time.Now().Add(-7*24*time.Hour), time.Now().Add(-5*time.Minute)) if err != nil { panic(err) } sumItem, err := agentsSummaryToItem(sum, count, double, disappeared, ctx) resource.AddItem(sumItem) // 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, false, ctx) if err != nil { panic(err) } resource.AddItem(actionItem) } respond(200, resource, respWriter, request, opid) }
// getInvestigator takes an investigatorid and returns an investigator func getInvestigator(respWriter http.ResponseWriter, request *http.Request) { var err error opid := getOpID(request) loc := fmt.Sprintf("%s%s", ctx.Server.Host, request.URL.String()) resource := cljs.New(loc) defer func() { if e := recover(); e != nil { emsg := fmt.Sprintf("%v", e) ctx.Channels.Log <- mig.Log{OpID: opid, Desc: emsg}.Err() resource.SetError(cljs.Error{Code: fmt.Sprintf("%.0f", opid), Message: emsg}) respond(http.StatusInternalServerError, resource, respWriter, request) } ctx.Channels.Log <- mig.Log{OpID: opid, Desc: "leaving getInvestigator()"}.Debug() }() iid, err := strconv.ParseFloat(request.URL.Query()["investigatorid"][0], 64) if err != nil { err = fmt.Errorf("Wrong parameters 'investigatorid': '%v'", err) panic(err) } // retrieve the investigator var inv mig.Investigator if iid > 0 { inv, err = ctx.DB.InvestigatorByID(iid) if err != nil { if fmt.Sprintf("%v", err) == "Error while retrieving investigator: 'sql: no rows in result set'" { // not found, return 404 resource.SetError(cljs.Error{ Code: fmt.Sprintf("%.0f", opid), Message: fmt.Sprintf("Investigator ID '%.0f' not found", iid)}) respond(http.StatusNotFound, resource, respWriter, request) return } else { panic(err) } } } else { // bad request, return 400 resource.SetError(cljs.Error{ Code: fmt.Sprintf("%.0f", opid), Message: fmt.Sprintf("Invalid Investigator ID '%.0f'", iid)}) respond(http.StatusBadRequest, resource, respWriter, request) return } // store the results in the resource investigatorItem, err := investigatorToItem(inv) if err != nil { panic(err) } resource.AddItem(investigatorItem) respond(http.StatusOK, resource, respWriter, request) }
// getCommand takes an actionid and a commandid and returns a command func getCommand(respWriter http.ResponseWriter, request *http.Request) { var err error opid := getOpID(request) loc := fmt.Sprintf("%s%s", ctx.Server.Host, request.URL.String()) resource := cljs.New(loc) defer func() { if e := recover(); e != nil { emsg := fmt.Sprintf("%v", e) ctx.Channels.Log <- mig.Log{OpID: opid, Desc: emsg}.Err() resource.SetError(cljs.Error{Code: fmt.Sprintf("%.0f", opid), Message: emsg}) respond(500, resource, respWriter, request) } ctx.Channels.Log <- mig.Log{OpID: opid, Desc: "leaving getCommand()"}.Debug() }() commandID, err := strconv.ParseFloat(request.URL.Query()["commandid"][0], 64) if err != nil { err = fmt.Errorf("Wrong parameters 'commandid': '%v'", err) panic(err) } // retrieve the command var cmd mig.Command if commandID > 0 { cmd, err = ctx.DB.CommandByID(commandID) if err != nil { if fmt.Sprintf("%v", err) == "Error while retrieving command: 'sql: no rows in result set'" { // not found, return 404 resource.SetError(cljs.Error{ Code: fmt.Sprintf("%.0f", opid), Message: fmt.Sprintf("Command ID '%.0f' not found", commandID)}) respond(404, resource, respWriter, request) return } else { panic(err) } } } else { // bad request, return 400 resource.SetError(cljs.Error{ Code: fmt.Sprintf("%.0f", opid), Message: fmt.Sprintf("Invalid Command ID '%.0f'", commandID)}) respond(400, resource, respWriter, request) return } // store the results in the resource commandItem, err := commandToItem(cmd) if err != nil { panic(err) } resource.AddItem(commandItem) respond(200, resource, respWriter, request) }
func getDashboard(respWriter http.ResponseWriter, request *http.Request) { var ( err error agentsStats mig.AgentsStats ) opid := getOpID(request) loc := fmt.Sprintf("%s%s", ctx.Server.Host, 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("%.0f", opid), Message: fmt.Sprintf("%v", e)}) respond(500, resource, respWriter, request) } ctx.Channels.Log <- mig.Log{OpID: opid, Desc: "leaving getDashboard()"}.Debug() }() stats, err := ctx.DB.GetAgentsStats(1) if err != nil { panic(err) } if len(stats) > 1 { panic(fmt.Sprintf("expected 1 set of agents stats, got %d", len(stats))) } if len(stats) == 1 { agentsStats = stats[0] sumItem, err := agentsSummaryToItem(agentsStats, ctx) if err != nil { panic(err) } resource.AddItem(sumItem) } // add the last 10 actions actions, err := ctx.DB.LastActions(10) 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, false, ctx) if err != nil { panic(err) } resource.AddItem(actionItem) } respond(200, resource, respWriter, request) }
func searchAgents(respWriter http.ResponseWriter, request *http.Request) { opid := mig.GenID() resource := cljs.New(request.URL.String()) 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 searchAgents()"}.Debug() }() respond(501, resource, respWriter, request, opid) }
func getAgent(respWriter http.ResponseWriter, request *http.Request) { var err error opid := getOpID(request) loc := fmt.Sprintf("%s%s", ctx.Server.Host, 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("%.0f", opid), Message: fmt.Sprintf("%v", e)}) respond(500, resource, respWriter, request) } ctx.Channels.Log <- mig.Log{OpID: opid, Desc: "leaving getAgentsDashboard()"}.Debug() }() agentID, err := strconv.ParseFloat(request.URL.Query()["agentid"][0], 64) if err != nil { err = fmt.Errorf("Wrong parameters 'agentid': '%v'", err) panic(err) } // retrieve the command var agt mig.Agent if agentID > 0 { agt, err = ctx.DB.AgentByID(agentID) if err != nil { if fmt.Sprintf("%v", err) == "Error while retrieving agent: 'sql: no rows in result set'" { // not found, return 404 resource.SetError(cljs.Error{ Code: fmt.Sprintf("%.0f", opid), Message: fmt.Sprintf("Agent ID '%.0f' not found", agentID)}) respond(404, resource, respWriter, request) return } else { panic(err) } } } else { // bad request, return 400 resource.SetError(cljs.Error{ Code: fmt.Sprintf("%.0f", opid), Message: fmt.Sprintf("Invalid Agent ID '%.0f'", agentID)}) respond(400, resource, respWriter, request) return } // store the results in the resource agentItem, err := agentToItem(agt) if err != nil { panic(err) } resource.AddItem(agentItem) respond(200, resource, respWriter, request) }
// authenticateLoader is used to authenticate requests that are made to the // loader API endpoints. Rather than operate on GPG signatures, the // authentication instead uses the submitted loader key func authenticateLoader(pass handler) handler { return func(w http.ResponseWriter, r *http.Request) { var ( err error ldr mig.LoaderEntry ) opid := getOpID(r) context.Set(r, opID, opid) context.Set(r, apiRequestCategory, RequestCategoryLoader) lkey := r.Header.Get("X-LOADERKEY") if lkey == "" { resource := cljs.New(fmt.Sprintf("%s%s", ctx.Server.Host, r.URL.String())) resource.SetError(cljs.Error{Code: fmt.Sprintf("%.0f", opid), Message: "X-LOADERKEY header not found"}) respond(http.StatusUnauthorized, resource, w, r) return } err = mig.ValidateLoaderPrefixAndKey(lkey) if err != nil { goto authfailed } ldr, err = hashAuthenticateLoader(lkey) if err != nil { goto authfailed } context.Set(r, loaderID, ldr.ID) context.Set(r, loaderName, ldr.Name) // accept request pass(w, r) return authfailed: context.Set(r, loaderName, "authfailed") resource := cljs.New(fmt.Sprintf("%s%s", ctx.Server.Host, r.URL.String())) resource.SetError(cljs.Error{Code: fmt.Sprintf("%.0f", opid), Message: fmt.Sprintf("Loader authorization failed")}) respond(http.StatusUnauthorized, resource, w, r) } }
// authenticate is called prior to processing incoming requests. it implements the client // authentication logic, which mostly consist of validating GPG signed tokens and setting the // identity of the signer in the request context func authenticate(pass handler) handler { return func(w http.ResponseWriter, r *http.Request) { var ( err error inv mig.Investigator ) opid := getOpID(r) context.Set(r, opID, opid) if !ctx.Authentication.Enabled { inv.Name = "authdisabled" inv.ID = 0 goto authorized } if r.Header.Get("X-PGPAUTHORIZATION") == "" { inv.Name = "authmissing" inv.ID = -1 resource := cljs.New(fmt.Sprintf("%s%s", ctx.Server.Host, r.URL.String())) resource.SetError(cljs.Error{Code: fmt.Sprintf("%.0f", opid), Message: "X-PGPAUTHORIZATION header not found"}) respond(401, resource, w, r) return } inv, err = verifySignedToken(r.Header.Get("X-PGPAUTHORIZATION")) if err != nil { inv.Name = "authfailed" inv.ID = -1 resource := cljs.New(fmt.Sprintf("%s%s", ctx.Server.Host, r.URL.String())) resource.SetError(cljs.Error{Code: fmt.Sprintf("%.0f", opid), Message: fmt.Sprintf("Authorization verification failed with error '%v'", err)}) respond(401, resource, w, r) return } authorized: // store investigator identity in request context context.Set(r, authenticatedInvName, inv.Name) context.Set(r, authenticatedInvID, inv.ID) // accept request pass(w, r) } }
// cancelCommand receives an action ID and a command ID and issues a cancellation order func cancelCommand(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("%.0f", opid), Message: fmt.Sprintf("%v", e)}) respond(500, resource, respWriter, request, opid) } ctx.Channels.Log <- mig.Log{OpID: opid, Desc: "leaving cancelCommand()"}.Debug() }() respond(501, resource, respWriter, request, opid) }
// Change key set on a loader entry func keyLoader(respWriter http.ResponseWriter, request *http.Request) { loc := fmt.Sprintf("%s%s", ctx.Server.Host, request.URL.String()) opid := getOpID(request) 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("%.0f", opid), Message: fmt.Sprintf("%v", e)}) respond(http.StatusInternalServerError, resource, respWriter, request) } ctx.Channels.Log <- mig.Log{OpID: opid, Desc: "leaving keyLoader()"}.Debug() }() err := request.ParseForm() if err != nil { panic(err) } ctx.Channels.Log <- mig.Log{OpID: opid, Desc: fmt.Sprintf("Received loader key change request")}.Debug() loaderid, err := strconv.ParseFloat(request.FormValue("loaderid"), 64) if err != nil { panic(err) } lkey := request.FormValue("loaderkey") if lkey == "" { // bad request, return 400 resource.SetError(cljs.Error{ Code: fmt.Sprintf("%.0f", opid), Message: "Invalid key specified"}) respond(http.StatusBadRequest, resource, respWriter, request) return } err = mig.ValidateLoaderKey(lkey) if err != nil { panic(err) } hashkey, salt, err := hashLoaderKey(lkey, nil) if err != nil { panic(err) } err = ctx.DB.LoaderUpdateKey(loaderid, hashkey, salt) if err != nil { panic(err) } respond(http.StatusOK, resource, respWriter, request) }
// Manipulate the status of an existing manifest func statusManifest(respWriter http.ResponseWriter, request *http.Request) { loc := fmt.Sprintf("%s%s", ctx.Server.Host, request.URL.String()) opid := getOpID(request) 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("%.0f", opid), Message: fmt.Sprintf("%v", e)}) respond(http.StatusInternalServerError, resource, respWriter, request) } ctx.Channels.Log <- mig.Log{OpID: opid, Desc: "leaving statusManifest()"}.Debug() }() err := request.ParseForm() if err != nil { panic(err) } ctx.Channels.Log <- mig.Log{OpID: opid, Desc: fmt.Sprintf("Received manifest status change request")}.Debug() manifestid, err := strconv.ParseFloat(request.FormValue("manifestid"), 64) if err != nil { panic(err) } sts := request.FormValue("status") // A manifest can only be marked as staged, or disabled. Once a // manifest has been disabled, it's status can no longer be changed. if sts == "staged" { err = ctx.DB.ManifestClearSignatures(manifestid) if err != nil { panic(err) } err = ctx.DB.ManifestUpdateStatus(manifestid, ctx.Manifest.RequiredSignatures) if err != nil { panic(err) } } else if sts == "disabled" { err = ctx.DB.ManifestDisable(manifestid) if err != nil { panic(err) } } else { panic("Invalid status specified, must be disabled or staged") } respond(http.StatusOK, resource, respWriter, request) }
// Return information describing an existing loader entry func getLoader(respWriter http.ResponseWriter, request *http.Request) { loc := fmt.Sprintf("%s%s", ctx.Server.Host, request.URL.String()) opid := getOpID(request) 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("%.0f", opid), Message: fmt.Sprintf("%v", e)}) respond(http.StatusInternalServerError, resource, respWriter, request) } ctx.Channels.Log <- mig.Log{OpID: opid, Desc: "leaving getLoader()"}.Debug() }() lid, err := strconv.ParseFloat(request.URL.Query()["loaderid"][0], 64) if err != nil { err = fmt.Errorf("Wrong parameters 'loaderid': '%v'", err) panic(err) } var le mig.LoaderEntry if lid > 0 { le, err = ctx.DB.GetLoaderFromID(lid) if err != nil { if fmt.Sprintf("%v", err) == "Error while retrieving loader: 'sql: no rows in result set'" { resource.SetError(cljs.Error{ Code: fmt.Sprintf("%.0f", opid), Message: fmt.Sprintf("Loader ID '%.0f' not found", lid)}) respond(http.StatusNotFound, resource, respWriter, request) return } else { panic(err) } } } else { // bad request, return 400 resource.SetError(cljs.Error{ Code: fmt.Sprintf("%.0f", opid), Message: fmt.Sprintf("Invalid Loader ID '%.0f'", lid)}) respond(http.StatusBadRequest, resource, respWriter, request) return } li, err := loaderEntryToItem(le, ctx) if err != nil { panic(err) } resource.AddItem(li) respond(http.StatusOK, resource, respWriter, request) }
// Add a new loader entry func newLoader(respWriter http.ResponseWriter, request *http.Request) { loc := fmt.Sprintf("%s%s", ctx.Server.Host, request.URL.String()) opid := getOpID(request) 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("%.0f", opid), Message: fmt.Sprintf("%v", e)}) respond(http.StatusInternalServerError, resource, respWriter, request) } ctx.Channels.Log <- mig.Log{OpID: opid, Desc: "leaving newLoader()"}.Debug() }() err := request.ParseForm() if err != nil { panic(err) } ctx.Channels.Log <- mig.Log{OpID: opid, Desc: fmt.Sprintf("Received new loader request")}.Debug() lestr := request.FormValue("loader") if lestr == "" { panic("no loader entry specified in form") } var le mig.LoaderEntry err = json.Unmarshal([]byte(lestr), &le) if err != nil { panic(err) } err = le.Validate() if err != nil { panic(err) } // Hash the loader key to provide it to LoaderAdd hkey, salt, err := hashLoaderKey(le.Key, nil) if err != nil { panic(err) } err = ctx.DB.LoaderAdd(le, hkey, salt) if err != nil { panic(err) } respond(http.StatusCreated, resource, respWriter, request) }
// updateInvestigator updates the status of an investigator in database func updateInvestigator(respWriter http.ResponseWriter, request *http.Request) { var err error opid := getOpID(request) loc := fmt.Sprintf("%s%s", ctx.Server.Host, request.URL.String()) resource := cljs.New(loc) defer func() { if e := recover(); e != nil { emsg := fmt.Sprintf("%v", e) ctx.Channels.Log <- mig.Log{OpID: opid, Desc: emsg}.Err() resource.SetError(cljs.Error{Code: fmt.Sprintf("%.0f", opid), Message: emsg}) respond(500, resource, respWriter, request) } ctx.Channels.Log <- mig.Log{OpID: opid, Desc: "leaving updateInvestigator()"}.Debug() }() var inv mig.Investigator err = request.ParseForm() if err != nil { panic(err) } iid := request.FormValue("id") if iid == "" { panic("Investigator ID must not be empty") } inv.ID, err = strconv.ParseFloat(iid, 64) if err != nil { panic(err) } inv.Status = request.FormValue("status") if inv.Status == "" { panic("Investigator status must not be empty") } // create the investigator in database err = ctx.DB.UpdateInvestigatorStatus(inv) if err != nil { panic(err) } ctx.Channels.Log <- mig.Log{OpID: opid, Desc: fmt.Sprintf("Investigator %.0f status changed to %s", inv.ID, inv.Status)} err = resource.AddItem(cljs.Item{ Href: fmt.Sprintf("%s/investigator?investigatorid=%.0f", ctx.Server.BaseURL, inv.ID), Data: []cljs.Data{{Name: "Investigator ID " + fmt.Sprintf("%.0f", inv.ID), Value: inv}}, }) respond(200, resource, respWriter, request) }
func getAPIResource(t string, ctx Context) (resource *cljs.Resource, err error) { resp, err := ctx.HTTP.Client.Get(t) if err != nil { return } body, err := ioutil.ReadAll(resp.Body) if err != nil { return } defer resp.Body.Close() resource = cljs.New("") err = json.Unmarshal(body, &resource) if err != nil { return } if resp.StatusCode != 200 { err = fmt.Errorf("HTTP %d: %v (code %s)", resp.StatusCode, resource.Collection.Error.Message, resource.Collection.Error.Code) return } return }
// Add a new manifest record func newManifest(respWriter http.ResponseWriter, request *http.Request) { loc := fmt.Sprintf("%s%s", ctx.Server.Host, request.URL.String()) opid := getOpID(request) 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("%.0f", opid), Message: fmt.Sprintf("%v", e)}) respond(http.StatusInternalServerError, resource, respWriter, request) } ctx.Channels.Log <- mig.Log{OpID: opid, Desc: "leaving newManifest()"}.Debug() }() err := request.ParseForm() if err != nil { panic(err) } ctx.Channels.Log <- mig.Log{OpID: opid, Desc: fmt.Sprintf("Received new manifest request")}.Debug() mrstr := request.FormValue("manifest") if mrstr == "" { panic("no manifest record specified in form") } var mr mig.ManifestRecord err = json.Unmarshal([]byte(mrstr), &mr) if err != nil { panic(err) } err = mr.Validate() if err != nil { panic(err) } err = ctx.DB.ManifestAdd(mr) if err != nil { panic(err) } respond(http.StatusCreated, resource, respWriter, 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.Path) 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 describeCreateAction()"}.Debug() }() err := resource.SetTemplate(cljs.Template{ Data: []cljs.Data{ {Name: "action", Value: "", Prompt: "Signed MIG Action"}, }, }) if err != nil { panic(err) } respond(200, resource, respWriter, request, opid) }
// 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()) 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 describeCancelCommand()"}.Debug() }() err := resource.SetTemplate(cljs.Template{ Data: []cljs.Data{ {Name: "actionid", Value: "[0-9]{1,20}", Prompt: "Action ID"}, {Name: "commandid", Value: "[0-9]{1,20}", Prompt: "Command ID"}, }, }) if err != nil { panic(err) } respond(200, resource, respWriter, request, opid) }
// getAction queries the database and retrieves the detail of an action func getAction(respWriter http.ResponseWriter, request *http.Request) { var err error opid := mig.GenID() resource := cljs.New(request.URL.String()) 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 getAction()"}.Debug() }() actionID, err := strconv.Atoi(request.URL.Query()["actionid"][0]) if err != nil { panic(err) } // retrieve the action eas := []mig.ExtendedAction{} iter := ctx.DB.Col.Action.Find(bson.M{"action.id": actionID}).Iter() err = iter.All(&eas) if err != nil { panic(err) } if len(eas) == 0 { resource.SetError(cljs.Error{Code: fmt.Sprintf("%d", opid), Message: "Action not found"}) respond(404, resource, respWriter, request, opid) } // store the results in the resource for _, ea := range eas { actionItem, err := extendedActionToItem(ea) if err != nil { panic(err) } resource.AddItem(actionItem) } respond(200, resource, respWriter, request, opid) }
// 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()) 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 describeCreateAction()"}.Debug() }() search := request.URL.Query()["search"][0] switch search { case "positiveresults": actionID, err := strconv.Atoi(request.URL.Query()["actionid"][0]) if err != nil { panic(err) } cmds, err := findPositiveResults(actionID) if err != nil { panic(err) } // store the results in the resource for _, cmd := range cmds { commandItem, err := commandToItem(cmd) if err != nil { panic(err) } resource.AddItem(commandItem) } respond(200, resource, respWriter, request, opid) default: panic("unknown search method") } }
// Request to sign an existing manifest func signManifest(respWriter http.ResponseWriter, request *http.Request) { loc := fmt.Sprintf("%s%s", ctx.Server.Host, request.URL.String()) opid := getOpID(request) 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("%.0f", opid), Message: fmt.Sprintf("%v", e)}) respond(http.StatusInternalServerError, resource, respWriter, request) } ctx.Channels.Log <- mig.Log{OpID: opid, Desc: "leaving signManifest()"}.Debug() }() err := request.ParseForm() if err != nil { panic(err) } ctx.Channels.Log <- mig.Log{OpID: opid, Desc: fmt.Sprintf("Received manifest sign request")}.Debug() manifestid, err := strconv.ParseFloat(request.FormValue("manifestid"), 64) if err != nil { panic(err) } sig := request.FormValue("signature") if sig == "" { panic("Invalid signature specified") } err = ctx.DB.ManifestAddSignature(manifestid, sig, getInvID(request), ctx.Manifest.RequiredSignatures) if err != nil { panic(err) } respond(http.StatusOK, resource, respWriter, request) }
// Enable or disable a loader entry func statusLoader(respWriter http.ResponseWriter, request *http.Request) { loc := fmt.Sprintf("%s%s", ctx.Server.Host, request.URL.String()) opid := getOpID(request) 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("%.0f", opid), Message: fmt.Sprintf("%v", e)}) respond(http.StatusInternalServerError, resource, respWriter, request) } ctx.Channels.Log <- mig.Log{OpID: opid, Desc: "leaving statusLoader()"}.Debug() }() err := request.ParseForm() if err != nil { panic(err) } ctx.Channels.Log <- mig.Log{OpID: opid, Desc: fmt.Sprintf("Received loader status change request")}.Debug() loaderid, err := strconv.ParseFloat(request.FormValue("loaderid"), 64) if err != nil { panic(err) } sts := request.FormValue("status") var setval bool if sts == "enabled" { setval = true } err = ctx.DB.LoaderUpdateStatus(loaderid, setval) if err != nil { panic(err) } respond(http.StatusOK, resource, respWriter, request) }
// describeCreateAction returns a resource that describes how to POST new actions func describeCreateAction(respWriter http.ResponseWriter, request *http.Request) { var err error opid := getOpID(request) loc := fmt.Sprintf("%s%s", ctx.Server.Host, 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("%.0f", opid), Message: fmt.Sprintf("%v", e)}) respond(500, resource, respWriter, request) } ctx.Channels.Log <- mig.Log{OpID: opid, Desc: "leaving describeCreateAction()"}.Debug() }() err = resource.SetTemplate(cljs.Template{ Data: []cljs.Data{ {Name: "action", Value: "URL encoded signed MIG action", Prompt: "Signed MIG Action"}, }, }) if err != nil { panic(err) } respond(200, resource, respWriter, request) }