diff --git a/cmd/server.go b/cmd/server.go index 8c85c28..fb4669c 100644 --- a/cmd/server.go +++ b/cmd/server.go @@ -5,7 +5,9 @@ package cmd import ( "context" + "crypto/subtle" "fmt" + "log" "embed" "html/template" @@ -69,6 +71,14 @@ func init() { // server.documents.buckets - --server-documents-buckets serverCmd.Flags().StringSlice("server-documents-buckets", nil, "Buckets that are allowed to be accessed by the API (config: server.documents.buckets)") viper.BindPFlag("server.documents.buckets", serverCmd.Flags().Lookup("server-documents-buckets")) + + // server.api.auth - --server-api-auth + serverCmd.Flags().Bool("server-api-auth", false, "Enable to allow key authentication for /v1 routes (config: server.api.auth)") + viper.BindPFlag("server.api.auth", serverCmd.Flags().Lookup("server-api-auth")) + + // server.api.key - --server-api-key + serverCmd.Flags().String("server-api-key", "", "Key to use for authenticating to /v1 routes") + viper.BindPFlag("server.api.key", serverCmd.Flags().Lookup("server-api-key")) } func RunServer() { @@ -82,11 +92,29 @@ func RunServer() { e.Pre(middleware.RemoveTrailingSlash()) + groupV1 := e.Group("/v1") + + groupV1.Use(middleware.AddTrailingSlash()) + + if viper.GetBool("server.api.auth") { + if len(viper.GetString("server.api.key")) < 10 { + log.Fatal("server.api.auth is enabled, but server.api.key is too small (needs at least 10 characters)") + } + + groupV1.Use(middleware.KeyAuth(func(key string, c echo.Context) (bool, error) { + return subtle.ConstantTimeCompare([]byte(key), []byte(viper.GetString("server.api.key"))) == 1, nil + })) + } + // API Routes - e.GET("/v1", handleV1) + groupV1.GET("", handleV1) - e.GET("/v1/bucket", handleV1Bucket) + groupV1.POST("/seed", handleV1Seed) + + groupV1.GET("/bucket", handleV1BucketList) + + groupV1.GET("/bucket/:bucket", handleV1BucketRead) // Static Routes @@ -130,8 +158,45 @@ func handleV1(c echo.Context) error { return c.JSON(http.StatusOK, routes) } -// handleV1Bucket affiche les buckets permis par server.documents.buckets, qui existent. -func handleV1Bucket(c echo.Context) error { +// handleV1Seed créé des buckets dans minio selon la liste de buckets dans server.documents.buckets +// Les buckets sont créés avec paramètres par défaut, et sont ensuite visible dans /v1/bucket. +func handleV1Seed(c echo.Context) error { + documents_buckets := viper.GetStringSlice("server.documents.buckets") + documents_endpoint := viper.GetString("server.documents.endpoint") + documents_access_key_id := viper.GetString("server.documents.access_key_id") + documents_secret_access_key := viper.GetString("server.documents.secret_access_key") + documents_use_ssl := viper.GetBool("server.documents.use_ssl") + + // Initialize minio client object + client, err := minio.New(documents_endpoint, &minio.Options{ + Creds: credentials.NewStaticV4(documents_access_key_id, documents_secret_access_key, ""), + Secure: documents_use_ssl, + }) + if err != nil { + return c.JSON(http.StatusInternalServerError, map[string]string{ + "message": "Error during minio#New", + }) + } + + var new_buckets []string + + for _, bucket := range documents_buckets { + if err = client.MakeBucket(context.Background(), bucket, minio.MakeBucketOptions{}); err != nil { + return c.JSON(http.StatusInternalServerError, map[string]string{ + "message": "Error during minio#MakeBucket", + }) + } + new_buckets = append(new_buckets, bucket) + } + + return c.JSON(http.StatusOK, map[string]interface{}{ + "message": "Buckets successfully created", + "buckets": new_buckets, + }) +} + +// handleV1BucketList affiche les buckets permis par server.documents.buckets, qui existent. +func handleV1BucketList(c echo.Context) error { documents_buckets := viper.GetStringSlice("server.documents.buckets") documents_endpoint := viper.GetString("server.documents.endpoint") documents_access_key_id := viper.GetString("server.documents.access_key_id") @@ -165,6 +230,54 @@ func handleV1Bucket(c echo.Context) error { return c.JSON(http.StatusOK, buckets) } +func handleV1BucketRead(c echo.Context) error { + documents_endpoint := viper.GetString("server.documents.endpoint") + documents_access_key_id := viper.GetString("server.documents.access_key_id") + documents_secret_access_key := viper.GetString("server.documents.secret_access_key") + documents_use_ssl := viper.GetBool("server.documents.use_ssl") + + bucket := c.Param("bucket") + + ctx, cancel := context.WithCancel(context.Background()) + + defer cancel() + + // Initialize minio client object + client, err := minio.New(documents_endpoint, &minio.Options{ + Creds: credentials.NewStaticV4(documents_access_key_id, documents_secret_access_key, ""), + Secure: documents_use_ssl, + }) + if err != nil { + return c.JSON(http.StatusInternalServerError, map[string]string{ + "message": "Error during minio#New", + }) + } + + exists, err := client.BucketExists(ctx, bucket) + if err != nil { + return c.JSON(http.StatusInternalServerError, "Error during minio#BucketExists") + } + + if !exists { + return c.JSON(http.StatusNotFound, map[string]string{"message": "Not found"}) + } + + var keys []string + + objectCh := client.ListObjects(ctx, bucket, minio.ListObjectsOptions{}) + for object := range objectCh { + if object.Err != nil { + return c.JSON(http.StatusInternalServerError, map[string]string{ + "message": "Error during minio#ListObjects", + }) + } + + keys = append(keys, object.Key) + } + + return c.JSON(http.StatusOK, keys) +} + // HTML Handlers func handleIndex(c echo.Context) error {