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
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
|
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": "key"'
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
king := "NOKING"
logChan := make(chan string)
go parser(logChan, &king)
// 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, logChan, 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()
var data map[string]any
if json.Unmarshal(body, &data) != nil {
addRotLog(&logs, &n, logChan, fmt.Sprintf(`{"authRequest": %s}`, string(body)))
http.Error(w, "Forbidden", http.StatusForbidden)
return
}
if email, ok := data["email"].(string); ok {
if rememberMe, ok := data["rememberMe"].(bool); ok {
addRotLog(&logs, &n, logChan, fmt.Sprintf(`{"authRequest": {"email": "%s", "password": "XXXXXXXX", "loginTime": "%s", "success": false, "rememberMe": %t}}`, email, time.Now().UTC(), rememberMe))
}
}
http.Error(w, "Forbidden", http.StatusForbidden)
})
http.HandleFunc("/v1/king", func(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, king)
})
// 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, parser chan string, value string) {
if strings.Contains(value, "\n") {
for _, v := range strings.Split(value, "\n") {
addRotLog(logs, last, parser, v)
}
} else {
if *last == log_length {
for i := 0; i < log_length-1; i++ {
logs[i] = logs[i+1]
}
logs[log_length-1] = value
parser <- value
} else {
logs[*last] = value
*last++
parser <- value
}
}
}
func parser(input chan string, king *string) {
for value := range input {
var data map[string]any
if json.Unmarshal([]byte(value), &data) == nil {
if k, ok := data["king"].(string); ok {
*king = k
}
}
}
}
// 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
})
}
|