func parseAclRequestTyped(cx *goweb.Context) (ids []string, err error) { var users []string query := util.Q(cx.Request.URL.Query()) params, _, err := request.ParseMultipartForm(cx.Request) if err != nil && err.Error() == "request Content-Type isn't multipart/form-data" && query.Has("users") { users = strings.Split(query.Value("users"), ",") } else if params["users"] != "" { users = strings.Split(params["users"], ",") } else { return nil, errors.New("Action requires list of comma seperated email address in 'users' parameter") } for _, v := range users { if isEmail(v) { u := user.User{Username: v, Email: v} if err := u.SetUuid(); err != nil { return nil, err } ids = append(ids, u.Uuid) } else if isUuid(v) { ids = append(ids, v) } else { return nil, errors.New("Unknown user id. Must be uuid or email address") } } return ids, nil }
// POST: /node func (cr *Controller) Create(cx *goweb.Context) { // Log Request and check for Auth request.Log(cx.Request) u, err := request.Authenticate(cx.Request) if err != nil && err.Error() != e.NoAuth { request.AuthError(err, cx) return } // Fake public user if u == nil { if conf.Bool(conf.Conf["anon-write"]) { u = &user.User{Uuid: ""} } else { cx.RespondWithErrorMessage(e.NoAuth, http.StatusUnauthorized) return } } // Parse uploaded form params, files, err := request.ParseMultipartForm(cx.Request) if err != nil { // If not multipart/form-data it will create an empty node. // TODO: create another request parser for non-multipart request // to handle this cleaner. if err.Error() == "request Content-Type isn't multipart/form-data" { n, err := node.CreateNodeUpload(u, params, files) if err != nil { logger.Error("Error at create empty: " + err.Error()) cx.RespondWithError(http.StatusInternalServerError) return } if n == nil { // Not sure how you could get an empty node with no error // Assume it's the user's fault cx.RespondWithError(http.StatusBadRequest) return } else { cx.RespondWithData(n) return } } else { // Some error other than request encoding. Theoretically // could be a lost db connection between user lookup and parsing. // Blame the user, Its probaby their fault anyway. logger.Error("Error parsing form: " + err.Error()) cx.RespondWithError(http.StatusBadRequest) return } } // Create node n, err := node.CreateNodeUpload(u, params, files) if err != nil { logger.Error("err@node_CreateNodeUpload: " + err.Error()) cx.RespondWithErrorMessage(err.Error(), http.StatusBadRequest) return } cx.RespondWithData(n) return }
func parseAclRequest(cx *goweb.Context) (ids map[string][]string, err error) { ids = map[string][]string{} users := map[string][]string{} query := util.Q(cx.Request.URL.Query()) params, _, err := request.ParseMultipartForm(cx.Request) if err != nil && err.Error() == "request Content-Type isn't multipart/form-data" && (query.Has("all") || query.Has("read") || query.Has("write") || query.Has("delete")) { if query.Has("all") { users["all"] = strings.Split(query.Value("all"), ",") } if query.Has("read") { users["read"] = strings.Split(query.Value("read"), ",") } if query.Has("write") { users["write"] = strings.Split(query.Value("write"), ",") } if query.Has("delete") { users["delete"] = strings.Split(query.Value("delete"), ",") } } else if params["all"] != "" || params["read"] != "" || params["write"] != "" || params["delete"] != "" { users["all"] = strings.Split(params["all"], ",") users["read"] = strings.Split(params["read"], ",") users["write"] = strings.Split(params["write"], ",") users["delete"] = strings.Split(params["delete"], ",") } else { return nil, errors.New("Action requires list of comma seperated email address in 'all', 'read', 'write', and/or 'delete' parameter") } for k, _ := range users { for _, v := range users[k] { if v != "" { if isEmail(v) { u := user.User{Username: v, Email: v} if err := u.SetUuid(); err != nil { return nil, err } ids[k] = append(ids[k], u.Uuid) } else if isUuid(v) { ids[k] = append(ids[k], v) } else { return nil, errors.New("Unknown user id. Must be uuid or email address") } } } } if len(ids["all"]) > 0 { ids["read"] = append(ids["read"], ids["all"]...) ids["write"] = append(ids["write"], ids["all"]...) ids["delete"] = append(ids["delete"], ids["all"]...) } delete(ids, "all") return ids, nil }
func parseAclRequestTyped(ctx context.Context) (ids []string, err error) { var users []string query := ctx.HttpRequest().URL.Query() params, _, err := request.ParseMultipartForm(ctx.HttpRequest()) if _, ok := query["users"]; ok && err != nil && err.Error() == "request Content-Type isn't multipart/form-data" { users = strings.Split(query.Get("users"), ",") } else if params["users"] != "" { users = strings.Split(params["users"], ",") } else { return nil, errors.New("Action requires list of comma separated usernames in 'users' parameter") } for _, v := range users { if uuid.Parse(v) != nil { ids = append(ids, v) } else { u := user.User{Username: v} if err := u.SetMongoInfo(); err != nil { return nil, err } ids = append(ids, u.Uuid) } } return ids, nil }
// POST: /node func (cr *NodeController) Create(ctx context.Context) error { u, err := request.Authenticate(ctx.HttpRequest()) if err != nil && err.Error() != e.NoAuth { return request.AuthError(err, ctx) } // Fake public user if u == nil { if conf.Bool(conf.Conf["anon-write"]) { u = &user.User{Uuid: ""} } else { return responder.RespondWithError(ctx, http.StatusUnauthorized, e.NoAuth) } } // Parse uploaded form params, files, err := request.ParseMultipartForm(ctx.HttpRequest()) if err != nil { if err.Error() == "request Content-Type isn't multipart/form-data" { // If not multipart/form-data it will try to read the Body of the // request. If the Body is not empty it will create a file from // the Body contents. If the Body is empty it will create an empty // node. if ctx.HttpRequest().ContentLength != 0 { params, files, err = request.DataUpload(ctx.HttpRequest()) if err != nil { err_msg := "Error uploading data from request body:" + err.Error() logger.Error(err_msg) return responder.RespondWithError(ctx, http.StatusInternalServerError, err_msg) } } n, cn_err := node.CreateNodeUpload(u, params, files) if cn_err != nil { err_msg := "Error at create empty node: " + cn_err.Error() logger.Error(err_msg) return responder.RespondWithError(ctx, http.StatusInternalServerError, err_msg) } if n == nil { // Not sure how you could get an empty node with no error // Assume it's the user's fault return responder.RespondWithError(ctx, http.StatusBadRequest, "Error, could not create node.") } else { return responder.RespondWithData(ctx, n) } } else { // Some error other than request encoding. Theoretically // could be a lost db connection between user lookup and parsing. // Blame the user, Its probaby their fault anyway. err_msg := "Error parsing form: " + err.Error() logger.Error(err_msg) return responder.RespondWithError(ctx, http.StatusBadRequest, err_msg) } } // Create node n, err := node.CreateNodeUpload(u, params, files) if err != nil { err_msg := "err@node_CreateNodeUpload: " + err.Error() logger.Error(err_msg) return responder.RespondWithError(ctx, http.StatusBadRequest, err_msg) } return responder.RespondWithData(ctx, n) }
// GET, POST, PUT, DELETE: /node/{nid}/acl/ // GET is the only action implemented here. func AclRequest(ctx context.Context) { nid := ctx.PathValue("nid") u, err := request.Authenticate(ctx.HttpRequest()) if err != nil && err.Error() != e.NoAuth { request.AuthError(err, ctx) return } // acl require auth even for public data if u == nil { responder.RespondWithError(ctx, http.StatusUnauthorized, e.NoAuth) return } // Load node and handle user unauthorized n, err := node.LoadUnauth(nid) if err != nil { if err.Error() == e.MongoDocNotFound { responder.RespondWithError(ctx, http.StatusNotFound, "Node not found") return } else { // In theory the db connection could be lost between // checking user and load but seems unlikely. err_msg := "Err@node_Read:LoadNode: " + err.Error() logger.Error(err_msg) responder.RespondWithError(ctx, http.StatusInternalServerError, err_msg) return } } // only the owner can view/edit acl's unless owner="" or owner=nil // note: owner can only be empty when anonymous node creation is enabled in shock config. if n.Acl.Owner != u.Uuid && n.Acl.Owner != "" { err_msg := "Only the node owner can edit/view node ACL's" logger.Error(err_msg) responder.RespondWithError(ctx, http.StatusUnauthorized, err_msg) return } if ctx.HttpRequest().Method == "GET" { if u.Uuid == n.Acl.Owner || rights["read"] { // this is pretty clumsy and I could have done it with a lot less code and cleaner // in perl or js, but I am not so familiar with GO yet (it always hurts the first time) // the return structure should probably not be a concatenated string, but rather a hash // for each entry containing Username, Fullname and Uuid // owner cu, err := user.FindByUuid(n.Acl.Owner) if err != nil { responder.RespondWithError(ctx, http.StatusInternalServerError, "Err@Uuid_Resolve: "+err.Error()) return } else { n.Acl.Owner = cu.Username + "|" + cu.Uuid } // read p1 := []string{} for _, v := range n.Acl.Read { cu, err := user.FindByUuid(v) if err != nil { responder.RespondWithError(ctx, http.StatusInternalServerError, "Err@Uuid_Resolve: "+err.Error()) return } else { p1 = append(p1, cu.Username + "|" + cu.Uuid) } } n.Acl.Read = p1 // write p2 := []string{} for _, v := range n.Acl.Write { cu, err := user.FindByUuid(v) if err != nil { responder.RespondWithError(ctx, http.StatusInternalServerError, "Err@Uuid_Resolve: "+err.Error()) return } else { p2 = append(p2, cu.Username + "|" + cu.Uuid) } } n.Acl.Write = p2 // delete p3 := []string{} for _, v := range n.Acl.Delete { cu, err := user.FindByUuid(v) if err != nil { responder.RespondWithError(ctx, http.StatusInternalServerError, "Err@Uuid_Resolve: "+err.Error()) return } else { p3 = append(p3, cu.Username + "|" + cu.Uuid) } } n.Acl.Delete = p3 responder.RespondWithData(ctx, n.Acl) } else { responder.RespondWithError(ctx, http.StatusNotImplemented, "This request type is not implemented.") } return } // GET, POST, PUT, DELETE: /node/{nid}/acl/{type} func AclTypedRequest(ctx context.Context) { nid := ctx.PathValue("nid") rtype := ctx.PathValue("type") u, err := request.Authenticate(ctx.HttpRequest()) if err != nil && err.Error() != e.NoAuth { request.AuthError(err, ctx) return } if !validAclTypes[rtype] { responder.RespondWithError(ctx, http.StatusBadRequest, "Invalid acl type") return } // acl require auth even for public data if u == nil { responder.RespondWithError(ctx, http.StatusUnauthorized, e.NoAuth) return } // Load node and handle user unauthorized n, err := node.LoadUnauth(nid) if err != nil { if err.Error() == e.MongoDocNotFound { responder.RespondWithError(ctx, http.StatusNotFound, "Node not found") return } else { // In theory the db connection could be lost between // checking user and load but seems unlikely. err_msg := "Err@node_Read:LoadNode: " + err.Error() logger.Error(err_msg) responder.RespondWithError(ctx, http.StatusInternalServerError, err_msg) return } } // only the owner can view/edit acl's unless owner="" or owner=nil // note: owner can only be empty when anonymous node creation is enabled in shock config. if n.Acl.Owner != u.Uuid && n.Acl.Owner != "" { err_msg := "Only the node owner can edit/view node ACL's" logger.Error(err_msg) responder.RespondWithError(ctx, http.StatusBadRequest, err_msg) return } requestMethod := ctx.HttpRequest().Method if requestMethod != "GET" { ids, err := parseAclRequestTyped(ctx) if err != nil { responder.RespondWithError(ctx, http.StatusBadRequest, err.Error()) return } if requestMethod == "POST" || requestMethod == "PUT" { if rtype == "owner" { if len(ids) == 1 { n.Acl.SetOwner(ids[0]) } else { responder.RespondWithError(ctx, http.StatusBadRequest, "Too many users. Nodes may have only one owner.") return } } else if rtype == "all" { for _, atype := range []string{"read", "write", "delete"} { for _, i := range ids { n.Acl.Set(i, map[string]bool{atype: true}) } } } else { for _, i := range ids { n.Acl.Set(i, map[string]bool{rtype: true}) } } n.Save() } else if requestMethod == "DELETE" { if rtype == "owner" { responder.RespondWithError(ctx, http.StatusBadRequest, "Deleting ownership is not a supported request type.") return } else if rtype == "all" { for _, atype := range []string{"read", "write", "delete"} { for _, i := range ids { n.Acl.UnSet(i, map[string]bool{atype: true}) } } } else { for _, i := range ids { n.Acl.UnSet(i, map[string]bool{rtype: true}) } } n.Save() } else { responder.RespondWithError(ctx, http.StatusNotImplemented, "This request type is not implemented.") return } } switch rtype { default: responder.RespondWithError(ctx, http.StatusNotImplemented, "This request type is not implemented.") case "read": responder.RespondWithData(ctx, map[string][]string{"read": n.Acl.Read}) case "write": responder.RespondWithData(ctx, map[string][]string{"write": n.Acl.Write}) case "delete": responder.RespondWithData(ctx, map[string][]string{"delete": n.Acl.Delete}) case "owner": responder.RespondWithData(ctx, map[string]string{"owner": n.Acl.Owner}) case "all": responder.RespondWithData(ctx, n.Acl) } return } func parseAclRequestTyped(ctx context.Context) (ids []string, err error) { var users []string query := ctx.HttpRequest().URL.Query() params, _, err := request.ParseMultipartForm(ctx.HttpRequest()) if _, ok := query["users"]; ok && err != nil && err.Error() == "request Content-Type isn't multipart/form-data" { users = strings.Split(query.Get("users"), ",") } else if params["users"] != "" { users = strings.Split(params["users"], ",") } else { return nil, errors.New("Action requires list of comma separated usernames in 'users' parameter") } for _, v := range users { if uuid.Parse(v) != nil { ids = append(ids, v) } else { u := user.User{Username: v} if err := u.SetMongoInfo(); err != nil { return nil, err } ids = append(ids, u.Uuid) } } return ids, nil }
// GET, PUT, DELETE: /node/{nid}/index/{idxType} func IndexTypedRequest(ctx context.Context) { nid := ctx.PathValue("nid") idxType := ctx.PathValue("idxType") u, err := request.Authenticate(ctx.HttpRequest()) if err != nil && err.Error() != e.NoAuth { request.AuthError(err, ctx) return } // Fake public user if u == nil { u = &user.User{Uuid: ""} } // Load node and handle user unauthorized n, err := node.Load(nid, u.Uuid) if err != nil { if err.Error() == e.UnAuth { responder.RespondWithError(ctx, http.StatusUnauthorized, e.UnAuth) return } else if err.Error() == e.MongoDocNotFound { responder.RespondWithError(ctx, http.StatusNotFound, "Node not found.") return } else { // In theory the db connection could be lost between // checking user and load but seems unlikely. err_msg := "Err@index:LoadNode: " + err.Error() logger.Error(err_msg) responder.RespondWithError(ctx, http.StatusInternalServerError, err_msg) return } } switch ctx.HttpRequest().Method { case "DELETE": rights := n.Acl.Check(u.Uuid) if !rights["write"] { responder.RespondWithError(ctx, http.StatusUnauthorized, e.UnAuth) return } if _, has := n.Indexes[idxType]; has { if err := n.DeleteIndex(idxType); err != nil { err_msg := err.Error() logger.Error(err_msg) responder.RespondWithError(ctx, http.StatusInternalServerError, err_msg) } responder.RespondOK(ctx) } else { responder.RespondWithError(ctx, http.StatusBadRequest, fmt.Sprintf("Node %s does not have index of type %s.", n.Id, idxType)) } case "GET": if v, has := n.Indexes[idxType]; has { responder.RespondWithData(ctx, map[string]interface{}{idxType: v}) } else { responder.RespondWithError(ctx, http.StatusBadRequest, fmt.Sprintf("Node %s does not have index of type %s.", n.Id, idxType)) } case "PUT": rights := n.Acl.Check(u.Uuid) if !rights["write"] { responder.RespondWithError(ctx, http.StatusUnauthorized, e.UnAuth) return } // Gather query params query := ctx.HttpRequest().URL.Query() _, forceRebuild := query["force_rebuild"] if _, has := n.Indexes[idxType]; has { if idxType == "size" { responder.RespondOK(ctx) return } else if !forceRebuild { responder.RespondWithError(ctx, http.StatusBadRequest, "This index already exists, please add the parameter 'force_rebuild=1' to force a rebuild of the existing index.") return } } if !n.HasFile() { responder.RespondWithError(ctx, http.StatusBadRequest, "Node has no file.") return } else if idxType == "" { responder.RespondWithError(ctx, http.StatusBadRequest, "Index create requires type.") return } if _, ok := index.Indexers[idxType]; !ok && idxType != "bai" && idxType != "subset" && idxType != "column" { responder.RespondWithError(ctx, http.StatusBadRequest, fmt.Sprintf("Index type %s unavailable.", idxType)) return } if idxType == "size" { responder.RespondWithError(ctx, http.StatusBadRequest, fmt.Sprintf("Index type size is a virtual index and does not require index building.")) return } if conf.Bool(conf.Conf["perf-log"]) { logger.Perf("START indexing: " + nid) } if idxType == "bai" { //bam index is created by the command-line tool samtools if n.Type == "subset" { responder.RespondWithError(ctx, http.StatusBadRequest, "Shock does not support bam index creation on subset nodes.") return } if ext := n.FileExt(); ext == ".bam" { if err := index.CreateBamIndex(n.FilePath()); err != nil { responder.RespondWithError(ctx, http.StatusInternalServerError, "Error while creating bam index.") return } responder.RespondOK(ctx) return } else { responder.RespondWithError(ctx, http.StatusBadRequest, "Index type bai requires .bam file.") return } } subsetSize := int64(0) count := int64(0) indexFormat := "" subsetName := "" if idxType == "subset" { // Utilizing the multipart form parser since we need to upload a file. params, files, err := request.ParseMultipartForm(ctx.HttpRequest()) if err != nil { responder.RespondWithError(ctx, http.StatusBadRequest, err.Error()) return } parentIndex, hasParent := params["parent_index"] if !hasParent { responder.RespondWithError(ctx, http.StatusBadRequest, "Index type subset requires parent_index param.") return } else if _, has := n.Indexes[parentIndex]; !has { responder.RespondWithError(ctx, http.StatusBadRequest, fmt.Sprintf("Node %s does not have index of type %s.", n.Id, parentIndex)) return } newIndex, hasName := params["index_name"] if !hasName { responder.RespondWithError(ctx, http.StatusBadRequest, "Index type subset requires index_name param.") return } else if _, reservedName := index.Indexers[newIndex]; reservedName || newIndex == "bai" { responder.RespondWithError(ctx, http.StatusBadRequest, fmt.Sprintf("%s is a reserved index name and cannot be used to create a custom subset index.", newIndex)) return } subsetName = newIndex subsetIndices, hasFile := files["subset_indices"] if !hasFile { responder.RespondWithError(ctx, http.StatusBadRequest, "Index type subset requires subset_indices file.") return } f, _ := os.Open(subsetIndices.Path) defer f.Close() idxer := index.NewSubsetIndexer(f) // we default to "array" index format for backwards compatibility indexFormat = "array" if n.Indexes[parentIndex].Format == "array" || n.Indexes[parentIndex].Format == "matrix" { indexFormat = n.Indexes[parentIndex].Format } count, subsetSize, err = index.CreateSubsetIndex(&idxer, n.IndexPath()+"/"+newIndex+".idx", n.IndexPath()+"/"+parentIndex+".idx", indexFormat, n.Indexes[parentIndex].TotalUnits) if err != nil { logger.Error("err " + err.Error()) responder.RespondWithError(ctx, http.StatusBadRequest, err.Error()) return } } else if idxType == "column" { // Gather query params query := ctx.HttpRequest().URL.Query() if n.Type == "subset" { responder.RespondWithError(ctx, http.StatusBadRequest, "Shock does not support column index creation on subset nodes.") return } if _, exists := query["number"]; !exists { err_msg := "Index type column requires a number parameter in the url." logger.Error(err_msg) responder.RespondWithError(ctx, http.StatusBadRequest, err_msg) return } num_str := query.Get("number") idxType = idxType + num_str num, err := strconv.Atoi(num_str) if err != nil || num < 1 { err_msg := "Index type column requires a number parameter in the url of an integer greater than zero." logger.Error(err_msg) responder.RespondWithError(ctx, http.StatusBadRequest, err_msg) return } f, _ := os.Open(n.FilePath()) defer f.Close() idxer := index.NewColumnIndexer(f) count, indexFormat, err = index.CreateColumnIndex(&idxer, num, n.IndexPath()+"/"+idxType+".idx") if err != nil { logger.Error("err " + err.Error()) responder.RespondWithError(ctx, http.StatusBadRequest, err.Error()) return } } else { if n.Type == "subset" && (idxType != "chunkrecord" || n.Subset.Parent.IndexName != "record") { responder.RespondWithError(ctx, http.StatusBadRequest, "For subset nodes, Shock currently only supports subset and chunkrecord indexes. Also, for a chunkrecord index, the subset node must have been generated from a record index.") return } newIndexer := index.Indexers[idxType] f, _ := os.Open(n.FilePath()) defer f.Close() var idxer index.Indexer if n.Type == "subset" { idxer = newIndexer(f, n.Type, n.Subset.Index.Format, n.IndexPath()+"/"+n.Subset.Parent.IndexName+".idx") } else { idxer = newIndexer(f, n.Type, "", "") } count, indexFormat, err = idxer.Create(n.IndexPath() + "/" + idxType + ".idx") if err != nil { logger.Error("err " + err.Error()) responder.RespondWithError(ctx, http.StatusBadRequest, err.Error()) return } } if count == 0 { responder.RespondWithError(ctx, http.StatusBadRequest, "Index empty.") return } idxInfo := node.IdxInfo{ Type: idxType, TotalUnits: count, AvgUnitSize: n.File.Size / count, Format: indexFormat, } //if idxType == "chunkrecord" { // idxInfo.AvgUnitSize = conf.CHUNK_SIZE //} if idxType == "subset" { idxType = subsetName idxInfo.AvgUnitSize = subsetSize / count } if err := n.SetIndexInfo(idxType, idxInfo); err != nil { logger.Error("[email protected]: " + err.Error()) } if conf.Bool(conf.Conf["perf-log"]) { logger.Perf("END indexing: " + nid) } responder.RespondOK(ctx) return default: responder.RespondWithError(ctx, http.StatusNotImplemented, "This request type is not implemented.") } return }
// PUT: /node/{id} -> multipart-form func (cr *NodeController) Replace(id string, ctx context.Context) error { u, err := request.Authenticate(ctx.HttpRequest()) if err != nil && err.Error() != e.NoAuth { return request.AuthError(err, ctx) } // public user (no auth) can be used in some cases if u == nil { if conf.ANON_WRITE { u = &user.User{Uuid: "public"} } else { return responder.RespondWithError(ctx, http.StatusUnauthorized, e.NoAuth) } } // Load node by id n, err := node.Load(id) if err != nil { if err == mgo.ErrNotFound { return responder.RespondWithError(ctx, http.StatusNotFound, e.NodeNotFound) } else { // In theory the db connection could be lost between // checking user and load but seems unlikely. err_msg := "Err@node_Update:LoadNode: " + id + ":" + err.Error() logger.Error(err_msg) return responder.RespondWithError(ctx, http.StatusInternalServerError, err_msg) } } rights := n.Acl.Check(u.Uuid) prights := n.Acl.Check("public") if rights["write"] == false && u.Admin == false && n.Acl.Owner != u.Uuid && prights["write"] == false { return responder.RespondWithError(ctx, http.StatusUnauthorized, e.UnAuth) } if conf.LOG_PERF { logger.Perf("START PUT data: " + id) } params, files, err := request.ParseMultipartForm(ctx.HttpRequest()) // clean up temp dir !! defer node.RemoveAllFormFiles(files) if err != nil { err_msg := "err@node_ParseMultipartForm: " + err.Error() logger.Error(err_msg) return responder.RespondWithError(ctx, http.StatusBadRequest, err_msg) } // need delete rights to set expiration if _, hasExpiration := params["expiration"]; hasExpiration { if rights["delete"] == false && u.Admin == false && n.Acl.Owner != u.Uuid && prights["delete"] == false { return responder.RespondWithError(ctx, http.StatusUnauthorized, e.UnAuth) } } if _, hasCopyData := params["copy_data"]; hasCopyData { _, err = node.Load(params["copy_data"]) if err != nil { return request.AuthError(err, ctx) } } if _, hasParentNode := params["parent_node"]; hasParentNode { _, err = node.Load(params["parent_node"]) if err != nil { return request.AuthError(err, ctx) } } err = n.Update(params, files) if err != nil { err_msg := "err@node_Update: " + id + ": " + err.Error() logger.Error(err_msg) return responder.RespondWithError(ctx, http.StatusBadRequest, err_msg) } responder.RespondWithData(ctx, n) if conf.LOG_PERF { logger.Perf("END PUT data: " + id) } return nil }
// PUT: /node/{id} -> multipart-form func (cr *Controller) Update(id string, cx *goweb.Context) { // Log Request and check for Auth request.Log(cx.Request) u, err := request.Authenticate(cx.Request) if err != nil && err.Error() != e.NoAuth { request.AuthError(err, cx) return } // Gather query params query := util.Q(cx.Request.URL.Query()) // Fake public user if u == nil { u = &user.User{Uuid: ""} } n, err := node.Load(id, u.Uuid) if err != nil { if err.Error() == e.UnAuth { cx.RespondWithError(http.StatusUnauthorized) return } else if err.Error() == e.MongoDocNotFound { cx.RespondWithNotFound() return } else { // In theory the db connection could be lost between // checking user and load but seems unlikely. logger.Error("Err@node_Update:LoadNode: " + err.Error()) cx.RespondWithError(http.StatusInternalServerError) return } } if query.Has("index") { if conf.Bool(conf.Conf["perf-log"]) { logger.Perf("START indexing: " + id) } if !n.HasFile() { cx.RespondWithErrorMessage("node file empty", http.StatusBadRequest) return } if query.Value("index") == "bai" { //bam index is created by the command-line tool samtools if ext := n.FileExt(); ext == ".bam" { if err := index.CreateBamIndex(n.FilePath()); err != nil { cx.RespondWithErrorMessage("Error while creating bam index", http.StatusBadRequest) return } return } else { cx.RespondWithErrorMessage("Index type bai requires .bam file", http.StatusBadRequest) return } } idxtype := query.Value("index") if _, ok := index.Indexers[idxtype]; !ok { cx.RespondWithErrorMessage("invalid index type", http.StatusBadRequest) return } newIndexer := index.Indexers[idxtype] f, _ := os.Open(n.FilePath()) defer f.Close() idxer := newIndexer(f) count, err := idxer.Create() if err != nil { logger.Error("err " + err.Error()) cx.RespondWithErrorMessage(err.Error(), http.StatusBadRequest) return } if err := idxer.Dump(n.IndexPath() + "/" + query.Value("index") + ".idx"); err != nil { logger.Error("err " + err.Error()) cx.RespondWithErrorMessage(err.Error(), http.StatusBadRequest) return } idxInfo := node.IdxInfo{ Type: query.Value("index"), TotalUnits: count, AvgUnitSize: n.File.Size / count, } if idxtype == "chunkrecord" { idxInfo.AvgUnitSize = conf.CHUNK_SIZE } if err := n.SetIndexInfo(query.Value("index"), idxInfo); err != nil { logger.Error("[email protected]: " + err.Error()) } if conf.Bool(conf.Conf["perf-log"]) { logger.Perf("END indexing: " + id) } cx.RespondWithOK() return } else { if conf.Bool(conf.Conf["perf-log"]) { logger.Perf("START PUT data: " + id) } params, files, err := request.ParseMultipartForm(cx.Request) if err != nil { logger.Error("err@node_ParseMultipartForm: " + err.Error()) cx.RespondWithError(http.StatusBadRequest) return } err = n.Update(params, files) if err != nil { errors := []string{e.FileImut, e.AttrImut, "parts cannot be less than 1"} for e := range errors { if err.Error() == errors[e] { cx.RespondWithErrorMessage(err.Error(), http.StatusBadRequest) return } } logger.Error("err@node_Update: " + id + ":" + err.Error()) cx.RespondWithErrorMessage(err.Error(), http.StatusBadRequest) return } cx.RespondWithData(n) if conf.Bool(conf.Conf["perf-log"]) { logger.Perf("END PUT data: " + id) } } return }
// PUT: /node/{id} -> multipart-form func (cr *NodeController) Replace(id string, ctx context.Context) error { u, err := request.Authenticate(ctx.HttpRequest()) if err != nil && err.Error() != e.NoAuth { return request.AuthError(err, ctx) } // Fake public user if u == nil { u = &user.User{Uuid: ""} } n, err := node.Load(id, u.Uuid) if err != nil { if err.Error() == e.UnAuth { return responder.RespondWithError(ctx, http.StatusUnauthorized, e.UnAuth) } else if err.Error() == e.MongoDocNotFound { return responder.RespondWithError(ctx, http.StatusNotFound, "Node not found") } else { // In theory the db connection could be lost between // checking user and load but seems unlikely. err_msg := "Err@node_Update:LoadNode: " + err.Error() logger.Error(err_msg) return responder.RespondWithError(ctx, http.StatusInternalServerError, err_msg) } } rights := n.Acl.Check(u.Uuid) if !rights["write"] { return responder.RespondWithError(ctx, http.StatusUnauthorized, e.UnAuth) } if conf.Bool(conf.Conf["perf-log"]) { logger.Perf("START PUT data: " + id) } params, files, err := request.ParseMultipartForm(ctx.HttpRequest()) if err != nil { err_msg := "err@node_ParseMultipartForm: " + err.Error() logger.Error(err_msg) return responder.RespondWithError(ctx, http.StatusBadRequest, err_msg) } if _, hasCopyData := params["copy_data"]; hasCopyData { _, err = node.Load(params["copy_data"], u.Uuid) if err != nil { return request.AuthError(err, ctx) } } if _, hasParentNode := params["parent_node"]; hasParentNode { _, err = node.Load(params["parent_node"], u.Uuid) if err != nil { return request.AuthError(err, ctx) } } err = n.Update(params, files) if err != nil { err_msg := "err@node_Update: " + id + ": " + err.Error() logger.Error(err_msg) return responder.RespondWithError(ctx, http.StatusBadRequest, err_msg) } responder.RespondWithData(ctx, n) if conf.Bool(conf.Conf["perf-log"]) { logger.Perf("END PUT data: " + id) } return nil }