improve (webdav): handle user restriction and password on the webdav server

This commit is contained in:
Mickael Kerjean 2019-05-08 11:30:23 +10:00
parent 5cf8b08f84
commit b54663016c
4 changed files with 86 additions and 23 deletions

View file

@ -38,6 +38,7 @@ var (
SECRET_KEY_DERIVATE_FOR_PROOF string
SECRET_KEY_DERIVATE_FOR_ADMIN string
SECRET_KEY_DERIVATE_FOR_USER string
SECRET_KEY_DERIVATE_FOR_HASH string
)
/*
@ -49,4 +50,5 @@ func InitSecretDerivate(secret string) {
SECRET_KEY_DERIVATE_FOR_PROOF = Hash("PROOF_" + SECRET_KEY, len(SECRET_KEY))
SECRET_KEY_DERIVATE_FOR_ADMIN = Hash("ADMIN_" + SECRET_KEY, len(SECRET_KEY))
SECRET_KEY_DERIVATE_FOR_USER = Hash("USER_" + SECRET_KEY, len(SECRET_KEY))
SECRET_KEY_DERIVATE_FOR_HASH = Hash("HASH_" + SECRET_KEY, len(SECRET_KEY))
}

View file

@ -132,7 +132,7 @@ func ShareVerifyProof(ctx App, res http.ResponseWriter, req *http.Request) {
}
// 3) process the proof sent by the user
submittedProof, err = model.ShareProofVerifier(&ctx, s, submittedProof);
submittedProof, err = model.ShareProofVerifier(s, submittedProof);
if err != nil {
submittedProof.Error = NewString(err.Error())
SendSuccessResult(res, submittedProof)

View file

@ -1,6 +1,8 @@
package middleware
import (
"bytes"
"encoding/base64"
"encoding/json"
"fmt"
. "github.com/mickael-kerjean/filestash/server/common"
@ -185,6 +187,38 @@ func _extractShare(req *http.Request) (Share, error) {
}
var verifiedProof []model.Proof = model.ShareProofGetAlreadyVerified(req)
username, password := func(authHeader string) (string, string){
decoded, err := base64.StdEncoding.DecodeString(
strings.TrimPrefix(authHeader, "Basic "),
)
if err != nil {
return "", ""
}
s := bytes.Split(decoded, []byte(":"))
if len(s) < 2 {
return "", ""
}
p := string(bytes.Join(s[1:], []byte(":")))
usr := regexp.MustCompile(`^(.*)\[([0-9a-zA-Z]+)\]$`).FindStringSubmatch(string(s[0]))
if len(usr) != 3 {
return "", p
}
if Hash(usr[1] + SECRET_KEY_DERIVATE_FOR_HASH, 10) != usr[2] {
return "", p
}
return usr[1], p
}(req.Header.Get("Authorization"))
if s.Users != nil && username != "" {
if v, ok := model.ShareProofVerifierEmail(*s.Users, username); ok {
verifiedProof = append(verifiedProof, model.Proof{ Key: "email", Value: v })
}
}
if s.Password != nil && password != "" {
if v, ok := model.ShareProofVerifierPassword(*s.Password, password); ok {
verifiedProof = append(verifiedProof, model.Proof{ Key: "password", Value: v })
}
}
var requiredProof []model.Proof = model.ShareProofGetRequired(s)
var remainingProof []model.Proof = model.ShareProofCalculateRemainings(requiredProof, verifiedProof)
if len(remainingProof) != 0 {

View file

@ -128,18 +128,20 @@ func ShareDelete(id string) error {
return err
}
func ShareProofVerifier(ctx *App, s Share, proof Proof) (Proof, error) {
func ShareProofVerifier(s Share, proof Proof) (Proof, error) {
p := proof
if proof.Key == "password" {
if s.Password == nil {
return p, NewError("No password required", 400)
}
time.Sleep(1000 * time.Millisecond)
if err := bcrypt.CompareHashAndPassword([]byte(*s.Password), []byte(proof.Value)); err != nil {
v, ok := ShareProofVerifierPassword(*s.Password, proof.Value);
if ok == false {
time.Sleep(1000 * time.Millisecond)
return p, ErrInvalidPassword
}
p.Value = *s.Password
p.Value = v
}
if proof.Key == "email" {
@ -147,23 +149,12 @@ func ShareProofVerifier(ctx *App, s Share, proof Proof) (Proof, error) {
if s.Users == nil {
return p, NewError("Authentication not required", 400)
}
var user *string
for _, possibleUser := range strings.Split(*s.Users, ",") {
possibleUser := strings.Trim(possibleUser, " ")
if proof.Value == possibleUser {
user = &possibleUser
break
} else if possibleUser[0:1] == "*" {
if strings.HasSuffix(proof.Value, strings.TrimPrefix(possibleUser, "*")) {
user = &possibleUser
break
}
}
}
if user == nil {
v, ok := ShareProofVerifierEmail(*s.Users, proof.Value)
if ok == false {
time.Sleep(1000 * time.Millisecond)
return p, NewError("No access was provided", 400)
return p, ErrNotAuthorized
}
user := v
// prepare the verification code
stmt, err := DB.Prepare("INSERT INTO Verification(key, code) VALUES(?, ?)");
@ -171,7 +162,7 @@ func ShareProofVerifier(ctx *App, s Share, proof Proof) (Proof, error) {
return p, err
}
code := RandomString(4)
if _, err := stmt.Exec("email::" + *user, code); err != nil {
if _, err := stmt.Exec("email::" + user, code); err != nil {
return p, err
}
@ -180,8 +171,9 @@ func ShareProofVerifier(ctx *App, s Share, proof Proof) (Proof, error) {
t := template.New("email")
t.Parse(TmplEmailVerification())
t.Execute(&b, struct{
Code string
}{code})
Code string
Username string
}{code, networkDriveUsernameEnc(user)})
p.Key = "code"
p.Value = ""
@ -249,6 +241,34 @@ func ShareProofVerifier(ctx *App, s Share, proof Proof) (Proof, error) {
return p, nil
}
func ShareProofVerifierPassword(hashed string, given string) (string, bool) {
if err := bcrypt.CompareHashAndPassword([]byte(hashed), []byte(given)); err != nil {
return "", false
}
return hashed, true
}
func ShareProofVerifierEmail(users string, wanted string) (string, bool) {
s := strings.Split(users, ",")
user := ""
for _, possibleUser := range s {
possibleUser := strings.Trim(possibleUser, " ")
if wanted == possibleUser {
user = possibleUser
break
} else if possibleUser[0:1] == "*" {
if strings.HasSuffix(wanted, strings.TrimPrefix(possibleUser, "*")) {
user = possibleUser
break
}
}
}
if user == "" {
return "", false
}
return user, true
}
func ShareProofGetAlreadyVerified(req *http.Request) []Proof {
var p []Proof
var cookieValue string
@ -303,6 +323,8 @@ func ShareProofCalculateRemainings(ref []Proof, mem []Proof) []Proof {
func shareProofAreEquivalent(ref Proof, p Proof) bool {
if ref.Key != p.Key {
return false
} else if ref.Value != "" && ref.Value == p.Value {
return true
}
for _, chunk := range strings.Split(ref.Value, ",") {
chunk = strings.Trim(chunk, " ")
@ -581,6 +603,7 @@ func TmplEmailVerification() string {
</td>
</tr>
</table>
<div style="margin-top:10px;font-style:italic;font-size:0.9em;">When mounted as a network drive, you can authenticate as: {{.Username}}</div>
</td>
</tr>
@ -609,3 +632,7 @@ func TmplEmailVerification() string {
</html>
`
}
func networkDriveUsernameEnc(email string) string {
return email + "[" + Hash(email + SECRET_KEY_DERIVATE_FOR_HASH, 10) + "]"
}