mirror of
https://github.com/fleetdm/fleet
synced 2026-05-24 09:28:54 +00:00
Add logging to capture user email upon successful login (#7927)
* Log user email upon successful login * Add user email to logger context * Use logging.With Extras for login email
This commit is contained in:
parent
474fd8fab8
commit
749ff9ec2b
5 changed files with 59 additions and 4 deletions
1
changes/issue-7888-login-logs
Normal file
1
changes/issue-7888-login-logs
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
* Added logging to capture user email upon successful login
|
||||||
|
|
@ -132,7 +132,6 @@ func (l *LoggingContext) Log(ctx context.Context, logger kitlog.Logger) {
|
||||||
}
|
}
|
||||||
keyvals = append(keyvals, "user", loggedInUser)
|
keyvals = append(keyvals, "user", loggedInUser)
|
||||||
}
|
}
|
||||||
|
|
||||||
requestMethod, ok := ctx.Value(kithttp.ContextKeyRequestMethod).(string)
|
requestMethod, ok := ctx.Value(kithttp.ContextKeyRequestMethod).(string)
|
||||||
if !ok {
|
if !ok {
|
||||||
requestMethod = ""
|
requestMethod = ""
|
||||||
|
|
|
||||||
|
|
@ -86,7 +86,7 @@ func (s *integrationLoggerTestSuite) TestLogger() {
|
||||||
assert.Equal(t, "GET", kv["method"])
|
assert.Equal(t, "GET", kv["method"])
|
||||||
assert.Equal(t, "/api/latest/fleet/config", kv["uri"])
|
assert.Equal(t, "/api/latest/fleet/config", kv["uri"])
|
||||||
assert.Equal(t, "admin1@example.com", kv["user"])
|
assert.Equal(t, "admin1@example.com", kv["user"])
|
||||||
case 2:
|
case 3:
|
||||||
assert.Equal(t, "debug", kv["level"])
|
assert.Equal(t, "debug", kv["level"])
|
||||||
assert.Equal(t, "POST", kv["method"])
|
assert.Equal(t, "POST", kv["method"])
|
||||||
assert.Equal(t, "/api/latest/fleet/queries", kv["uri"])
|
assert.Equal(t, "/api/latest/fleet/queries", kv["uri"])
|
||||||
|
|
@ -99,6 +99,61 @@ func (s *integrationLoggerTestSuite) TestLogger() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *integrationLoggerTestSuite) TestLoggerLogin() {
|
||||||
|
t := s.T()
|
||||||
|
|
||||||
|
type logEntry struct {
|
||||||
|
key string
|
||||||
|
val string
|
||||||
|
}
|
||||||
|
|
||||||
|
testCases := []struct {
|
||||||
|
loginRequest loginRequest
|
||||||
|
expectedStatus int
|
||||||
|
expectedLogs []logEntry
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
loginRequest: loginRequest{Email: testUsers["admin1"].Email, Password: testUsers["admin1"].PlaintextPassword},
|
||||||
|
expectedStatus: http.StatusOK,
|
||||||
|
expectedLogs: []logEntry{{"email", testUsers["admin1"].Email}},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
loginRequest: loginRequest{Email: testUsers["admin1"].Email, Password: "n074v411dp455w02d"},
|
||||||
|
expectedStatus: http.StatusUnauthorized,
|
||||||
|
expectedLogs: []logEntry{
|
||||||
|
{"email", testUsers["admin1"].Email},
|
||||||
|
{"level", "error"},
|
||||||
|
{"internal", "invalid password"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
loginRequest: loginRequest{Email: "h4x0r@3x4mp13.c0m", Password: "n074v411dp455w02d"},
|
||||||
|
expectedStatus: http.StatusUnauthorized,
|
||||||
|
expectedLogs: []logEntry{
|
||||||
|
{"email", "h4x0r@3x4mp13.c0m"},
|
||||||
|
{"level", "error"},
|
||||||
|
{"internal", "user not found"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
var resp loginResponse
|
||||||
|
for _, tt := range testCases {
|
||||||
|
s.DoJSON("POST", "/api/latest/fleet/login", tt.loginRequest, tt.expectedStatus, &resp)
|
||||||
|
logString := s.buf.String()
|
||||||
|
parts := strings.Split(strings.TrimSpace(logString), "\n")
|
||||||
|
require.Len(t, parts, 1)
|
||||||
|
logData := make(map[string]string)
|
||||||
|
require.NoError(t, json.Unmarshal([]byte(parts[0]), &logData))
|
||||||
|
|
||||||
|
require.NotContains(t, logData, "user") // logger context is set to skip user
|
||||||
|
|
||||||
|
for _, e := range tt.expectedLogs {
|
||||||
|
assert.Equal(t, logData[e.key], e.val)
|
||||||
|
}
|
||||||
|
s.buf.Reset()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func (s *integrationLoggerTestSuite) TestOsqueryEndpointsLogErrors() {
|
func (s *integrationLoggerTestSuite) TestOsqueryEndpointsLogErrors() {
|
||||||
t := s.T()
|
t := s.T()
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -153,7 +153,7 @@ func (svc *Service) Login(ctx context.Context, email, password string) (*fleet.U
|
||||||
// skipauth: No user context available yet to authorize against.
|
// skipauth: No user context available yet to authorize against.
|
||||||
svc.authz.SkipAuthorization(ctx)
|
svc.authz.SkipAuthorization(ctx)
|
||||||
|
|
||||||
logging.WithLevel(logging.WithNoUser(ctx), level.Info)
|
logging.WithLevel(logging.WithExtras(logging.WithNoUser(ctx), "email", email), level.Info)
|
||||||
|
|
||||||
// If there is an error, sleep until the request has taken at least 1
|
// If there is an error, sleep until the request has taken at least 1
|
||||||
// second. This means that generally a login failure for any reason will
|
// second. This means that generally a login failure for any reason will
|
||||||
|
|
|
||||||
|
|
@ -98,7 +98,7 @@ func TestAuthenticate(t *testing.T) {
|
||||||
svc := newTestService(t, ds, nil, nil)
|
svc := newTestService(t, ds, nil, nil)
|
||||||
createTestUsers(t, ds)
|
createTestUsers(t, ds)
|
||||||
|
|
||||||
var loginTests = []struct {
|
loginTests := []struct {
|
||||||
name string
|
name string
|
||||||
email string
|
email string
|
||||||
password string
|
password string
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue