X-Git-Url: http://www.git.cypherpunks.ru/?p=gocheese.git;a=blobdiff_plain;f=passwd.go;h=7664dc5776ab1e795e457893f17b35cd28fbe300;hp=f3b19b833cc11bcf3dc5f8f8fbe82505c8435d78;hb=HEAD;hpb=ff8005cd81eda18fc9b0c6950c4a25000a7dff7e diff --git a/passwd.go b/passwd.go index f3b19b8..e5edc52 100644 --- a/passwd.go +++ b/passwd.go @@ -1,41 +1,51 @@ -/* -GoCheese -- Python private package repository and caching proxy -Copyright (C) 2019-2023 Sergey Matveev - 2019-2023 Elena Balakhonova - -This program is free software: you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation, version 3 of the License. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program. If not, see . -*/ +// GoCheese -- Python private package repository and caching proxy +// Copyright (C) 2019-2024 Sergey Matveev +// 2019-2024 Elena Balakhonova +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, version 3 of the License. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . package main import ( "bufio" + "context" "errors" "log" + "net/http" "os" "strings" "sync" ) var ( - Passwords map[string]Auther = make(map[string]Auther) + Passwords map[string]*User = make(map[string]*User) PasswordsM sync.RWMutex ) +type CtxUserKeyType struct{} + +var CtxUserKey CtxUserKeyType + type Auther interface { Auth(password string) bool } +type User struct { + name string + ro bool + auther Auther +} + func strToAuther(verifier string) (string, Auther, error) { st := strings.SplitN(verifier, "$", 3) if len(st) != 3 || st[0] != "" { @@ -64,8 +74,8 @@ func passwdReader(fd *os.File) bool { continue } splitted := strings.Split(t, ":") - if len(splitted) != 2 { - log.Println("wrong login:password format:", t) + if len(splitted) < 2 { + log.Println("wrong login:password[:ro] format:", t) isGood = false continue } @@ -84,9 +94,20 @@ func passwdReader(fd *os.File) bool { isGood = false continue } + var ro bool + if len(splitted) > 2 { + switch splitted[2] { + case "ro": + ro = true + default: + log.Println("wrong format of optional field:", t) + isGood = false + continue + } + } log.Println("adding password for:", login) PasswordsM.Lock() - Passwords[login] = auther + Passwords[login] = &User{name: login, ro: ro, auther: auther} PasswordsM.Unlock() } return isGood @@ -103,3 +124,27 @@ func passwdLister(fd *os.File) { fd.WriteString(login + "\n") } } + +func checkAuth(handler http.HandlerFunc) http.HandlerFunc { + return func(w http.ResponseWriter, r *http.Request) { + username, password, gotAuth := r.BasicAuth() + var user *User + if gotAuth { + PasswordsM.RLock() + user = Passwords[username] + PasswordsM.RUnlock() + } + var passwordValid bool + if gotAuth && user != nil { + passwordValid = user.auther.Auth(password) + } + if (gotAuth && user == nil) || + (user != nil && !passwordValid) || + (*AuthRequired && !gotAuth) { + log.Println(r.RemoteAddr, "unauthenticated", username) + http.Error(w, "unauthenticated", http.StatusUnauthorized) + return + } + handler(w, r.WithContext(context.WithValue(r.Context(), CtxUserKey, user))) + } +}