diff --git a/Makefile b/Makefile index 2bcdc1e1..bddcfd7e 100644 --- a/Makefile +++ b/Makefile @@ -24,7 +24,7 @@ # THE SOFTWARE. ### -RELEASE_VERSION="1.2-RC1" +RELEASE_VERSION="1.2-RC2" RELEASE_DIR="release/plik-$(RELEASE_VERSION)" RELEASE_TARGETS=darwin-386 darwin-amd64 freebsd-386 \ freebsd-amd64 linux-386 linux-amd64 linux-arm openbsd-386 \ diff --git a/README.md b/README.md index 256099dc..4f5f9f5e 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ # Plik -Plik is an simple and powerful file uploading system written in golang. +Plik is a scalable & friendly temporary file upload system ( wetransfer like ) in golang. ### Main features - Multiple data backends : File, OpenStack Swift, WeedFS @@ -21,7 +21,7 @@ Plik is an simple and powerful file uploading system written in golang. - Upload restriction : Source IP / Token ### Version -1.2-RC1 +1.2-RC2 ### Installation @@ -295,4 +295,5 @@ The screenshot is then removed of your home directory to avoid garbage. Contributions are welcome, feel free to open issues and/or submit pull requests. Please make your pull requests against the current development (RC) branch, not against master. -Please run/update the test suite using the makefile test target. \ No newline at end of file +Please run/update the test suite using the makefile test target. + diff --git a/client/config/config.go b/client/config/config.go index 9ae336a7..b1a2e8b4 100644 --- a/client/config/config.go +++ b/client/config/config.go @@ -159,7 +159,16 @@ func Load() (err error) { // Stat file _, err = os.Stat(configFile) if err != nil { - // File not present. Ask for domain + // File not found. + + // Check if quiet mode ( you'll have to pass --server flag ) + for _, arg := range os.Args[1:] { + if arg == "-q" || arg == "--quiet" { + return + } + } + + // Ask for domain var domain string fmt.Printf("Please enter your plik domain [default:http://127.0.0.1:8080] : ") _, err := fmt.Scanf("%s", &domain) @@ -315,11 +324,11 @@ func UnmarshalArgs(arguments map[string]interface{}) (err error) { // Configure the archive backend archiveBackend, err = archive.NewArchiveBackend(Config.ArchiveMethod, Config.ArchiveOptions) if err != nil { - return fmt.Errorf("Invalid archive params : %s\n", err) + return fmt.Errorf("Invalid archive params : %s", err) } err = archiveBackend.Configure(arguments) if err != nil { - return fmt.Errorf("Invalid archive params : %s\n", err) + return fmt.Errorf("Invalid archive params : %s", err) } Debug("Archive backend configuration : " + utils.Sdump(archiveBackend.GetConfiguration())) @@ -380,7 +389,7 @@ func UnmarshalArgs(arguments map[string]interface{}) (err error) { } ttl, err := strconv.Atoi(ttlStr) if err != nil { - return fmt.Errorf("Invalid TTL %s", arguments["--ttl"].(string)) + return fmt.Errorf("Invalid TTL %s\n", arguments["--ttl"].(string)) } Upload.TTL = ttl * mul } @@ -397,11 +406,11 @@ func UnmarshalArgs(arguments map[string]interface{}) (err error) { // Configure crypto backend cryptoBackend, err = crypto.NewCryptoBackend(secureMethod, Config.SecureOptions) if err != nil { - return fmt.Errorf("Invalid secure params : %s\n", err) + return fmt.Errorf("Invalid secure params : %s", err) } err = cryptoBackend.Configure(arguments) if err != nil { - return fmt.Errorf("Invalid secure params : %s\n", err) + return fmt.Errorf("Invalid secure params : %s", err) } Debug("Crypto backend configuration : " + utils.Sdump(cryptoBackend.GetConfiguration())) diff --git a/client/plik.go b/client/plik.go index ad97608d..6d54477b 100644 --- a/client/plik.go +++ b/client/plik.go @@ -114,7 +114,7 @@ Options: // Unmarshal arguments in configuration err = config.UnmarshalArgs(arguments) if err != nil { - fmt.Printf("%s", err) + fmt.Printf("%s\n", err) os.Exit(1) } @@ -477,6 +477,11 @@ func updateClient(updateFlag bool) (err error) { } resp, err := makeRequest(req) + if err != nil { + err = fmt.Errorf("Unable to get server version : %s", err) + return + } + defer resp.Body.Close() if resp.StatusCode == 200 { diff --git a/client/plik.sh b/client/plik.sh index a36fb5a1..94ea59e6 100755 --- a/client/plik.sh +++ b/client/plik.sh @@ -38,28 +38,47 @@ function setTtl() { ## Vars # PLIK_URL=${PLIK_URL-"http://127.0.0.1:8080"} -PASSPHRASE="" +PLIK_TOKEN=${PLIK_TOKEN-""} QUIET=false SECURE=false +PASSPHRASE="" ARCHIVE=false ONESHOT=false REMOVABLE=false TTL=0 +# +## Read ~/.plikrc file +# + +PLIKRC=${PLIKRC-"$HOME/.plikrc"} +if [ -e "$PLIKRC" ]; then + URL=$(grep URL $PLIKRC | grep -Po '(http[^\"]*)') + if [ "$URL" != "" ]; then + PLIK_URL=$URL + fi + TOKEN=$(grep Token $PLIKRC | sed -n 's/^.*"\(.*\)".*$/\1/p' ) + if [ "$TOKEN" != "" ]; then + PLIK_TOKEN=$TOKEN + fi +fi + # ## Parse arguments # -declare -a files +declare -a files while [ $# -gt 0 ] ; do case "$1" in - -q) QUIET=true ; shift ;; - -s) SECURE=true ; shift ;; - -a) ARCHIVE=true ; shift ;; + -u) shift ; PLIK_URL="$1" ; shift ;; + -T) shift ; PLIK_TOKEN="$1" ; shift ;; -o) ONESHOT=true ; shift ;; -r) REMOVABLE=true ; shift ;; -t) shift ; setTtl $1 ; shift ;; + -a) ARCHIVE=true ; shift ;; + -s) SECURE=true ; shift ;; -p) SECURE=true ; shift ; PASSPHRASE="$1" ; shift ;; + -q) QUIET=true ; shift ;; --) shift ;; -*) qecho "bad option '$1'" ; exit 1 ;; *) files=("${files[@]}" "$1") ; shift ;; @@ -70,26 +89,37 @@ if [ "${#files[@]}" == 0 ]; then qecho "No files specified !" exit 1 fi -if [ -e "$HOME/.plikrc" ]; then - URL=$(grep URL ~/.plikrc | grep -Po '(http[^\"]*)') - - if [ "$URL" != "" ]; then - PLIK_URL=$URL - fi -fi - # ## Create new upload # -qecho -e "Creating upload on $PLIK_URL...\n" +if [ "$PLIK_TOKEN" != "" ]; then + AUTH_TOKEN_HEADER="-H \"X-PlikToken: $PLIK_TOKEN\"" +fi + OPTIONS="{ \"OneShot\" : $ONESHOT, \"Removable\" : $REMOVABLE, \"Ttl\" : $TTL }" -NEW_UPLOAD_RESP=$(curl -s -X POST -d "$OPTIONS" ${PLIK_URL}/upload) +qecho -e "Create new upload on $PLIK_URL...\n" + +CREATE_UPLOAD_CMD="curl -s -X POST $AUTH_TOKEN_HEADER -d '$OPTIONS' ${PLIK_URL}/upload" +NEW_UPLOAD_RESP=$(eval $CREATE_UPLOAD_CMD) UPLOAD_ID=$(echo $NEW_UPLOAD_RESP | jsonValue id) + +# Handle error +if [ "$UPLOAD_ID" == "" ]; then + ERROR_MSG=$(echo $NEW_UPLOAD_RESP | jsonValue message) + if [ "$ERROR_MSG" != "" ]; then + echo $ERROR_MSG + elif [ "$NEW_UPLOAD_RESP" != "" ]; then + echo $NEW_UPLOAD_RESP + fi + exit 1 +fi + UPLOAD_TOKEN=$(echo $NEW_UPLOAD_RESP | jsonValue uploadToken) -qecho -e " --> ${green}$PLIK_URL/#/?id=$UPLOAD_ID${endColor}\n" +UPLOAD_TOKEN_HEADER="-H \"X-UploadToken: $UPLOAD_TOKEN\"" +qecho -e " --> ${green}$PLIK_URL/#/?id=$UPLOAD_ID${endColor}\n" # ## Test if we have to archive @@ -154,13 +184,25 @@ do fi if [ "$STDIN" == true ]; then - UPLOAD_COMMAND+="curl -s -X POST --header \"X-UploadToken: $UPLOAD_TOKEN\" -F \"file=@-;filename=$FILENAME\" $PLIK_URL/file/$UPLOAD_ID" + UPLOAD_COMMAND+="curl -s -X POST $AUTH_TOKEN_HEADER $UPLOAD_TOKEN_HEADER -F \"file=@-;filename=$FILENAME\" $PLIK_URL/file/$UPLOAD_ID" else - UPLOAD_COMMAND+="curl -s -X POST --header \"X-UploadToken: $UPLOAD_TOKEN\" -F \"file=@$FILE;filename=$FILENAME\" $PLIK_URL/file/$UPLOAD_ID" + UPLOAD_COMMAND+="curl -s -X POST $AUTH_TOKEN_HEADER $UPLOAD_TOKEN_HEADER -F \"file=@$FILE;filename=$FILENAME\" $PLIK_URL/file/$UPLOAD_ID" fi FILE_RESP=$(eval $UPLOAD_COMMAND) FILE_ID=$(echo $FILE_RESP | jsonValue id) + + # Handle error + if [ "$FILE_ID" == "" ]; then + ERROR_MSG=$(echo $FILE_RESP | jsonValue message) + if [ "$ERROR_MSG" != "" ]; then + echo $ERROR_MSG + elif [ "$FILE_RESP" != "" ]; then + echo $FILE_RESP + fi + exit 1 + fi + FILE_MD5=$(echo $FILE_RESP | jsonValue fileMd5) FILE_NAME=$(echo $FILE_RESP | jsonValue fileName) FILE_STATUS=$(echo $FILE_RESP | jsonValue status) diff --git a/client/test.sh b/client/test.sh index cc72bb83..7cbf6221 100755 --- a/client/test.sh +++ b/client/test.sh @@ -393,6 +393,26 @@ grep "$URL/file/.*/.*/FILE1" $CLIENT_LOG >/dev/null 2>/dev/null echo "OK" +#--------------------------------------------- + +echo -n " - quiet and no .plikrc : " + +before +rm $PLIKRC +cp $SPECIMEN $TMPDIR/upload/FILE1 +upload -q +test $(cat $CLIENT_LOG | wc -l) -eq 1 +grep "$URL/file/.*/.*/FILE1" $CLIENT_LOG >/dev/null 2>/dev/null + +before +rm $PLIKRC +cp $SPECIMEN $TMPDIR/upload/FILE1 +upload --quiet +test $(cat $CLIENT_LOG | wc -l) -eq 1 +grep "$URL/file/.*/.*/FILE1" $CLIENT_LOG >/dev/null 2>/dev/null + +echo "OK" + ### # Tar archive ### diff --git a/server/Godeps/Godeps.json b/server/Godeps/Godeps.json index 63909766..bd070ae4 100644 --- a/server/Godeps/Godeps.json +++ b/server/Godeps/Godeps.json @@ -1,6 +1,7 @@ { "ImportPath": "github.com/root-gg/plik/server", "GoVersion": "go1.5.1", + "GodepVersion": "v74", "Packages": [ "./..." ], @@ -34,7 +35,7 @@ }, { "ImportPath": "github.com/facebookgo/httpdown", - "Rev": "1fa03998d20119dfe4ef73f56a638d83048052e2" + "Rev": "a3b1354551a26449fbe05f5d855937f6e7acbd71" }, { "ImportPath": "github.com/facebookgo/stats", diff --git a/server/Godeps/_workspace/src/github.com/facebookgo/httpdown/.travis.yml b/server/Godeps/_workspace/src/github.com/facebookgo/httpdown/.travis.yml index 3a2548e2..ea316cfe 100644 --- a/server/Godeps/_workspace/src/github.com/facebookgo/httpdown/.travis.yml +++ b/server/Godeps/_workspace/src/github.com/facebookgo/httpdown/.travis.yml @@ -1,7 +1,7 @@ language: go go: - - 1.4 + - 1.6 before_install: - go get -v golang.org/x/tools/cmd/vet diff --git a/server/Godeps/_workspace/src/github.com/facebookgo/httpdown/httpdown.go b/server/Godeps/_workspace/src/github.com/facebookgo/httpdown/httpdown.go index 16fc051c..056488ea 100644 --- a/server/Godeps/_workspace/src/github.com/facebookgo/httpdown/httpdown.go +++ b/server/Godeps/_workspace/src/github.com/facebookgo/httpdown/httpdown.go @@ -5,7 +5,6 @@ package httpdown import ( "crypto/tls" "fmt" - "log" "net" "net/http" "os" @@ -349,7 +348,6 @@ func ListenAndServe(s *http.Server, hd *HTTP) error { if err != nil { return err } - log.Printf("serving on http://%s/ with pid %d\n", s.Addr, os.Getpid()) waiterr := make(chan error, 1) go func() { @@ -365,9 +363,8 @@ func ListenAndServe(s *http.Server, hd *HTTP) error { if err != nil { return err } - case s := <-signals: + case <-signals: signal.Stop(signals) - log.Printf("signal received: %s\n", s) if err := hs.Stop(); err != nil { return err } @@ -375,6 +372,5 @@ func ListenAndServe(s *http.Server, hd *HTTP) error { return err } } - log.Println("exiting") return nil } diff --git a/server/Godeps/_workspace/src/github.com/facebookgo/httpdown/httpdown_example/main.go b/server/Godeps/_workspace/src/github.com/facebookgo/httpdown/httpdown_example/main.go deleted file mode 100644 index 7eaca258..00000000 --- a/server/Godeps/_workspace/src/github.com/facebookgo/httpdown/httpdown_example/main.go +++ /dev/null @@ -1,43 +0,0 @@ -package main - -import ( - "flag" - "fmt" - "net/http" - "os" - "time" - - "github.com/root-gg/plik/server/Godeps/_workspace/src/github.com/facebookgo/httpdown" -) - -func handler(w http.ResponseWriter, r *http.Request) { - duration, err := time.ParseDuration(r.FormValue("duration")) - if err != nil { - http.Error(w, err.Error(), 400) - return - } - fmt.Fprintf(w, "going to sleep %s with pid %d\n", duration, os.Getpid()) - w.(http.Flusher).Flush() - time.Sleep(duration) - fmt.Fprintf(w, "slept %s with pid %d\n", duration, os.Getpid()) -} - -func main() { - server := &http.Server{ - Addr: "127.0.0.1:8080", - Handler: http.HandlerFunc(handler), - } - hd := &httpdown.HTTP{ - StopTimeout: 10 * time.Second, - KillTimeout: 1 * time.Second, - } - - flag.StringVar(&server.Addr, "addr", server.Addr, "http address") - flag.DurationVar(&hd.StopTimeout, "stop-timeout", hd.StopTimeout, "stop timeout") - flag.DurationVar(&hd.KillTimeout, "kill-timeout", hd.KillTimeout, "kill timeout") - flag.Parse() - - if err := httpdown.ListenAndServe(server, hd); err != nil { - panic(err) - } -} diff --git a/server/handlers/getFile.go b/server/handlers/getFile.go index e0504728..d3757902 100644 --- a/server/handlers/getFile.go +++ b/server/handlers/getFile.go @@ -67,7 +67,7 @@ func GetFile(ctx *juliet.Context, resp http.ResponseWriter, req *http.Request) { // If upload has OneShot option, test if file has not been already downloaded once if upload.OneShot && file.Status == "downloaded" { log.Warningf("File %s has already been downloaded", file.Name) - common.Fail(ctx, req, resp, "File %s has already been downloaded", 404) + common.Fail(ctx, req, resp, fmt.Sprintf("File %s has already been downloaded", file.Name), 404) return } diff --git a/server/handlers/ovh.go b/server/handlers/ovh.go index 73eedc4f..b1238fe7 100644 --- a/server/handlers/ovh.go +++ b/server/handlers/ovh.go @@ -157,19 +157,31 @@ func OvhLogin(ctx *juliet.Context, resp http.ResponseWriter, req *http.Request) return } - // Store session jwt in secure cookie - sessionCookie := &http.Cookie{} - sessionCookie.HttpOnly = true - sessionCookie.Secure = true - sessionCookie.Name = "plik-ovh-session" - sessionCookie.Value = sessionString - sessionCookie.MaxAge = int(time.Now().Add(5 * time.Minute).Unix()) - sessionCookie.Path = "/" - http.SetCookie(resp, sessionCookie) + // Store temporary session jwt in secure cookie + ovhAuthCookie := &http.Cookie{} + ovhAuthCookie.HttpOnly = true + ovhAuthCookie.Secure = true + ovhAuthCookie.Name = "plik-ovh-session" + ovhAuthCookie.Value = sessionString + ovhAuthCookie.MaxAge = int(time.Now().Add(5 * time.Minute).Unix()) + ovhAuthCookie.Path = "/" + http.SetCookie(resp, ovhAuthCookie) resp.Write([]byte(userConsentResponse.ValidationURL)) } +// Remove temporary session cookie +func cleanOvhAuthSessionCookie(resp http.ResponseWriter) { + ovhAuthCookie := &http.Cookie{} + ovhAuthCookie.HttpOnly = true + ovhAuthCookie.Secure = true + ovhAuthCookie.Name = "plik-ovh-session" + ovhAuthCookie.Value = "" + ovhAuthCookie.MaxAge = -1 + ovhAuthCookie.Path = "/" + http.SetCookie(resp, ovhAuthCookie) +} + // OvhCallback authenticate ovh user. func OvhCallback(ctx *juliet.Context, resp http.ResponseWriter, req *http.Request) { log := common.GetLogger(ctx) @@ -188,14 +200,14 @@ func OvhCallback(ctx *juliet.Context, resp http.ResponseWriter, req *http.Reques // Get state from secure cookie ovhSessionCookie, err := req.Cookie("plik-ovh-session") - if err != nil && ovhSessionCookie != nil { + if err != nil || ovhSessionCookie == nil { log.Warning("Missing OVH session cookie") common.Fail(ctx, req, resp, "Missing OVH session cookie", 400) return } // Parse session cookie - ovhSession, err := jwt.Parse(ovhSessionCookie.Value, func(t *jwt.Token) (interface{}, error) { + ovhAuthCookie, err := jwt.Parse(ovhSessionCookie.Value, func(t *jwt.Token) (interface{}, error) { // Verify signing algorithm if _, ok := t.Method.(*jwt.SigningMethodHMAC); !ok { return nil, fmt.Errorf("Unexpected siging method : %v", t.Header["alg"]) @@ -205,22 +217,25 @@ func OvhCallback(ctx *juliet.Context, resp http.ResponseWriter, req *http.Reques }) if err != nil { log.Warningf("Invalid OVH session cookie : %s", err) + cleanOvhAuthSessionCookie(resp) common.Fail(ctx, req, resp, "Invalid OVH session cookie", 400) return } // Get OVH consumer key from session - ovhConsumerKey, ok := ovhSession.Claims["ovh-consumer-key"] + ovhConsumerKey, ok := ovhAuthCookie.Claims["ovh-consumer-key"] if !ok { log.Warning("Invalid OVH session cookie : missing ovh-consumer-key") + cleanOvhAuthSessionCookie(resp) common.Fail(ctx, req, resp, "Invalid OVH session cookie : missing ovh-consumer-key", 500) return } // Get OVH API endpoint - endpoint, ok := ovhSession.Claims["ovh-api-endpoint"] + endpoint, ok := ovhAuthCookie.Claims["ovh-api-endpoint"] if !ok { log.Warning("Invalid OVH session cookie : missing ovh-api-endpoint") + cleanOvhAuthSessionCookie(resp) common.Fail(ctx, req, resp, "Invalid OVH session cookie : missing ovh-api-endpoint", 400) return } @@ -230,6 +245,7 @@ func OvhCallback(ctx *juliet.Context, resp http.ResponseWriter, req *http.Reques ovhReq, err := http.NewRequest("GET", url, nil) if err != nil { log.Warningf("Unable to create new http GET request to %s : %s", url, err) + cleanOvhAuthSessionCookie(resp) common.Fail(ctx, req, resp, "Unable to create new http GET request to OVH API", 500) return } @@ -256,6 +272,7 @@ func OvhCallback(ctx *juliet.Context, resp http.ResponseWriter, req *http.Reques ovhResp, err := client.Do(ovhReq) if err != nil { log.Warningf("Error with ovh API %s : %s", url, err) + cleanOvhAuthSessionCookie(resp) common.Fail(ctx, req, resp, "Error with ovh API", 500) return } @@ -263,6 +280,7 @@ func OvhCallback(ctx *juliet.Context, resp http.ResponseWriter, req *http.Reques ovhRespBody, err := decodeOVHResponse(ovhResp) if err != nil { log.Warningf("Error with ovh API %s : %s", url, err) + cleanOvhAuthSessionCookie(resp) common.Fail(ctx, req, resp, fmt.Sprintf("Error with ovh API : %s", err), 500) return } @@ -272,6 +290,7 @@ func OvhCallback(ctx *juliet.Context, resp http.ResponseWriter, req *http.Reques err = json.Unmarshal(ovhRespBody, &userInfo) if err != nil { log.Warningf("Unable to unserialize OVH API response : %s", err) + cleanOvhAuthSessionCookie(resp) common.Fail(ctx, req, resp, "Unable to unserialize OVH API response", 500) return } @@ -281,7 +300,9 @@ func OvhCallback(ctx *juliet.Context, resp http.ResponseWriter, req *http.Reques // Get user from metadata backend user, err := metadataBackend.GetMetaDataBackend().GetUser(ctx, userID, "") if err != nil { - err = fmt.Errorf("Unable to get user from metadata backend : %s", err) + log.Warningf("Unable to get user from metadata backend : %s", err) + cleanOvhAuthSessionCookie(resp) + common.Fail(ctx, req, resp, "Unable to get user from metadata backend", 500) return } @@ -298,11 +319,13 @@ func OvhCallback(ctx *juliet.Context, resp http.ResponseWriter, req *http.Reques err = metadataBackend.GetMetaDataBackend().SaveUser(ctx, user) if err != nil { log.Warningf("Unable to save user to metadata backend : %s", err) + cleanOvhAuthSessionCookie(resp) common.Fail(ctx, req, resp, "Authentification error", 403) return } } else { log.Warning("Unable to create user from untrusted source IP address") + cleanOvhAuthSessionCookie(resp) common.Fail(ctx, req, resp, "Unable to create user from untrusted source IP address", 403) return } @@ -317,6 +340,7 @@ func OvhCallback(ctx *juliet.Context, resp http.ResponseWriter, req *http.Reques xsrfToken, err := uuid.NewV4() if err != nil { log.Warning("Unable to generate xsrf token") + cleanOvhAuthSessionCookie(resp) common.Fail(ctx, req, resp, "Unable to generate xsrf token", 500) return } @@ -325,10 +349,14 @@ func OvhCallback(ctx *juliet.Context, resp http.ResponseWriter, req *http.Reques sessionString, err := session.SignedString([]byte(common.Config.OvhAPISecret)) if err != nil { log.Warningf("Unable to sign session cookie : %s", err) + cleanOvhAuthSessionCookie(resp) common.Fail(ctx, req, resp, "Authentification error", 403) return } + // Remove temporary ovh auth session cookie + cleanOvhAuthSessionCookie(resp) + // Store session jwt in secure cookie sessionCookie := &http.Cookie{} sessionCookie.HttpOnly = true diff --git a/server/metadataBackend/bolt/bolt.go b/server/metadataBackend/bolt/bolt.go index a76ac21c..16559dfa 100644 --- a/server/metadataBackend/bolt/bolt.go +++ b/server/metadataBackend/bolt/bolt.go @@ -610,7 +610,7 @@ func (bmb *MetadataBackend) GetUploadsToRemove(ctx *juliet.Context) (ids []strin } // Extract upload id from key ( 16 last bytes ) - ids = append(ids, string(k[8:])) + ids = append(ids, string(k[10:])) } return nil diff --git a/server/middleware/upload.go b/server/middleware/upload.go index 9baea501..7087e51d 100644 --- a/server/middleware/upload.go +++ b/server/middleware/upload.go @@ -81,11 +81,29 @@ func Upload(ctx *juliet.Context, next http.Handler) http.Handler { forbidden := func() { resp.Header().Set("WWW-Authenticate", "Basic realm=\"plik\"") + + // Shouldn't redirect here to let the browser ask for credentials and retry + ctx.Set("redirect", false) + common.Fail(ctx, req, resp, "Please provide valid credentials to access this upload", 401) } + // Check upload token + uploadToken := req.Header.Get("X-UploadToken") + if uploadToken != "" && uploadToken == upload.UploadToken { + upload.IsAdmin = true + } else { + // Check if upload belongs to user + if common.Config.Authentication && upload.User != "" { + user := common.GetUser(ctx) + if user != nil && user.ID == upload.User { + upload.IsAdmin = true + } + } + } + // Handle basic auth if upload is password protected - if upload.ProtectedByPassword { + if upload.ProtectedByPassword && !upload.IsAdmin { if req.Header.Get("Authorization") == "" { log.Warning("Missing Authorization header") forbidden() @@ -120,20 +138,6 @@ func Upload(ctx *juliet.Context, next http.Handler) http.Handler { } } - // Check upload token - uploadToken := req.Header.Get("X-UploadToken") - if uploadToken != "" && uploadToken == upload.UploadToken { - upload.IsAdmin = true - } else { - // Check if upload belongs to user - if common.Config.Authentication && upload.User != "" { - user := common.GetUser(ctx) - if user != nil && user.ID == upload.User { - upload.IsAdmin = true - } - } - } - next.ServeHTTP(resp, req) }) } diff --git a/server/plik.go b/server/plik.go index a9b49e91..1aff438c 100644 --- a/server/plik.go +++ b/server/plik.go @@ -107,11 +107,11 @@ func main() { r.Handle("/file/{uploadID}/{fileID}/{filename}", tokenChain.Append(middleware.Upload, middleware.File).Then(handlers.AddFile)).Methods("POST") r.Handle("/file/{uploadID}/{fileID}/{filename}", authChain.Append(middleware.Upload, middleware.File).Then(handlers.RemoveFile)).Methods("DELETE") r.Handle("/file/{uploadID}/{fileID}/{filename}", authChainWithRedirect.Append(middleware.Upload, middleware.File).Then(handlers.GetFile)).Methods("HEAD", "GET") - r.Handle("/file/{uploadID}/{fileID}/{filename}/yubikey/{yubikey}", authChainWithRedirect.Then(handlers.GetFile)).Methods("GET") + r.Handle("/file/{uploadID}/{fileID}/{filename}/yubikey/{yubikey}", authChainWithRedirect.Append(middleware.Upload, middleware.File).Then(handlers.GetFile)).Methods("HEAD", "GET") r.Handle("/stream/{uploadID}/{fileID}/{filename}", tokenChain.Append(middleware.Upload, middleware.File).Then(handlers.AddFile)).Methods("POST") r.Handle("/stream/{uploadID}/{fileID}/{filename}", authChain.Append(middleware.Upload, middleware.File).Then(handlers.RemoveFile)).Methods("DELETE") r.Handle("/stream/{uploadID}/{fileID}/{filename}", authChainWithRedirect.Append(middleware.Upload, middleware.File).Then(handlers.GetFile)).Methods("HEAD", "GET") - r.Handle("/stream/{uploadID}/{fileID}/{filename}/yubikey/{yubikey}", authChainWithRedirect.Then(handlers.GetFile)).Methods("GET") + r.Handle("/stream/{uploadID}/{fileID}/{filename}/yubikey/{yubikey}", authChainWithRedirect.Append(middleware.Upload, middleware.File).Then(handlers.GetFile)).Methods("HEAD", "GET") r.Handle("/auth/google/login", authChain.Then(handlers.GoogleLogin)).Methods("GET") r.Handle("/auth/google/callback", stdChainWithRedirect.Then(handlers.GoogleCallback)).Methods("GET") r.Handle("/auth/ovh/login", authChain.Then(handlers.OvhLogin)).Methods("GET") @@ -133,10 +133,12 @@ func main() { // Start HTTP server var err error var server *http.Server + var proto string address := common.Config.ListenAddress + ":" + strconv.Itoa(common.Config.ListenPort) if common.Config.SslEnabled { + proto = "https" // Load cert cert, err := tls.LoadX509KeyPair(common.Config.SslCert, common.Config.SslKey) @@ -147,9 +149,12 @@ func main() { tlsConfig := &tls.Config{MinVersion: tls.VersionTLS10, Certificates: []tls.Certificate{cert}} server = &http.Server{Addr: address, Handler: r, TLSConfig: tlsConfig} } else { + proto = "http" server = &http.Server{Addr: address, Handler: r} } + log.Infof("Starting http server at %s://%s", proto, address) + err = httpdown.ListenAndServe(server, hd) if err != nil { log.Fatalf("Unable to start HTTP server : %s", err) diff --git a/server/plikd.cfg b/server/plikd.cfg index e0cce907..fec5013b 100644 --- a/server/plikd.cfg +++ b/server/plikd.cfg @@ -7,7 +7,7 @@ # Global params # -LogLevel = "DEBUG" # Other levels : DEBUG, INFO, WARNING, CRITICAL, FATAL +LogLevel = "INFO" # Other levels : DEBUG, INFO, WARNING, CRITICAL, FATAL ListenPort = 8080 ListenAddress = "0.0.0.0" MaxFileSize = 10737418240 # 10GB @@ -49,6 +49,11 @@ StreamMode = true # Enable stream mode ## # Data backend is for storing raw files # +# Example using File : +# +# [DataBackendConfig] +# Directory = "files" +# # Example using OpenStack Swift : # # [DataBackendConfig] @@ -58,6 +63,12 @@ StreamMode = true # Enable stream mode # Container = "plik" # Password = "#######" # +# Example using SeaweedFS : +# +# [DataBackendConfig] +# MasterURL = "http://127.0.0.1:9333" +# ReplicationPattern = "000" +# [DataBackendConfig] Directory = "files" @@ -67,6 +78,16 @@ Directory = "files" ## # Metadata backend is for storing upload information (file names, size, md5, statuses,...) # +# Example using BoltDB : +# +# [MetadataBackendConfig] +# Path = "plik.db" +# +# Example using File (deprecated) : +# +# [MetadataBackendConfig] +# Directory = "files" +# # Example using MongoDB : # # [MetadataBackendConfig] @@ -79,7 +100,7 @@ Directory = "files" # [MetadataBackendConfig] -Directory = "files" +Path = "plik.db" ####