Fixed AWS upload / download

This commit is contained in:
Marc Ole Bulling
2021-05-16 23:34:29 +02:00
parent 19e542c436
commit e42586fcd6
9 changed files with 90 additions and 86 deletions

View File

@@ -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")

View File

@@ -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)
}

View File

@@ -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)

View File

@@ -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

View File

@@ -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)

View File

@@ -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)

View File

@@ -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")
}

View File

@@ -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)
}

View File

@@ -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, "<a href=\"https://")
@@ -95,28 +94,3 @@ func TestDeleteObject(t *testing.T) {
test.IsEqualBool(t, result, true)
test.IsNil(t, err)
}
func TestIsCredentialProvided(t *testing.T) {
os.Unsetenv("GOKAPI_AWS_REGION")
os.Unsetenv("GOKAPI_AWS_KEY")
os.Unsetenv("GOKAPI_KEY_SECRET")
os.Unsetenv("GOKAPI_AWS_BUCKET")
environmentHolder = environment.Environment{}
test.IsEqualBool(t, IsCredentialProvided(false), false)
environmentHolder = environment.Environment{}
os.Setenv("GOKAPI_AWS_REGION", "valid")
environmentHolder = environment.Environment{}
test.IsEqualBool(t, IsCredentialProvided(false), false)
environmentHolder = environment.Environment{}
os.Setenv("GOKAPI_AWS_KEY", "valid")
test.IsEqualBool(t, IsCredentialProvided(false), false)
test.IsEqualBool(t, IsCredentialProvided(true), false)
environmentHolder = environment.Environment{}
os.Setenv("GOKAPI_AWS_KEY_SECRET", "valid")
test.IsEqualBool(t, IsCredentialProvided(false), false)
test.IsEqualBool(t, IsCredentialProvided(true), false)
environmentHolder = environment.Environment{}
os.Setenv("GOKAPI_AWS_BUCKET", "valid")
test.IsEqualBool(t, IsCredentialProvided(false), true)
test.IsEqualBool(t, IsCredentialProvided(true), true)
}