lib/auditlog/auditlog.go (121 lines of code) (raw):
// Copyright 2019 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// Package auditlog contains logging structs.
package auditlog
import (
"context"
"net/http"
"strconv"
"cloud.google.com/go/logging" /* copybara-comment */
"github.com/GoogleCloudPlatform/healthcare-federated-access-services/lib/globalflags" /* copybara-comment: globalflags */
"github.com/GoogleCloudPlatform/healthcare-federated-access-services/lib/serviceinfo" /* copybara-comment: serviceinfo */
glog "github.com/golang/glog" /* copybara-comment */
)
var (
// LogSync ensure that logs are written sync.
// Useful for testing.
LogSync bool
// SearchableFields lists all labels allow to search via FA endpoints
SearchableFields = []string{
"httpRequest.requestMethod",
"labels.token_id",
"labels.token_issuer",
"labels.tracing_id",
"labels.request_endpoint",
"labels.request_path",
"labels.error_type",
"labels.resource",
"labels.ttl",
"labels.cart_id",
}
)
const (
// TypeRequestLog log type string for access log
TypeRequestLog = "request"
// TypePolicyLog log type string for policy log
TypePolicyLog = "policy_decision"
)
// RequestLog logs the http endpoint accessing.
type RequestLog struct {
// TokenID is the id of the token, maybe "jti".
TokenID string
// TokenSubject is the "sub" of the token.
TokenSubject string
// TokenIssuer is the iss of the token.
TokenIssuer string
// TracingID is the id of request from proxies.
TracingID string
// RequestMethod is the http method of the request.
RequestMethod string
// RequestEndpoint is the absolute abstract path of the request endpoint registration.
RequestEndpoint string
// RequestPath is the request's path with concrete variables (such as realm) filled in
// as per the caller's absolute path.
RequestPath string
// RequestProtocol is the protocol used for this request (such as "HTTP/1.1")
RequestProtocol string
// RequestIP is the requester IP.
RequestIP string
// ErrorType formats like "no_token" for search.
ErrorType string
// ResponseCode is the response code.
ResponseCode int
// Request stores the http.Request.
Request *http.Request
// PassAuthCheck if the request pass the auth checker.
PassAuthCheck bool
// Payload of the log.
Payload interface{}
}
// WriteRequestLog puts the access log to StackDriver.
func WriteRequestLog(ctx context.Context, client *logging.Client, log *RequestLog) {
labels := map[string]string{
"type": TypeRequestLog,
"token_id": log.TokenID,
"token_subject": log.TokenSubject,
"token_issuer": log.TokenIssuer,
"tracing_id": log.TracingID,
"request_endpoint": log.RequestEndpoint,
"request_path": log.RequestPath,
"error_type": log.ErrorType,
"pass_auth_check": strconv.FormatBool(log.PassAuthCheck),
"project_id": serviceinfo.Project,
"service_type": serviceinfo.Type,
"service_name": serviceinfo.Name,
}
req := &logging.HTTPRequest{
Request: log.Request,
RemoteIP: log.RequestIP,
Status: log.ResponseCode,
}
entry := logging.Entry{
Labels: labels,
Payload: log.Payload,
HTTPRequest: req,
}
writeLog(client, entry)
}
// PolicyDecisionLog logs the dataset access request be granted or denied and the reason.
type PolicyDecisionLog struct {
// TokenID is the id of the token, maybe "jti".
TokenID string
// TokenSubject is the "sub" of the token.
TokenSubject string
// TokenIssuer is the iss of the token.
TokenIssuer string
// Resource identifies the dataset.
Resource string
// TTL that user requested to grant.
TTL string
// PassAuthCheck if the request pass the auth checker.
PassAuthCheck bool
// ErrorType of deny.
ErrorType string
// CartID of request.
CartID string
// ConfigRevision the request using. Can use /config/history/{revision} to see the policy.
ConfigRevision int64
// Message of deny.
Message interface{}
}
// WritePolicyDecisionLog puts the policy decision log to StackDriver.
func WritePolicyDecisionLog(client *logging.Client, log *PolicyDecisionLog) {
labels := map[string]string{
"type": TypePolicyLog,
"token_id": log.TokenID,
"token_subject": log.TokenSubject,
"token_issuer": log.TokenIssuer,
"pass_auth_check": strconv.FormatBool(log.PassAuthCheck),
"error_type": log.ErrorType,
"resource": log.Resource,
"ttl": log.TTL,
"project_id": serviceinfo.Project,
"service_type": serviceinfo.Type,
"service_name": serviceinfo.Name,
"cart_id": log.CartID,
"config_revision": strconv.FormatInt(log.ConfigRevision, 10),
}
entry := logging.Entry{
Labels: labels,
Payload: log.Message,
}
writeLog(client, entry)
}
func writeLog(client *logging.Client, e logging.Entry) {
ctx := context.Background() /* TODO: pass context to here */
if globalflags.DisableAuditLog {
return
}
if client == nil {
glog.Info("no logging client is provided for audit logging")
return
}
if LogSync {
client.Logger("federated-access-audit").LogSync(ctx, e)
} else {
client.Logger("federated-access-audit").Log(e)
}
}