Skip to content
Snippets Groups Projects
api-logs.go 6.84 KiB
Newer Older
Francé Wilke's avatar
Francé Wilke committed
package api_logs
Francé Wilke's avatar
Francé Wilke committed
	"github.com/thoas/go-funk"
Francé Wilke's avatar
Francé Wilke committed
	"gitlab.bob.co.za/bob-public-utils/bobgroup-go-utils/number_utils"
	"net/url"
func GenerateIncomingAPILog(startTime time.Time, requestID *string, claim map[string]interface{}, req events.APIGatewayProxyRequest, res events.APIGatewayProxyResponse) ApiLog {
	currentRequestID := ""
	if requestID != nil {
		currentRequestID = *requestID
	}

	var authType string
	var authUsername string
	if req.RequestContext.Identity.CognitoAuthenticationType != "" {
		authType = "cognito"
		split := strings.Split(req.RequestContext.Identity.CognitoAuthenticationProvider, ":")
		if len(split) > 0 {
			authUsername = split[len(split)-1] // = part after last ':'
		}
	} else {
		authType = "iam"
		split := strings.Split(req.RequestContext.Identity.UserArn, ":user/")
		if len(split) > 0 {
			authUsername = split[len(split)-1] // = part after ':user/'
	userID, _ := claim["UserID"].(int64)
	username, _ := claim["Username"].(string)
	accountID, _ := claim["AccountID"].(int64)
	providerID, _ := claim["ProviderID"].(int64)

	if accountID == 0 {
		if accountIDParam, ok := req.QueryStringParameters["account_id"]; ok {
Francé Wilke's avatar
Francé Wilke committed
			if i64, err := number_utils.StringToInt64(accountIDParam); err == nil && i64 > 0 {
	if providerID == 0 {
		if providerIDParam, ok := req.QueryStringParameters["provider_id"]; ok {
Francé Wilke's avatar
Francé Wilke committed
			if i64, err := number_utils.StringToInt64(providerIDParam); err == nil && i64 > 0 {
				providerID = i64
			}
		}
	}

Francé Wilke's avatar
Francé Wilke committed
	typeString := "api-incoming"
	if funk.Contains(req.Path, "webhook") {
		typeString = "webhook-incoming"
	}

	// Remove the API key in the header
	if req.Headers["authorization"] != "" {
		req.Headers["authorization"] = "***"
	}
	if req.Headers["Authorization"] != "" {
		req.Headers["Authorization"] = "***"
	}

	apiLog := ApiLog{
		StartTime:           startTime,
		EndTime:             endTime,
		DurMs:               endTime.Sub(startTime).Milliseconds(),
Francé Wilke's avatar
Francé Wilke committed
		Type:                typeString,
		Method:              req.HTTPMethod,
		Address:             req.RequestContext.DomainName,
		Path:                req.Path,
		ResponseCode:        res.StatusCode,
		RequestID:           currentRequestID,
		InitialAuthType:     authType,
		InitialAuthUsername: authUsername,
		SourceIP:            req.RequestContext.Identity.SourceIP,
		UserAgent:           req.RequestContext.Identity.UserAgent,
		ProviderID:          providerID,
		Request: ApiLogRequest{
			Headers:         req.Headers,
			QueryParameters: req.QueryStringParameters,
			BodySize:        len(req.Body),
Francé Wilke's avatar
Francé Wilke committed
			Body:            req.Body,
		},
		Response: ApiLogResponse{
			Headers:  res.Headers,
			BodySize: len(res.Body),
	// also copy multi-value query parameters to the log as CSV array values
	for n, as := range req.MultiValueQueryStringParameters {
		apiLog.Request.QueryParameters[n] = "[" + strings.Join(as, ",") + "]"
	}

Francé Wilke's avatar
Francé Wilke committed
	return apiLog
}
func GenerateOutgoingAPILog(startTime time.Time, requestID *string, claim map[string]interface{}, urlString string, method string, requestBody string, requestHeaders map[string]string, responseBody string, responseHeaders map[string]string, responseCode int) ApiLog {
	endTime := time.Now()

	currentRequestID := ""
	if requestID != nil {
		currentRequestID = *requestID
	}

	userID, _ := claim["UserID"].(int64)
	username, _ := claim["Username"].(string)
	accountID, _ := claim["AccountID"].(int64)
	providerID, _ := claim["ProviderID"].(int64)

	params := map[string]string{}
	parsedURL, err := url.Parse(urlString)
	if err == nil {
		for n, v := range parsedURL.Query() {
			params[n] = strings.Join(v, ",")
		}
		path = parsedURL.Path
		address = parsedURL.Host
Francé Wilke's avatar
Francé Wilke committed
	typeString := "api-outgoing"
	for k, v := range requestHeaders {
		if strings.ToLower(k) == "x-bobgroup-type" && strings.ToLower(v) == "webhook" {
			typeString = "webhook-outgoing"
			break
		}
	apiLog := ApiLog{
		StartTime:    startTime,
		EndTime:      endTime,
		DurMs:        endTime.Sub(startTime).Milliseconds(),
Francé Wilke's avatar
Francé Wilke committed
		Type:         typeString,
		Method:       method,
		ResponseCode: responseCode,
		RequestID:    currentRequestID,
		AccountID:    accountID,
		ProviderID:   providerID,
		Request: ApiLogRequest{
Francé Wilke's avatar
Francé Wilke committed
			Headers:         requestHeaders,
			QueryParameters: params,
			BodySize:        len(requestBody),
Francé Wilke's avatar
Francé Wilke committed
			Body:            requestBody,
		},
		Response: ApiLogResponse{
			Headers:  responseHeaders,
Francé Wilke's avatar
Francé Wilke committed
			Body:     responseBody,
Francé Wilke's avatar
Francé Wilke committed
	return apiLog
}
// ApiLog is the SQS event details struct encoded as JSON document, sent to SQS, to be logged for each API handler executed.
	StartTime           time.Time      `json:"start_time"`
	EndTime             time.Time      `json:"end_time"`
	DurMs               int64          `json:"duration_ms"` // duration in milliseconds
	Type                string         `json:"type"`        // incoming-api or outgoing-api
	Method              string         `json:"method"`
	Address             string         `json:"address"` // server address for incoming and outgoing
	Path                string         `json:"path"`
	ResponseCode        int            `json:"response_code"`
	RequestID           string         `json:"request_id"`
	InitialAuthUsername string         `json:"initial_auth_username,omitempty"`
	InitialAuthType     string         `json:"initial_auth_type,omitempty"`
	AccountID           int64          `json:"account_id,omitempty"`
	ProviderID          int64          `json:"provider_id,omitempty"`
	UserID              int64          `json:"user_id,omitempty"`
	Username            string         `json:"username,omitempty"`
	SourceIP            string         `json:"source_ip,omitempty"`  // only logged for incoming API
	UserAgent           string         `json:"user_agent,omitempty"` // only for incoming, indicate type of browser when UI
	Request             ApiLogRequest  `json:"request"`
	Response            ApiLogResponse `json:"response"`
	Headers         map[string]string `json:"headers,omitempty" search:"flattened"`
	QueryParameters map[string]string `json:"query_parameters,omitempty" search:"flattened"`
	BodySize        int               `json:"body_size" search:"long"` // set even when body is truncated/omitted
	Body            string            `json:"body,omitempty"`          // json body as a string
	Headers  map[string]string `json:"headers,omitempty" search:"flattened"`
	BodySize int               `json:"body_size"`      // set even when body is truncated/omitted
	Body     string            `json:"body,omitempty"` // json content as a string