mirror of
https://github.com/netbirdio/netbird.git
synced 2026-04-17 15:56:39 +00:00
add more filters
This commit is contained in:
@@ -3,6 +3,7 @@ package accesslogs
|
||||
import (
|
||||
"net/http"
|
||||
"strconv"
|
||||
"time"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -18,21 +19,33 @@ type AccessLogFilter struct {
|
||||
Page int
|
||||
// PageSize is the number of records per page
|
||||
PageSize int
|
||||
|
||||
// Filtering parameters
|
||||
SourceIP *string // Filter by source IP address
|
||||
Host *string // Filter by host header
|
||||
Path *string // Filter by request path (supports LIKE pattern)
|
||||
UserID *string // Filter by authenticated user ID
|
||||
UserEmail *string // Filter by user email (requires user lookup)
|
||||
UserName *string // Filter by user name (requires user lookup)
|
||||
Method *string // Filter by HTTP method
|
||||
StatusCode *int // Filter by HTTP status code
|
||||
StartDate *time.Time // Filter by timestamp >= start_date
|
||||
EndDate *time.Time // Filter by timestamp <= end_date
|
||||
}
|
||||
|
||||
// ParseFromRequest parses pagination parameters from HTTP request query parameters
|
||||
// ParseFromRequest parses pagination and filter parameters from HTTP request query parameters
|
||||
func (f *AccessLogFilter) ParseFromRequest(r *http.Request) {
|
||||
// Parse page number (default: 1)
|
||||
queryParams := r.URL.Query()
|
||||
|
||||
f.Page = 1
|
||||
if pageStr := r.URL.Query().Get("page"); pageStr != "" {
|
||||
if pageStr := queryParams.Get("page"); pageStr != "" {
|
||||
if page, err := strconv.Atoi(pageStr); err == nil && page > 0 {
|
||||
f.Page = page
|
||||
}
|
||||
}
|
||||
|
||||
// Parse page size (default: DefaultPageSize, max: MaxPageSize)
|
||||
f.PageSize = DefaultPageSize
|
||||
if pageSizeStr := r.URL.Query().Get("page_size"); pageSizeStr != "" {
|
||||
if pageSizeStr := queryParams.Get("page_size"); pageSizeStr != "" {
|
||||
if pageSize, err := strconv.Atoi(pageSizeStr); err == nil && pageSize > 0 {
|
||||
f.PageSize = pageSize
|
||||
if f.PageSize > MaxPageSize {
|
||||
@@ -40,6 +53,54 @@ func (f *AccessLogFilter) ParseFromRequest(r *http.Request) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if sourceIP := queryParams.Get("source_ip"); sourceIP != "" {
|
||||
f.SourceIP = &sourceIP
|
||||
}
|
||||
|
||||
if host := queryParams.Get("host"); host != "" {
|
||||
f.Host = &host
|
||||
}
|
||||
|
||||
if path := queryParams.Get("path"); path != "" {
|
||||
f.Path = &path
|
||||
}
|
||||
|
||||
if userID := queryParams.Get("user_id"); userID != "" {
|
||||
f.UserID = &userID
|
||||
}
|
||||
|
||||
if userEmail := queryParams.Get("user_email"); userEmail != "" {
|
||||
f.UserEmail = &userEmail
|
||||
}
|
||||
|
||||
if userName := queryParams.Get("user_name"); userName != "" {
|
||||
f.UserName = &userName
|
||||
}
|
||||
|
||||
if method := queryParams.Get("method"); method != "" {
|
||||
f.Method = &method
|
||||
}
|
||||
|
||||
if statusCodeStr := queryParams.Get("status_code"); statusCodeStr != "" {
|
||||
if statusCode, err := strconv.Atoi(statusCodeStr); err == nil && statusCode > 0 {
|
||||
f.StatusCode = &statusCode
|
||||
}
|
||||
}
|
||||
|
||||
if startDate := queryParams.Get("start_date"); startDate != "" {
|
||||
parsedStartDate, err := time.Parse(time.RFC3339, startDate)
|
||||
if err == nil {
|
||||
f.StartDate = &parsedStartDate
|
||||
}
|
||||
}
|
||||
|
||||
if endDate := queryParams.Get("end_date"); endDate != "" {
|
||||
parsedEndDate, err := time.Parse(time.RFC3339, endDate)
|
||||
if err == nil {
|
||||
f.EndDate = &parsedEndDate
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// GetOffset calculates the database offset for pagination
|
||||
|
||||
@@ -6,5 +6,5 @@ import (
|
||||
|
||||
type Manager interface {
|
||||
SaveAccessLog(ctx context.Context, proxyLog *AccessLogEntry) error
|
||||
GetAllAccessLogs(ctx context.Context, accountID, userID string, filter AccessLogFilter) ([]*AccessLogEntry, int64, error)
|
||||
GetAllAccessLogs(ctx context.Context, accountID, userID string, filter *AccessLogFilter) ([]*AccessLogEntry, int64, error)
|
||||
}
|
||||
|
||||
@@ -30,11 +30,10 @@ func (h *handler) getAccessLogs(w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
// Parse pagination parameters from request
|
||||
var filter accesslogs.AccessLogFilter
|
||||
filter.ParseFromRequest(r)
|
||||
|
||||
logs, totalCount, err := h.manager.GetAllAccessLogs(r.Context(), userAuth.AccountId, userAuth.UserId, filter)
|
||||
logs, totalCount, err := h.manager.GetAllAccessLogs(r.Context(), userAuth.AccountId, userAuth.UserId, &filter)
|
||||
if err != nil {
|
||||
util.WriteError(r.Context(), err, w)
|
||||
return
|
||||
@@ -45,7 +44,6 @@ func (h *handler) getAccessLogs(w http.ResponseWriter, r *http.Request) {
|
||||
apiLogs = append(apiLogs, *log.ToAPIResponse())
|
||||
}
|
||||
|
||||
// Return paginated response
|
||||
response := &api.ProxyAccessLogsResponse{
|
||||
Data: apiLogs,
|
||||
Page: filter.Page,
|
||||
|
||||
@@ -2,6 +2,7 @@ package manager
|
||||
|
||||
import (
|
||||
"context"
|
||||
"strings"
|
||||
|
||||
log "github.com/sirupsen/logrus"
|
||||
|
||||
@@ -55,8 +56,8 @@ func (m *managerImpl) SaveAccessLog(ctx context.Context, logEntry *accesslogs.Ac
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetAllAccessLogs retrieves access logs for an account with pagination
|
||||
func (m *managerImpl) GetAllAccessLogs(ctx context.Context, accountID, userID string, filter accesslogs.AccessLogFilter) ([]*accesslogs.AccessLogEntry, int64, error) {
|
||||
// GetAllAccessLogs retrieves access logs for an account with pagination and filtering
|
||||
func (m *managerImpl) GetAllAccessLogs(ctx context.Context, accountID, userID string, filter *accesslogs.AccessLogFilter) ([]*accesslogs.AccessLogEntry, int64, error) {
|
||||
ok, err := m.permissionsManager.ValidateUserPermissions(ctx, accountID, userID, modules.Services, operations.Read)
|
||||
if err != nil {
|
||||
return nil, 0, status.NewPermissionValidationError(err)
|
||||
@@ -65,10 +66,43 @@ func (m *managerImpl) GetAllAccessLogs(ctx context.Context, accountID, userID st
|
||||
return nil, 0, status.NewPermissionDeniedError()
|
||||
}
|
||||
|
||||
logs, totalCount, err := m.store.GetAccountAccessLogs(ctx, store.LockingStrengthNone, accountID, filter)
|
||||
if err := m.resolveUserFilters(ctx, accountID, filter); err != nil {
|
||||
log.WithContext(ctx).Warnf("failed to resolve user filters: %v", err)
|
||||
}
|
||||
|
||||
logs, totalCount, err := m.store.GetAccountAccessLogs(ctx, store.LockingStrengthNone, accountID, *filter)
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
|
||||
return logs, totalCount, nil
|
||||
}
|
||||
|
||||
// resolveUserFilters converts user email/name filters to user ID filter
|
||||
func (m *managerImpl) resolveUserFilters(ctx context.Context, accountID string, filter *accesslogs.AccessLogFilter) error {
|
||||
if filter.UserEmail == nil && filter.UserName == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
users, err := m.store.GetAccountUsers(ctx, store.LockingStrengthNone, accountID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var matchingUserIDs []string
|
||||
for _, user := range users {
|
||||
if filter.UserEmail != nil && strings.Contains(strings.ToLower(user.Email), strings.ToLower(*filter.UserEmail)) {
|
||||
matchingUserIDs = append(matchingUserIDs, user.Id)
|
||||
continue
|
||||
}
|
||||
if filter.UserName != nil && strings.Contains(strings.ToLower(user.Name), strings.ToLower(*filter.UserName)) {
|
||||
matchingUserIDs = append(matchingUserIDs, user.Id)
|
||||
}
|
||||
}
|
||||
|
||||
if len(matchingUserIDs) > 0 {
|
||||
filter.UserID = &matchingUserIDs[0]
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -5061,24 +5061,28 @@ func (s *SqlStore) CreateAccessLog(ctx context.Context, logEntry *accesslogs.Acc
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetAccountAccessLogs retrieves access logs for a given account with pagination
|
||||
// GetAccountAccessLogs retrieves access logs for a given account with pagination and filtering
|
||||
func (s *SqlStore) GetAccountAccessLogs(ctx context.Context, lockStrength LockingStrength, accountID string, filter accesslogs.AccessLogFilter) ([]*accesslogs.AccessLogEntry, int64, error) {
|
||||
var logs []*accesslogs.AccessLogEntry
|
||||
var totalCount int64
|
||||
|
||||
// Count total records for pagination metadata
|
||||
countQuery := s.db.WithContext(ctx).
|
||||
baseQuery := s.db.WithContext(ctx).
|
||||
Model(&accesslogs.AccessLogEntry{}).
|
||||
Where(accountIDCondition, accountID)
|
||||
|
||||
if err := countQuery.Count(&totalCount).Error; err != nil {
|
||||
baseQuery = s.applyAccessLogFilters(baseQuery, filter)
|
||||
|
||||
if err := baseQuery.Count(&totalCount).Error; err != nil {
|
||||
log.WithContext(ctx).Errorf("failed to count access logs: %v", err)
|
||||
return nil, 0, status.Errorf(status.Internal, "failed to count access logs")
|
||||
}
|
||||
|
||||
// Query with pagination
|
||||
query := s.db.WithContext(ctx).
|
||||
Where(accountIDCondition, accountID).
|
||||
Where(accountIDCondition, accountID)
|
||||
|
||||
query = s.applyAccessLogFilters(query, filter)
|
||||
|
||||
query = query.
|
||||
Order("timestamp DESC").
|
||||
Limit(filter.GetLimit()).
|
||||
Offset(filter.GetOffset())
|
||||
@@ -5096,6 +5100,44 @@ func (s *SqlStore) GetAccountAccessLogs(ctx context.Context, lockStrength Lockin
|
||||
return logs, totalCount, nil
|
||||
}
|
||||
|
||||
// applyAccessLogFilters applies filter conditions to the query
|
||||
func (s *SqlStore) applyAccessLogFilters(query *gorm.DB, filter accesslogs.AccessLogFilter) *gorm.DB {
|
||||
if filter.SourceIP != nil {
|
||||
query = query.Where("source_ip = ?", *filter.SourceIP)
|
||||
}
|
||||
|
||||
if filter.Host != nil {
|
||||
query = query.Where("host = ?", *filter.Host)
|
||||
}
|
||||
|
||||
if filter.Path != nil {
|
||||
// Support LIKE pattern for path filtering
|
||||
query = query.Where("path LIKE ?", "%"+*filter.Path+"%")
|
||||
}
|
||||
|
||||
if filter.UserID != nil {
|
||||
query = query.Where("user_id = ?", *filter.UserID)
|
||||
}
|
||||
|
||||
if filter.Method != nil {
|
||||
query = query.Where("method = ?", *filter.Method)
|
||||
}
|
||||
|
||||
if filter.StatusCode != nil {
|
||||
query = query.Where("status_code = ?", *filter.StatusCode)
|
||||
}
|
||||
|
||||
if filter.StartDate != nil {
|
||||
query = query.Where("timestamp >= ?", *filter.StartDate)
|
||||
}
|
||||
|
||||
if filter.EndDate != nil {
|
||||
query = query.Where("timestamp <= ?", *filter.EndDate)
|
||||
}
|
||||
|
||||
return query
|
||||
}
|
||||
|
||||
func (s *SqlStore) GetReverseProxyTargetByTargetID(ctx context.Context, lockStrength LockingStrength, accountID string, targetID string) (*reverseproxy.Target, error) {
|
||||
tx := s.db
|
||||
if lockStrength != LockingStrengthNone {
|
||||
|
||||
@@ -6389,6 +6389,61 @@ paths:
|
||||
minimum: 1
|
||||
maximum: 100
|
||||
description: Number of items per page (max 100)
|
||||
- in: query
|
||||
name: source_ip
|
||||
schema:
|
||||
type: string
|
||||
description: Filter by source IP address
|
||||
- in: query
|
||||
name: host
|
||||
schema:
|
||||
type: string
|
||||
description: Filter by host header
|
||||
- in: query
|
||||
name: path
|
||||
schema:
|
||||
type: string
|
||||
description: Filter by request path (supports partial matching)
|
||||
- in: query
|
||||
name: user_id
|
||||
schema:
|
||||
type: string
|
||||
description: Filter by authenticated user ID
|
||||
- in: query
|
||||
name: user_email
|
||||
schema:
|
||||
type: string
|
||||
description: Filter by user email (partial matching)
|
||||
- in: query
|
||||
name: user_name
|
||||
schema:
|
||||
type: string
|
||||
description: Filter by user name (partial matching)
|
||||
- in: query
|
||||
name: method
|
||||
schema:
|
||||
type: string
|
||||
enum: [GET, POST, PUT, PATCH, DELETE, HEAD, OPTIONS]
|
||||
description: Filter by HTTP method
|
||||
- in: query
|
||||
name: status_code
|
||||
schema:
|
||||
type: integer
|
||||
minimum: 100
|
||||
maximum: 599
|
||||
description: Filter by HTTP status code
|
||||
- in: query
|
||||
name: start_date
|
||||
schema:
|
||||
type: string
|
||||
format: date-time
|
||||
description: Filter by timestamp >= start_date (RFC3339 format)
|
||||
- in: query
|
||||
name: end_date
|
||||
schema:
|
||||
type: string
|
||||
format: date-time
|
||||
description: Filter by timestamp <= end_date (RFC3339 format)
|
||||
responses:
|
||||
"200":
|
||||
description: Paginated list of reverse proxy access logs
|
||||
|
||||
Reference in New Issue
Block a user