Refactoring, enhancing unit tests

This commit is contained in:
Marc Ole Bulling
2021-04-30 12:46:36 +02:00
parent f92a0f1776
commit d635d62268
25 changed files with 698 additions and 398 deletions

View File

@@ -1,6 +1,6 @@
# Gokapi
[![Go Report Card](https://goreportcard.com/badge/github.com/forceu/gokapi)](https://goreportcard.com/report/github.com/forceu/gokapi)
<a href='https://github.com/jpoles1/gopherbadger' target='_blank'>![gopherbadger-tag-do-not-edit](https://img.shields.io/badge/Go%20Coverage-87%25-brightgreen.svg?longCache=true&style=flat)</a>
<a href='https://github.com/jpoles1/gopherbadger' target='_blank'>![gopherbadger-tag-do-not-edit](https://img.shields.io/badge/Go%20Coverage-81%25-brightgreen.svg?longCache=true&style=flat)</a>
[![Docker Pulls](https://img.shields.io/docker/pulls/f0rc3/gokapi.svg)](https://hub.docker.com/r/f0rc3/gokapi/)

View File

@@ -18,9 +18,9 @@ import (
// Version is the current version in readable form.
// The go generate call below needs to be modified as well
const Version = "1.1.4-dev"
const Version = "1.2.0-dev"
//go:generate sh "../../build/setVersionTemplate.sh" "1.1.4-dev"
//go:generate sh "../../build/setVersionTemplate.sh" "1.2.0-dev"
// Main routine that is called on startup
func main() {

View File

@@ -1,7 +1,7 @@
package main
import (
"Gokapi/pkg/test"
"Gokapi/internal/test"
"os"
"testing"
)

View File

@@ -7,14 +7,12 @@ Loading and saving of the persistent configuration
import (
"Gokapi/internal/environment"
"Gokapi/internal/helper"
"Gokapi/internal/storage/filestructure"
"Gokapi/internal/webserver/sessionstructure"
"Gokapi/internal/models"
"crypto/sha1"
"encoding/hex"
"encoding/json"
"fmt"
"golang.org/x/crypto/ssh/terminal"
"log"
"os"
"strconv"
"strings"
@@ -35,30 +33,31 @@ var Environment environment.Environment
var ServerSettings Configuration
// Version of the configuration structure. Used for upgrading
const currentConfigVersion = 5
const currentConfigVersion = 6
// For locking the Files variable
var mutex sync.Mutex
// For locking this object to prevent race conditions
var mutexSessions sync.Mutex
// Configuration is a struct that contains the global configuration
type Configuration struct {
Port string `json:"Port"`
AdminName string `json:"AdminName"`
AdminPassword string `json:"AdminPassword"`
ServerUrl string `json:"ServerUrl"`
DefaultDownloads int `json:"DefaultDownloads"`
DefaultExpiry int `json:"DefaultExpiry"`
DefaultPassword string `json:"DefaultPassword"`
RedirectUrl string `json:"RedirectUrl"`
Sessions map[string]sessionstructure.Session `json:"Sessions"`
Files map[string]filestructure.File `json:"Files"`
Hotlinks map[string]filestructure.Hotlink `json:"Hotlinks"`
DownloadStatus map[string]filestructure.DownloadStatus `json:"DownloadStatus"`
ConfigVersion int `json:"ConfigVersion"`
SaltAdmin string `json:"SaltAdmin"`
SaltFiles string `json:"SaltFiles"`
LengthId int `json:"LengthId"`
DataDir string `json:"DataDir"`
Port string `json:"Port"`
AdminName string `json:"AdminName"`
AdminPassword string `json:"AdminPassword"`
ServerUrl string `json:"ServerUrl"`
DefaultDownloads int `json:"DefaultDownloads"`
DefaultExpiry int `json:"DefaultExpiry"`
DefaultPassword string `json:"DefaultPassword"`
RedirectUrl string `json:"RedirectUrl"`
Sessions map[string]models.Session `json:"Sessions"`
Files map[string]models.File `json:"Files"`
Hotlinks map[string]models.Hotlink `json:"Hotlinks"`
DownloadStatus map[string]models.DownloadStatus `json:"DownloadStatus"`
ApiKeys map[string]models.ApiKey `json:"ApiKeys"`
ConfigVersion int `json:"ConfigVersion"`
SaltAdmin string `json:"SaltAdmin"`
SaltFiles string `json:"SaltFiles"`
LengthId int `json:"LengthId"`
DataDir string `json:"DataDir"`
}
// Load loads the configuration or creates the folder structure and a default configuration
@@ -69,29 +68,25 @@ func Load() {
generateDefaultConfig()
}
file, err := os.Open(Environment.ConfigPath)
if err != nil {
log.Fatal(err)
}
helper.Check(err)
defer file.Close()
decoder := json.NewDecoder(file)
ServerSettings = Configuration{}
err = decoder.Decode(&ServerSettings)
if err != nil {
log.Fatal(err)
}
helper.Check(err)
updateConfig()
helper.CreateDir(ServerSettings.DataDir)
}
// Lock locks configuration to prevent race conditions (blocking)
func Lock() {
mutex.Lock()
// LockSessions locks sessions to prevent race conditions (blocking)
func LockSessions() {
mutexSessions.Lock()
}
// UnlockAndSave unlocks and saves the configuration
func UnlockAndSave() {
// UnlockSessionsAndSave unlocks sessions and saves the configuration
func UnlockSessionsAndSave() {
Save()
mutex.Unlock()
mutexSessions.Unlock()
}
// Upgrades the ServerSettings if saved with a previous version
@@ -105,18 +100,21 @@ func updateConfig() {
}
// < v1.1.3
if ServerSettings.ConfigVersion < 4 {
ServerSettings.Hotlinks = make(map[string]filestructure.Hotlink)
ServerSettings.Hotlinks = make(map[string]models.Hotlink)
}
// < v1.1.4
if ServerSettings.ConfigVersion < 5 {
ServerSettings.LengthId = 15
ServerSettings.DownloadStatus = make(map[string]filestructure.DownloadStatus)
ServerSettings.DownloadStatus = make(map[string]models.DownloadStatus)
for _, file := range ServerSettings.Files {
file.ContentType = "application/octet-stream"
ServerSettings.Files[file.Id] = file
}
}
// < v1.2.0
if ServerSettings.ConfigVersion < 6 {
ServerSettings.ApiKeys = make(map[string]models.ApiKey)
}
if ServerSettings.ConfigVersion < currentConfigVersion {
fmt.Println("Successfully upgraded database")
@@ -158,9 +156,11 @@ func generateDefaultConfig() {
DefaultDownloads: 1,
DefaultExpiry: 14,
RedirectUrl: redirect,
Files: make(map[string]filestructure.File),
Sessions: make(map[string]sessionstructure.Session),
Hotlinks: make(map[string]filestructure.Hotlink),
Files: make(map[string]models.File),
Sessions: make(map[string]models.Session),
Hotlinks: make(map[string]models.Hotlink),
ApiKeys: make(map[string]models.ApiKey),
DownloadStatus: make(map[string]models.DownloadStatus),
ConfigVersion: currentConfigVersion,
SaltAdmin: saltAdmin,
SaltFiles: saltFiles,

View File

@@ -3,27 +3,48 @@ package configuration
import (
"Gokapi/internal/environment"
testconfiguration "Gokapi/internal/test"
"Gokapi/pkg/test"
testconfiguration2 "Gokapi/internal/test/testconfiguration"
"os"
"testing"
"time"
)
func TestMain(m *testing.M) {
testconfiguration.Create(false)
testconfiguration2.Create(false)
exitVal := m.Run()
testconfiguration.Delete()
testconfiguration2.Delete()
os.Exit(exitVal)
}
func TestLoad(t *testing.T) {
Load()
test.IsEqualString(t, Environment.ConfigDir, "test")
test.IsEqualString(t, ServerSettings.Port, "127.0.0.1:53843")
test.IsEqualString(t, ServerSettings.AdminName, "test")
test.IsEqualString(t, ServerSettings.ServerUrl, "http://127.0.0.1:53843/")
test.IsEqualString(t, ServerSettings.AdminPassword, "10340aece68aa4fb14507ae45b05506026f276cf")
test.IsEqualString(t, HashPassword("testtest", false), "10340aece68aa4fb14507ae45b05506026f276cf")
test.IsEqualInt(t, ServerSettings.LengthId, 20)
testconfiguration.IsEqualString(t, Environment.ConfigDir, "test")
testconfiguration.IsEqualString(t, ServerSettings.Port, "127.0.0.1:53843")
testconfiguration.IsEqualString(t, ServerSettings.AdminName, "test")
testconfiguration.IsEqualString(t, ServerSettings.ServerUrl, "http://127.0.0.1:53843/")
testconfiguration.IsEqualString(t, ServerSettings.AdminPassword, "10340aece68aa4fb14507ae45b05506026f276cf")
testconfiguration.IsEqualString(t, HashPassword("testtest", false), "10340aece68aa4fb14507ae45b05506026f276cf")
testconfiguration.IsEqualInt(t, ServerSettings.LengthId, 20)
}
func TestMutex(t *testing.T) {
finished := make(chan bool)
oldValue := ServerSettings.ConfigVersion
go func() {
time.Sleep(100 * time.Millisecond)
LockSessions()
testconfiguration.IsEqualInt(t, ServerSettings.ConfigVersion, -9)
ServerSettings.ConfigVersion = oldValue
UnlockSessionsAndSave()
testconfiguration.IsEqualInt(t, ServerSettings.ConfigVersion, oldValue)
finished <- true
}()
LockSessions()
ServerSettings.ConfigVersion = -9
time.Sleep(150 * time.Millisecond)
testconfiguration.IsEqualInt(t, ServerSettings.ConfigVersion, -9)
UnlockSessionsAndSave()
<-finished
}
func TestCreateNewConfig(t *testing.T) {
@@ -36,112 +57,121 @@ func TestCreateNewConfig(t *testing.T) {
os.Setenv("GOKAPI_SALT_ADMIN", "salt123")
os.Setenv("GOKAPI_LOCALHOST", "false")
Load()
test.IsEqualString(t, Environment.ConfigDir, "test")
test.IsEqualString(t, ServerSettings.Port, ":1234")
test.IsEqualString(t, ServerSettings.AdminName, "test2")
test.IsEqualString(t, ServerSettings.ServerUrl, "http://test.com/")
test.IsEqualString(t, ServerSettings.RedirectUrl, "http://test2.com")
test.IsEqualString(t, ServerSettings.AdminPassword, "5bbf5684437a4c658d2e0890d784694afb63f715")
test.IsEqualString(t, HashPassword("testtest2", false), "5bbf5684437a4c658d2e0890d784694afb63f715")
test.IsEqualInt(t, ServerSettings.LengthId, 15)
testconfiguration.IsEqualString(t, Environment.ConfigDir, "test")
testconfiguration.IsEqualString(t, ServerSettings.Port, ":1234")
testconfiguration.IsEqualString(t, ServerSettings.AdminName, "test2")
testconfiguration.IsEqualString(t, ServerSettings.ServerUrl, "http://test.com/")
testconfiguration.IsEqualString(t, ServerSettings.RedirectUrl, "http://test2.com")
testconfiguration.IsEqualString(t, ServerSettings.AdminPassword, "5bbf5684437a4c658d2e0890d784694afb63f715")
testconfiguration.IsEqualString(t, HashPassword("testtest2", false), "5bbf5684437a4c658d2e0890d784694afb63f715")
testconfiguration.IsEqualInt(t, ServerSettings.LengthId, 15)
os.Remove("test/config.json")
os.Unsetenv("GOKAPI_SALT_ADMIN")
Load()
testconfiguration.IsEqualInt(t, len(ServerSettings.SaltAdmin), 30)
testconfiguration.IsNotEqualString(t, ServerSettings.SaltAdmin, "eefwkjqweduiotbrkl##$2342brerlk2321")
os.Unsetenv("GOKAPI_USERNAME")
os.Unsetenv("GOKAPI_PASSWORD")
os.Unsetenv("GOKAPI_PORT")
os.Unsetenv("GOKAPI_EXTERNAL_URL")
os.Unsetenv("GOKAPI_REDIRECT_URL")
os.Unsetenv("GOKAPI_SALT_ADMIN")
os.Unsetenv("GOKAPI_LOCALHOST")
}
func TestUpgradeDb(t *testing.T) {
testconfiguration2.WriteUpgradeConfigFile()
Load()
ServerSettings.ConfigVersion = 0
updateConfig()
testconfiguration.IsEqualString(t, ServerSettings.SaltAdmin, "eefwkjqweduiotbrkl##$2342brerlk2321")
testconfiguration.IsEqualString(t, ServerSettings.SaltFiles, "P1UI5sRNDwuBgOvOYhNsmucZ2pqo4KEvOoqqbpdu")
testconfiguration.IsEqualString(t, ServerSettings.DataDir, Environment.DataDir)
testconfiguration.IsEqualInt(t, ServerSettings.LengthId, 15)
testconfiguration.IsEqualBool(t, ServerSettings.Hotlinks == nil, false)
testconfiguration.IsEqualBool(t, ServerSettings.DownloadStatus == nil, false)
testconfiguration.IsEqualString(t, ServerSettings.Files["MgXJLe4XLfpXcL12ec4i"].ContentType, "application/octet-stream")
testconfiguration.IsEqualInt(t, ServerSettings.ConfigVersion, currentConfigVersion)
testconfiguration2.Create(false)
Load()
test.IsEqualString(t, ServerSettings.SaltAdmin, "eefwkjqweduiotbrkl##$2342brerlk2321")
test.IsEqualInt(t, ServerSettings.LengthId, 15)
test.IsEqualInt(t, ServerSettings.ConfigVersion, currentConfigVersion)
}
func TestAskForUsername(t *testing.T) {
original := testconfiguration.StartMockInputStdin(t, "admin")
original := testconfiguration2.StartMockInputStdin("admin")
output := askForUsername()
testconfiguration.StopMockInputStdin(original)
test.IsEqualString(t, output, "admin")
testconfiguration2.StopMockInputStdin(original)
testconfiguration.IsEqualString(t, output, "admin")
}
func TestIsValidPortNumber(t *testing.T) {
test.IsEqualBool(t, isValidPortNumber("invalid"), false)
test.IsEqualBool(t, isValidPortNumber("-1"), false)
test.IsEqualBool(t, isValidPortNumber("0"), true)
test.IsEqualBool(t, isValidPortNumber("100"), true)
test.IsEqualBool(t, isValidPortNumber("65353"), true)
test.IsEqualBool(t, isValidPortNumber("65354"), false)
testconfiguration.IsEqualBool(t, isValidPortNumber("invalid"), false)
testconfiguration.IsEqualBool(t, isValidPortNumber("-1"), false)
testconfiguration.IsEqualBool(t, isValidPortNumber("0"), true)
testconfiguration.IsEqualBool(t, isValidPortNumber("100"), true)
testconfiguration.IsEqualBool(t, isValidPortNumber("65353"), true)
testconfiguration.IsEqualBool(t, isValidPortNumber("65354"), false)
}
func TestHashPassword(t *testing.T) {
test.IsEqualString(t, HashPassword("123", false), "45dcdc57b6a50bd0020fabc958ae254406713559")
test.IsEqualString(t, HashPassword("", false), "")
test.IsEqualString(t, HashPassword("123", true), "e143a1801faba4c5c6fdc2e823127c988940f72e")
testconfiguration.IsEqualString(t, HashPassword("123", false), "423b63a68c68bd7e07b14590927c1e9a473fe035")
testconfiguration.IsEqualString(t, HashPassword("", false), "")
testconfiguration.IsEqualString(t, HashPassword("123", true), "7b30508aa9b233ab4b8a11b2af5816bdb58ca3e7")
}
func TestIsValidUrl(t *testing.T) {
test.IsEqualBool(t, isValidUrl("http://"), false)
test.IsEqualBool(t, isValidUrl("https://"), false)
test.IsEqualBool(t, isValidUrl("invalid"), false)
test.IsEqualBool(t, isValidUrl("http://abc"), true)
test.IsEqualBool(t, isValidUrl("https://abc"), true)
testconfiguration.IsEqualBool(t, isValidUrl("http://"), false)
testconfiguration.IsEqualBool(t, isValidUrl("https://"), false)
testconfiguration.IsEqualBool(t, isValidUrl("invalid"), false)
testconfiguration.IsEqualBool(t, isValidUrl("http://abc"), true)
testconfiguration.IsEqualBool(t, isValidUrl("https://abc"), true)
}
func TestAddTrailingSlash(t *testing.T) {
test.IsEqualString(t, addTrailingSlash("abc"), "abc/")
test.IsEqualString(t, addTrailingSlash("abc/"), "abc/")
test.IsEqualString(t, addTrailingSlash("/"), "/")
test.IsEqualString(t, addTrailingSlash(""), "/")
testconfiguration.IsEqualString(t, addTrailingSlash("abc"), "abc/")
testconfiguration.IsEqualString(t, addTrailingSlash("abc/"), "abc/")
testconfiguration.IsEqualString(t, addTrailingSlash("/"), "/")
testconfiguration.IsEqualString(t, addTrailingSlash(""), "/")
}
func TestAskForRedirect(t *testing.T) {
original := testconfiguration.StartMockInputStdin(t, "")
original := testconfiguration2.StartMockInputStdin("")
url := askForRedirect()
testconfiguration.StopMockInputStdin(original)
test.IsEqualString(t, url, "https://github.com/Forceu/Gokapi/")
original = testconfiguration.StartMockInputStdin(t, "https://test.com")
testconfiguration2.StopMockInputStdin(original)
testconfiguration.IsEqualString(t, url, "https://github.com/Forceu/Gokapi/")
original = testconfiguration2.StartMockInputStdin("https://test.com")
url = askForRedirect()
testconfiguration.StopMockInputStdin(original)
test.IsEqualString(t, url, "https://test.com")
testconfiguration2.StopMockInputStdin(original)
testconfiguration.IsEqualString(t, url, "https://test.com")
}
func TestAskForLocalOnly(t *testing.T) {
environment.IsDocker = "true"
test.IsEqualString(t, askForLocalOnly(), environment.IsTrue)
testconfiguration.IsEqualString(t, askForLocalOnly(), environment.IsTrue)
environment.IsDocker = "false"
original := testconfiguration.StartMockInputStdin(t, "")
test.IsEqualString(t, askForLocalOnly(), environment.IsTrue)
testconfiguration.StopMockInputStdin(original)
original = testconfiguration.StartMockInputStdin(t, "no")
test.IsEqualString(t, askForLocalOnly(), environment.IsFalse)
testconfiguration.StopMockInputStdin(original)
original = testconfiguration.StartMockInputStdin(t, "yes")
test.IsEqualString(t, askForLocalOnly(), environment.IsTrue)
testconfiguration.StopMockInputStdin(original)
original = testconfiguration.StartMockInputStdin(t, "n")
test.IsEqualString(t, askForLocalOnly(), environment.IsFalse)
testconfiguration.StopMockInputStdin(original)
original := testconfiguration2.StartMockInputStdin("")
testconfiguration.IsEqualString(t, askForLocalOnly(), environment.IsTrue)
testconfiguration2.StopMockInputStdin(original)
original = testconfiguration2.StartMockInputStdin("no")
testconfiguration.IsEqualString(t, askForLocalOnly(), environment.IsFalse)
testconfiguration2.StopMockInputStdin(original)
original = testconfiguration2.StartMockInputStdin("yes")
testconfiguration.IsEqualString(t, askForLocalOnly(), environment.IsTrue)
testconfiguration2.StopMockInputStdin(original)
original = testconfiguration2.StartMockInputStdin("n")
testconfiguration.IsEqualString(t, askForLocalOnly(), environment.IsFalse)
testconfiguration2.StopMockInputStdin(original)
}
func TestAskForPort(t *testing.T) {
original := testconfiguration.StartMockInputStdin(t, "8000")
test.IsEqualString(t, askForPort(), "8000")
testconfiguration.StopMockInputStdin(original)
original = testconfiguration.StartMockInputStdin(t, "")
test.IsEqualString(t, askForPort(), defaultPort)
testconfiguration.StopMockInputStdin(original)
original := testconfiguration2.StartMockInputStdin("8000")
testconfiguration.IsEqualString(t, askForPort(), "8000")
testconfiguration2.StopMockInputStdin(original)
original = testconfiguration2.StartMockInputStdin("")
testconfiguration.IsEqualString(t, askForPort(), defaultPort)
testconfiguration2.StopMockInputStdin(original)
}
func TestAskForUrl(t *testing.T) {
original := testconfiguration.StartMockInputStdin(t, "https://test.com")
test.IsEqualString(t, askForUrl("1234"), "https://test.com/")
testconfiguration.StopMockInputStdin(original)
original = testconfiguration.StartMockInputStdin(t, "")
test.IsEqualString(t, askForUrl("1234"), "http://127.0.0.1:1234/")
testconfiguration.StopMockInputStdin(original)
original := testconfiguration2.StartMockInputStdin("https://test.com")
testconfiguration.IsEqualString(t, askForUrl("1234"), "https://test.com/")
testconfiguration2.StopMockInputStdin(original)
original = testconfiguration2.StartMockInputStdin("")
testconfiguration.IsEqualString(t, askForUrl("1234"), "http://127.0.0.1:1234/")
testconfiguration2.StopMockInputStdin(original)
}

View File

@@ -3,12 +3,12 @@ package downloadstatus
import (
"Gokapi/internal/configuration"
"Gokapi/internal/helper"
"Gokapi/internal/storage/filestructure"
"Gokapi/internal/models"
"time"
)
// SetDownload creates a new DownloadStatus struct and returns its Id
func SetDownload(file filestructure.File) string {
func SetDownload(file models.File) string {
status := newDownloadStatus(file)
configuration.ServerSettings.DownloadStatus[status.Id] = status
return status.Id
@@ -30,8 +30,8 @@ func Clean() {
}
// newDownloadStatus initialises the a new DownloadStatus item
func newDownloadStatus(file filestructure.File) filestructure.DownloadStatus {
s := filestructure.DownloadStatus{
func newDownloadStatus(file models.File) models.DownloadStatus {
s := models.DownloadStatus{
Id: helper.GenerateRandomString(30),
FileId: file.Id,
ExpireAt: time.Now().Add(24 * time.Hour).Unix(),
@@ -40,7 +40,7 @@ func newDownloadStatus(file filestructure.File) filestructure.DownloadStatus {
}
// IsCurrentlyDownloading returns true if file is currently being downloaded
func IsCurrentlyDownloading(file filestructure.File) bool {
func IsCurrentlyDownloading(file models.File) bool {
for _, status := range configuration.ServerSettings.DownloadStatus {
if status.FileId == file.Id {
if status.ExpireAt > time.Now().Unix() {

View File

@@ -2,19 +2,19 @@ package downloadstatus
import (
"Gokapi/internal/configuration"
"Gokapi/internal/storage/filestructure"
"Gokapi/pkg/test"
"Gokapi/internal/models"
"Gokapi/internal/test"
"os"
"testing"
"time"
)
var testFile filestructure.File
var testFile models.File
var statusId string
func TestMain(m *testing.M) {
configuration.ServerSettings.DownloadStatus = make(map[string]filestructure.DownloadStatus)
testFile = filestructure.File{
configuration.ServerSettings.DownloadStatus = make(map[string]models.DownloadStatus)
testFile = models.File{
Id: "test",
Name: "testName",
Size: "3 B",
@@ -28,7 +28,7 @@ func TestMain(m *testing.M) {
}
func TestNewDownloadStatus(t *testing.T) {
status := newDownloadStatus(filestructure.File{Id: "testId"})
status := newDownloadStatus(models.File{Id: "testId"})
test.IsNotEmpty(t, status.Id)
test.IsEqualString(t, status.FileId, "testId")
test.IsEqualBool(t, status.ExpireAt > time.Now().Unix(), true)
@@ -54,7 +54,7 @@ func TestSetComplete(t *testing.T) {
func TestIsCurrentlyDownloading(t *testing.T) {
statusId = SetDownload(testFile)
test.IsEqualBool(t, IsCurrentlyDownloading(testFile), true)
test.IsEqualBool(t, IsCurrentlyDownloading(filestructure.File{Id: "notDownloading"}), false)
test.IsEqualBool(t, IsCurrentlyDownloading(models.File{Id: "notDownloading"}), false)
}
func TestClean(t *testing.T) {

View File

@@ -1,7 +1,7 @@
package environment
import (
"Gokapi/pkg/test"
"Gokapi/internal/test"
"os"
"testing"
)
@@ -25,4 +25,9 @@ func TestEnvLoad(t *testing.T) {
env = New()
test.IsEqualString(t, env.WebserverLocalhost, "")
os.Setenv("GOKAPI_LENGTH_ID", "15")
os.Setenv("GOKAPI_LOCALHOST", "invalid")
os.Setenv("GOKAPI_LENGTH_ID", "invalid")
env = New()
test.IsEqualString(t, env.WebserverLocalhost, "")
test.IsEqualInt(t, env.LengthId, -1)
}

View File

@@ -2,35 +2,35 @@ package helper
import (
testconfiguration "Gokapi/internal/test"
"Gokapi/pkg/test"
testconfiguration2 "Gokapi/internal/test/testconfiguration"
"io/ioutil"
"os"
"testing"
)
func TestIsInArray(t *testing.T) {
test.IsEqualBool(t, IsInArray([]string{"test", "test2", "test3"}, "test2"), true)
test.IsEqualBool(t, IsInArray([]string{"test", "test2", "test3"}, "invalid"), false)
testconfiguration.IsEqualBool(t, IsInArray([]string{"test", "test2", "test3"}, "test2"), true)
testconfiguration.IsEqualBool(t, IsInArray([]string{"test", "test2", "test3"}, "invalid"), false)
}
func TestFolderCreation(t *testing.T) {
test.IsEqualBool(t, FolderExists("invalid"), false)
test.IsEqualBool(t, FileExists("invalid/file"), false)
testconfiguration.IsEqualBool(t, FolderExists("invalid"), false)
testconfiguration.IsEqualBool(t, FileExists("invalid/file"), false)
CreateDir("invalid")
test.IsEqualBool(t, FolderExists("invalid"), true)
testconfiguration.IsEqualBool(t, FolderExists("invalid"), true)
err := ioutil.WriteFile("invalid/file", []byte("test"), 0644)
if err != nil {
t.Error(err)
}
test.IsEqualBool(t, FileExists("invalid/file"), true)
testconfiguration.IsEqualBool(t, FileExists("invalid/file"), true)
os.RemoveAll("invalid")
}
func TestReadLine(t *testing.T) {
original := testconfiguration.StartMockInputStdin(t, "test")
original := testconfiguration2.StartMockInputStdin("test")
output := ReadLine()
testconfiguration.StopMockInputStdin(original)
test.IsEqualString(t, output, "test")
testconfiguration2.StopMockInputStdin(original)
testconfiguration.IsEqualString(t, output, "test")
}
func TestGetFileSize(t *testing.T) {
@@ -40,12 +40,12 @@ func TestGetFileSize(t *testing.T) {
t.Fatal(err)
}
size, _ := GetFileSize(file)
test.IsEqualInt(t, int(size), 0)
testconfiguration.IsEqualInt(t, int(size), 0)
os.WriteFile("testfile", []byte("123"), 0777)
size, _ = GetFileSize(file)
test.IsEqualInt(t, int(size), 3)
testconfiguration.IsEqualInt(t, int(size), 3)
file, _ = os.OpenFile("invalid", os.O_RDONLY, 0644)
size, _ = GetFileSize(file)
test.IsEqualInt(t, int(size), 0)
testconfiguration.IsEqualInt(t, int(size), 0)
os.Remove("testfile")
}

View File

@@ -1,7 +1,7 @@
package helper
import (
"Gokapi/pkg/test"
"Gokapi/internal/test"
"testing"
)

16
internal/models/Api.go Normal file
View File

@@ -0,0 +1,16 @@
package models
type ApiKey struct {
Id string `json:"Id"`
FriendlyName string `json:"FriendlyName"`
LastUsed int64 `json:"LastUsed"`
}
type UploadItem struct {
Id string `json:"Id"`
Name string `json:"Name"`
Filesize int64 `json:"Filesize"`
Expiry int64 `json:"Expiry"`
DownloadsRemaining int `json:"DownloadsRemaining"`
Url string `json:"Url"`
}

View File

@@ -1,4 +1,4 @@
package filestructure
package models
import (
"encoding/json"

View File

@@ -1,7 +1,7 @@
package filestructure
package models
import (
"Gokapi/pkg/test"
"Gokapi/internal/test"
"testing"
)

View File

@@ -1,4 +1,4 @@
package sessionstructure
package models
// Session contains cookie parameter
type Session struct {

View File

@@ -8,10 +8,11 @@ import (
"Gokapi/internal/configuration"
"Gokapi/internal/configuration/downloadstatus"
"Gokapi/internal/helper"
"Gokapi/internal/storage/filestructure"
"Gokapi/internal/models"
"crypto/sha1"
"encoding/hex"
"fmt"
"io"
"io/ioutil"
"mime/multipart"
"net/http"
@@ -25,20 +26,15 @@ import (
// NewFile creates a new file in the system. Called after an upload has been completed. If a file with the same sha256 hash
// already exists, it is deduplicated. This function gathers information about the file, creates an ID and saves
// it into the global configuration.
func NewFile(fileContent *multipart.File, fileHeader *multipart.FileHeader, expireAt int64, downloads int, password string) (filestructure.File, error) {
fileBytes, err := ioutil.ReadAll(*fileContent)
func NewFile(fileContent io.Reader, fileHeader *multipart.FileHeader, expireAt int64, downloads int, password string) (models.File, error) {
fileBytes, err := ioutil.ReadAll(fileContent)
if err != nil {
return filestructure.File{}, err
return models.File{}, err
}
return processUpload(&fileBytes, fileHeader, expireAt, downloads, password)
}
// Called by NewFile, split into second function to make unit testing easier
func processUpload(fileContent *[]byte, fileHeader *multipart.FileHeader, expireAt int64, downloads int, password string) (filestructure.File, error) {
id := helper.GenerateRandomString(configuration.ServerSettings.LengthId)
hash := sha1.New()
hash.Write(*fileContent)
file := filestructure.File{
hash.Write(fileBytes)
file := models.File{
Id: id,
Name: fileHeader.Filename,
SHA256: hex.EncodeToString(hash.Sum(nil)),
@@ -55,10 +51,10 @@ func processUpload(fileContent *[]byte, fileHeader *multipart.FileHeader, expire
if !helper.FileExists(configuration.ServerSettings.DataDir + "/" + file.SHA256) {
destinationFile, err := os.OpenFile(filename, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0644)
if err != nil {
return filestructure.File{}, err
return models.File{}, err
}
defer destinationFile.Close()
destinationFile.Write(*fileContent)
destinationFile.Write(fileBytes)
}
configuration.Save()
return file, nil
@@ -67,14 +63,14 @@ func processUpload(fileContent *[]byte, fileHeader *multipart.FileHeader, expire
var imageFileExtensions = []string{".jpg", ".jpeg", ".png", ".gif", ".webp", ".bmp", ".svg"}
// If file is an image, create link for hotlinking
func addHotlink(file *filestructure.File) {
func addHotlink(file *models.File) {
extension := strings.ToLower(filepath.Ext(file.Name))
if !helper.IsInArray(imageFileExtensions, extension) {
return
}
link := helper.GenerateRandomString(40) + extension
file.HotlinkId = link
configuration.ServerSettings.Hotlinks[link] = filestructure.Hotlink{
configuration.ServerSettings.Hotlinks[link] = models.Hotlink{
Id: link,
FileId: file.Id,
}
@@ -82,8 +78,8 @@ func addHotlink(file *filestructure.File) {
// GetFile gets the file by id. Returns (empty File, false) if invalid / expired file
// or (file, true) if valid file
func GetFile(id string) (filestructure.File, bool) {
var emptyResult = filestructure.File{}
func GetFile(id string) (models.File, bool) {
var emptyResult = models.File{}
if id == "" {
return emptyResult, false
}
@@ -99,8 +95,8 @@ func GetFile(id string) (filestructure.File, bool) {
// GetFileByHotlink gets the file by hotlink id. Returns (empty File, false) if invalid / expired file
// or (file, true) if valid file
func GetFileByHotlink(id string) (filestructure.File, bool) {
var emptyResult = filestructure.File{}
func GetFileByHotlink(id string) (models.File, bool) {
var emptyResult = models.File{}
if id == "" {
return emptyResult, false
}
@@ -109,7 +105,7 @@ func GetFileByHotlink(id string) (filestructure.File, bool) {
}
// ServeFile subtracts a download allowance and serves the file to the browser
func ServeFile(file filestructure.File, w http.ResponseWriter, r *http.Request, forceDownload bool) {
func ServeFile(file models.File, w http.ResponseWriter, r *http.Request, forceDownload bool) {
file.DownloadsRemaining = file.DownloadsRemaining - 1
configuration.ServerSettings.Files[file.Id] = file
storageData, err := os.OpenFile(configuration.ServerSettings.DataDir+"/"+file.SHA256, os.O_RDONLY, 0644)

View File

@@ -3,9 +3,10 @@ package storage
import (
"Gokapi/internal/configuration"
"Gokapi/internal/helper"
"Gokapi/internal/storage/filestructure"
"Gokapi/internal/models"
testconfiguration "Gokapi/internal/test"
"Gokapi/pkg/test"
testconfiguration2 "Gokapi/internal/test/testconfiguration"
"bytes"
"io/ioutil"
"mime/multipart"
"net/http/httptest"
@@ -15,10 +16,10 @@ import (
)
func TestMain(m *testing.M) {
testconfiguration.Create(true)
testconfiguration2.Create(true)
configuration.Load()
exitVal := m.Run()
testconfiguration.Delete()
testconfiguration2.Delete()
os.Exit(exitVal)
}
@@ -26,42 +27,42 @@ var idNewFile string
func TestGetFile(t *testing.T) {
_, result := GetFile("invalid")
test.IsEqualBool(t, result, false)
testconfiguration.IsEqualBool(t, result, false)
file, result := GetFile("Wzol7LyY2QVczXynJtVo")
test.IsEqualBool(t, result, true)
test.IsEqualString(t, file.Id, "Wzol7LyY2QVczXynJtVo")
test.IsEqualString(t, file.Name, "smallfile2")
test.IsEqualString(t, file.Size, "8 B")
test.IsEqualInt(t, file.DownloadsRemaining, 1)
testconfiguration.IsEqualBool(t, result, true)
testconfiguration.IsEqualString(t, file.Id, "Wzol7LyY2QVczXynJtVo")
testconfiguration.IsEqualString(t, file.Name, "smallfile2")
testconfiguration.IsEqualString(t, file.Size, "8 B")
testconfiguration.IsEqualInt(t, file.DownloadsRemaining, 1)
_, result = GetFile("deletedfile1234")
test.IsEqualBool(t, result, false)
testconfiguration.IsEqualBool(t, result, false)
}
func TestGetFileByHotlink(t *testing.T) {
_, result := GetFileByHotlink("invalid")
test.IsEqualBool(t, result, false)
testconfiguration.IsEqualBool(t, result, false)
_, result = GetFileByHotlink("")
test.IsEqualBool(t, result, false)
testconfiguration.IsEqualBool(t, result, false)
file, result := GetFileByHotlink("PhSs6mFtf8O5YGlLMfNw9rYXx9XRNkzCnJZpQBi7inunv3Z4A.jpg")
test.IsEqualBool(t, result, true)
test.IsEqualString(t, file.Id, "n1tSTAGj8zan9KaT4u6p")
test.IsEqualString(t, file.Name, "picture.jpg")
test.IsEqualString(t, file.Size, "4 B")
test.IsEqualInt(t, file.DownloadsRemaining, 1)
testconfiguration.IsEqualBool(t, result, true)
testconfiguration.IsEqualString(t, file.Id, "n1tSTAGj8zan9KaT4u6p")
testconfiguration.IsEqualString(t, file.Name, "picture.jpg")
testconfiguration.IsEqualString(t, file.Size, "4 B")
testconfiguration.IsEqualInt(t, file.DownloadsRemaining, 1)
}
func TestAddHotlink(t *testing.T) {
file := filestructure.File{Name: "test.dat", Id: "testIdE"}
file := models.File{Name: "test.dat", Id: "testIdE"}
addHotlink(&file)
test.IsEqualString(t, file.HotlinkId, "")
file = filestructure.File{Name: "test.jpg", Id: "testId"}
testconfiguration.IsEqualString(t, file.HotlinkId, "")
file = models.File{Name: "test.jpg", Id: "testId"}
addHotlink(&file)
test.IsEqualInt(t, len(file.HotlinkId), 44)
testconfiguration.IsEqualInt(t, len(file.HotlinkId), 44)
lastCharacters := file.HotlinkId[len(file.HotlinkId)-4:]
test.IsEqualBool(t, lastCharacters == ".jpg", true)
test.IsEqualString(t, configuration.ServerSettings.Hotlinks[file.HotlinkId].FileId, "testId")
test.IsEqualString(t, configuration.ServerSettings.Hotlinks[file.HotlinkId].Id, file.HotlinkId)
testconfiguration.IsEqualBool(t, lastCharacters == ".jpg", true)
testconfiguration.IsEqualString(t, configuration.ServerSettings.Hotlinks[file.HotlinkId].FileId, "testId")
testconfiguration.IsEqualString(t, configuration.ServerSettings.Hotlinks[file.HotlinkId].Id, file.HotlinkId)
}
func TestNewFile(t *testing.T) {
@@ -75,79 +76,78 @@ func TestNewFile(t *testing.T) {
Header: mimeHeader,
Size: int64(len(content)),
}
file, err := processUpload(&content, &header, 2147483600, 1, "")
test.IsNil(t, err)
test.IsEqualString(t, file.Name, "test.dat")
test.IsEqualString(t, file.SHA256, "f1474c19eff0fc8998fa6e1b1f7bf31793b103a6")
test.IsEqualString(t, file.HotlinkId, "")
test.IsEqualString(t, file.PasswordHash, "")
test.IsEqualString(t, file.Size, "35 B")
test.IsEqualString(t, file.ExpireAtString, "2038-01-19 03:13")
test.IsEqualInt(t, file.DownloadsRemaining, 1)
test.IsEqualInt(t, len(file.Id), 20)
test.IsEqualInt(t, int(file.ExpireAt), 2147483600)
file, err := NewFile(bytes.NewReader(content), &header, 2147483600, 1, "")
testconfiguration.IsNil(t, err)
testconfiguration.IsEqualString(t, file.Name, "test.dat")
testconfiguration.IsEqualString(t, file.SHA256, "f1474c19eff0fc8998fa6e1b1f7bf31793b103a6")
testconfiguration.IsEqualString(t, file.HotlinkId, "")
testconfiguration.IsEqualString(t, file.PasswordHash, "")
testconfiguration.IsEqualString(t, file.Size, "35 B")
testconfiguration.IsEqualString(t, file.ExpireAtString, "2038-01-19 03:13")
testconfiguration.IsEqualInt(t, file.DownloadsRemaining, 1)
testconfiguration.IsEqualInt(t, len(file.Id), 20)
testconfiguration.IsEqualInt(t, int(file.ExpireAt), 2147483600)
idNewFile = file.Id
}
func TestServeFile(t *testing.T) {
file, result := GetFile(idNewFile)
test.IsEqualBool(t, result, true)
testconfiguration.IsEqualBool(t, result, true)
r := httptest.NewRequest("GET", "/upload", nil)
w := httptest.NewRecorder()
ServeFile(file, w, r, true)
_, result = GetFile(idNewFile)
test.IsEqualBool(t, result, false)
testconfiguration.IsEqualBool(t, result, false)
test.IsEqualString(t, w.Result().Header.Get("Content-Disposition"), "attachment; filename=\"test.dat\"")
test.IsEqualString(t, w.Result().Header.Get("Content-Length"), "35")
test.IsEqualString(t, w.Result().Header.Get("Content-Type"), "text")
testconfiguration.IsEqualString(t, w.Result().Header.Get("Content-Disposition"), "attachment; filename=\"test.dat\"")
testconfiguration.IsEqualString(t, w.Result().Header.Get("Content-Length"), "35")
testconfiguration.IsEqualString(t, w.Result().Header.Get("Content-Type"), "text")
content, err := ioutil.ReadAll(w.Result().Body)
test.IsNil(t, err)
test.IsEqualString(t, string(content), "This is a file for testing purposes")
testconfiguration.IsNil(t, err)
testconfiguration.IsEqualString(t, string(content), "This is a file for testing purposes")
}
func TestCleanUp(t *testing.T) {
test.IsEqualString(t, configuration.ServerSettings.Files["cleanuptest123456789"].Name, "cleanup")
test.IsEqualString(t, configuration.ServerSettings.Files["Wzol7LyY2QVczXynJtVo"].Name, "smallfile2")
test.IsEqualString(t, configuration.ServerSettings.Files["e4TjE7CokWK0giiLNxDL"].Name, "smallfile2")
test.IsEqualString(t, configuration.ServerSettings.Files["wefffewhtrhhtrhtrhtr"].Name, "smallfile3")
test.IsEqualString(t, configuration.ServerSettings.Files["n1tSTAGj8zan9KaT4u6p"].Name, "picture.jpg")
test.IsEqualString(t, configuration.ServerSettings.Files["deletedfile123456789"].Name, "DeletedFile")
test.IsEqualBool(t, helper.FileExists("test/data/2341354656543213246465465465432456898794"), true)
testconfiguration.IsEqualString(t, configuration.ServerSettings.Files["cleanuptest123456789"].Name, "cleanup")
testconfiguration.IsEqualString(t, configuration.ServerSettings.Files["Wzol7LyY2QVczXynJtVo"].Name, "smallfile2")
testconfiguration.IsEqualString(t, configuration.ServerSettings.Files["e4TjE7CokWK0giiLNxDL"].Name, "smallfile2")
testconfiguration.IsEqualString(t, configuration.ServerSettings.Files["wefffewhtrhhtrhtrhtr"].Name, "smallfile3")
testconfiguration.IsEqualString(t, configuration.ServerSettings.Files["n1tSTAGj8zan9KaT4u6p"].Name, "picture.jpg")
testconfiguration.IsEqualString(t, configuration.ServerSettings.Files["deletedfile123456789"].Name, "DeletedFile")
testconfiguration.IsEqualBool(t, helper.FileExists("test/data/2341354656543213246465465465432456898794"), true)
CleanUp(false)
test.IsEqualString(t, configuration.ServerSettings.Files["cleanuptest123456789"].Name, "cleanup")
test.IsEqualBool(t, helper.FileExists("test/data/2341354656543213246465465465432456898794"), true)
test.IsEqualString(t, configuration.ServerSettings.Files["deletedfile123456789"].Name, "")
test.IsEqualString(t, configuration.ServerSettings.Files["Wzol7LyY2QVczXynJtVo"].Name, "smallfile2")
test.IsEqualString(t, configuration.ServerSettings.Files["e4TjE7CokWK0giiLNxDL"].Name, "smallfile2")
test.IsEqualString(t, configuration.ServerSettings.Files["wefffewhtrhhtrhtrhtr"].Name, "smallfile3")
test.IsEqualString(t, configuration.ServerSettings.Files["n1tSTAGj8zan9KaT4u6p"].Name, "picture.jpg")
testconfiguration.IsEqualString(t, configuration.ServerSettings.Files["cleanuptest123456789"].Name, "cleanup")
testconfiguration.IsEqualBool(t, helper.FileExists("test/data/2341354656543213246465465465432456898794"), true)
testconfiguration.IsEqualString(t, configuration.ServerSettings.Files["deletedfile123456789"].Name, "")
testconfiguration.IsEqualString(t, configuration.ServerSettings.Files["Wzol7LyY2QVczXynJtVo"].Name, "smallfile2")
testconfiguration.IsEqualString(t, configuration.ServerSettings.Files["e4TjE7CokWK0giiLNxDL"].Name, "smallfile2")
testconfiguration.IsEqualString(t, configuration.ServerSettings.Files["wefffewhtrhhtrhtrhtr"].Name, "smallfile3")
testconfiguration.IsEqualString(t, configuration.ServerSettings.Files["n1tSTAGj8zan9KaT4u6p"].Name, "picture.jpg")
file, _ := GetFile("n1tSTAGj8zan9KaT4u6p")
file.DownloadsRemaining = 0
configuration.ServerSettings.Files["n1tSTAGj8zan9KaT4u6p"] = file
CleanUp(false)
test.IsEqualBool(t, helper.FileExists("test/data/a8fdc205a9f19cc1c7507a60c4f01b13d11d7fd0"), false)
test.IsEqualString(t, configuration.ServerSettings.Files["n1tSTAGj8zan9KaT4u6p"].Name, "")
test.IsEqualString(t, configuration.ServerSettings.Files["deletedfile123456789"].Name, "")
test.IsEqualString(t, configuration.ServerSettings.Files["Wzol7LyY2QVczXynJtVo"].Name, "smallfile2")
test.IsEqualString(t, configuration.ServerSettings.Files["e4TjE7CokWK0giiLNxDL"].Name, "smallfile2")
test.IsEqualString(t, configuration.ServerSettings.Files["wefffewhtrhhtrhtrhtr"].Name, "smallfile3")
testconfiguration.IsEqualBool(t, helper.FileExists("test/data/a8fdc205a9f19cc1c7507a60c4f01b13d11d7fd0"), false)
testconfiguration.IsEqualString(t, configuration.ServerSettings.Files["n1tSTAGj8zan9KaT4u6p"].Name, "")
testconfiguration.IsEqualString(t, configuration.ServerSettings.Files["deletedfile123456789"].Name, "")
testconfiguration.IsEqualString(t, configuration.ServerSettings.Files["Wzol7LyY2QVczXynJtVo"].Name, "smallfile2")
testconfiguration.IsEqualString(t, configuration.ServerSettings.Files["e4TjE7CokWK0giiLNxDL"].Name, "smallfile2")
testconfiguration.IsEqualString(t, configuration.ServerSettings.Files["wefffewhtrhhtrhtrhtr"].Name, "smallfile3")
file, _ = GetFile("Wzol7LyY2QVczXynJtVo")
file.DownloadsRemaining = 0
configuration.ServerSettings.Files["Wzol7LyY2QVczXynJtVo"] = file
CleanUp(false)
test.IsEqualBool(t, helper.FileExists("test/data/e017693e4a04a59d0b0f400fe98177fe7ee13cf7"), true)
test.IsEqualString(t, configuration.ServerSettings.Files["Wzol7LyY2QVczXynJtVo"].Name, "")
test.IsEqualString(t, configuration.ServerSettings.Files["n1tSTAGj8zan9KaT4u6p"].Name, "")
test.IsEqualString(t, configuration.ServerSettings.Files["deletedfile123456789"].Name, "")
test.IsEqualString(t, configuration.ServerSettings.Files["e4TjE7CokWK0giiLNxDL"].Name, "smallfile2")
test.IsEqualString(t, configuration.ServerSettings.Files["wefffewhtrhhtrhtrhtr"].Name, "smallfile3")
testconfiguration.IsEqualBool(t, helper.FileExists("test/data/e017693e4a04a59d0b0f400fe98177fe7ee13cf7"), true)
testconfiguration.IsEqualString(t, configuration.ServerSettings.Files["Wzol7LyY2QVczXynJtVo"].Name, "")
testconfiguration.IsEqualString(t, configuration.ServerSettings.Files["n1tSTAGj8zan9KaT4u6p"].Name, "")
testconfiguration.IsEqualString(t, configuration.ServerSettings.Files["deletedfile123456789"].Name, "")
testconfiguration.IsEqualString(t, configuration.ServerSettings.Files["e4TjE7CokWK0giiLNxDL"].Name, "smallfile2")
testconfiguration.IsEqualString(t, configuration.ServerSettings.Files["wefffewhtrhhtrhtrhtr"].Name, "smallfile3")
file, _ = GetFile("e4TjE7CokWK0giiLNxDL")
file.DownloadsRemaining = 0
@@ -157,17 +157,17 @@ func TestCleanUp(t *testing.T) {
configuration.ServerSettings.Files["wefffewhtrhhtrhtrhtr"] = file
CleanUp(false)
test.IsEqualBool(t, helper.FileExists("test/data/e017693e4a04a59d0b0f400fe98177fe7ee13cf7"), false)
test.IsEqualString(t, configuration.ServerSettings.Files["Wzol7LyY2QVczXynJtVo"].Name, "")
test.IsEqualString(t, configuration.ServerSettings.Files["n1tSTAGj8zan9KaT4u6p"].Name, "")
test.IsEqualString(t, configuration.ServerSettings.Files["deletedfile123456789"].Name, "")
test.IsEqualString(t, configuration.ServerSettings.Files["e4TjE7CokWK0giiLNxDL"].Name, "")
test.IsEqualString(t, configuration.ServerSettings.Files["wefffewhtrhhtrhtrhtr"].Name, "")
testconfiguration.IsEqualBool(t, helper.FileExists("test/data/e017693e4a04a59d0b0f400fe98177fe7ee13cf7"), false)
testconfiguration.IsEqualString(t, configuration.ServerSettings.Files["Wzol7LyY2QVczXynJtVo"].Name, "")
testconfiguration.IsEqualString(t, configuration.ServerSettings.Files["n1tSTAGj8zan9KaT4u6p"].Name, "")
testconfiguration.IsEqualString(t, configuration.ServerSettings.Files["deletedfile123456789"].Name, "")
testconfiguration.IsEqualString(t, configuration.ServerSettings.Files["e4TjE7CokWK0giiLNxDL"].Name, "")
testconfiguration.IsEqualString(t, configuration.ServerSettings.Files["wefffewhtrhhtrhtrhtr"].Name, "")
test.IsEqualString(t, configuration.ServerSettings.Files["cleanuptest123456789"].Name, "cleanup")
test.IsEqualBool(t, helper.FileExists("test/data/2341354656543213246465465465432456898794"), true)
configuration.ServerSettings.DownloadStatus = make(map[string]filestructure.DownloadStatus)
testconfiguration.IsEqualString(t, configuration.ServerSettings.Files["cleanuptest123456789"].Name, "cleanup")
testconfiguration.IsEqualBool(t, helper.FileExists("test/data/2341354656543213246465465465432456898794"), true)
configuration.ServerSettings.DownloadStatus = make(map[string]models.DownloadStatus)
CleanUp(false)
test.IsEqualString(t, configuration.ServerSettings.Files["cleanuptest123456789"].Name, "")
test.IsEqualBool(t, helper.FileExists("test/data/2341354656543213246465465465432456898794"), false)
testconfiguration.IsEqualString(t, configuration.ServerSettings.Files["cleanuptest123456789"].Name, "")
testconfiguration.IsEqualBool(t, helper.FileExists("test/data/2341354656543213246465465465432456898794"), false)
}

View File

@@ -1,26 +0,0 @@
package testconfiguration
import (
"Gokapi/internal/helper"
"Gokapi/pkg/test"
"testing"
)
func TestCreate(t *testing.T) {
Create(true)
test.IsEqualBool(t, helper.FolderExists("test"), true)
test.IsEqualBool(t, helper.FileExists("test/config.json"), true)
test.IsEqualBool(t, helper.FileExists("test/data/a8fdc205a9f19cc1c7507a60c4f01b13d11d7fd0"), true)
}
func TestDelete(t *testing.T) {
Delete()
test.IsEqualBool(t, helper.FolderExists("test"), false)
}
func TestMockInputStdin(t *testing.T) {
original := StartMockInputStdin(t, "test")
result := helper.ReadLine()
StopMockInputStdin(original)
test.IsEqualString(t, result, "test")
}

View File

@@ -1,3 +1,5 @@
// +build test
package test
import (
@@ -12,53 +14,70 @@ import (
"path/filepath"
"strconv"
"strings"
"testing"
)
type MockT interface {
Errorf(format string, args ...interface{})
}
// IsEqualString fails test if got and want are not identical
func IsEqualString(t *testing.T, got, want string) {
func IsEqualString(t MockT, got, want string) {
if got != want {
t.Errorf("Assertion failed, got: %s, want: %s.", got, want)
}
}
// IsNotEqualString fails test if got and want are not identical
func IsNotEqualString(t MockT, got, want string) {
if got == want {
t.Errorf("Assertion failed, got: %s, want: not %s.", got, want)
}
}
// IsEqualBool fails test if got and want are not identical
func IsEqualBool(t *testing.T, got, want bool) {
func IsEqualBool(t MockT, got, want bool) {
if got != want {
t.Errorf("Assertion failed, got: %t, want: %t.", got, want)
}
}
// IsEqualInt fails test if got and want are not identical
func IsEqualInt(t *testing.T, got, want int) {
func IsEqualInt(t MockT, got, want int) {
if got != want {
t.Errorf("Assertion failed, got: %d, want: %d.", got, want)
}
}
// IsNotEmpty fails test if string is empty
func IsNotEmpty(t *testing.T, s string) {
func IsNotEmpty(t MockT, s string) {
if s == "" {
t.Errorf("Assertion failed, got: %s, want: empty.", s)
}
}
// IsEmpty fails test if string is not empty
func IsEmpty(t *testing.T, s string) {
func IsEmpty(t MockT, s string) {
if s != "" {
t.Errorf("Assertion failed, got: %s, want: empty.", s)
}
}
// IsNil fails test if error not nil
func IsNil(t *testing.T, got error) {
func IsNil(t MockT, got error) {
if got != nil {
t.Errorf("Assertion failed, got: %s, want: nil.", got.Error())
t.Errorf("Assertion failed, got: %s, want: nil.", got.(error).Error())
}
}
// IsNotNil fails test if error is nil
func IsNotNil(t MockT, got error) {
if got == nil {
t.Errorf("Assertion failed, got: nil, want: not nil.")
}
}
// HttpPageResult tests if a http server is outputting the correct result
func HttpPageResult(t *testing.T, config HttpTestConfig) []*http.Cookie {
func HttpPageResult(t MockT, config HttpTestConfig) []*http.Cookie {
config.init()
client := &http.Client{}
@@ -86,16 +105,16 @@ func HttpPageResult(t *testing.T, config HttpTestConfig) []*http.Cookie {
content, err := ioutil.ReadAll(resp.Body)
IsNil(t, err)
if config.IsHtml && !bytes.Contains(content, []byte("</html>")) {
t.Error(config.Url + ": Incorrect response")
t.Errorf(config.Url + ": Incorrect response")
}
for _, requiredString := range config.RequiredContent {
if !bytes.Contains(content, []byte(requiredString)) {
t.Error(config.Url + ": Incorrect response. Got:\n" + string(content))
t.Errorf(config.Url + ": Incorrect response. Got:\n" + string(content))
}
}
for _, excludedString := range config.ExcludedContent {
if bytes.Contains(content, []byte(excludedString)) {
t.Error(config.Url + ": Incorrect response. Got:\n" + string(content))
t.Errorf(config.Url + ": Incorrect response. Got:\n" + string(content))
}
}
resp.Body.Close()
@@ -141,7 +160,7 @@ type PostBody struct {
}
// HttpPostRequest sends a post request
func HttpPostRequest(t *testing.T, config HttpTestConfig) {
func HttpPostRequest(t MockT, config HttpTestConfig) {
file, err := os.Open(config.UploadFileName)
IsNil(t, err)
defer file.Close()
@@ -170,12 +189,12 @@ func HttpPostRequest(t *testing.T, config HttpTestConfig) {
for _, requiredString := range config.RequiredContent {
if !bytes.Contains(content, []byte(requiredString)) {
t.Error(config.Url + ": Incorrect response. Got:\n" + string(content))
t.Errorf(config.Url + ": Incorrect response. Got:\n" + string(content))
}
}
for _, excludedString := range config.ExcludedContent {
if bytes.Contains(content, []byte(excludedString)) {
t.Error(config.Url + ": Incorrect response. Got:\n" + string(content))
t.Errorf(config.Url + ": Incorrect response. Got:\n" + string(content))
}
}
}

View File

@@ -0,0 +1,73 @@
package test
import (
"errors"
"testing"
)
var (
wantFail bool
isFailed = false
)
type MockTest struct {
reference *testing.T
}
func (t MockTest) Errorf(format string, args ...interface{}) {
isFailed = true
}
func (t *MockTest) WantFail() {
t.Check()
isFailed = false
wantFail = true
}
func (t *MockTest) WantNoFail() {
t.Check()
isFailed = false
wantFail = false
}
func (t *MockTest) Check() {
if wantFail != isFailed {
t.reference.Error("Test failed")
}
}
func TestFunctions(t *testing.T) {
mockT := MockTest{reference: t}
mockT.WantNoFail()
IsEqualString(mockT, "test", "test")
mockT.WantNoFail()
IsNotEqualString(mockT, "test", "test2")
mockT.WantNoFail()
IsEqualBool(mockT, true, true)
mockT.WantNoFail()
IsEqualInt(mockT, 1, 1)
mockT.WantNoFail()
IsNotEmpty(mockT, "notEmpty")
mockT.WantNoFail()
IsEmpty(mockT, "")
mockT.WantNoFail()
IsNil(mockT, nil)
mockT.WantNoFail()
IsNotNil(mockT, errors.New("hello"))
mockT.WantFail()
IsEqualString(mockT, "test", "test2")
mockT.WantFail()
IsNotEqualString(mockT, "test", "test")
mockT.WantFail()
IsEqualBool(mockT, true, false)
mockT.WantFail()
IsEqualInt(mockT, 1, 2)
mockT.WantFail()
IsNotEmpty(mockT, "")
mockT.WantFail()
IsEmpty(mockT, "notEmpty")
mockT.WantFail()
IsNil(mockT, errors.New("hello"))
mockT.WantFail()
IsNotNil(mockT, nil)
mockT.Check()
}

View File

@@ -4,15 +4,19 @@ package testconfiguration
import (
"os"
"testing"
)
const (
dataDir = "test"
configFile = dataDir + "/config.json"
)
// Create creates a configuration for unit testing
func Create(initFiles bool) {
os.Setenv("GOKAPI_CONFIG_DIR", "test")
os.Setenv("GOKAPI_DATA_DIR", "test")
os.Mkdir("test", 0777)
os.WriteFile("test/config.json", configTestFile, 0777)
os.Mkdir(dataDir, 0777)
os.WriteFile(configFile, configTestFile, 0777)
if initFiles {
os.Mkdir("test/data", 0777)
os.WriteFile("test/data/a8fdc205a9f19cc1c7507a60c4f01b13d11d7fd0", []byte("123"), 0777)
@@ -23,21 +27,26 @@ func Create(initFiles bool) {
}
}
// WriteUpgradeConfigFile writes a Gokapi v1.1.0 config file
func WriteUpgradeConfigFile() {
os.Mkdir(dataDir, 0777)
os.WriteFile(configFile, configUpgradeTestFile, 0777)
}
// Delete deletes the configuration for unit testing
func Delete() {
os.RemoveAll("test")
os.RemoveAll(dataDir)
}
// StartMockInputStdin simulates a user input on stdin. Call StopMockInputStdin afterwards!
func StartMockInputStdin(t *testing.T, input string) *os.File {
func StartMockInputStdin(input string) *os.File {
r, w, err := os.Pipe()
if err != nil {
t.Fatal(err)
panic(err)
}
_, err = w.Write([]byte(input))
if err != nil {
t.Error(err)
panic(err)
}
w.Close()
@@ -189,9 +198,65 @@ var configTestFile = []byte(`{
"ExpireAt":2147483646
}
},
"ConfigVersion":5,
"ApiKeys":{
"validkey":{
"Id":"validkey",
"FriendlyName":"Test Key",
"LastUsed":1619367972
}
},
"ConfigVersion":6,
"SaltAdmin":"LW6fW4Pjv8GtdWVLSZD66gYEev6NAaXxOVBw7C",
"SaltFiles":"lL5wMTtnVCn5TPbpRaSe4vAQodWW0hgk00WCZE",
"LengthId":20,
"DataDir":"test/data"
}`)
var configUpgradeTestFile = []byte(`{
"Port":"127.0.0.1:53842",
"AdminName":"admin",
"AdminPassword":"7450c2403ab85f0e8d5436818b66b99fdd287ac6",
"ServerUrl":"https://gokapi.url/",
"DefaultDownloads":1,
"DefaultExpiry":14,
"DefaultPassword":"123",
"RedirectUrl":"https://github.com/Forceu/Gokapi/",
"Sessions":{
"y0t-OQGF5UPFHyFOLab38SNjrc_a4xdIHTsZclkLpxuSwwTzS_qEETsinkgVIdWNMnQjhcaZtgCoJdpu":{
"RenewAt":1619774155,
"ValidUntil":1622362555
}
},
"Files":{
"MgXJLe4XLfpXcL12ec4i":{
"Id":"MgXJLe4XLfpXcL12ec4i",
"Name":"gokapi-linux_amd64",
"Size":"10.2 MB",
"SHA256":"b08f5989e1c6d57b45fffe39a8edc5da715799b7",
"ExpireAt":1620980170,
"ExpireAtString":"2021-05-14 10:16",
"DownloadsRemaining":1,
"PasswordHash":"e143a1801faba4c5c6fdc2e823127c988940f72e"
},
"doLN1pgbb945DfhGottx":{
"Id":"doLN1pgbb945DfhGottx",
"Name":"config.json",
"Size":"945 B",
"SHA256":"d2d6fd5fbf4a4bb1b1ae2f19130dd75b5adc0a0b",
"ExpireAt":1620980181,
"ExpireAtString":"2021-05-14 10:16",
"DownloadsRemaining":1,
"PasswordHash":"e143a1801faba4c5c6fdc2e823127c988940f72e"
},
"q06tcBco9gdJTf_pZ8xf":{
"Id":"q06tcBco9gdJTf_pZ8xf",
"Name":"gokapi-linux_amd64",
"Size":"10.2 MB",
"SHA256":"b08f5989e1c6d57b45fffe39a8edc5da715799b7",
"ExpireAt":1620980160,
"ExpireAtString":"2021-05-14 10:16",
"DownloadsRemaining":1,
"PasswordHash":""
}
}
}`)

View File

@@ -0,0 +1,33 @@
package testconfiguration
import (
"Gokapi/internal/helper"
"Gokapi/internal/test"
"os"
"testing"
)
func TestCreate(t *testing.T) {
Create(true)
test.IsEqualBool(t, helper.FolderExists(dataDir), true)
test.IsEqualBool(t, helper.FileExists(configFile), true)
test.IsEqualBool(t, helper.FileExists("test/data/a8fdc205a9f19cc1c7507a60c4f01b13d11d7fd0"), true)
}
func TestDelete(t *testing.T) {
Delete()
test.IsEqualBool(t, helper.FolderExists(dataDir), false)
}
func TestMockInputStdin(t *testing.T) {
original := StartMockInputStdin(dataDir)
result := helper.ReadLine()
StopMockInputStdin(original)
test.IsEqualString(t, result, dataDir)
}
func TestSetUpgradeConfigFile(t *testing.T) {
os.Remove(configFile)
WriteUpgradeConfigFile()
test.IsEqualBool(t, helper.FileExists(configFile), true)
TestDelete(t)
}

View File

@@ -7,8 +7,9 @@ Handling of webserver and requests / uploads
import (
"Gokapi/internal/configuration"
"Gokapi/internal/helper"
"Gokapi/internal/models"
"Gokapi/internal/storage"
"Gokapi/internal/storage/filestructure"
"Gokapi/internal/webserver/sessionmanager"
"embed"
"fmt"
"html/template"
@@ -101,7 +102,7 @@ func redirect(w http.ResponseWriter, url string) {
// Handling of /logout
func doLogout(w http.ResponseWriter, r *http.Request) {
logoutSession(w, r)
sessionmanager.LogoutSession(w, r)
redirect(w, "login")
}
@@ -134,7 +135,7 @@ func showLogin(w http.ResponseWriter, r *http.Request) {
failedLogin := false
if pw != "" && user != "" {
if strings.ToLower(user) == strings.ToLower(configuration.ServerSettings.AdminName) && configuration.HashPassword(pw, false) == configuration.ServerSettings.AdminPassword {
createSession(w, false)
sessionmanager.CreateSession(w, false)
redirect(w, "admin")
return
}
@@ -261,7 +262,7 @@ type DownloadView struct {
// UploadView contains parameters for the admin menu template
type UploadView struct {
Items []filestructure.File
Items []models.File
Url string
HotlinkUrl string
TimeNow int64
@@ -276,7 +277,7 @@ type UploadView struct {
// Converts the globalConfig variable to an UploadView struct to pass the infos to
// the admin template
func (u *UploadView) convertGlobalConfig() *UploadView {
var result []filestructure.File
var result []models.File
for _, element := range configuration.ServerSettings.Files {
result = append(result, element)
}
@@ -323,7 +324,7 @@ func uploadFile(w http.ResponseWriter, r *http.Request) {
configuration.ServerSettings.DefaultPassword = password
file, header, err := r.FormFile("file")
responseError(w, err)
result, err := storage.NewFile(&file, header, time.Now().Add(time.Duration(expiryDaysInt)*time.Hour*24).Unix(), allowedDownloadsInt, password)
result, err := storage.NewFile(file, header, time.Now().Add(time.Duration(expiryDaysInt)*time.Hour*24).Unix(), allowedDownloadsInt, password)
responseError(w, err)
defer file.Close()
_, err = fmt.Fprint(w, result.ToJsonResult(configuration.ServerSettings.ServerUrl))
@@ -357,7 +358,7 @@ func downloadFile(w http.ResponseWriter, r *http.Request) {
// Checks if the user is logged in as an admin
func isAuthenticated(w http.ResponseWriter, r *http.Request, isUpload bool) bool {
if isValidSession(w, r) {
if sessionmanager.IsValidSession(w, r) {
return true
}
if isUpload {
@@ -370,7 +371,7 @@ func isAuthenticated(w http.ResponseWriter, r *http.Request, isUpload bool) bool
}
// Write a cookie if the user has entered a correct password for a password-protected file
func writeFilePwCookie(w http.ResponseWriter, file filestructure.File) {
func writeFilePwCookie(w http.ResponseWriter, file models.File) {
http.SetCookie(w, &http.Cookie{
Name: "p" + file.Id,
Value: file.PasswordHash,
@@ -380,7 +381,7 @@ func writeFilePwCookie(w http.ResponseWriter, file filestructure.File) {
// Checks if a cookie contains the correct password hash for a password-protected file
// If incorrect, a 3 second delay is introduced unless the cookie was empty.
func isValidPwCookie(r *http.Request, file filestructure.File) bool {
func isValidPwCookie(r *http.Request, file models.File) bool {
cookie, err := r.Cookie("p" + file.Id)
if err == nil {
if cookie.Value == file.PasswordHash {

View File

@@ -3,7 +3,7 @@ package webserver
import (
"Gokapi/internal/configuration"
testconfiguration "Gokapi/internal/test"
"Gokapi/pkg/test"
testconfiguration2 "Gokapi/internal/test/testconfiguration"
"html/template"
"io/fs"
"os"
@@ -13,12 +13,12 @@ import (
)
func TestMain(m *testing.M) {
testconfiguration.Create(true)
testconfiguration2.Create(true)
configuration.Load()
go Start()
time.Sleep(1 * time.Second)
exitVal := m.Run()
testconfiguration.Delete()
testconfiguration2.Delete()
os.Exit(exitVal)
}
@@ -38,7 +38,7 @@ func TestEmbedFs(t *testing.T) {
func TestIndexRedirect(t *testing.T) {
t.Parallel()
test.HttpPageResult(t, test.HttpTestConfig{
testconfiguration.HttpPageResult(t, testconfiguration.HttpTestConfig{
Url: "http://localhost:53843/",
RequiredContent: []string{"<html><head><meta http-equiv=\"Refresh\" content=\"0; URL=./index\"></head></html>"},
IsHtml: true,
@@ -46,7 +46,7 @@ func TestIndexRedirect(t *testing.T) {
}
func TestIndexFile(t *testing.T) {
t.Parallel()
test.HttpPageResult(t, test.HttpTestConfig{
testconfiguration.HttpPageResult(t, testconfiguration.HttpTestConfig{
Url: "http://localhost:53843/index",
RequiredContent: []string{configuration.ServerSettings.RedirectUrl},
IsHtml: true,
@@ -54,14 +54,14 @@ func TestIndexFile(t *testing.T) {
}
func TestStaticDirs(t *testing.T) {
t.Parallel()
test.HttpPageResult(t, test.HttpTestConfig{
testconfiguration.HttpPageResult(t, testconfiguration.HttpTestConfig{
Url: "http://localhost:53843/css/cover.css",
RequiredContent: []string{".btn-secondary:hover"},
})
}
func TestLogin(t *testing.T) {
t.Parallel()
test.HttpPageResult(t, test.HttpTestConfig{
testconfiguration.HttpPageResult(t, testconfiguration.HttpTestConfig{
Url: "http://localhost:53843/login",
RequiredContent: []string{"id=\"uname_hidden\""},
IsHtml: true,
@@ -69,7 +69,7 @@ func TestLogin(t *testing.T) {
}
func TestAdminNoAuth(t *testing.T) {
t.Parallel()
test.HttpPageResult(t, test.HttpTestConfig{
testconfiguration.HttpPageResult(t, testconfiguration.HttpTestConfig{
Url: "http://localhost:53843/admin",
RequiredContent: []string{"URL=./login\""},
IsHtml: true,
@@ -77,11 +77,11 @@ func TestAdminNoAuth(t *testing.T) {
}
func TestAdminAuth(t *testing.T) {
t.Parallel()
test.HttpPageResult(t, test.HttpTestConfig{
testconfiguration.HttpPageResult(t, testconfiguration.HttpTestConfig{
Url: "http://localhost:53843/admin",
RequiredContent: []string{"Downloads remaining"},
IsHtml: true,
Cookies: []test.Cookie{{
Cookies: []testconfiguration.Cookie{{
Name: "session_token",
Value: "validsession",
}},
@@ -89,11 +89,11 @@ func TestAdminAuth(t *testing.T) {
}
func TestAdminExpiredAuth(t *testing.T) {
t.Parallel()
test.HttpPageResult(t, test.HttpTestConfig{
testconfiguration.HttpPageResult(t, testconfiguration.HttpTestConfig{
Url: "http://localhost:53843/admin",
RequiredContent: []string{"URL=./login\""},
IsHtml: true,
Cookies: []test.Cookie{{
Cookies: []testconfiguration.Cookie{{
Name: "session_token",
Value: "expiredsession",
}},
@@ -102,11 +102,11 @@ func TestAdminExpiredAuth(t *testing.T) {
func TestAdminRenewalAuth(t *testing.T) {
t.Parallel()
cookies := test.HttpPageResult(t, test.HttpTestConfig{
cookies := testconfiguration.HttpPageResult(t, testconfiguration.HttpTestConfig{
Url: "http://localhost:53843/admin",
RequiredContent: []string{"Downloads remaining"},
IsHtml: true,
Cookies: []test.Cookie{{
Cookies: []testconfiguration.Cookie{{
Name: "session_token",
Value: "needsRenewal",
}},
@@ -121,11 +121,11 @@ func TestAdminRenewalAuth(t *testing.T) {
if sessionCookie == "needsRenewal" {
t.Error("Session not renewed")
}
test.HttpPageResult(t, test.HttpTestConfig{
testconfiguration.HttpPageResult(t, testconfiguration.HttpTestConfig{
Url: "http://localhost:53843/admin",
RequiredContent: []string{"Downloads remaining"},
IsHtml: true,
Cookies: []test.Cookie{{
Cookies: []testconfiguration.Cookie{{
Name: "session_token",
Value: sessionCookie,
}},
@@ -134,11 +134,11 @@ func TestAdminRenewalAuth(t *testing.T) {
func TestAdminInvalidAuth(t *testing.T) {
t.Parallel()
test.HttpPageResult(t, test.HttpTestConfig{
testconfiguration.HttpPageResult(t, testconfiguration.HttpTestConfig{
Url: "http://localhost:53843/admin",
RequiredContent: []string{"URL=./login\""},
IsHtml: true,
Cookies: []test.Cookie{{
Cookies: []testconfiguration.Cookie{{
Name: "session_token",
Value: "invalid",
}},
@@ -147,7 +147,7 @@ func TestAdminInvalidAuth(t *testing.T) {
func TestInvalidLink(t *testing.T) {
t.Parallel()
test.HttpPageResult(t, test.HttpTestConfig{
testconfiguration.HttpPageResult(t, testconfiguration.HttpTestConfig{
Url: "http://localhost:53843/d?id=123",
RequiredContent: []string{"URL=./error\""},
IsHtml: true,
@@ -155,7 +155,7 @@ func TestInvalidLink(t *testing.T) {
}
func TestError(t *testing.T) {
t.Parallel()
test.HttpPageResult(t, test.HttpTestConfig{
testconfiguration.HttpPageResult(t, testconfiguration.HttpTestConfig{
Url: "http://localhost:53843/error",
RequiredContent: []string{"this file cannot be found"},
IsHtml: true,
@@ -163,7 +163,7 @@ func TestError(t *testing.T) {
}
func TestForgotPw(t *testing.T) {
t.Parallel()
test.HttpPageResult(t, test.HttpTestConfig{
testconfiguration.HttpPageResult(t, testconfiguration.HttpTestConfig{
Url: "http://localhost:53843/forgotpw",
RequiredContent: []string{"--reset-pw"},
IsHtml: true,
@@ -171,63 +171,63 @@ func TestForgotPw(t *testing.T) {
}
func TestLoginCorrect(t *testing.T) {
t.Parallel()
test.HttpPageResult(t, test.HttpTestConfig{
testconfiguration.HttpPageResult(t, testconfiguration.HttpTestConfig{
Url: "http://localhost:53843/login",
RequiredContent: []string{"URL=./admin\""},
IsHtml: true,
Method: "POST",
PostValues: []test.PostBody{{"username", "test"}, {"password", "testtest"}},
PostValues: []testconfiguration.PostBody{{"username", "test"}, {"password", "testtest"}},
})
}
func TestLoginIncorrectPassword(t *testing.T) {
t.Parallel()
test.HttpPageResult(t, test.HttpTestConfig{
testconfiguration.HttpPageResult(t, testconfiguration.HttpTestConfig{
Url: "http://localhost:53843/login",
RequiredContent: []string{"Incorrect username or password"},
IsHtml: true,
Method: "POST",
PostValues: []test.PostBody{{"username", "test"}, {"password", "incorrect"}},
PostValues: []testconfiguration.PostBody{{"username", "test"}, {"password", "incorrect"}},
})
}
func TestLoginIncorrectUsername(t *testing.T) {
t.Parallel()
test.HttpPageResult(t, test.HttpTestConfig{
testconfiguration.HttpPageResult(t, testconfiguration.HttpTestConfig{
Url: "http://localhost:53843/login",
RequiredContent: []string{"Incorrect username or password"},
IsHtml: true,
Method: "POST",
PostValues: []test.PostBody{{"username", "incorrect"}, {"password", "incorrect"}},
PostValues: []testconfiguration.PostBody{{"username", "incorrect"}, {"password", "incorrect"}},
})
}
func TestLogout(t *testing.T) {
t.Parallel()
test.HttpPageResult(t, test.HttpTestConfig{
testconfiguration.HttpPageResult(t, testconfiguration.HttpTestConfig{
Url: "http://localhost:53843/admin",
RequiredContent: []string{"Downloads remaining"},
IsHtml: true,
Cookies: []test.Cookie{{
Cookies: []testconfiguration.Cookie{{
Name: "session_token",
Value: "logoutsession",
}},
})
// Logout
test.HttpPageResult(t, test.HttpTestConfig{
testconfiguration.HttpPageResult(t, testconfiguration.HttpTestConfig{
Url: "http://localhost:53843/logout",
RequiredContent: []string{"URL=./login\""},
IsHtml: true,
Cookies: []test.Cookie{{
Cookies: []testconfiguration.Cookie{{
Name: "session_token",
Value: "logoutsession",
}},
})
// Admin after logout
test.HttpPageResult(t, test.HttpTestConfig{
testconfiguration.HttpPageResult(t, testconfiguration.HttpTestConfig{
Url: "http://localhost:53843/admin",
RequiredContent: []string{"URL=./login\""},
IsHtml: true,
Cookies: []test.Cookie{{
Cookies: []testconfiguration.Cookie{{
Name: "session_token",
Value: "logoutsession",
}},
@@ -236,12 +236,12 @@ func TestLogout(t *testing.T) {
func TestDownloadHotlink(t *testing.T) {
t.Parallel()
test.HttpPageResult(t, test.HttpTestConfig{
testconfiguration.HttpPageResult(t, testconfiguration.HttpTestConfig{
Url: "http://127.0.0.1:53843/hotlink/PhSs6mFtf8O5YGlLMfNw9rYXx9XRNkzCnJZpQBi7inunv3Z4A.jpg",
RequiredContent: []string{"123"},
})
// Download expired hotlink
test.HttpPageResult(t, test.HttpTestConfig{
testconfiguration.HttpPageResult(t, testconfiguration.HttpTestConfig{
Url: "http://127.0.0.1:53843/hotlink/PhSs6mFtf8O5YGlLMfNw9rYXx9XRNkzCnJZpQBi7inunv3Z4A.jpg",
RequiredContent: []string{"Created with GIMP"},
})
@@ -250,24 +250,24 @@ func TestDownloadHotlink(t *testing.T) {
func TestDownloadNoPassword(t *testing.T) {
t.Parallel()
// Show download page
test.HttpPageResult(t, test.HttpTestConfig{
testconfiguration.HttpPageResult(t, testconfiguration.HttpTestConfig{
Url: "http://127.0.0.1:53843/d?id=Wzol7LyY2QVczXynJtVo",
IsHtml: true,
RequiredContent: []string{"smallfile2"},
})
// Download
test.HttpPageResult(t, test.HttpTestConfig{
testconfiguration.HttpPageResult(t, testconfiguration.HttpTestConfig{
Url: "http://127.0.0.1:53843/downloadFile?id=Wzol7LyY2QVczXynJtVo",
RequiredContent: []string{"789"},
})
// Show download page expired file
test.HttpPageResult(t, test.HttpTestConfig{
testconfiguration.HttpPageResult(t, testconfiguration.HttpTestConfig{
Url: "http://127.0.0.1:53843/d?id=Wzol7LyY2QVczXynJtVo",
IsHtml: true,
RequiredContent: []string{"URL=./error\""},
})
// Download expired file
test.HttpPageResult(t, test.HttpTestConfig{
testconfiguration.HttpPageResult(t, testconfiguration.HttpTestConfig{
Url: "http://127.0.0.1:53843/downloadFile?id=Wzol7LyY2QVczXynJtVo",
IsHtml: true,
RequiredContent: []string{"URL=./error\""},
@@ -276,7 +276,7 @@ func TestDownloadNoPassword(t *testing.T) {
func TestDownloadPagePassword(t *testing.T) {
t.Parallel()
test.HttpPageResult(t, test.HttpTestConfig{
testconfiguration.HttpPageResult(t, testconfiguration.HttpTestConfig{
Url: "http://127.0.0.1:53843/d?id=jpLXGJKigM4hjtA6T6sN",
IsHtml: true,
RequiredContent: []string{"Password required"},
@@ -284,44 +284,44 @@ func TestDownloadPagePassword(t *testing.T) {
}
func TestDownloadPageIncorrectPassword(t *testing.T) {
t.Parallel()
test.HttpPageResult(t, test.HttpTestConfig{
testconfiguration.HttpPageResult(t, testconfiguration.HttpTestConfig{
Url: "http://127.0.0.1:53843/d?id=jpLXGJKigM4hjtA6T6sN",
IsHtml: true,
RequiredContent: []string{"Incorrect password!"},
Method: "POST",
PostValues: []test.PostBody{{"password", "incorrect"}},
PostValues: []testconfiguration.PostBody{{"password", "incorrect"}},
})
}
func TestDownloadIncorrectPasswordCookie(t *testing.T) {
t.Parallel()
test.HttpPageResult(t, test.HttpTestConfig{
testconfiguration.HttpPageResult(t, testconfiguration.HttpTestConfig{
Url: "http://127.0.0.1:53843/d?id=jpLXGJKigM4hjtA6T6sN",
IsHtml: true,
RequiredContent: []string{"Password required"},
Cookies: []test.Cookie{{"pjpLXGJKigM4hjtA6T6sN", "invalid"}},
Cookies: []testconfiguration.Cookie{{"pjpLXGJKigM4hjtA6T6sN", "invalid"}},
})
}
func TestDownloadIncorrectPassword(t *testing.T) {
t.Parallel()
test.HttpPageResult(t, test.HttpTestConfig{
testconfiguration.HttpPageResult(t, testconfiguration.HttpTestConfig{
Url: "http://127.0.0.1:53843/downloadFile?id=jpLXGJKigM4hjtA6T6sN",
IsHtml: true,
RequiredContent: []string{"URL=./d?id=jpLXGJKigM4hjtA6T6sN"},
Cookies: []test.Cookie{{"pjpLXGJKigM4hjtA6T6sN", "invalid"}},
Cookies: []testconfiguration.Cookie{{"pjpLXGJKigM4hjtA6T6sN", "invalid"}},
})
}
func TestDownloadCorrectPassword(t *testing.T) {
t.Parallel()
// Submit download page correct password
cookies := test.HttpPageResult(t, test.HttpTestConfig{
cookies := testconfiguration.HttpPageResult(t, testconfiguration.HttpTestConfig{
Url: "http://127.0.0.1:53843/d?id=jpLXGJKigM4hjtA6T6sN2",
IsHtml: true,
RequiredContent: []string{"URL=./d?id=jpLXGJKigM4hjtA6T6sN2"},
Method: "POST",
PostValues: []test.PostBody{{"password", "123"}},
PostValues: []testconfiguration.PostBody{{"password", "123"}},
})
pwCookie := ""
for _, cookie := range cookies {
@@ -334,23 +334,23 @@ func TestDownloadCorrectPassword(t *testing.T) {
t.Error("Cookie not set")
}
// Show download page correct password
test.HttpPageResult(t, test.HttpTestConfig{
testconfiguration.HttpPageResult(t, testconfiguration.HttpTestConfig{
Url: "http://127.0.0.1:53843/d?id=jpLXGJKigM4hjtA6T6sN2",
IsHtml: true,
RequiredContent: []string{"smallfile"},
Cookies: []test.Cookie{{"pjpLXGJKigM4hjtA6T6sN2", pwCookie}},
Cookies: []testconfiguration.Cookie{{"pjpLXGJKigM4hjtA6T6sN2", pwCookie}},
})
// Download correct password
test.HttpPageResult(t, test.HttpTestConfig{
testconfiguration.HttpPageResult(t, testconfiguration.HttpTestConfig{
Url: "http://127.0.0.1:53843/downloadFile?id=jpLXGJKigM4hjtA6T6sN2",
RequiredContent: []string{"456"},
Cookies: []test.Cookie{{"pjpLXGJKigM4hjtA6T6sN2", pwCookie}},
Cookies: []testconfiguration.Cookie{{"pjpLXGJKigM4hjtA6T6sN2", pwCookie}},
})
}
func TestDeleteFileNonAuth(t *testing.T) {
t.Parallel()
test.HttpPageResult(t, test.HttpTestConfig{
testconfiguration.HttpPageResult(t, testconfiguration.HttpTestConfig{
Url: "http://127.0.0.1:53843/delete?id=e4TjE7CokWK0giiLNxDL",
IsHtml: true,
RequiredContent: []string{"URL=./login"},
@@ -359,20 +359,20 @@ func TestDeleteFileNonAuth(t *testing.T) {
func TestDeleteFileInvalidKey(t *testing.T) {
t.Parallel()
test.HttpPageResult(t, test.HttpTestConfig{
testconfiguration.HttpPageResult(t, testconfiguration.HttpTestConfig{
Url: "http://127.0.0.1:53843/delete",
IsHtml: true,
RequiredContent: []string{"URL=./admin"},
Cookies: []test.Cookie{{
Cookies: []testconfiguration.Cookie{{
Name: "session_token",
Value: "validsession",
}},
})
test.HttpPageResult(t, test.HttpTestConfig{
testconfiguration.HttpPageResult(t, testconfiguration.HttpTestConfig{
Url: "http://127.0.0.1:53843/delete?id=",
IsHtml: true,
RequiredContent: []string{"URL=./admin"},
Cookies: []test.Cookie{{
Cookies: []testconfiguration.Cookie{{
Name: "session_token",
Value: "validsession",
}},
@@ -381,11 +381,11 @@ func TestDeleteFileInvalidKey(t *testing.T) {
func TestDeleteFile(t *testing.T) {
t.Parallel()
test.HttpPageResult(t, test.HttpTestConfig{
testconfiguration.HttpPageResult(t, testconfiguration.HttpTestConfig{
Url: "http://127.0.0.1:53843/delete?id=e4TjE7CokWK0giiLNxDL",
IsHtml: true,
RequiredContent: []string{"URL=./admin"},
Cookies: []test.Cookie{{
Cookies: []testconfiguration.Cookie{{
Name: "session_token",
Value: "validsession",
}},
@@ -394,7 +394,7 @@ func TestDeleteFile(t *testing.T) {
func TestPostUploadNoAuth(t *testing.T) {
t.Parallel()
test.HttpPostRequest(t, test.HttpTestConfig{
testconfiguration.HttpPostRequest(t, testconfiguration.HttpTestConfig{
Url: "http://127.0.0.1:53843/upload",
UploadFileName: "test/fileupload.jpg",
UploadFieldName: "file",
@@ -402,13 +402,13 @@ func TestPostUploadNoAuth(t *testing.T) {
})
}
func TestPostUpload(t *testing.T) {
test.HttpPostRequest(t, test.HttpTestConfig{
testconfiguration.HttpPostRequest(t, testconfiguration.HttpTestConfig{
Url: "http://127.0.0.1:53843/upload",
UploadFileName: "test/fileupload.jpg",
UploadFieldName: "file",
RequiredContent: []string{"{\"Result\":\"OK\"", "\"Name\":\"fileupload.jpg\"", "\"SHA256\":\"a9993e364706816aba3e25717850c26c9cd0d89d\"", "DownloadsRemaining\":3"},
ExcludedContent: []string{"\"Id\":\"\"", "HotlinkId\":\"\""},
Cookies: []test.Cookie{{
Cookies: []testconfiguration.Cookie{{
Name: "session_token",
Value: "validsession",
}},

View File

@@ -1,4 +1,4 @@
package webserver
package sessionmanager
/**
Manages the sessions for the admin user or to access password protected files
@@ -7,7 +7,7 @@ Manages the sessions for the admin user or to access password protected files
import (
"Gokapi/internal/configuration"
"Gokapi/internal/helper"
"Gokapi/internal/webserver/sessionstructure"
"Gokapi/internal/models"
"net/http"
"time"
)
@@ -15,67 +15,63 @@ import (
// If no login occurred during this time, the admin session will be deleted. Default 30 days
const cookieLifeAdmin = 30 * 24 * time.Hour
// Checks if the user is submitting a valid session token
// IsValidSession checks if the user is submitting a valid session token
// If valid session is found, useSession will be called
// Returns true if authenticated, otherwise false
func isValidSession(w http.ResponseWriter, r *http.Request) bool {
func IsValidSession(w http.ResponseWriter, r *http.Request) bool {
cookie, err := r.Cookie("session_token")
if err == nil {
sessionString := cookie.Value
if sessionString != "" {
configuration.Lock()
defer func() { configuration.UnlockAndSave() }()
configuration.LockSessions()
defer func() { configuration.UnlockSessionsAndSave() }()
_, ok := configuration.ServerSettings.Sessions[sessionString]
if ok {
return useSession(w, sessionString, true)
return useSession(w, sessionString)
}
}
}
return false
}
// Checks if a session is still valid. Changes the session string if it has
// been used for more than an hour to limit session hijacking
// useSession checks if a session is still valid. It Changes the session string
// if it has // been used for more than an hour to limit session hijacking
// Returns true if session is still valid
// Returns false if session is invalid (and deletes it)
func useSession(w http.ResponseWriter, sessionString string, isLocked bool) bool {
if !isLocked {
configuration.Lock()
defer func() { configuration.UnlockAndSave() }()
}
func useSession(w http.ResponseWriter, sessionString string) bool {
session := configuration.ServerSettings.Sessions[sessionString]
if session.ValidUntil < time.Now().Unix() {
delete(configuration.ServerSettings.Sessions, sessionString)
return false
}
if session.RenewAt < time.Now().Unix() {
createSession(w, true)
CreateSession(w, true)
delete(configuration.ServerSettings.Sessions, sessionString)
}
return true
}
// Creates a new session - called after login with correct username / password
func createSession(w http.ResponseWriter, isLocked bool) {
// CreateSession creates a new session - called after login with correct username / password
func CreateSession(w http.ResponseWriter, isLocked bool) {
if !isLocked {
configuration.Lock()
defer func() { configuration.UnlockAndSave() }()
configuration.LockSessions()
defer func() { configuration.UnlockSessionsAndSave() }()
}
sessionString := helper.GenerateRandomString(60)
configuration.ServerSettings.Sessions[sessionString] = sessionstructure.Session{
configuration.ServerSettings.Sessions[sessionString] = models.Session{
RenewAt: time.Now().Add(time.Hour).Unix(),
ValidUntil: time.Now().Add(cookieLifeAdmin).Unix(),
}
writeSessionCookie(w, sessionString, time.Now().Add(cookieLifeAdmin))
}
// Logs out user and deletes session
func logoutSession(w http.ResponseWriter, r *http.Request) {
// LogoutSession logs out user and deletes session
func LogoutSession(w http.ResponseWriter, r *http.Request) {
cookie, err := r.Cookie("session_token")
if err == nil {
configuration.Lock()
configuration.LockSessions()
delete(configuration.ServerSettings.Sessions, cookie.Value)
configuration.UnlockAndSave()
configuration.UnlockSessionsAndSave()
}
writeSessionCookie(w, "", time.Now())
}

View File

@@ -0,0 +1,92 @@
package sessionmanager
import (
"Gokapi/internal/configuration"
"Gokapi/internal/test"
"Gokapi/internal/test/testconfiguration"
"net/http"
"net/http/httptest"
"os"
"testing"
)
var newSession string
func TestMain(m *testing.M) {
testconfiguration.Create(true)
configuration.Load()
exitVal := m.Run()
testconfiguration.Delete()
os.Exit(exitVal)
}
func getRecorder(cookies []test.Cookie) (*httptest.ResponseRecorder, *http.Request) {
w := httptest.NewRecorder()
r := httptest.NewRequest("GET", "/", nil)
if cookies != nil {
for _, cookie := range cookies {
r.AddCookie(&http.Cookie{
Name: cookie.Name,
Value: cookie.Value,
Path: "/",
})
}
}
return w, r
}
func TestIsValidSession(t *testing.T) {
test.IsEqualBool(t, IsValidSession(getRecorder(nil)), false)
test.IsEqualBool(t, IsValidSession(getRecorder([]test.Cookie{{
Name: "session_token",
Value: "invalid"},
})), false)
test.IsEqualBool(t, IsValidSession(getRecorder([]test.Cookie{{
Name: "session_token",
Value: "expiredsession"},
})), false)
test.IsEqualBool(t, IsValidSession(getRecorder([]test.Cookie{{
Name: "session_token",
Value: "validsession"},
})), true)
w, r := getRecorder([]test.Cookie{{
Name: "session_token",
Value: "needsRenewal"},
})
test.IsEqualBool(t, IsValidSession(w, r), true)
cookies := w.Result().Cookies()
test.IsEqualInt(t, len(cookies), 1)
test.IsEqualString(t, cookies[0].Name, "session_token")
session := cookies[0].Value
test.IsEqualInt(t, len(session), 60)
test.IsNotEqualString(t, session, "needsRenewal")
}
func TestCreateSession(t *testing.T) {
w, _ := getRecorder(nil)
CreateSession(w, false)
cookies := w.Result().Cookies()
test.IsEqualInt(t, len(cookies), 1)
test.IsEqualString(t, cookies[0].Name, "session_token")
newSession = cookies[0].Value
test.IsEqualInt(t, len(newSession), 60)
test.IsEqualBool(t, IsValidSession(getRecorder([]test.Cookie{{
Name: "session_token",
Value: newSession},
})), true)
}
func TestLogoutSession(t *testing.T) {
test.IsEqualBool(t, IsValidSession(getRecorder([]test.Cookie{{
Name: "session_token",
Value: newSession},
})), true)
LogoutSession(getRecorder([]test.Cookie{{
Name: "session_token",
Value: newSession},
}))
test.IsEqualBool(t, IsValidSession(getRecorder([]test.Cookie{{
Name: "session_token",
Value: newSession},
})), false)
}