diff --git a/cmd/gokapi/Main.go b/cmd/gokapi/Main.go index 2bbfb05..0d29efe 100644 --- a/cmd/gokapi/Main.go +++ b/cmd/gokapi/Main.go @@ -35,8 +35,8 @@ func main() { configuration.Load() resetPassword(passedFlags) createSsl(passedFlags) - aws.Init() - if aws.IsCredentialProvided(true) { + + if aws.Init() { fmt.Println("Saving new files to cloud storage") } else { fmt.Println("Saving new files to local storage") diff --git a/internal/configuration/Configuration.go b/internal/configuration/Configuration.go index ae5aece..adf7a0e 100644 --- a/internal/configuration/Configuration.go +++ b/internal/configuration/Configuration.go @@ -57,7 +57,6 @@ type Configuration struct { SaltFiles string `json:"SaltFiles"` LengthId int `json:"LengthId"` DataDir string `json:"DataDir"` - AwsBucket string `json:"AwsBucket"` MaxMemory int `json:"MaxMemory"` UseSsl bool `json:"UseSsl"` } @@ -77,7 +76,6 @@ func Load() { err = decoder.Decode(&serverSettings) helper.Check(err) updateConfig() - serverSettings.AwsBucket = Environment.AwsBucket serverSettings.MaxMemory = Environment.MaxMemory helper.CreateDir(serverSettings.DataDir) } diff --git a/internal/configuration/Configuration_test.go b/internal/configuration/Configuration_test.go index f8db30d..797d82f 100644 --- a/internal/configuration/Configuration_test.go +++ b/internal/configuration/Configuration_test.go @@ -11,7 +11,6 @@ import ( func TestMain(m *testing.M) { testconfiguration.Create(false) - os.Setenv("GOKAPI_AWS_BUCKET", "bucket") exitVal := m.Run() testconfiguration.Delete() os.Exit(exitVal) @@ -70,7 +69,6 @@ func TestCreateNewConfig(t *testing.T) { test.IsEqualString(t, serverSettings.RedirectUrl, "http://test2.com") test.IsEqualString(t, serverSettings.AdminPassword, "5bbf5684437a4c658d2e0890d784694afb63f715") test.IsEqualString(t, HashPassword("testtest2", false), "5bbf5684437a4c658d2e0890d784694afb63f715") - test.IsEqualString(t, serverSettings.AwsBucket, "bucket") test.IsEqualInt(t, serverSettings.LengthId, 15) test.IsEqualBool(t, serverSettings.UseSsl, false) os.Remove("test/config.json") @@ -101,7 +99,6 @@ func TestUpgradeDb(t *testing.T) { test.IsEqualBool(t, serverSettings.DownloadStatus == nil, false) test.IsEqualString(t, serverSettings.Files["MgXJLe4XLfpXcL12ec4i"].ContentType, "application/octet-stream") test.IsEqualInt(t, serverSettings.ConfigVersion, currentConfigVersion) - test.IsEqualString(t, serverSettings.AwsBucket, "bucket") test.IsEqualBool(t, serverSettings.UseSsl, true) os.Unsetenv("GOKAPI_USE_SSL") testconfiguration.Create(false) diff --git a/internal/storage/FileServing.go b/internal/storage/FileServing.go index 41c9b40..d77a28a 100644 --- a/internal/storage/FileServing.go +++ b/internal/storage/FileServing.go @@ -45,13 +45,16 @@ func NewFile(fileContent io.Reader, fileHeader *multipart.FileHeader, uploadRequ ContentType: fileHeader.Header.Get("Content-Type"), } addHotlink(&file) + if aws.IsAvailable() { + aws.AddBucketName(&file) + } settings := configuration.GetServerSettings() filename := settings.DataDir + "/" + file.SHA256 dataDir := settings.DataDir - file.AwsBucket = settings.AwsBucket settings.Files[id] = file configuration.ReleaseAndSave() - if aws.IsCredentialProvided(false) { + if aws.IsAvailable() { + aws.AddBucketName(&file) _, err := aws.Upload(reader, file) if err != nil { return models.File{}, err @@ -174,6 +177,8 @@ func ServeFile(file models.File, w http.ResponseWriter, r *http.Request, forceDo defer storageData.Close() if forceDownload { w.Header().Set("Content-Disposition", "attachment; filename=\""+file.Name+"\"") + } else { + w.Header().Set("Content-Disposition", "inline; filename=\""+file.Name+"\"") } w.Header().Set("Content-Length", strconv.FormatInt(size, 10)) w.Header().Set("Content-Type", file.ContentType) @@ -183,7 +188,7 @@ func ServeFile(file models.File, w http.ResponseWriter, r *http.Request, forceDo } else { // If file is stored on AWS downloadstatus.SetDownload(file) - err := aws.RedirectToDownload(w, r, file) + err := aws.RedirectToDownload(w, r, file, forceDownload) helper.Check(err) // We are not setting a download complete status, as there is no reliable way to confirm that the // file has been completely downloaded. It expires automatically after 24 hours. @@ -277,6 +282,12 @@ func DeleteFile(keyId string) bool { } item.ExpireAt = 0 settings.Files[keyId] = item + for _, status := range settings.DownloadStatus { + if status.FileId == item.Id { + status.ExpireAt = 0 + settings.DownloadStatus[status.Id] = status + } + } configuration.Release() CleanUp(false) return true diff --git a/internal/storage/FileServing_test.go b/internal/storage/FileServing_test.go index 19d601b..5bc454d 100644 --- a/internal/storage/FileServing_test.go +++ b/internal/storage/FileServing_test.go @@ -131,7 +131,7 @@ func TestNewFile(t *testing.T) { bigFile.Close() os.Remove("bigfile") - if aws.IsAvailable { + if aws.IsIncludedInBuild { testconfiguration.EnableS3() file, err = NewFile(bytes.NewReader(content), &header, request) test.IsNil(t, err) @@ -158,7 +158,7 @@ func TestServeFile(t *testing.T) { test.IsNil(t, err) test.IsEqualString(t, string(content), "This is a file for testing purposes") - if aws.IsAvailable { + if aws.IsIncludedInBuild { testconfiguration.EnableS3() r = httptest.NewRequest("GET", "/upload", nil) w = httptest.NewRecorder() @@ -236,7 +236,7 @@ func TestCleanUp(t *testing.T) { test.IsEqualString(t, settings.Files["cleanuptest123456789"].Name, "") test.FileDoesNotExist(t, "test/data/2341354656543213246465465465432456898794") - if aws.IsAvailable { + if aws.IsIncludedInBuild { testconfiguration.EnableS3() test.IsEqualString(t, settings.Files["awsTest1234567890123"].Name, "Aws Test File") testconfiguration.DisableS3() @@ -259,7 +259,7 @@ func TestDeleteFile(t *testing.T) { result = DeleteFile("") test.IsEqualBool(t, result, false) - if aws.IsAvailable { + if aws.IsIncludedInBuild { testconfiguration.EnableS3() result, err := aws.FileExists(settings.Files["awsTest1234567890123"]) test.IsEqualBool(t, result, true) diff --git a/internal/storage/cloudstorage/aws/AwsS3.go b/internal/storage/cloudstorage/aws/AwsS3.go index f827ae9..be9fcee 100644 --- a/internal/storage/cloudstorage/aws/AwsS3.go +++ b/internal/storage/cloudstorage/aws/AwsS3.go @@ -4,7 +4,6 @@ package aws import ( "Gokapi/internal/configuration/cloudconfig" - "Gokapi/internal/environment" "Gokapi/internal/models" "fmt" "github.com/aws/aws-sdk-go/aws" @@ -19,51 +18,48 @@ import ( ) var awsConfig models.AwsConfig -var environmentHolder environment.Environment -// IsAvailable is true if Gokapi has been compiled with AWS support or the API is being mocked -const IsAvailable = true +var isCorrectLogin bool + +// IsIncludedInBuild is true if Gokapi has been compiled with AWS support or the API is being mocked +const IsIncludedInBuild = true // IsMockApi is true if the API is being mocked and therefore can only be used for testing purposes const IsMockApi = false -// IsCredentialProvided returns true if all credentials are provided -func IsCredentialProvided(checkIfValid bool) bool { - e := getEnvironment() - isProvided := e.IsAwsProvided() - if !isProvided { - return false - } - if checkIfValid { - return isValidLogin() - } - return true -} - -func getEnvironment() *environment.Environment { - if environmentHolder == (environment.Environment{}) { - environmentHolder = environment.New() - } - return &environmentHolder -} - -// Init reads the credentials for AWS -func Init() { +// Init reads the credentials for AWS. Returns true if valid +func Init() bool { config, ok := cloudconfig.Load() if ok { + fmt.Println("AWS config loaded") awsConfig = config.Aws } + return isValidLogin() +} + +// AddBucketName adds the bucket name to the file to be stored +func AddBucketName(file *models.File) { + file.AwsBucket = awsConfig.Bucket +} + +// IsAvailable returns true if valid credentials have been passed +func IsAvailable() bool { + return isCorrectLogin } func isValidLogin() bool { - sess := createSession() - svc := s3.New(sess) - _, err := svc.Config.Credentials.Get() + if !awsConfig.IsAllProvided() { + return false + } + _, err := FileExists(models.File{AwsBucket: awsConfig.Bucket, SHA256: "invalid"}) if err != nil { - fmt.Println("WARNING: AWS login not successful: " + err.Error()) + fmt.Println("WARNING: AWS login not successful") + fmt.Println(err.Error()) + isCorrectLogin = false return false } fmt.Println("AWS login successful") + isCorrectLogin = true return true } @@ -110,14 +106,21 @@ func Download(writer io.WriterAt, file models.File) (int64, error) { // RedirectToDownload creates a presigned link that is valid for 15 seconds and redirects the // client to this url -func RedirectToDownload(w http.ResponseWriter, r *http.Request, file models.File) error { +func RedirectToDownload(w http.ResponseWriter, r *http.Request, file models.File, forceDownload bool) error { sess := createSession() s3svc := s3.New(sess) + contentDisposition := "inline; filename=\"" + file.Name + "\"" + if forceDownload { + contentDisposition = "Attachment; filename=\"" + file.Name + "\"" + } + req, _ := s3svc.GetObjectRequest(&s3.GetObjectInput{ Bucket: aws.String(file.AwsBucket), Key: aws.String(file.SHA256), - ResponseContentDisposition: aws.String("filename=" + file.Name), + ResponseContentDisposition: aws.String(contentDisposition), + ResponseCacheControl: aws.String("no-store"), + ResponseContentType: aws.String(file.ContentType), }) url, err := req.Presign(15 * time.Second) diff --git a/internal/storage/cloudstorage/aws/AwsS3_mock.go b/internal/storage/cloudstorage/aws/AwsS3_mock.go index ea605a7..ed838f0 100644 --- a/internal/storage/cloudstorage/aws/AwsS3_mock.go +++ b/internal/storage/cloudstorage/aws/AwsS3_mock.go @@ -12,6 +12,7 @@ import ( ) var uploadedFiles []models.File +var isCorrectLogin bool const ( region = "mock-region-1" @@ -20,25 +21,39 @@ const ( accessKey = "accKey" ) -// Init reads the credentials for AWS -func Init() { -} - -// IsAvailable is true if Gokapi has been compiled with AWS support or the API is being mocked -const IsAvailable = true +// IsIncludedInBuild is true if Gokapi has been compiled with AWS support or the API is being mocked +const IsIncludedInBuild = true // IsMockApi is true if the API is being mocked and therefore can only be used for testing purposes const IsMockApi = true +// Init reads the credentials for AWS +func Init() bool { + return isValidCredentials() +} + +// IsAvailable returns true if valid credentials have been passed +func IsAvailable() bool { + return isCorrectLogin +} + + +// AddBucketName adds the bucket name to the file to be stored +func AddBucketName(file *models.File) { + file.AwsBucket = bucketName +} + func isValidCredentials() bool { requiredKeys := []string{"GOKAPI_AWS_BUCKET", "AWS_REGION", "AWS_ACCESS_KEY_ID", "AWS_SECRET_ACCESS_KEY"} requiredValues := []string{bucketName, region, accessId, accessKey} for i, key := range requiredKeys { val, _ := os.LookupEnv(key) if val != requiredValues[i] { + isCorrectLogin = false return false } } + isCorrectLogin = true return true } @@ -93,7 +108,7 @@ func isUploaded(file models.File) bool { // RedirectToDownload creates a presigned link that is valid for 15 seconds and redirects the // client to this url -func RedirectToDownload(w http.ResponseWriter, r *http.Request, file models.File) error { +func RedirectToDownload(w http.ResponseWriter, r *http.Request, file models.File, forceDownload bool) error { if !isValidCredentials() { return errors.New("invalid credentials / invalid bucket / invalid region") } diff --git a/internal/storage/cloudstorage/aws/AwsS3_slim.go b/internal/storage/cloudstorage/aws/AwsS3_slim.go index 2ca4916..5d3bec9 100644 --- a/internal/storage/cloudstorage/aws/AwsS3_slim.go +++ b/internal/storage/cloudstorage/aws/AwsS3_slim.go @@ -11,21 +11,27 @@ import ( const errorString = "AWS not supported in this build" -// IsAvailable is true if Gokapi has been compiled with AWS support or the API is being mocked -const IsAvailable = false +// IsIncludedInBuild is true if Gokapi has been compiled with AWS support or the API is being mocked +const IsIncludedInBuild = false // IsMockApi is true if the API is being mocked and therefore can only be used for testing purposes const IsMockApi = false // Init reads the credentials for AWS -func Init() { +func Init() bool { + return false } -// IsCredentialProvided returns true if all credentials are provided, however does not check them to be valid -func IsCredentialProvided(checkIfValid bool) bool { +// IsAvailable returns true if valid credentials have been passed +func IsAvailable() bool { return false } +// AddBucketName adds the bucket name to the file to be stored +func AddBucketName(file *models.File) { + return +} + // Upload uploads a file to AWS func Upload(input io.Reader, file models.File) (string, error) { return "", errors.New(errorString) @@ -38,7 +44,7 @@ func Download(writer io.WriterAt, file models.File) (int64, error) { // RedirectToDownload creates a presigned link that is valid for 15 seconds and redirects the // client to this url -func RedirectToDownload(w http.ResponseWriter, r *http.Request, file models.File) error { +func RedirectToDownload(w http.ResponseWriter, r *http.Request, file models.File, forceDownload bool) error { return errors.New(errorString) } diff --git a/internal/storage/cloudstorage/aws/AwsS3_test.go b/internal/storage/cloudstorage/aws/AwsS3_test.go index f61ef67..e961132 100644 --- a/internal/storage/cloudstorage/aws/AwsS3_test.go +++ b/internal/storage/cloudstorage/aws/AwsS3_test.go @@ -4,7 +4,6 @@ package aws import ( - "Gokapi/internal/environment" "Gokapi/internal/models" "Gokapi/internal/test" "fmt" @@ -61,7 +60,7 @@ func TestDownloadFromAws(t *testing.T) { func TestRedirectToDownload(t *testing.T) { w := httptest.NewRecorder() r := httptest.NewRequest("GET", "/download", nil) - err := RedirectToDownload(w, r, testFile) + err := RedirectToDownload(w, r, testFile, false) test.IsNil(t, err) fmt.Println(w.Body.String()) test.ResponseBodyContains(t, w, "