1 // GoCheese -- Python private package repository and caching proxy
2 // Copyright (C) 2019-2024 Sergey Matveev <stargrave@stargrave.org>
3 // 2019-2024 Elena Balakhonova <balakhonova_e@riseup.net>
5 // This program is free software: you can redistribute it and/or modify
6 // it under the terms of the GNU General Public License as published by
7 // the Free Software Foundation, version 3 of the License.
9 // This program is distributed in the hope that it will be useful,
10 // but WITHOUT ANY WARRANTY; without even the implied warranty of
11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 // GNU General Public License for more details.
14 // You should have received a copy of the GNU General Public License
15 // along with this program. If not, see <http://www.gnu.org/licenses/>.
31 Passwords map[string]*User = make(map[string]*User)
32 PasswordsM sync.RWMutex
35 type CtxUserKeyType struct{}
37 var CtxUserKey CtxUserKeyType
39 type Auther interface {
40 Auth(password string) bool
49 func strToAuther(verifier string) (string, Auther, error) {
50 st := strings.SplitN(verifier, "$", 3)
51 if len(st) != 3 || st[0] != "" {
52 return "", nil, errors.New("invalid verifier structure")
59 auther, err = parseArgon2i(st[2])
61 auther, err = parseSHA256(st[2])
63 err = errors.New("unknown hashing algorithm")
65 return algorithm, auther, err
68 func passwdReader(fd *os.File) bool {
70 scanner := bufio.NewScanner(fd)
76 splitted := strings.Split(t, ":")
77 if len(splitted) < 2 {
78 log.Println("wrong login:password[:ro] format:", t)
85 log.Println("deleting login:", login)
87 delete(Passwords, login)
91 _, auther, err := strToAuther(passwd)
93 log.Println("login:", login, "invalid password:", err)
98 if len(splitted) > 2 {
103 log.Println("wrong format of optional field:", t)
108 log.Println("adding password for:", login)
110 Passwords[login] = &User{name: login, ro: ro, auther: auther}
116 func passwdLister(fd *os.File) {
118 logins := make([]string, 0, len(Passwords))
119 for login := range Passwords {
120 logins = append(logins, login)
123 for _, login := range logins {
124 fd.WriteString(login + "\n")
128 func checkAuth(handler http.HandlerFunc) http.HandlerFunc {
129 return func(w http.ResponseWriter, r *http.Request) {
130 username, password, gotAuth := r.BasicAuth()
134 user = Passwords[username]
137 var passwordValid bool
138 if gotAuth && user != nil {
139 passwordValid = user.auther.Auth(password)
141 if (gotAuth && user == nil) ||
142 (user != nil && !passwordValid) ||
143 (*AuthRequired && !gotAuth) {
144 log.Println(r.RemoteAddr, "unauthenticated", username)
145 http.Error(w, "unauthenticated", http.StatusUnauthorized)
148 handler(w, r.WithContext(context.WithValue(r.Context(), CtxUserKey, user)))