doBasicAuth authenticates the request using HTTP Basic Auth. Both the "admin" and "mod" roles are accepted. The authenticated UserRecord is stored in the request context so downstream handlers can access it via GetAuthedUser(r).
(next http.HandlerFunc)
| 28 | // in the request context so downstream handlers can access it via |
| 29 | // GetAuthedUser(r). |
| 30 | func doBasicAuth(next http.HandlerFunc) http.HandlerFunc { |
| 31 | return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { |
| 32 | |
| 33 | if IsInternalRequest(r) { |
| 34 | next.ServeHTTP(w, r) |
| 35 | return |
| 36 | } |
| 37 | |
| 38 | authHeader := r.Header.Get("Authorization") |
| 39 | |
| 40 | authCacheMu.RLock() |
| 41 | expiry, cached := authCache[authHeader] |
| 42 | cachedUser := authUserCache[authHeader] |
| 43 | authCacheMu.RUnlock() |
| 44 | |
| 45 | if cached && expiry.After(time.Now()) && cachedUser != nil { |
| 46 | r = r.WithContext(withAuthedUser(r.Context(), cachedUser)) |
| 47 | next.ServeHTTP(w, r) |
| 48 | return |
| 49 | } |
| 50 | |
| 51 | // Evict stale cache entry if present. |
| 52 | if cached { |
| 53 | authCacheMu.Lock() |
| 54 | delete(authCache, authHeader) |
| 55 | delete(authUserCache, authHeader) |
| 56 | authCacheMu.Unlock() |
| 57 | } |
| 58 | |
| 59 | username, password, ok := r.BasicAuth() |
| 60 | if ok { |
| 61 | uRecord, err := users.LoadUser(username, true) |
| 62 | if err == nil && uRecord.PasswordMatches(password) { |
| 63 | if uRecord.Role == users.RoleAdmin || uRecord.Role == users.RoleMod { |
| 64 | |
| 65 | mudlog.Warn("ADMIN LOGIN", "username", username, "role", uRecord.Role, "success", true) |
| 66 | |
| 67 | authCacheMu.Lock() |
| 68 | authCache[authHeader] = time.Now().Add(time.Minute * 30) |
| 69 | authUserCache[authHeader] = uRecord |
| 70 | authCacheMu.Unlock() |
| 71 | |
| 72 | r = r.WithContext(withAuthedUser(r.Context(), uRecord)) |
| 73 | next.ServeHTTP(w, r) |
| 74 | return |
| 75 | |
| 76 | } else { |
| 77 | mudlog.Error("ADMIN LOGIN", "username", username, "success", false, "error", "Role="+uRecord.Role) |
| 78 | } |
| 79 | } else if err != nil { |
| 80 | mudlog.Error("ADMIN LOGIN", "username", username, "success", false, "error", err) |
| 81 | } |
| 82 | } |
| 83 | |
| 84 | w.Header().Set("WWW-Authenticate", `Basic realm="restricted", charset="UTF-8"`) |
| 85 | http.Error(w, "Unauthorized", http.StatusUnauthorized) |
| 86 | }) |
| 87 | } |