1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
|
package main
import (
"encoding/json"
"fmt"
"io"
"net"
"net/http"
"regexp"
// "strconv"
"strings"
"time"
)
// Example:
// curl 'http://localhost:8080/v1/auth/login' -X POST -H 'Content-Type: application/json' --data-raw $'{}}\n{"king": 5'
const log_length = 100
type accessLog struct {
ClientAddr string `json:"clientAddr"`
RequestedPath string `json:"requestedPath"`
RequestTime time.Time `json:"requestTime"`
HttpMethod string `json:"httpMethod"`
}
type loginAttemptLog struct {
Email string `json:"email"`
Password string `json:"password"`
LoginTime time.Time `json:"loginTime"`
Success bool `json:"success"`
RememberMe bool `json:"rememberMe"`
}
func main() {
fs := http.FileServer(http.Dir("static"))
var logs [log_length]string
n := 0
// Define a handler function for the root path
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
jsonData, _ := json.Marshal(accessLog{
ClientAddr: RedactIP(r.RemoteAddr),
RequestedPath: r.URL.Path,
RequestTime: time.Now().UTC(),
HttpMethod: r.Method,
})
addRotLog(&logs, &n, string(jsonData))
// Serve the index.html file from the static directory
http.StripPrefix("/", fs).ServeHTTP(w, r)
})
http.HandleFunc("/logs", createGetLogs(&logs))
http.HandleFunc("/v1/auth/login", func(w http.ResponseWriter, r *http.Request) {
body, err := io.ReadAll(r.Body)
if err != nil {
http.Error(w, "Bad Request", http.StatusBadRequest)
return
}
defer r.Body.Close()
// Attempt to parse JSON (optional, if you still want to try)
var data map[string]any
if json.Unmarshal(body, &data) != nil {
addRotLog(&logs, &n, fmt.Sprintf(`{"authRequest": %s}`, string(body))) // Logs unmodified string
http.Error(w, "Forbidden", http.StatusForbidden)
return
}
if email, ok := data["email"].(string); ok {
if rememberMe, ok := data["rememberMe"].(bool); ok {
addRotLog(&logs, &n, fmt.Sprintf(`{"authRequest": {"email": "%s", "password": "XXXXXXXX", "loginTime": "%s", "success": false, "rememberMe": %t}}`, email, time.Now().UTC(), rememberMe))
}
}
http.Error(w, "Forbidden", http.StatusForbidden)
})
// Start the server on port 8080
fmt.Println("Server is listening on port 8080...")
err := http.ListenAndServe(":8080", nil)
if err != nil {
fmt.Printf("Error starting server: %s\n", err)
}
}
func createGetLogs(logs *[log_length]string) func(http.ResponseWriter, *http.Request) {
return func(w http.ResponseWriter, r *http.Request) {
for _, s := range logs {
fmt.Fprintln(w, s)
}
}
}
func addRotLog(logs *[log_length]string, last *int, value string) {
if strings.Contains(value, "\n") {
for _, v := range strings.Split(value, "\n") {
addRotLog(logs, last, v)
}
} else {
if *last == log_length {
for i := 0; i < log_length-1; i++ {
logs[i] = logs[i+1]
}
logs[log_length-1] = value
} else {
logs[*last] = value
*last++
}
}
}
// RedactIP partially redacts an IP address by replacing the last octet with 'xxx'
func RedactIP(input string) string {
ipRegex := `\b(?:\d{1,3}\.){3}\d{1,3}\b`
re := regexp.MustCompile(ipRegex)
return re.ReplaceAllStringFunc(input, func(match string) string {
if ip := net.ParseIP(match); ip != nil {
parts := strings.Split(match, ".")
if len(parts) == 4 {
parts[3] = "XXX"
return strings.Join(parts, ".")
}
}
return match
})
}
|