func (api *Api) Logout(w rest.ResponseWriter, r *rest.Request) { // Check credentials login := r.PathParam("login") guid := r.PathParam("GUID") session := Session{} var errl error = nil if session, errl = api.CheckLogin(login, guid); errl != nil { rest.Error(w, errl.Error(), 400) return } mutex.Lock() if err := api.db.Delete(&session).Error; err != nil { rest.Error(w, err.Error(), 400) mutex.Unlock() return } mutex.Unlock() logit("User '" + login + "' logged out") api.LogActivity(session.Id, "User '"+login+"' logged out") w.WriteJson("Success") }
func (api *Api) DeleteEnv(w rest.ResponseWriter, r *rest.Request) { // Check credentials login := r.PathParam("login") guid := r.PathParam("GUID") // Only admin is allowed if login != "admin" { rest.Error(w, "Not allowed", 400) return } session := Session{} var errl error if session, errl = api.CheckLogin(login, guid); errl != nil { rest.Error(w, errl.Error(), 401) return } defer api.TouchSession(guid) // Delete id := 0 if id, errl = strconv.Atoi(r.PathParam("id")); errl != nil { rest.Error(w, "Invalid id.", 400) return } env := Env{} mutex.Lock() if api.db.First(&env, id).RecordNotFound() { mutex.Unlock() rest.Error(w, "Record not found.", 400) return } mutex.Unlock() mutex.Lock() if err := api.db.Delete(&env).Error; err != nil { mutex.Unlock() rest.Error(w, err.Error(), 400) return } mutex.Unlock() dc := Dc{} mutex.Lock() api.db.First(&dc, env.DcId) mutex.Unlock() text := fmt.Sprintf("Deleted environment '%s->%s'.", dc.SysName, env.SysName) api.LogActivity(session.Id, text) w.WriteJson("Success") }
func (api *Api) GetAllEnvCaps(w rest.ResponseWriter, r *rest.Request) { // Check credentials login := r.PathParam("login") guid := r.PathParam("GUID") // Only admin is allowed if login != "admin" { rest.Error(w, "Not allowed", 400) return } //session := Session{} var errl error = nil //if session,errl = api.CheckLogin( login, guid ); errl != nil { if _, errl = api.CheckLogin(login, guid); errl != nil { rest.Error(w, errl.Error(), 401) return } defer api.TouchSession(guid) qs := r.URL.Query() // Query string - map[string][]string envcaps := []EnvCap{} if len(qs["code"]) > 0 { srch := qs["code"][0] mutex.Lock() api.db.Order("code").Find(&envcaps, "code = ?", srch) mutex.Unlock() } else { mutex.Lock() err := api.db.Order("Code").Find(&envcaps) mutex.Unlock() if err.Error != nil { if !err.RecordNotFound() { rest.Error(w, err.Error.Error(), 500) return } } } // Create a slice of maps from users struct // to selectively copy database fields for display u := make([]map[string]interface{}, len(envcaps)) for i := range envcaps { u[i] = make(map[string]interface{}) u[i]["Id"] = envcaps[i].Id u[i]["Code"] = envcaps[i].Code u[i]["Desc"] = envcaps[i].Desc } // Too much noise //api.LogActivity( session.Id, "Sent list of users" ) w.WriteJson(&u) }
// DeleteUser processes "DELETE /users" queries. // func (api *Api) DeleteUser(w rest.ResponseWriter, r *rest.Request) { // Check credentials login := r.PathParam("login") guid := r.PathParam("GUID") // Only admin is allowed if login != "admin" { rest.Error(w, "Not allowed", 400) return } session := Session{} var errl error if session, errl = api.CheckLogin(login, guid); errl != nil { rest.Error(w, errl.Error(), 401) return } defer api.TouchSession(guid) // Delete id := 0 if id, errl = strconv.Atoi(r.PathParam("id")); errl != nil { rest.Error(w, "Invalid id.", 400) return } user := User{} mutex.Lock() if api.db.First(&user, id).RecordNotFound() { mutex.Unlock() rest.Error(w, "Record not found.", 400) return } mutex.Unlock() mutex.Lock() if err := api.db.Delete(&user).Error; err != nil { mutex.Unlock() rest.Error(w, err.Error(), 400) return } mutex.Unlock() api.LogActivity(session.Id, "Deleted user '"+user.Login+"'.") w.WriteJson("Success") }
func (api *Api) AddJob(w rest.ResponseWriter, r *rest.Request) { // Decode json post data into JobIn struct logit(fmt.Sprintf("Connection from %s", r.RemoteAddr)) job := JobIn{} if err := r.DecodeJsonPayload(&job); err != nil { rest.Error(w, "Invalid data format received.", 400) return } else if job.JobID == 0 { rest.Error(w, "Incorrect data format received.", 400) return } // Check the password matches if job.Key != config.WorkerKey { rest.Error(w, "Invalid key", 400) return } // Add the job to the job list api.AppendJob(job) if api.Guid() == "" { api.loginmutex.Lock() if err := api.Login(); err != nil { // Can't send this error to the Manager so must return it here logit(fmt.Sprintf("Error: %s", err.Error())) rest.Error(w, err.Error(), 400) api.loginmutex.Unlock() return } api.loginmutex.Unlock() } if err := api.sendStatus(job, JobOut{ Status: STATUS_NOTSTARTED, StatusReason: "About to start job", StatusPercent: 0, Errors: 0, }); err != nil { logit(fmt.Sprintf("Error: %s", err.Error())) } w.WriteJson(job) //a := fmt.Sprintf("%#v",job) //logit(a) go api.execCmd(job) }
func (api *Api) DeleteJob(w rest.ResponseWriter, r *rest.Request) { // Check credentials login := r.PathParam("login") guid := r.PathParam("GUID") // Only admin is allowed if login == "admin" { rest.Error(w, "Not allowed", 400) return } session := Session{} var errl error if session, errl = api.CheckLogin(login, guid); errl != nil { rest.Error(w, errl.Error(), 401) return } defer api.TouchSession(guid) // Delete id := 0 if id, errl = strconv.Atoi(r.PathParam("id")); errl != nil { rest.Error(w, "Invalid id.", 400) return } job := Job{} mutex.Lock() if api.db.First(&job, id).RecordNotFound() { mutex.Unlock() rest.Error(w, "Record not found.", 400) return } mutex.Unlock() mutex.Lock() if err := api.db.Delete(&job).Error; err != nil { mutex.Unlock() rest.Error(w, err.Error(), 400) return } mutex.Unlock() api.LogActivity(session.Id, fmt.Sprintf("Deleted job %d.", job.Id)) w.WriteJson(&job) }
func (api *Api) AddOutputLine(w rest.ResponseWriter, r *rest.Request) { login := r.PathParam("login") guid := r.PathParam("GUID") // Admin is not allowed if login == "admin" { rest.Error(w, "Not allowed", 400) return } // Check credentials //session := Session{} var errl error if _, errl = api.CheckLoginNoExpiry(login, guid); errl != nil { rest.Error(w, errl.Error(), 401) return } //defer api.TouchSession( guid ) outputLineData := OutputLine{} if err := r.DecodeJsonPayload(&outputLineData); err != nil { rest.Error(w, "Invalid data format received.", 400) return } else if outputLineData.JobId == 0 { rest.Error(w, "Incorrect data format received.", 400) return } // Add OutputLine mutex.Lock() if err := api.db.Save(&outputLineData).Error; err != nil { mutex.Unlock() rest.Error(w, err.Error(), 400) return } mutex.Unlock() //text := "" //fmt.Sprintf( text,"%d",outputLineData.JobId ) //api.LogActivity( session.Id, "Started outputLine logging for job '"+ // text+"'." ) w.WriteJson("Success") }
func (api *Api) DeleteOutputLine(w rest.ResponseWriter, r *rest.Request) { // Check credentials login := r.PathParam("login") guid := r.PathParam("GUID") // Admin is not allowed if login == "admin" { rest.Error(w, "Not allowed", 400) return } session := Session{} var errl error if session, errl = api.CheckLogin(login, guid); errl != nil { rest.Error(w, errl.Error(), 401) return } defer api.TouchSession(guid) // Delete id := 0 if id, errl = strconv.Atoi(r.PathParam("id")); errl != nil { rest.Error(w, "Invalid id.", 400) return } outputline := OutputLine{} mutex.Lock() if err := api.db.Where("job_id = ?", id).Delete(&outputline).Error; err != nil { mutex.Unlock() rest.Error(w, err.Error(), 400) return } mutex.Unlock() api.LogActivity(session.Id, fmt.Sprintf("Deleted outputlines for job %d.", id)) w.WriteJson("Success") }
func (api *Api) AddPerm(w rest.ResponseWriter, r *rest.Request) { // Check credentials login := r.PathParam("login") guid := r.PathParam("GUID") // Only admin is allowed if login != "admin" { rest.Error(w, "Not allowed", 400) return } session := Session{} var errl error if session, errl = api.CheckLogin(login, guid); errl != nil { rest.Error(w, errl.Error(), 401) return } defer api.TouchSession(guid) // Can't add if it exists already permData := Perm{} if err := r.DecodeJsonPayload(&permData); err != nil { rest.Error(w, "Invalid data format received.", 400) return } else if permData.UserId == 0 { rest.Error(w, "Incorrect data format received.", 400) return } perm := Perm{} mutex.Lock() if !api.db.Find(&perm, "env_id = ? and user_id = ?", permData.EnvId, permData.UserId).RecordNotFound() { mutex.Unlock() rest.Error(w, "Record exists.", 400) return } mutex.Unlock() // Add perm mutex.Lock() if err := api.db.Save(&permData).Error; err != nil { mutex.Unlock() rest.Error(w, err.Error(), 400) return } mutex.Unlock() text := fmt.Sprintf("Added new environment permission. PermID = '%d'.", permData.Id) api.LogActivity(session.Id, text) w.WriteJson(permData) }
func (api *Api) DeleteJob(w rest.ResponseWriter, r *rest.Request) { // Decode json post data into JobIn struct logit(fmt.Sprintf("Connection from %s", r.RemoteAddr)) job := JobIn{} if err := r.DecodeJsonPayload(&job); err != nil { rest.Error(w, "Invalid data format received.", 400) return } else if job.JobID == 0 { rest.Error(w, "Incorrect data format received.", 400) return } // Check the password matches if job.Key != config.WorkerKey { rest.Error(w, "Invalid key", 400) return } oldjob, err := api.FindJob(job.JobID) if err != nil { rest.Error(w, "Job not found", 400) return } // So status can be updated correctly api.SetUserCancel(oldjob.JobID) // Kill the whole process group (-pid) syscall.Kill(int(oldjob.Pid)*-1, syscall.SIGKILL) // RemoveJob is done if the Wait fails in execCmd (exec.go) // And wait will fail 'cos we just killed it. // //api.RemoveJob( oldjob.JobID ) w.WriteJson(job) }
func (api *Api) AddEnvCap(w rest.ResponseWriter, r *rest.Request) { // Check credentials login := r.PathParam("login") guid := r.PathParam("GUID") // Only admin is allowed if login != "admin" { rest.Error(w, "Not allowed", 400) return } session := Session{} var errl error if session, errl = api.CheckLogin(login, guid); errl != nil { rest.Error(w, errl.Error(), 401) return } defer api.TouchSession(guid) // Can't add if it exists already EnvCapData := EnvCap{} if err := r.DecodeJsonPayload(&EnvCapData); err != nil { rest.Error(w, "Invalid data format received.", 400) return } else if len(EnvCapData.Code) == 0 || len(EnvCapData.Desc) == 0 { rest.Error(w, "A required field is empty.", 400) return } EnvCap := EnvCap{} mutex.Lock() if !api.db.Find(&EnvCap, "code = ?", EnvCapData.Code).RecordNotFound() { mutex.Unlock() rest.Error(w, "Record exists.", 400) return } mutex.Unlock() // Add EnvCap mutex.Lock() if err := api.db.Save(&EnvCapData).Error; err != nil { mutex.Unlock() rest.Error(w, err.Error(), 400) return } mutex.Unlock() text := fmt.Sprintf("Added new EnvCap, '%s'.", EnvCapData.Code) api.LogActivity(session.Id, text) w.WriteJson(EnvCapData) }
func (api *Api) AddRepo(w rest.ResponseWriter, r *rest.Request) { // Check credentials login := r.PathParam("login") guid := r.PathParam("GUID") // Only admin is allowed if login != "admin" { rest.Error(w, "Not allowed", 400) return } session := Session{} var errl error if session, errl = api.CheckLogin(login, guid); errl != nil { rest.Error(w, errl.Error(), 401) return } defer api.TouchSession(guid) // Can't add if it exists already repoData := Repo{} if err := r.DecodeJsonPayload(&repoData); err != nil { rest.Error(w, "Invalid data format received.", 400) return } else if len(repoData.Url) == 0 { rest.Error(w, "Incorrect data format received.", 400) return } repo := Repo{} mutex.Lock() if !api.db.Find(&repo, "Url = ?", repoData.Url). RecordNotFound() { mutex.Unlock() rest.Error(w, "Record exists.", 400) return } mutex.Unlock() // Add repo mutex.Lock() if err := api.db.Save(&repoData).Error; err != nil { mutex.Unlock() rest.Error(w, err.Error(), 400) return } mutex.Unlock() api.LogActivity(session.Id, "Added new repo '"+repoData.Url+"'.") w.WriteJson(repoData) }
func (api *Api) GetAllEnvs(w rest.ResponseWriter, r *rest.Request) { // Check credentials login := r.PathParam("login") guid := r.PathParam("GUID") // Anyone can view envs //session := Session{} var errl error = nil //if session,errl = api.CheckLogin( login, guid ); errl != nil { if _, errl = api.CheckLogin(login, guid); errl != nil { rest.Error(w, errl.Error(), 401) return } defer api.TouchSession(guid) envs := []Env{} qs := r.URL.Query() // Query string - map[string][]string if login == "admin" { if len(qs["sys_name"]) > 0 { srch := qs["sys_name"][0] if len(qs["dc_id"]) > 0 { dcid := qs["dc_id"][0] mutex.Lock() api.db.Order("sys_name").Find(&envs, "sys_name = ? and dc_id = ?", srch, dcid) mutex.Unlock() } else { mutex.Lock() api.db.Order("sys_name").Find(&envs, "sys_name = ?", srch) mutex.Unlock() } /* if api.db.Order("sys_name"). Find(&envs, "sys_name = ?", srch).RecordNotFound() { rest.Error(w, "No results.", 400) return } */ } else { mutex.Lock() err := api.db.Order("dc_id,sys_name").Find(&envs) mutex.Unlock() if err.Error != nil { if !err.RecordNotFound() { rest.Error(w, err.Error.Error(), 500) return } } } } else { //Not admin // Only return readable/writeable envs for the current user additional_where := "" if len(qs["env_id"]) > 0 { additional_where = "AND envs.id = " + qs["env_id"][0] } if len(qs["writeable"]) > 0 { // only writeable envs mutex.Lock() api.db.Where("envs.id in (SELECT perms.env_id from perms "+ "LEFT JOIN users on users.id=perms.user_id "+ "WHERE users.login=? and perms.writeable=1) "+ additional_where, login).Find(&envs) mutex.Unlock() } else { // readable or writeable envs mutex.Lock() api.db.Where("envs.id in (SELECT perms.env_id from perms "+ "LEFT JOIN users on users.id=perms.user_id "+ "WHERE users.login=? and perms.enabled=1) "+ additional_where, login).Find(&envs) mutex.Unlock() } } // Create a slice of maps from users struct // to selectively copy database fields for display u := make([]map[string]interface{}, len(envs)) for i := range envs { u[i] = make(map[string]interface{}) u[i]["Id"] = envs[i].Id u[i]["DispName"] = envs[i].DispName u[i]["SysName"] = envs[i].SysName //u[i]["WorkerIp"] = envs[i].WorkerIp //u[i]["WorkerPort"] = envs[i].WorkerPort u[i]["WorkerUrl"] = envs[i].WorkerUrl if login == "admin" { u[i]["WorkerKey"] = envs[i].WorkerKey } u[i]["CreatedAt"] = envs[i].CreatedAt dc := Dc{} mutex.Lock() api.db.Model(&envs[i]).Related(&dc) mutex.Unlock() u[i]["DcSysName"] = dc.SysName u[i]["DcDispName"] = dc.DispName u[i]["DcId"] = dc.Id } // Too much noise //api.LogActivity( session.Id, "Sent list of users" ) w.WriteJson(&u) }
// GetAllUsers processes "GET /users" queries. // func (api *Api) GetAllUsers(w rest.ResponseWriter, r *rest.Request) { // Check credentials login := r.PathParam("login") guid := r.PathParam("GUID") // Only admin is allowed if login != "admin" { rest.Error(w, "Not allowed", 400) return } //session := Session{} var errl error = nil //if session,errl = api.CheckLogin( login, guid ); errl != nil { if _, errl = api.CheckLogin(login, guid); errl != nil { rest.Error(w, errl.Error(), 401) return } defer api.TouchSession(guid) users := []User{} qs := r.URL.Query() // map[string][]string if len(qs["login"]) > 0 { srch := qs["login"][0] mutex.Lock() if api.db.Order("login"). Find(&users, "login = ?", srch).RecordNotFound() { mutex.Unlock() rest.Error(w, "No results.", 400) return } mutex.Unlock() } else { mutex.Lock() if api.db.Order("login").Find(&users).RecordNotFound() { mutex.Unlock() rest.Error(w, "Empty Table.", 400) return } mutex.Unlock() } // Create a slice of maps from users struct // to selectively copy database fields for display u := make([]map[string]interface{}, len(users)) for i := range users { u[i] = make(map[string]interface{}) u[i]["Id"] = users[i].Id u[i]["Login"] = users[i].Login u[i]["Forename"] = users[i].Forename u[i]["Surname"] = users[i].Surname u[i]["Enabled"] = users[i].Enabled u[i]["CreatedAt"] = users[i].CreatedAt u[i]["Email"] = users[i].Email } // Too much noise //api.LogActivity( session.Id, "Sent list of users" ) w.WriteJson(&u) }
func (api *Api) AddDcCapMap(w rest.ResponseWriter, r *rest.Request) { // Check credentials login := r.PathParam("login") guid := r.PathParam("GUID") // Only admin is allowed if login != "admin" { rest.Error(w, "Not allowed", 400) return } session := Session{} var errl error if session, errl = api.CheckLogin(login, guid); errl != nil { rest.Error(w, errl.Error(), 401) return } defer api.TouchSession(guid) // Can't add if it exists already dcCapMapData := DcCapMap{} if err := r.DecodeJsonPayload(&dcCapMapData); err != nil { rest.Error(w, "Invalid data format received.", 400) return } else if dcCapMapData.DcId == 0 || dcCapMapData.DcCapId == 0 { rest.Error(w, "A required field is empty.", 400) return } dcCapMap := DcCapMap{} mutex.Lock() if !api.db.Find(&dcCapMap, "dc_id = ? and dc_cap_id = ?", dcCapMapData.DcId, dcCapMapData.DcCapId).RecordNotFound() { mutex.Unlock() rest.Error(w, "Record exists.", 400) return } mutex.Unlock() // Check that DcId and DcCapId exist dc := Dc{} mutex.Lock() if api.db.Find(&dc, dcCapMapData.DcId).RecordNotFound() { mutex.Unlock() rest.Error(w, "Invalid data centre id.", 400) return } mutex.Unlock() dcCap := DcCap{} mutex.Lock() if api.db.Find(&dcCap, dcCapMapData.DcCapId).RecordNotFound() { mutex.Unlock() rest.Error(w, "Invalid data centre capability id.", 400) return } mutex.Unlock() // Add DcCapMap mutex.Lock() if err := api.db.Save(&dcCapMapData).Error; err != nil { mutex.Unlock() rest.Error(w, err.Error(), 400) return } mutex.Unlock() text := fmt.Sprintf("Added new DcCapMap, '%d'.", dcCapMapData.Id) api.LogActivity(session.Id, text) w.WriteJson(dcCapMapData) }
// UpdateUser processes "PUT /users" queries. // func (api *Api) UpdateUser(w rest.ResponseWriter, r *rest.Request) { // Check credentials login := r.PathParam("login") guid := r.PathParam("GUID") // Only admin is allowed if login != "admin" { rest.Error(w, "Not allowed", 400) return } session := Session{} var errl error if session, errl = api.CheckLogin(login, guid); errl != nil { rest.Error(w, errl.Error(), 401) return } defer api.TouchSession(guid) // Ensure user exists id := r.PathParam("id") // Check that the id string is a number if _, err := strconv.Atoi(id); err != nil { rest.Error(w, "Invalid id.", 400) return } // Load data from db, then ... user := User{} mutex.Lock() if api.db.Find(&user, id).RecordNotFound() { mutex.Unlock() //rest.Error(w, err.Error(), 400) rest.Error(w, "Record not found.", 400) return } mutex.Unlock() // FIXME: DecodeJsonPayload(&somethingelse) then // merge with 'user' manually. This will remove // the 'password can't begin with $' limitation. // ... overwrite any sent fields if err := r.DecodeJsonPayload(&user); err != nil { //rest.Error(w, err.Error(), 400) rest.Error(w, "Invalid data format received.", 400) return } // Add user if !strings.HasPrefix(user.Passhash, "$") { c := &Crypt{} c.Pass = []byte(user.Passhash) c.Crypt() user.Passhash = string(c.Hash) } // Force the use of the path id over an id in the payload Id, _ := strconv.Atoi(id) user.Id = int64(Id) mutex.Lock() if err := api.db.Save(&user).Error; err != nil { mutex.Unlock() rest.Error(w, err.Error(), 400) return } mutex.Unlock() api.LogActivity(session.Id, "Updated user details for '"+user.Login+"'.") w.WriteJson(user) }
func (api *Api) UpdatePlugin(w rest.ResponseWriter, r *rest.Request) { // Check credentials login := r.PathParam("login") guid := r.PathParam("GUID") // Only admin is allowed if login != "admin" { rest.Error(w, "Not allowed", 400) return } session := Session{} var errl error if session, errl = api.CheckLogin(login, guid); errl != nil { rest.Error(w, errl.Error(), 401) return } defer api.TouchSession(guid) // Ensure user exists id := r.PathParam("id") // Check that the id string is a number if _, err := strconv.Atoi(id); err != nil { rest.Error(w, "Invalid id.", 400) return } // Load data from db, then ... plugin := Plugin{} mutex.Lock() if api.db.Find(&plugin, id).RecordNotFound() { mutex.Unlock() //rest.Error(w, err.Error(), 400) rest.Error(w, "Record not found.", 400) return } mutex.Unlock() // ... overwrite any sent fields if err := r.DecodeJsonPayload(&plugin); err != nil { //rest.Error(w, err.Error(), 400) rest.Error(w, "Invalid data format received.", 400) return } // Force the use of the path id over an id in the payload Id, _ := strconv.Atoi(id) plugin.Id = int64(Id) // Make sure parent exists pluginSrch := Plugin{} mutex.Lock() if len(plugin.Parent) > 0 && api.db.Find(&pluginSrch, "name = ?", plugin.Parent).RecordNotFound() { mutex.Unlock() rest.Error(w, "Parent not found.", 400) return } mutex.Unlock() if pluginSrch.Id == plugin.Id { rest.Error(w, "Cannot be a parent of itself.", 400) return } mutex.Lock() if err := api.db.Save(&plugin).Error; err != nil { mutex.Unlock() rest.Error(w, err.Error(), 400) return } mutex.Unlock() api.LogActivity(session.Id, "Updated plugin details for '"+plugin.Name+"'.") w.WriteJson("Success") }
func (api *Api) UpdateScript(w rest.ResponseWriter, r *rest.Request) { // Check credentials login := r.PathParam("login") guid := r.PathParam("GUID") // Only admin is allowed if login != "admin" { rest.Error(w, "Not allowed", 400) return } session := Session{} var errl error if session, errl = api.CheckLogin(login, guid); errl != nil { rest.Error(w, errl.Error(), 401) return } defer api.TouchSession(guid) // Ensure user exists id := r.PathParam("id") // Check that the id string is a number if _, err := strconv.Atoi(id); err != nil { rest.Error(w, "Invalid id.", 400) return } // Load data from db, then ... script := Script{} mutex.Lock() if api.db.Find(&script, id).RecordNotFound() { mutex.Unlock() //rest.Error(w, err.Error(), 400) rest.Error(w, "Record not found.", 400) return } mutex.Unlock() // ... overwrite any sent fields if err := r.DecodeJsonPayload(&script); err != nil { //rest.Error(w, err.Error(), 400) rest.Error(w, "Invalid data format received.", 400) return } script_srch := Script{} mutex.Lock() if !api.db.Find(&script_srch, "name = ? and id != ?", script.Name, script.Id).RecordNotFound() { mutex.Unlock() rest.Error(w, "Record exists.", 400) return } mutex.Unlock() // Work out type: // Write to disk then use unix 'file -b' (brief) if len(script.Source) > 0 { if err := ioutil.WriteFile(os.TempDir()+"/obdi_scriptcheck", script.Source, 0644); err != nil { script.Type = "Write file failed. Type of script unknown. (" + err.Error() + ")" } else { runCmd := exec.Command("file", "-b", os.TempDir()+"/obdi_scriptcheck") output, err := runCmd.Output() if err != nil { script.Type = "Unix 'file' failed. Type of script unknown." + " (" + err.Error() + ")" } else { script.Type = string(output) } } } // Force the use of the path id over an id in the payload Id, _ := strconv.Atoi(id) script.Id = int64(Id) mutex.Lock() if err := api.db.Save(&script).Error; err != nil { mutex.Unlock() rest.Error(w, err.Error(), 400) return } mutex.Unlock() api.LogActivity(session.Id, "Updated data centre details for '"+script.Name+"'.") w.WriteJson(script) }
func (api *Api) RunPluginUsingRPC(w rest.ResponseWriter, r *rest.Request, pluginFile, port, queryType string) ([]byte, error) { // Exec the go plugin // Look for ./plugins/<endpoint>/<subitem> // or ./plugins/<endpoint> reply := []byte{} cmd := exec.Command(pluginFile, port) // Set up buffer for stdout stdout, err := cmd.StdoutPipe() if err != nil { txt := fmt.Sprintf("exec.StdoutPipe error. %s", err) logit(txt) rest.Error(w, txt, 400) return reply, ApiError{"Error"} } rdr := bufio.NewReader(stdout) err = cmd.Start() if err != nil { txt := fmt.Sprintf("exec.Command error. %s", err) logit(txt) rest.Error(w, txt, 400) return reply, ApiError{"Error"} } // Make the RPC call type Args struct { PathParams map[string]string QueryString map[string][]string PostData []byte QueryType string } // Give it 5ms to start time.Sleep(5 * time.Millisecond) client, err := rpc.Dial("tcp", ":"+port) numtries := 1 for { if err != nil { // Retry every millisecond time.Sleep(5 * time.Millisecond) //logit( "Retrying connection to port " + port ) client, err = rpc.Dial("tcp", ":"+port) if numtries > 2000 { txt := fmt.Sprintf("Could not connect to plugin"+ " on port "+port, err) rest.Error(w, txt, 400) logit(txt) // TODO: Kill the client! err = cmd.Wait() return reply, ApiError{"Error"} } numtries += 1 continue } break } var postData []byte switch queryType { case "GET": //do nowt case "POST": // We don't know how to decode the data so save it raw postData, err = ioutil.ReadAll(r.Body) r.Body.Close() if err != nil { txt := fmt.Sprintf("Could not get post data. %s", err) rest.Error(w, txt, 400) logit(txt) err = cmd.Wait() return reply, ApiError{"Error"} } case "PUT": // We don't know how to decode the data so save it raw postData, err = ioutil.ReadAll(r.Body) r.Body.Close() if err != nil { txt := fmt.Sprintf("Could not get post data. %s", err) rest.Error(w, txt, 400) logit(txt) err = cmd.Wait() return reply, ApiError{"Error"} } case "DELETE": //do nowt } args := &Args{r.PathParams, r.URL.Query(), postData, queryType} args.PathParams["PluginDatabasePath"] = config.PluginDbPath err = client.Call("Plugin.HandleRequest", args, &reply) if err != nil { txt := fmt.Sprintf("client.Call error. %s", err) rest.Error(w, txt, 400) logit(txt) err = cmd.Wait() return reply, ApiError{"Error"} } client.Close() line := "" for err == nil { line, err = rdr.ReadString('\n') if len(line) > 2 { logit(line) } } err = cmd.Wait() return reply, nil }
func (api *Api) GetAllOutputLines(w rest.ResponseWriter, r *rest.Request) { // Check credentials login := r.PathParam("login") guid := r.PathParam("GUID") // Admin is not allowed if login == "admin" { rest.Error(w, "Not allowed", 400) return } //session := Session{} var errl error = nil //if session,errl = api.CheckLogin( login, guid ); errl != nil { if _, errl = api.CheckLogin(login, guid); errl != nil { rest.Error(w, errl.Error(), 401) return } defer api.TouchSession(guid) outputlines := []OutputLine{} qs := r.URL.Query() // Query string - map[string][]string if len(qs["job_id"]) > 0 { srch := qs["job_id"][0] if len(qs["top"]) > 0 { mutex.Lock() api.db.Order("serial").Limit(qs["top"][0]).Find(&outputlines, "job_id = ?", srch) mutex.Unlock() } else if len(qs["bottom"]) > 0 { mutex.Lock() // TODO last X lines but *in* order api.db.Order("serial desc").Limit(qs["bottom"][0]). Find(&outputlines, "job_id = ?", srch) mutex.Unlock() } else { mutex.Lock() api.db.Order("serial").Find(&outputlines, "job_id = ?", srch) mutex.Unlock() } } else { mutex.Lock() err := api.db.Order("serial").Find(&outputlines) mutex.Unlock() if err.Error != nil { if !err.RecordNotFound() { rest.Error(w, err.Error.Error(), 500) return } } } // Create a slice of maps from users struct // to selectively copy database fields for display u := make([]map[string]interface{}, len(outputlines)) for i := range outputlines { u[i] = make(map[string]interface{}) u[i]["Id"] = outputlines[i].Id u[i]["Serial"] = outputlines[i].Serial u[i]["JobId"] = outputlines[i].JobId u[i]["Text"] = outputlines[i].Text } // Too much noise //api.LogActivity( session.Id, "Sent list of users" ) w.WriteJson(&u) }
// DoLogin processes "POST /login" queries. // // Checks login name and passhash stored in database. // If correct then 200 header and GUID are sent. // any previous sessions for that user are closed. // new session entry is made in session table. // If not correct then 400 header with error message. // func (api *Api) DoLogin(w rest.ResponseWriter, r *rest.Request) { // Get the Login and Password userData := struct{ Login, Password string }{} if err := r.DecodeJsonPayload(&userData); err != nil { rest.Error(w, "Invalid data format received.", 400) return } else if len(userData.Login) == 0 || len(userData.Password) == 0 { rest.Error(w, "Incorrect data format received.", 400) return } //fmt.Printf( "\n%#v\n", userData ) // Get passhash for login from database user := User{} mutex.Lock() if api.db.Where(User{Login: userData.Login}). First(&user).RecordNotFound() { rest.Error(w, "User or password error.", 400) mutex.Unlock() return } mutex.Unlock() // Check password against hash c := &Crypt{} c.Pass = []byte(userData.Password) c.Hash = []byte(user.Passhash) if err := c.Check(); err != nil { rest.Error(w, "User or password error.", 400) return } // The user's password matches. // Delete old session(s) and create a new one. guid := NewGUID() session := Session{} for { session = Session{} mutex.Lock() if api.db.Where(Session{UserId: user.Id}). First(&session).RecordNotFound() { session = Session{ Guid: guid, UserId: user.Id, } if err := api.db.Save(&session).Error; err != nil { rest.Error(w, err.Error(), 400) mutex.Unlock() return } mutex.Unlock() break } else { if err := api.db.Delete(&session).Error; err != nil { rest.Error(w, err.Error(), 400) mutex.Unlock() return } } mutex.Unlock() } logit("User '" + user.Login + "' logged in") api.LogActivity(session.Id, "User '"+user.Login+"' logged in.") w.WriteJson(struct{ GUID string }{guid}) }
func (api *Api) GetAllPlugins(w rest.ResponseWriter, r *rest.Request) { // Check credentials login := r.PathParam("login") guid := r.PathParam("GUID") // Anyone can view plugins /* if login != "admin" { rest.Error(w, "Not allowed", 400) return } */ //session := Session{} var errl error = nil //if session,errl = api.CheckLogin( login, guid ); errl != nil { if _, errl = api.CheckLogin(login, guid); errl != nil { rest.Error(w, errl.Error(), 401) return } defer api.TouchSession(guid) plugins := []Plugin{} qs := r.URL.Query() // Query string - map[string][]string if len(qs["name"]) > 0 { srch := qs["name"][0] mutex.Lock() api.db.Order("name").Find(&plugins, "name = ?", srch) mutex.Unlock() /* if api.db.Order("name"). Find(&plugins, "name = ?", srch).RecordNotFound() { rest.Error(w, "No results.", 400) return } */ } else { // No results is not an error mutex.Lock() err := api.db.Order("name").Find(&plugins) mutex.Unlock() if err.Error != nil { if !err.RecordNotFound() { rest.Error(w, err.Error.Error(), 500) return } } } // Create a slice of maps from users struct // to selectively copy database fields for display u := make([]map[string]interface{}, len(plugins)) for i := range plugins { u[i] = make(map[string]interface{}) u[i]["Id"] = plugins[i].Id u[i]["Name"] = plugins[i].Name u[i]["Desc"] = plugins[i].Desc u[i]["Parent"] = plugins[i].Parent u[i]["HasView"] = plugins[i].HasView u[i]["CreatedAt"] = plugins[i].CreatedAt // UpdatedAt doesn't get updated 'cos we use Save // //u[i]["UpdatedAt"] = plugins[i].CreatedAt } // Too much noise //api.LogActivity( session.Id, "Sent list of users" ) w.WriteJson(&u) }
func (api *Api) UpdateEnvCap(w rest.ResponseWriter, r *rest.Request) { // Check credentials login := r.PathParam("login") guid := r.PathParam("GUID") // Only admin is allowed if login != "admin" { rest.Error(w, "Not allowed", 400) return } session := Session{} var errl error if session, errl = api.CheckLogin(login, guid); errl != nil { rest.Error(w, errl.Error(), 401) return } defer api.TouchSession(guid) // Ensure user exists id := r.PathParam("id") // Check that the id string is a number if _, err := strconv.Atoi(id); err != nil { rest.Error(w, "Invalid id.", 400) return } // Load data from db, then ... EnvCap := EnvCap{} mutex.Lock() if api.db.Find(&EnvCap, id).RecordNotFound() { mutex.Unlock() //rest.Error(w, err.Error(), 400) rest.Error(w, "Record not found.", 400) return } mutex.Unlock() // ... overwrite any sent fields if err := r.DecodeJsonPayload(&EnvCap); err != nil { //rest.Error(w, err.Error(), 400) rest.Error(w, "Invalid data format received.", 400) return } // Force the use of the path id over an id in the payload Id, _ := strconv.Atoi(id) EnvCap.Id = int64(Id) mutex.Lock() if err := api.db.Save(&EnvCap).Error; err != nil { mutex.Unlock() rest.Error(w, err.Error(), 400) return } mutex.Unlock() text := fmt.Sprintf("Updated EnvCap, '%s'.", EnvCap.Code) api.LogActivity(session.Id, text) w.WriteJson("Success") }
func (api *Api) GetAllScripts(w rest.ResponseWriter, r *rest.Request) { // Check credentials login := r.PathParam("login") guid := r.PathParam("GUID") // Anyone can read the list of scripts /* if login != "admin" { rest.Error(w, "Not allowed", 400) return }*/ //session := Session{} var errl error = nil //if session,errl = api.CheckLogin( login, guid ); errl != nil { if _, errl = api.CheckLogin(login, guid); errl != nil { rest.Error(w, errl.Error(), 401) return } defer api.TouchSession(guid) scripts := []Script{} qs := r.URL.Query() // Query string - map[string][]string if len(qs["id"]) > 0 { srch := qs["id"][0] mutex.Lock() api.db.Order("id").Find(&scripts, "id = ?", srch) mutex.Unlock() /* if api.db.Order("id"). Find(&scripts, "id = ?", srch).RecordNotFound() { rest.Error(w, "No results.", 400) return } */ } else if len(qs["name"]) > 0 { srch := qs["name"][0] mutex.Lock() api.db.Order("name").Find(&scripts, "name = ?", srch) mutex.Unlock() /* if api.db.Order("name"). Find(&scripts, "name = ?", srch).RecordNotFound() { rest.Error(w, "No results.", 400) return } */ } else { // No results is not an error mutex.Lock() err := api.db.Order("id").Find(&scripts) mutex.Unlock() if err.Error != nil { if !err.RecordNotFound() { rest.Error(w, err.Error.Error(), 500) return } } } // Create a slice of maps from users struct // to selectively copy database fields for display u := make([]map[string]interface{}, len(scripts)) for i := range scripts { u[i] = make(map[string]interface{}) u[i]["Id"] = scripts[i].Id u[i]["Name"] = scripts[i].Name u[i]["Desc"] = scripts[i].Desc // 'Source' doesn't go through Unmarshall so // is output as base64, good. Use nosource to // exclude this field. if len(qs["nosource"]) == 0 { u[i]["Source"] = scripts[i].Source } u[i]["Type"] = scripts[i].Type } // Too much noise //api.LogActivity( session.Id, "Sent list of users" ) w.WriteJson(&u) }
func (api *Api) AddEnv(w rest.ResponseWriter, r *rest.Request) { // Check credentials login := r.PathParam("login") guid := r.PathParam("GUID") // Only admin is allowed if login != "admin" { rest.Error(w, "Not allowed", 400) return } session := Session{} var errl error if session, errl = api.CheckLogin(login, guid); errl != nil { rest.Error(w, errl.Error(), 401) return } defer api.TouchSession(guid) // Can't add if it exists already envData := Env{} if err := r.DecodeJsonPayload(&envData); err != nil { rest.Error(w, "Invalid data format received.", 400) return } else if len(envData.SysName) == 0 { rest.Error(w, "Incorrect data format received.", 400) return } env := Env{} mutex.Lock() if !api.db.Find(&env, "sys_name = ? and dc_id = ?", envData.SysName, envData.DcId).RecordNotFound() { mutex.Unlock() rest.Error(w, "Record exists.", 400) return } mutex.Unlock() // Add env mutex.Lock() if err := api.db.Save(&envData).Error; err != nil { mutex.Unlock() rest.Error(w, err.Error(), 400) return } mutex.Unlock() dc := Dc{} mutex.Lock() api.db.First(&dc, envData.DcId) mutex.Unlock() text := fmt.Sprintf("Added new environment '%s->%s'.", dc.SysName, envData.SysName) api.LogActivity(session.Id, text) w.WriteJson(envData) }
// AddUser processes "POST /users" queries. // func (api *Api) AddUser(w rest.ResponseWriter, r *rest.Request) { // Check credentials login := r.PathParam("login") guid := r.PathParam("GUID") // Only admin is allowed if login != "admin" { rest.Error(w, "Not allowed", 400) return } session := Session{} var errl error if session, errl = api.CheckLogin(login, guid); errl != nil { rest.Error(w, errl.Error(), 401) return } defer api.TouchSession(guid) // Can't add if it exists already userData := User{} if err := r.DecodeJsonPayload(&userData); err != nil { rest.Error(w, "Invalid data format received.", 400) return } else if len(userData.Login) == 0 { rest.Error(w, "Incorrect data format received.", 400) return } user := User{} mutex.Lock() if !api.db.Find(&user, "login = ?", userData.Login).RecordNotFound() { mutex.Unlock() rest.Error(w, "Record exists.", 400) return } mutex.Unlock() // Add user if len(userData.Passhash) == 0 { rest.Error(w, "Empty password not allowed.", 400) return } c := &Crypt{} c.Pass = []byte(userData.Passhash) c.Crypt() userData.Passhash = string(c.Hash) mutex.Lock() if err := api.db.Save(&userData).Error; err != nil { mutex.Unlock() rest.Error(w, err.Error(), 400) return } mutex.Unlock() api.LogActivity(session.Id, "Added new user '"+userData.Login+"'.") w.WriteJson(userData) }
func (api *Api) CompilePlugin(w rest.ResponseWriter, pluginFile, endpoint, subitem string) error { api.compile.Lock() sourceDir := path.Join(config.GoPluginSource, endpoint, "go") sourceFile := path.Join(sourceDir, subitem+".go") compile := false if statExe, err := os.Stat(pluginFile); os.IsNotExist(err) { compile = true } else { if statSrc, err := os.Stat(sourceFile); !os.IsNotExist(err) { if statExe.ModTime().Before(statSrc.ModTime()) { logit("Plugin is older than source. Recompiling plugin.") compile = true } else { compile = false } } } if compile == true { logit("Plugin does not exist. Compiling plugin.") if _, err := os.Stat(sourceFile); os.IsNotExist(err) { txt := fmt.Sprintf("Plugin endpoint '%s/%s' does not exist.", endpoint, subitem) logit(txt) rest.Error(w, txt, 400) api.compile.Unlock() return ApiError{"Error"} } // Make plugin directory if err := os.Mkdir(path.Dir(pluginFile), 01750); err != nil { if !os.IsExist(err) { txt := fmt.Sprintf("Plugin endpoint '%s/%s' does not exist. "+ "Compile failed creating the directory"+ " '%s'. System said '%s'.", endpoint, subitem, pluginFile, err) logit(txt) rest.Error(w, txt, 400) api.compile.Unlock() return ApiError{"Error"} } } // Compile os.Setenv("PATH", "/usr/bin:/bin:"+path.Join(config.GoRoot, "bin")) cmd := exec.Command("go", "build", "-o", pluginFile, sourceFile) // Need GOROOT and GOPATH to compile cmd.Env = append(cmd.Env, "GOROOT="+config.GoRoot) cmd.Env = append(cmd.Env, "GOPATH="+sourceDir) // Can't find the go binary without PATH being set cmd.Env = append(cmd.Env, "PATH="+os.Getenv("PATH")) var sout bytes.Buffer var serr bytes.Buffer cmd.Stdout = &sout cmd.Stderr = &serr err := cmd.Run() if err != nil { txt := fmt.Sprintf("Plugin endpoint '%s/%s' does not exist. "+ "Compile failed for '%s'."+ " System said '%s'. STDOUT: %s STDERR:", endpoint, subitem, pluginFile, err, cmd.Stdout, cmd.Stderr) logit(txt) rest.Error(w, txt, 400) api.compile.Unlock() return ApiError{"Error"} } } api.compile.Unlock() return nil }
func (api *Api) AddPlugin(w rest.ResponseWriter, r *rest.Request) { // Check credentials login := r.PathParam("login") guid := r.PathParam("GUID") // Only admin is allowed if login != "admin" { rest.Error(w, "Not allowed", 400) return } session := Session{} var errl error if session, errl = api.CheckLogin(login, guid); errl != nil { rest.Error(w, errl.Error(), 401) return } defer api.TouchSession(guid) // Can't add if it exists already pluginData := Plugin{} if err := r.DecodeJsonPayload(&pluginData); err != nil { rest.Error(w, "Invalid data format received.", 400) return } else if len(pluginData.Name) == 0 { rest.Error(w, "Incorrect data format received.", 400) return } plugin := Plugin{} mutex.Lock() if !api.db.Find(&plugin, "name = ?", pluginData.Name). RecordNotFound() { mutex.Unlock() rest.Error(w, "Record exists.", 400) return } mutex.Unlock() // Make sure parent exists mutex.Lock() if len(pluginData.Parent) > 0 && api.db.Find(&plugin, "name = ?", pluginData.Parent).RecordNotFound() { mutex.Unlock() rest.Error(w, "Parent not found.", 400) return } mutex.Unlock() // Add plugin mutex.Lock() if err := api.db.Save(&pluginData).Error; err != nil { mutex.Unlock() rest.Error(w, err.Error(), 400) return } mutex.Unlock() api.LogActivity(session.Id, "Added new plugin '"+pluginData.Name+"'.") w.WriteJson(pluginData) }
func (api *Api) GenericPutEndpoint(w rest.ResponseWriter, r *rest.Request) { // Reserve the tcp plugin port now iport := api.Port() port := strconv.FormatInt(iport, 10) defer api.DecrementPort(iport) // Check credentials login := r.PathParam("login") guid := r.PathParam("GUID") // Admin is not allowed if login == "admin" { rest.Error(w, "Not allowed", 400) return } var errl error = nil if _, errl = api.CheckLogin(login, guid); errl != nil { logit(errl.Error()) rest.Error(w, errl.Error(), 401) return } defer api.TouchSession(guid) // If the plugin isn't available try to compile it endpoint := r.PathParam("endpoint") subitem := r.PathParam("subitem") pluginFile := path.Join(config.GoPluginDir, endpoint, subitem) if err := api.CompilePlugin(w, pluginFile, endpoint, subitem); err != nil { return } // Run the Go plugin var reply []byte var err error if reply, err = api.RunPluginUsingRPC(w, r, pluginFile, port, "PUT"); err != nil { return // Just return full error was written in RunPluginUsingRPC } // Decode arbitrary JSON. Pull out the mandatory PluginReturn // and PluginError fields - all plugins send at least these. var generic interface{} json.Unmarshal(reply, &generic) genericReply := generic.(map[string]interface{}) var pluginReturn int64 var pluginError string for k, v := range genericReply { if k == "PluginReturn" { pluginReturn = int64(v.(float64)) } if k == "PluginError" { pluginError = v.(string) } } // Return, 0 - success, 1 - error if pluginReturn == 1 { txt := fmt.Sprintf("Plugin returned error. %s", pluginError) rest.Error(w, txt, 400) logit(txt) return } // Response is already json encoded so send it raw w.(http.ResponseWriter).Write(reply) // Too much noise //api.LogActivity( session.Id, "Sent list of users" ) }
func (api *Api) AddScript(w rest.ResponseWriter, r *rest.Request) { // Check credentials login := r.PathParam("login") guid := r.PathParam("GUID") // Only admin is allowed if login != "admin" { rest.Error(w, "Not allowed", 400) return } session := Session{} var errl error if session, errl = api.CheckLogin(login, guid); errl != nil { rest.Error(w, errl.Error(), 401) return } defer api.TouchSession(guid) // Can't add if it exists already scriptData := Script{} if err := r.DecodeJsonPayload(&scriptData); err != nil { rest.Error(w, "Invalid data format received.", 400) return } else if len(scriptData.Source) == 0 { rest.Error(w, "Incorrect data format received.", 400) return } script := Script{} mutex.Lock() if !api.db.Find(&script, "name = ?", scriptData.Name).RecordNotFound() { mutex.Unlock() rest.Error(w, "Record exists.", 400) return } mutex.Unlock() // Work out type: // Write to disk then use unix 'file -b' (brief) if err := ioutil.WriteFile(os.TempDir()+"/obdi_scriptcheck", scriptData.Source, 0644); err != nil { scriptData.Type = "Unknown type of script" } else { runCmd := exec.Command("file", "-b", os.TempDir()+"/obdi_scriptcheck") output, err := runCmd.Output() if err != nil { scriptData.Type = "Unknown type of script" } else { scriptData.Type = string(output) } } // Add script mutex.Lock() if err := api.db.Save(&scriptData).Error; err != nil { mutex.Unlock() rest.Error(w, err.Error(), 400) return } mutex.Unlock() // Try to start the script text := fmt.Sprintf("Added new script, %s.", scriptData.Name) api.LogActivity(session.Id, text) scriptData.Source = []byte{} w.WriteJson(scriptData) }