Example #1
0
func PreAuthRequest(ctx context.Context) {
	id := ctx.PathValue("id")
	if p, err := preauth.Load(id); err != nil {
		err_msg := "err:@preAuth load: " + err.Error()
		logger.Error(err_msg)
		responder.RespondWithError(ctx, 500, err_msg)
		return
	} else {
		if n, err := node.LoadUnauth(p.NodeId); err == nil {
			switch p.Type {
			case "download":
				filename := n.Id
				if fn, has := p.Options["filename"]; has {
					filename = fn
				}
				streamDownload(ctx, n, filename)
				preauth.Delete(id)
				return
			default:
				responder.RespondWithError(ctx, 500, "Preauthorization type not supported: "+p.Type)
			}
		} else {
			err_msg := "err:@preAuth loadnode: " + err.Error()
			logger.Error(err_msg)
			responder.RespondWithError(ctx, 500, err_msg)
		}
	}
	return
}
Example #2
0
File: acl.go Project: MG-RAST/Shock
// GET, POST, PUT, DELETE: /node/{nid}/acl/
// GET is the only action implemented here.
func AclRequest(ctx context.Context) {
	nid := ctx.PathValue("nid")
	rmeth := ctx.HttpRequest().Method

	u, err := request.Authenticate(ctx.HttpRequest())
	if err != nil && err.Error() != e.NoAuth {
		request.AuthError(err, ctx)
		return
	}

	// public user (no auth) can perform a GET operation with the proper node permissions
	if u == nil {
		if rmeth == "GET" && conf.ANON_READ {
			u = &user.User{Uuid: "public"}
		} else {
			responder.RespondWithError(ctx, http.StatusUnauthorized, e.NoAuth)
			return
		}
	}

	// Load node by id
	n, err := node.Load(nid)
	if err != nil {
		if err == mgo.ErrNotFound {
			responder.RespondWithError(ctx, http.StatusNotFound, e.NodeNotFound)
			return
		} else {
			// In theory the db connection could be lost between
			// checking user and load but seems unlikely.
			err_msg := "Err@node_Acl:LoadNode: " + nid + err.Error()
			logger.Error(err_msg)
			responder.RespondWithError(ctx, http.StatusInternalServerError, err_msg)
			return
		}
	}

	// Only the owner, an admin, or someone with read access can view acl's.
	//
	// NOTE: If the node is publicly owned, then anyone can view all acl's. The owner can only
	//       be "public" when anonymous node creation (ANON_WRITE) is enabled in Shock config.

	rights := n.Acl.Check(u.Uuid)
	if n.Acl.Owner != u.Uuid && u.Admin == false && n.Acl.Owner != "public" && rights["read"] == false {
		responder.RespondWithError(ctx, http.StatusUnauthorized, e.UnAuth)
		return
	}

	if rmeth == "GET" {
		query := ctx.HttpRequest().URL.Query()
		verbosity := ""
		if _, ok := query["verbosity"]; ok {
			verbosity = query.Get("verbosity")
		}
		responder.RespondWithData(ctx, n.Acl.FormatDisplayAcl(verbosity))
	} else {
		responder.RespondWithError(ctx, http.StatusNotImplemented, "This request type is not implemented.")
	}
	return
}
Example #3
0
File: acl.go Project: MG-RAST/Shock
// GET, POST, PUT, DELETE: /node/{nid}/acl/{type}
func AclTypedRequest(ctx context.Context) {
	nid := ctx.PathValue("nid")
	rtype := ctx.PathValue("type")
	rmeth := ctx.HttpRequest().Method
	query := ctx.HttpRequest().URL.Query()
	verbosity := ""
	if _, ok := query["verbosity"]; ok {
		verbosity = query.Get("verbosity")
	}

	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
	}

	// Load node by id
	n, err := node.Load(nid)
	if err != nil {
		if err == mgo.ErrNotFound {
			responder.RespondWithError(ctx, http.StatusNotFound, e.NodeNotFound)
			return
		} else {
			// In theory the db connection could be lost between
			// checking user and load but seems unlikely.
			err_msg := "Err@node_Acl:LoadNode: " + nid + err.Error()
			logger.Error(err_msg)
			responder.RespondWithError(ctx, http.StatusInternalServerError, err_msg)
			return
		}
	}

	// public user (no auth) can perform a GET operation given the proper node permissions
	if u == nil {
		rights := n.Acl.Check("public")
		if rmeth == "GET" && conf.ANON_READ && (rights["read"] || n.Acl.Owner == "public") {
			responder.RespondWithData(ctx, n.Acl.FormatDisplayAcl(verbosity))
			return
		} else {
			responder.RespondWithError(ctx, http.StatusUnauthorized, e.NoAuth)
			return
		}
	}

	// Users that are not an admin or the node owner can only delete themselves from an ACL.
	if n.Acl.Owner != u.Uuid && u.Admin == false {
		// Users that are not an admin or the node owner cannot remove public from ACL's.
		if rtype == "public_read" || rtype == "public_write" || rtype == "public_delete" || rtype == "public_all" {
			responder.RespondWithError(ctx, http.StatusBadRequest, "Users that are not node owners can only delete themselves from ACLs.")
			return
		}

		// Parse user list
		ids, err := parseAclRequestTyped(ctx)
		if err != nil {
			responder.RespondWithError(ctx, http.StatusBadRequest, err.Error())
			return
		}
		if rmeth == "DELETE" {
			if len(ids) != 1 || (len(ids) == 1 && ids[0] != u.Uuid) {
				responder.RespondWithError(ctx, http.StatusBadRequest, "Users that are not node owners can delete only themselves from ACLs.")
				return
			}
			if rtype == "owner" {
				responder.RespondWithError(ctx, http.StatusBadRequest, "Deleting node ownership is not a supported request type.")
				return
			}
			if rtype == "all" {
				n.Acl.UnSet(ids[0], map[string]bool{"read": true, "write": true, "delete": true})
			} else {
				n.Acl.UnSet(ids[0], map[string]bool{rtype: true})
			}
			n.Save()
			responder.RespondWithData(ctx, n.Acl.FormatDisplayAcl(verbosity))
			return
		}
		responder.RespondWithError(ctx, http.StatusBadRequest, "Users that are not node owners can only delete themselves from ACLs.")
		return
	}

	// At this point we know we're dealing with an admin or the node owner.
	// Admins and node owners can view/edit/delete ACLs
	if rmeth == "GET" {
		responder.RespondWithData(ctx, n.Acl.FormatDisplayAcl(verbosity))
		return
	} else if rmeth == "POST" || rmeth == "PUT" {
		if rtype == "public_read" || rtype == "public_write" || rtype == "public_delete" || rtype == "public_all" {
			if rtype == "public_read" {
				n.Acl.Set("public", map[string]bool{"read": true})
			} else if rtype == "public_write" {
				n.Acl.Set("public", map[string]bool{"write": true})
			} else if rtype == "public_delete" {
				n.Acl.Set("public", map[string]bool{"delete": true})
			} else if rtype == "public_all" {
				n.Acl.Set("public", map[string]bool{"read": true, "write": true, "delete": true})
			}
			n.Save()
			responder.RespondWithData(ctx, n.Acl.FormatDisplayAcl(verbosity))
			return
		}

		// Parse user list
		ids, err := parseAclRequestTyped(ctx)
		if err != nil {
			responder.RespondWithError(ctx, http.StatusBadRequest, err.Error())
			return
		}
		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 _, i := range ids {
				n.Acl.Set(i, map[string]bool{"read": true, "write": true, "delete": true})
			}
		} else {
			for _, i := range ids {
				n.Acl.Set(i, map[string]bool{rtype: true})
			}
		}
		n.Save()
		responder.RespondWithData(ctx, n.Acl.FormatDisplayAcl(verbosity))
		return
	} else if rmeth == "DELETE" {
		if rtype == "public_read" || rtype == "public_write" || rtype == "public_delete" || rtype == "public_all" {
			if rtype == "public_read" {
				n.Acl.UnSet("public", map[string]bool{"read": true})
			} else if rtype == "public_write" {
				n.Acl.UnSet("public", map[string]bool{"write": true})
			} else if rtype == "public_delete" {
				n.Acl.UnSet("public", map[string]bool{"delete": true})
			} else if rtype == "public_all" {
				n.Acl.UnSet("public", map[string]bool{"read": true, "write": true, "delete": true})
			}
			n.Save()
			responder.RespondWithData(ctx, n.Acl.FormatDisplayAcl(verbosity))
			return
		}

		// Parse user list
		ids, err := parseAclRequestTyped(ctx)
		if err != nil {
			responder.RespondWithError(ctx, http.StatusBadRequest, err.Error())
			return
		}
		if rtype == "owner" {
			responder.RespondWithError(ctx, http.StatusBadRequest, "Deleting ownership is not a supported request type.")
			return
		} else if rtype == "all" {
			for _, i := range ids {
				n.Acl.UnSet(i, map[string]bool{"read": true, "write": true, "delete": true})
			}
		} else {
			for _, i := range ids {
				n.Acl.UnSet(i, map[string]bool{rtype: true})
			}
		}
		n.Save()
		responder.RespondWithData(ctx, n.Acl.FormatDisplayAcl(verbosity))
		return
	} else {
		responder.RespondWithError(ctx, http.StatusNotImplemented, "This request type is not implemented.")
		return
	}
}
Example #4
0
// 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
}