github.com/minio/console@v1.4.1/pkg/logger/message/audit/entry.go (about)

     1  // This file is part of MinIO Console Server
     2  // Copyright (c) 2022 MinIO, Inc.
     3  //
     4  // This program is free software: you can redistribute it and/or modify
     5  // it under the terms of the GNU Affero General Public License as published by
     6  // the Free Software Foundation, either version 3 of the License, or
     7  // (at your option) any later version.
     8  //
     9  // This program is distributed in the hope that it will be useful,
    10  // but WITHOUT ANY WARRANTY; without even the implied warranty of
    11  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    12  // GNU Affero General Public License for more details.
    13  //
    14  // You should have received a copy of the GNU Affero General Public License
    15  // along with this program.  If not, see <http://www.gnu.org/licenses/>.
    16  
    17  package audit
    18  
    19  import (
    20  	"net/http"
    21  	"os"
    22  	"strings"
    23  	"time"
    24  
    25  	"github.com/google/uuid"
    26  
    27  	"github.com/golang-jwt/jwt/v4"
    28  
    29  	"github.com/minio/console/pkg/utils"
    30  
    31  	xhttp "github.com/minio/console/pkg/http"
    32  )
    33  
    34  // Version - represents the current version of audit log structure.
    35  const Version = "1"
    36  
    37  // ObjectVersion object version key/versionId
    38  type ObjectVersion struct {
    39  	ObjectName string `json:"objectName"`
    40  	VersionID  string `json:"versionId,omitempty"`
    41  }
    42  
    43  // Entry - audit entry logs.
    44  type Entry struct {
    45  	Version      string    `json:"version"`
    46  	DeploymentID string    `json:"deploymentid,omitempty"`
    47  	Time         time.Time `json:"time"`
    48  	Trigger      string    `json:"trigger"`
    49  	API          struct {
    50  		Path            string `json:"path,omitempty"`
    51  		Status          string `json:"status,omitempty"`
    52  		Method          string `json:"method"`
    53  		StatusCode      int    `json:"statusCode,omitempty"`
    54  		InputBytes      int64  `json:"rx"`
    55  		OutputBytes     int64  `json:"tx"`
    56  		TimeToFirstByte string `json:"timeToFirstByte,omitempty"`
    57  		TimeToResponse  string `json:"timeToResponse,omitempty"`
    58  	} `json:"api"`
    59  	RemoteHost string                 `json:"remotehost,omitempty"`
    60  	RequestID  string                 `json:"requestID,omitempty"`
    61  	SessionID  string                 `json:"sessionID,omitempty"`
    62  	UserAgent  string                 `json:"userAgent,omitempty"`
    63  	ReqClaims  map[string]interface{} `json:"requestClaims,omitempty"`
    64  	ReqQuery   map[string]string      `json:"requestQuery,omitempty"`
    65  	ReqHeader  map[string]string      `json:"requestHeader,omitempty"`
    66  	RespHeader map[string]string      `json:"responseHeader,omitempty"`
    67  	Tags       map[string]interface{} `json:"tags,omitempty"`
    68  }
    69  
    70  // NewEntry - constructs an audit entry object with some fields filled
    71  func NewEntry(deploymentID string) Entry {
    72  	return Entry{
    73  		Version:      Version,
    74  		DeploymentID: deploymentID,
    75  		Time:         time.Now().UTC(),
    76  	}
    77  }
    78  
    79  // ToEntry - constructs an audit entry from a http request
    80  func ToEntry(w http.ResponseWriter, r *http.Request, reqClaims map[string]interface{}, deploymentID string) Entry {
    81  	entry := NewEntry(deploymentID)
    82  
    83  	entry.RemoteHost = r.RemoteAddr
    84  	entry.UserAgent = r.UserAgent()
    85  	entry.ReqClaims = reqClaims
    86  
    87  	q := r.URL.Query()
    88  	reqQuery := make(map[string]string, len(q))
    89  	for k, v := range q {
    90  		reqQuery[k] = strings.Join(v, ",")
    91  	}
    92  	entry.ReqQuery = reqQuery
    93  
    94  	reqHeader := make(map[string]string, len(r.Header))
    95  	for k, v := range r.Header {
    96  		reqHeader[k] = strings.Join(v, ",")
    97  	}
    98  	entry.ReqHeader = reqHeader
    99  
   100  	wh := w.Header()
   101  
   102  	var requestID interface{}
   103  	requestID = r.Context().Value(utils.ContextRequestID)
   104  	if requestID == nil {
   105  		requestID = uuid.NewString()
   106  	}
   107  	entry.RequestID = requestID.(string)
   108  
   109  	if val := r.Context().Value(utils.ContextRequestUserID); val != nil {
   110  		sessionID := val.(string)
   111  		if os.Getenv("CONSOLE_OPERATOR_MODE") != "" && os.Getenv("CONSOLE_OPERATOR_MODE") == "on" {
   112  			claims := jwt.MapClaims{}
   113  			_, _ = jwt.ParseWithClaims(sessionID, claims, nil)
   114  			if sub, ok := claims["sub"]; ok {
   115  				sessionID = sub.(string)
   116  			}
   117  		}
   118  		entry.SessionID = sessionID
   119  	}
   120  
   121  	respHeader := make(map[string]string, len(wh))
   122  	for k, v := range wh {
   123  		respHeader[k] = strings.Join(v, ",")
   124  	}
   125  	entry.RespHeader = respHeader
   126  
   127  	if etag := respHeader[xhttp.ETag]; etag != "" {
   128  		respHeader[xhttp.ETag] = strings.Trim(etag, `"`)
   129  	}
   130  
   131  	return entry
   132  }