working on download container

templ
Johannes Bülow 2023-12-30 15:28:54 +01:00
parent 6be55038ea
commit 6f9c0c176d
Signed by untrusted user who does not match committer: jmb
GPG Key ID: B56971CF7B8F83A6
16 changed files with 335 additions and 58 deletions

View File

@ -12,7 +12,7 @@ clean:
rm -f db.sqlite
templ:
rm -f web/template/*_templ.go
rm -f web/templates/*_templ.go
templ fmt .
templ generate

20
db/attachments.go Normal file
View File

@ -0,0 +1,20 @@
package db
// new creates a new and empty attachment
func (attachment *Attachment) new() (*Attachment, error) {
attachment = &Attachment{}
if err := conn.Create(attachment).Error; err != nil {
return attachment, err
}
return attachment, nil
}
// save saves the (modified) Attachment
func (attachment *Attachment) save() error {
attachment = &Attachment{}
if err := conn.Create(attachment).Error; err != nil {
return err
}
return nil
}

45
db/container.go Normal file
View File

@ -0,0 +1,45 @@
package db
// new creates a new and empty container
func (container *Container) New() (*Container, error) {
container = &Container{}
if err := conn.Create(container).Error; err != nil {
return container, err
}
return container, nil
}
// save saves the (modified) container
func (container *Container) Save() error {
container = &Container{}
if err := conn.Save(container).Error; err != nil {
return err
}
return nil
}
// delete deltes the Container
func (container *Container) Delete() error {
container = &Container{}
if err := conn.Delete(container).Error; err != nil {
return err
}
return nil
}
// ContainerByID gets the Container by its DB ID
func ContainerByID(id uint) *Container {
var container Container
conn.First(&container, id)
return &container
}
// ContainerByFolder retrieves the Container by its folder UUID
func ContainerByFolder(folder string) (*Container, error) {
var container Container
if err := conn.First(&container, folder).Error; err != nil {
return &container, err
}
return &container, nil
}

View File

@ -6,6 +6,40 @@ import (
"strconv"
)
// newWithProperties Creates a new file with the given properties
func (file *File) newWithProperties(name string, url string, comment string, blob string) (*File, error) {
file = &File{
Name: name,
Comment: &comment,
Blob: blob,
Properties: FileProperties{
Url: url,
},
}
if err := conn.Create(file).Error; err != nil {
return file, err
}
return file, nil
}
// creates new empty file
func (file *File) new() (*File,error) {
if err := conn.Create(file).Error; err != nil {
return file, err
}
return file, nil
}
// save writes the given file to the Database
func (file *File) save() error {
if err := conn.Save(file).Error; err != nil {
return err
}
return nil
}
func CreateFile(name string, url string, comment string, blob string) (uint, error) {
file := File{
Name: name,

View File

@ -72,3 +72,15 @@ type FileProperties struct {
Extension string
Url string
}
type Container struct {
gorm.Model
ContainerID string
FileID uint
Status int
Port int
User string
Password string //This has to be stored somewhere in plain text...
ProxyFolder string
Image string
}

View File

@ -16,17 +16,17 @@ import (
// DownloadFile Downloads file from a given URL, stores it in a Minio object. This function also calls the Static
// Analysis function
func DownloadFile(rawURL string, blob string, id uint) error {
func DownloadFile(rawURL string, blob string, id uint) (uint, error) {
ctx := context.Background()
fileURL, err := url.Parse(rawURL)
if err != nil {
log.Println(err)
return err
return 0, err
}
response, err := http.Get(rawURL)
if err != nil {
log.Println(err)
return err
return 0, err
}
defer func(Body io.ReadCloser) {
err := Body.Close()
@ -48,13 +48,13 @@ func DownloadFile(rawURL string, blob string, id uint) error {
minio.PutObjectOptions{ContentType: contentType})
if err != nil {
log.Printf("Could not create File object: %v \n", err)
return err
return 0, err
}
log.Printf("Successfully uploaded %s of size %d\n", blob, objectInfo.Size)
if err != nil {
log.Println(err)
return err
return 0, err
}
go RunStaticAnalysis(id)
return nil
return 0, nil
}

View File

@ -3,30 +3,39 @@ package files
import (
"context"
"log"
"mime/multipart"
"github.com/gin-gonic/gin"
"github.com/minio/minio-go/v7"
"github.com/spf13/viper"
"github.com/spf13/viper"
"github.com/google/uuid"
"git.jmbit.de/filegate/filegate/db"
)
func UploadFile(id uint, blob string, c *gin.Context) error {
func UploadFile(file *multipart.FileHeader, name string, url string, comment string) (uint, error) {
ctx := context.Background()
file, err := c.FormFile("file")
if err != nil {
return err
}
db.UpdateOriginalName(id, file.Filename)
objectInfo, err := MinioClient.PutObject(ctx, viper.GetString("minio.bucket"), blob, c.Request.Body, c.Request.ContentLength,
minio.PutObjectOptions{ContentType: c.ContentType()})
blob := uuid.NewString()
fileReader, err := file.Open()
if err != nil {
log.Printf("Could not create File object: %v \n", err)
return err
return 0, err
}
fileSize := file.Size
fileID, err := db.CreateFile(name, url, comment, blob)
if err != nil {
log.Printf("Could not create File object: %v \n", err)
return fileID, err
}
db.UpdateOriginalName(fileID, file.Filename)
objectInfo, err := MinioClient.PutObject(ctx, viper.GetString("minio.bucket"), blob, fileReader, fileSize,
minio.PutObjectOptions{ContentType: "application/octet-stream"})
if err != nil {
log.Printf("Could not create File object: %v \n", err)
return fileID, err
}
log.Printf("Successfully uploaded %s of size %d\n", blob, objectInfo.Size)
go RunStaticAnalysis(id)
return nil
go RunStaticAnalysis(fileID)
return fileID, nil
}
// GetFile returns a pointer to a File stored in Minio by passing it the UUID ("Blob"-ID) of the file

View File

@ -1,41 +1,116 @@
package pods
import (
"fmt"
"log"
"github.com/containers/podman/v4/pkg/bindings/containers"
"github.com/containers/podman/v4/pkg/bindings/images"
"github.com/containers/podman/v4/pkg/specgen"
"log"
"github.com/google/uuid"
"git.jmbit.de/filegate/filegate/db"
"git.jmbit.de/filegate/filegate/utils"
)
func CreateDownloadContainer(user string, passwd string, folder string) string {
// CreateDownloadContainer() creates Download Container and returns path UUID
func CreateDownloadContainer() (string, error) {
image := "docker.io/linuxserver/firefox:version-116.0-r0"
user := "abc"
folder := uuid.NewString()
passwd, err := utils.RandomString(32)
if err != nil {
log.Printf("Error creating Download Container PW: %v", err)
passwd = ""
}
container, err := initializeDownloadContainer(user, passwd, folder, image)
if err != nil {
return container.ProxyFolder, err
}
container, err = startDownloadContainer(container)
if err != nil {
return container.ProxyFolder, err
}
err = registerDownloadContainer(container)
return container.ProxyFolder, err
}
// DestroyDownloadContainer resolves the Container ID from its folder UUID and destroys it
func DestroyDownloadContainer(folder string) {
}
// initializeDownloadContainer() creates the Container Struct
func initializeDownloadContainer(
user string,
passwd string,
folder string,
image string,
) (*db.Container, error) {
container := &db.Container{
User: user,
Password: passwd,
ProxyFolder: folder,
Image: image,
}
container, err := container.New()
if err != nil {
log.Printf("Error initializing Download Container Object: %v", err)
}
return container, err
}
// startDownloadContainer() takes a Container struct and creates the DownloadContainer for it
func startDownloadContainer(container *db.Container) (*db.Container, error) {
image := "docker.io/linuxserver/firefox:latest"
conn := socketConnection()
_, err := images.Pull(conn, image, nil)
if err != nil {
log.Println(err)
}
s := specgen.NewSpecGenerator(image, false)
s.Env["CUSTOM_USER"] = user
s.Env["PASSWORD"] = passwd
s.Env["CUSTOM_USER"] = container.User
s.Env["PASSWORD"] = container.Password
s.Env["START_DOCKER"] = "False"
s.Env["TITLE"] = "Filegate Download Browser"
s.Env["SUBFOLDER"] = folder
s.Env["PUID"] = "1000"
s.Env["GUID"] = "1000"
// Use the Containers ID as port offset
// TODO: Use better networking to access Download Container
s.Env["CUSTOM_PORT"] = fmt.Sprintf("%d", 3000+container.ID)
s.Env["SUBFOLDER"] = fmt.Sprintf("/browser/%s/", container.ProxyFolder)
createResponse, err := containers.CreateWithSpec(conn, s, nil)
if err != nil {
log.Println(err)
return container, err
}
if err := containers.Start(conn, createResponse.ID, nil); err != nil {
log.Println(err)
return container, err
}
return createResponse.ID
container.ContainerID = createResponse.ID
return container, nil
}
func DestroyDownloadContainer(id string) {
conn := socketConnection()
if err := containers.Kill(conn, id, nil); err != nil {
log.Println(err)
}
if err, _ := containers.Remove(conn, id, nil); err != nil {
log.Println(err)
}
// registerDownloadContainer() saves the Container to the Database
func registerDownloadContainer(container *db.Container) error {
err := container.Save()
return err
}
// removeDownloadContainer() removes the Container from Podman and the Database
func removeDownloadContainer(container *db.Container) error {
conn := socketConnection()
if err := containers.Kill(conn, container.ContainerID, nil); err != nil {
log.Println(err)
return err
}
if _, err := containers.Remove(conn, container.ContainerID, nil); err != nil {
log.Println(err)
return err
}
container.Delete()
return nil
}

View File

@ -1,4 +1,4 @@
package files
package service
import (
"crypto/md5"
@ -14,6 +14,7 @@ import (
"github.com/spf13/viper"
"git.jmbit.de/filegate/filegate/db"
"git.jmbit.de/filegate/filegate/files"
)
func mimeType(path string) string {
@ -79,14 +80,19 @@ func fileSize(path string) int64 {
func RunStaticAnalysis(id uint) {
file := db.GetFileByID(id)
filepath := fmt.Sprintf("%s/%d/%s", viper.GetString("tempfiles"), file.ID, file.Properties.OriginalName)
filepath := fmt.Sprintf(
"%s/%d/%s",
viper.GetString("tempfiles"),
file.ID,
file.Properties.OriginalName,
)
err := os.Mkdir(fmt.Sprintf("%s/%d", viper.GetString("tempfiles"), file.ID), 700)
if err != nil {
log.Printf("Error Creating analysis directory, %v", err)
return
}
fileObject, err := GetFile(file.Blob)
fileObject, err := files.GetFile(file.Blob)
if err != nil {
log.Printf("Error getting file from S3, %v", err)
fileObject.Close()
@ -113,5 +119,14 @@ func RunStaticAnalysis(id uint) {
fileSize := fileSize(filepath)
md5Sum, sha1Sum, sha256Sum := fileChecksums(filepath)
db.SetSimpleAttributes(id, mimeType, fileSize, sha256Sum, sha1Sum, md5Sum, fileCmd, fileExtension)
db.SetSimpleAttributes(
id,
mimeType,
fileSize,
sha256Sum,
sha1Sum,
md5Sum,
fileCmd,
fileExtension,
)
}

View File

@ -9,13 +9,13 @@ import (
func reverseProxy(c *gin.Context, target string) {
director := func(req *http.Request) {
r := c.Request
//r := c.Request
req.URL.Scheme = "http"
req.URL.Host = target
req.Header["my-header"] = []string{r.Header.Get("my-header")}
// Golang camelcases headers
delete(req.Header, "My-Header")
//req.Header["my-header"] = []string{r.Header.Get("my-header")}
//// Golang camelcases headers
//delete(req.Header, "My-Header")
}
proxy := &httputil.ReverseProxy{Director: director}
proxy.ServeHTTP(c.Writer, c.Request)

View File

@ -1,2 +0,0 @@
package templates

View File

@ -1,8 +0,0 @@
// Code generated by templ - DO NOT EDIT.
// templ: version: 0.2.476
package templates
//lint:file-ignore SA4006 This context is only used if a nested component is present.
import "github.com/a-h/templ"

14
web/ui/browser.go Normal file
View File

@ -0,0 +1,14 @@
package ui
import (
"net/http"
"github.com/gin-gonic/gin"
"git.jmbit.de/filegate/filegate/utils"
"git.jmbit.de/filegate/filegate/web/templates"
)
func getBrowser(c *gin.Context) {
c.HTML(http.StatusOK, "", templates.Index(utils.GenMetaContent(c), nil))
}

View File

@ -25,7 +25,7 @@ func postLogin(c *gin.Context) {
metaContent := utils.GenMetaContent(c)
metaContent.ErrorTitle = "Error"
metaContent.ErrorText = err.Error()
c.HTML(http.StatusUnauthorized, "", templates.Login(metaContent, "Login"))
c.HTML(http.StatusUnauthorized, "", templates.Login(metaContent, "Login", err))
log.Println(err)
return
} else {

View File

@ -1,10 +1,13 @@
package ui
import (
"fmt"
"log"
"net/http"
"github.com/gin-gonic/gin"
"git.jmbit.de/filegate/filegate/files"
"git.jmbit.de/filegate/filegate/utils"
"git.jmbit.de/filegate/filegate/web/templates"
)
@ -13,6 +16,62 @@ func getNewFile(c *gin.Context) {
c.HTML(http.StatusOK, "", templates.NewFilePage(utils.GenMetaContent(c), "New File", nil))
}
func postNewFile(c *gin.Context) {
c.HTML(http.StatusOK, "", templates.NewFilePage(utils.GenMetaContent(c), "New File", nil))
func postNewFileUpload(c *gin.Context) {
name := c.PostForm("name")
url := "N/A"
comment := c.PostForm("comment")
file, err := c.FormFile("file")
if err != nil {
newFileError(c, err)
return
}
fileid, err := files.UploadFile(file, name, url, comment)
if err != nil {
newFileError(c, err)
return
}
c.Redirect(http.StatusTemporaryRedirect, fmt.Sprintf("/file/%d", fileid))
}
func postNewFileDownload(c *gin.Context) {
name := c.PostForm("name")
url := c.PostForm("url")
comment := c.PostForm("comment")
file, err := c.FormFile("file")
if err != nil {
newFileError(c, err)
return
}
fileid, err := files.UploadFile(file, name, url, comment)
if err != nil {
newFileError(c, err)
return
}
c.Redirect(http.StatusTemporaryRedirect, fmt.Sprintf("/file/%d", fileid))
}
func postNewFileBrowser(c *gin.Context) {
name := c.PostForm("name")
url := c.PostForm("url")
comment := c.PostForm("comment")
file, err := c.FormFile("file")
if err != nil {
newFileError(c, err)
return
}
fileid, err := files.UploadFile(file, name, url, comment)
if err != nil {
newFileError(c, err)
return
}
c.Redirect(http.StatusTemporaryRedirect, fmt.Sprintf("/file/%d", fileid))
}
func newFileError(c *gin.Context, err error) {
metaContent := utils.GenMetaContent(c)
metaContent.ErrorTitle = "Error"
metaContent.ErrorText = err.Error()
c.HTML(http.StatusOK, "", templates.NewFilePage(utils.GenMetaContent(c), "New File", err))
log.Println(err)
return
}

View File

@ -10,9 +10,13 @@ func GroupWeb(router *gin.Engine) *gin.Engine {
router.POST("/", index)
router.GET("/login.html", getLogin)
router.POST("/login.html", postLogin)
router.GET("/file/", getFileListPage)
router.GET("/file/new/", getNewFile)
router.POST("/file/new/", postNewFile)
file := router.Group("/file/")
file.GET("/", getFileListPage)
file.GET("/new/", getNewFile)
file.POST("/new/upload", postNewFileUpload)
file.POST("/new/download", postNewFileDownload)
file.POST("/new/browser", postNewFileBrowser)
router.GET("/browser/:id", getBrowser)
return router
}