|
@@ -28,111 +28,51 @@ func newAuthMiddleware(s *storage.Storage) *authMiddleware {
|
|
|
|
|
|
|
|
func (m *authMiddleware) validateApiKey(next http.Handler) http.Handler {
|
|
func (m *authMiddleware) validateApiKey(next http.Handler) http.Handler {
|
|
|
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
|
- clientIP := request.ClientIP(r)
|
|
|
|
|
-
|
|
|
|
|
- var token string
|
|
|
|
|
- if r.Method == http.MethodPost {
|
|
|
|
|
- if err := r.ParseForm(); err != nil {
|
|
|
|
|
- slog.Warn("[GoogleReader] Could not parse request form data",
|
|
|
|
|
- slog.Bool("authentication_failed", true),
|
|
|
|
|
- slog.String("client_ip", clientIP),
|
|
|
|
|
- slog.String("user_agent", r.UserAgent()),
|
|
|
|
|
- slog.Any("error", err),
|
|
|
|
|
- )
|
|
|
|
|
- sendUnauthorizedResponse(w, r)
|
|
|
|
|
- return
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- token = r.Form.Get("T")
|
|
|
|
|
- if token == "" {
|
|
|
|
|
- slog.Warn("[GoogleReader] Post-Form T field is empty",
|
|
|
|
|
- slog.Bool("authentication_failed", true),
|
|
|
|
|
- slog.String("client_ip", clientIP),
|
|
|
|
|
- slog.String("user_agent", r.UserAgent()),
|
|
|
|
|
- )
|
|
|
|
|
- sendUnauthorizedResponse(w, r)
|
|
|
|
|
- return
|
|
|
|
|
- }
|
|
|
|
|
- } else {
|
|
|
|
|
- authorization := r.Header.Get("Authorization")
|
|
|
|
|
-
|
|
|
|
|
- if authorization == "" {
|
|
|
|
|
- slog.Warn("[GoogleReader] No token provided",
|
|
|
|
|
- slog.Bool("authentication_failed", true),
|
|
|
|
|
- slog.String("client_ip", clientIP),
|
|
|
|
|
- slog.String("user_agent", r.UserAgent()),
|
|
|
|
|
- )
|
|
|
|
|
- sendUnauthorizedResponse(w, r)
|
|
|
|
|
- return
|
|
|
|
|
- }
|
|
|
|
|
- fields := strings.Fields(authorization)
|
|
|
|
|
- if len(fields) != 2 {
|
|
|
|
|
- slog.Warn("[GoogleReader] Authorization header does not have the expected GoogleLogin format auth=xxxxxx",
|
|
|
|
|
- slog.Bool("authentication_failed", true),
|
|
|
|
|
- slog.String("client_ip", clientIP),
|
|
|
|
|
- slog.String("user_agent", r.UserAgent()),
|
|
|
|
|
- )
|
|
|
|
|
- sendUnauthorizedResponse(w, r)
|
|
|
|
|
- return
|
|
|
|
|
- }
|
|
|
|
|
- if fields[0] != "GoogleLogin" {
|
|
|
|
|
- slog.Warn("[GoogleReader] Authorization header does not begin with GoogleLogin",
|
|
|
|
|
- slog.Bool("authentication_failed", true),
|
|
|
|
|
- slog.String("client_ip", clientIP),
|
|
|
|
|
- slog.String("user_agent", r.UserAgent()),
|
|
|
|
|
- )
|
|
|
|
|
- sendUnauthorizedResponse(w, r)
|
|
|
|
|
- return
|
|
|
|
|
- }
|
|
|
|
|
- auths := strings.Split(fields[1], "=")
|
|
|
|
|
- if len(auths) != 2 {
|
|
|
|
|
- slog.Warn("[GoogleReader] Authorization header does not have the expected GoogleLogin format auth=xxxxxx",
|
|
|
|
|
- slog.Bool("authentication_failed", true),
|
|
|
|
|
- slog.String("client_ip", clientIP),
|
|
|
|
|
- slog.String("user_agent", r.UserAgent()),
|
|
|
|
|
- )
|
|
|
|
|
- sendUnauthorizedResponse(w, r)
|
|
|
|
|
- return
|
|
|
|
|
- }
|
|
|
|
|
- if auths[0] != "auth" {
|
|
|
|
|
- slog.Warn("[GoogleReader] Authorization header does not have the expected GoogleLogin format auth=xxxxxx",
|
|
|
|
|
- slog.Bool("authentication_failed", true),
|
|
|
|
|
- slog.String("client_ip", clientIP),
|
|
|
|
|
- slog.String("user_agent", r.UserAgent()),
|
|
|
|
|
- )
|
|
|
|
|
- sendUnauthorizedResponse(w, r)
|
|
|
|
|
- return
|
|
|
|
|
- }
|
|
|
|
|
- token = auths[1]
|
|
|
|
|
|
|
+ m.serveValidated(w, r, next)
|
|
|
|
|
+ })
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+func (m *authMiddleware) serveValidated(w http.ResponseWriter, r *http.Request, next http.Handler) {
|
|
|
|
|
+ clientIP := request.ClientIP(r)
|
|
|
|
|
+
|
|
|
|
|
+ var token string
|
|
|
|
|
+ if r.Method == http.MethodPost {
|
|
|
|
|
+ if err := r.ParseForm(); err != nil {
|
|
|
|
|
+ slog.Warn("[GoogleReader] Could not parse request form data",
|
|
|
|
|
+ slog.Bool("authentication_failed", true),
|
|
|
|
|
+ slog.String("client_ip", clientIP),
|
|
|
|
|
+ slog.String("user_agent", r.UserAgent()),
|
|
|
|
|
+ slog.Any("error", err),
|
|
|
|
|
+ )
|
|
|
|
|
+ sendUnauthorizedResponse(w, r)
|
|
|
|
|
+ return
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- parts := strings.Split(token, "/")
|
|
|
|
|
- if len(parts) != 2 {
|
|
|
|
|
- slog.Warn("[GoogleReader] Auth token does not have the expected structure username/hash",
|
|
|
|
|
|
|
+ token = r.Form.Get("T")
|
|
|
|
|
+ if token == "" {
|
|
|
|
|
+ slog.Warn("[GoogleReader] Post-Form T field is empty",
|
|
|
slog.Bool("authentication_failed", true),
|
|
slog.Bool("authentication_failed", true),
|
|
|
slog.String("client_ip", clientIP),
|
|
slog.String("client_ip", clientIP),
|
|
|
slog.String("user_agent", r.UserAgent()),
|
|
slog.String("user_agent", r.UserAgent()),
|
|
|
- slog.String("token", token),
|
|
|
|
|
)
|
|
)
|
|
|
sendUnauthorizedResponse(w, r)
|
|
sendUnauthorizedResponse(w, r)
|
|
|
return
|
|
return
|
|
|
}
|
|
}
|
|
|
- var integration *model.Integration
|
|
|
|
|
- var user *model.User
|
|
|
|
|
- var err error
|
|
|
|
|
- if integration, err = m.store.GoogleReaderUserGetIntegration(parts[0]); err != nil {
|
|
|
|
|
- slog.Warn("[GoogleReader] No user found with the given Google Reader username",
|
|
|
|
|
|
|
+ } else {
|
|
|
|
|
+ authorization := r.Header.Get("Authorization")
|
|
|
|
|
+
|
|
|
|
|
+ if authorization == "" {
|
|
|
|
|
+ slog.Warn("[GoogleReader] No token provided",
|
|
|
slog.Bool("authentication_failed", true),
|
|
slog.Bool("authentication_failed", true),
|
|
|
slog.String("client_ip", clientIP),
|
|
slog.String("client_ip", clientIP),
|
|
|
slog.String("user_agent", r.UserAgent()),
|
|
slog.String("user_agent", r.UserAgent()),
|
|
|
- slog.Any("error", err),
|
|
|
|
|
)
|
|
)
|
|
|
sendUnauthorizedResponse(w, r)
|
|
sendUnauthorizedResponse(w, r)
|
|
|
return
|
|
return
|
|
|
}
|
|
}
|
|
|
- expectedToken := getAuthToken(integration.GoogleReaderUsername, integration.GoogleReaderPassword)
|
|
|
|
|
- if !crypto.ConstantTimeCmp(expectedToken, token) {
|
|
|
|
|
- slog.Warn("[GoogleReader] Token does not match",
|
|
|
|
|
|
|
+ fields := strings.Fields(authorization)
|
|
|
|
|
+ if len(fields) != 2 {
|
|
|
|
|
+ slog.Warn("[GoogleReader] Authorization header does not have the expected GoogleLogin format auth=xxxxxx",
|
|
|
slog.Bool("authentication_failed", true),
|
|
slog.Bool("authentication_failed", true),
|
|
|
slog.String("client_ip", clientIP),
|
|
slog.String("client_ip", clientIP),
|
|
|
slog.String("user_agent", r.UserAgent()),
|
|
slog.String("user_agent", r.UserAgent()),
|
|
@@ -140,19 +80,27 @@ func (m *authMiddleware) validateApiKey(next http.Handler) http.Handler {
|
|
|
sendUnauthorizedResponse(w, r)
|
|
sendUnauthorizedResponse(w, r)
|
|
|
return
|
|
return
|
|
|
}
|
|
}
|
|
|
- if user, err = m.store.UserByID(integration.UserID); err != nil {
|
|
|
|
|
- slog.Error("[GoogleReader] Unable to fetch user from database",
|
|
|
|
|
|
|
+ if fields[0] != "GoogleLogin" {
|
|
|
|
|
+ slog.Warn("[GoogleReader] Authorization header does not begin with GoogleLogin",
|
|
|
slog.Bool("authentication_failed", true),
|
|
slog.Bool("authentication_failed", true),
|
|
|
slog.String("client_ip", clientIP),
|
|
slog.String("client_ip", clientIP),
|
|
|
slog.String("user_agent", r.UserAgent()),
|
|
slog.String("user_agent", r.UserAgent()),
|
|
|
- slog.Any("error", err),
|
|
|
|
|
)
|
|
)
|
|
|
sendUnauthorizedResponse(w, r)
|
|
sendUnauthorizedResponse(w, r)
|
|
|
return
|
|
return
|
|
|
}
|
|
}
|
|
|
-
|
|
|
|
|
- if user == nil {
|
|
|
|
|
- slog.Warn("[GoogleReader] No user found with the given Google Reader credentials",
|
|
|
|
|
|
|
+ auths := strings.Split(fields[1], "=")
|
|
|
|
|
+ if len(auths) != 2 {
|
|
|
|
|
+ slog.Warn("[GoogleReader] Authorization header does not have the expected GoogleLogin format auth=xxxxxx",
|
|
|
|
|
+ slog.Bool("authentication_failed", true),
|
|
|
|
|
+ slog.String("client_ip", clientIP),
|
|
|
|
|
+ slog.String("user_agent", r.UserAgent()),
|
|
|
|
|
+ )
|
|
|
|
|
+ sendUnauthorizedResponse(w, r)
|
|
|
|
|
+ return
|
|
|
|
|
+ }
|
|
|
|
|
+ if auths[0] != "auth" {
|
|
|
|
|
+ slog.Warn("[GoogleReader] Authorization header does not have the expected GoogleLogin format auth=xxxxxx",
|
|
|
slog.Bool("authentication_failed", true),
|
|
slog.Bool("authentication_failed", true),
|
|
|
slog.String("client_ip", clientIP),
|
|
slog.String("client_ip", clientIP),
|
|
|
slog.String("user_agent", r.UserAgent()),
|
|
slog.String("user_agent", r.UserAgent()),
|
|
@@ -160,19 +108,75 @@ func (m *authMiddleware) validateApiKey(next http.Handler) http.Handler {
|
|
|
sendUnauthorizedResponse(w, r)
|
|
sendUnauthorizedResponse(w, r)
|
|
|
return
|
|
return
|
|
|
}
|
|
}
|
|
|
|
|
+ token = auths[1]
|
|
|
|
|
+ }
|
|
|
|
|
|
|
|
- m.store.SetLastLogin(integration.UserID)
|
|
|
|
|
|
|
+ parts := strings.Split(token, "/")
|
|
|
|
|
+ if len(parts) != 2 {
|
|
|
|
|
+ slog.Warn("[GoogleReader] Auth token does not have the expected structure username/hash",
|
|
|
|
|
+ slog.Bool("authentication_failed", true),
|
|
|
|
|
+ slog.String("client_ip", clientIP),
|
|
|
|
|
+ slog.String("user_agent", r.UserAgent()),
|
|
|
|
|
+ slog.String("token", token),
|
|
|
|
|
+ )
|
|
|
|
|
+ sendUnauthorizedResponse(w, r)
|
|
|
|
|
+ return
|
|
|
|
|
+ }
|
|
|
|
|
+ var integration *model.Integration
|
|
|
|
|
+ var user *model.User
|
|
|
|
|
+ var err error
|
|
|
|
|
+ if integration, err = m.store.GoogleReaderUserGetIntegration(parts[0]); err != nil {
|
|
|
|
|
+ slog.Warn("[GoogleReader] No user found with the given Google Reader username",
|
|
|
|
|
+ slog.Bool("authentication_failed", true),
|
|
|
|
|
+ slog.String("client_ip", clientIP),
|
|
|
|
|
+ slog.String("user_agent", r.UserAgent()),
|
|
|
|
|
+ slog.Any("error", err),
|
|
|
|
|
+ )
|
|
|
|
|
+ sendUnauthorizedResponse(w, r)
|
|
|
|
|
+ return
|
|
|
|
|
+ }
|
|
|
|
|
+ expectedToken := getAuthToken(integration.GoogleReaderUsername, integration.GoogleReaderPassword)
|
|
|
|
|
+ if !crypto.ConstantTimeCmp(expectedToken, token) {
|
|
|
|
|
+ slog.Warn("[GoogleReader] Token does not match",
|
|
|
|
|
+ slog.Bool("authentication_failed", true),
|
|
|
|
|
+ slog.String("client_ip", clientIP),
|
|
|
|
|
+ slog.String("user_agent", r.UserAgent()),
|
|
|
|
|
+ )
|
|
|
|
|
+ sendUnauthorizedResponse(w, r)
|
|
|
|
|
+ return
|
|
|
|
|
+ }
|
|
|
|
|
+ if user, err = m.store.UserByID(integration.UserID); err != nil {
|
|
|
|
|
+ slog.Error("[GoogleReader] Unable to fetch user from database",
|
|
|
|
|
+ slog.Bool("authentication_failed", true),
|
|
|
|
|
+ slog.String("client_ip", clientIP),
|
|
|
|
|
+ slog.String("user_agent", r.UserAgent()),
|
|
|
|
|
+ slog.Any("error", err),
|
|
|
|
|
+ )
|
|
|
|
|
+ sendUnauthorizedResponse(w, r)
|
|
|
|
|
+ return
|
|
|
|
|
+ }
|
|
|
|
|
|
|
|
- ctx := r.Context()
|
|
|
|
|
- ctx = context.WithValue(ctx, request.UserIDContextKey, user.ID)
|
|
|
|
|
- ctx = context.WithValue(ctx, request.UserNameContextKey, user.Username)
|
|
|
|
|
- ctx = context.WithValue(ctx, request.UserTimezoneContextKey, user.Timezone)
|
|
|
|
|
- ctx = context.WithValue(ctx, request.IsAdminUserContextKey, user.IsAdmin)
|
|
|
|
|
- ctx = context.WithValue(ctx, request.IsAuthenticatedContextKey, true)
|
|
|
|
|
- ctx = context.WithValue(ctx, request.GoogleReaderTokenKey, token)
|
|
|
|
|
|
|
+ if user == nil {
|
|
|
|
|
+ slog.Warn("[GoogleReader] No user found with the given Google Reader credentials",
|
|
|
|
|
+ slog.Bool("authentication_failed", true),
|
|
|
|
|
+ slog.String("client_ip", clientIP),
|
|
|
|
|
+ slog.String("user_agent", r.UserAgent()),
|
|
|
|
|
+ )
|
|
|
|
|
+ sendUnauthorizedResponse(w, r)
|
|
|
|
|
+ return
|
|
|
|
|
+ }
|
|
|
|
|
|
|
|
- next.ServeHTTP(w, r.WithContext(ctx))
|
|
|
|
|
- })
|
|
|
|
|
|
|
+ m.store.SetLastLogin(integration.UserID)
|
|
|
|
|
+
|
|
|
|
|
+ ctx := r.Context()
|
|
|
|
|
+ ctx = context.WithValue(ctx, request.UserIDContextKey, user.ID)
|
|
|
|
|
+ ctx = context.WithValue(ctx, request.UserNameContextKey, user.Username)
|
|
|
|
|
+ ctx = context.WithValue(ctx, request.UserTimezoneContextKey, user.Timezone)
|
|
|
|
|
+ ctx = context.WithValue(ctx, request.IsAdminUserContextKey, user.IsAdmin)
|
|
|
|
|
+ ctx = context.WithValue(ctx, request.IsAuthenticatedContextKey, true)
|
|
|
|
|
+ ctx = context.WithValue(ctx, request.GoogleReaderTokenKey, token)
|
|
|
|
|
+
|
|
|
|
|
+ next.ServeHTTP(w, r.WithContext(ctx))
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
func getAuthToken(username, password string) string {
|
|
func getAuthToken(username, password string) string {
|