github.com/wallyworld/juju@v0.0.0-20161013125918-6cf1bc9d917a/apiserver/observer/audit.go (about) 1 // Copyright 2016 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 package observer 4 5 import ( 6 "fmt" 7 "net/http" 8 "time" 9 10 "github.com/juju/errors" 11 "github.com/juju/version" 12 "gopkg.in/juju/names.v2" 13 14 "github.com/juju/juju/audit" 15 "github.com/juju/juju/rpc" 16 ) 17 18 // Context defines things an Audit observer need know about to operate 19 // correctly. 20 type AuditContext struct { 21 22 // JujuServerVersion is the version of jujud. 23 JujuServerVersion version.Number 24 25 // ModelUUID is the UUID of the model the audit observer is 26 // currently running on. 27 ModelUUID string 28 } 29 30 type ErrorHandler func(error) 31 32 // NewAudit creates a new Audit with the information provided via the Context. 33 func NewAudit(ctx *AuditContext, handleAuditEntry audit.AuditEntrySinkFn, errorHandler ErrorHandler) *Audit { 34 return &Audit{ 35 jujuServerVersion: ctx.JujuServerVersion, 36 modelUUID: ctx.ModelUUID, 37 errorHandler: errorHandler, 38 handleAuditEntry: handleAuditEntry, 39 } 40 } 41 42 // Audit is an observer which will log APIServer requests using the 43 // function provided. 44 type Audit struct { 45 jujuServerVersion version.Number 46 modelUUID string 47 errorHandler ErrorHandler 48 handleAuditEntry audit.AuditEntrySinkFn 49 50 // state represents information that's built up as methods on this 51 // type are called. We segregate this to ensure it's clear what 52 // information is transient in case we want to extract it 53 // later. It's an anonymous struct so this doesn't leak outside 54 // this type. 55 state struct { 56 remoteAddress string 57 authenticatedTag string 58 } 59 } 60 61 // Login implements Observer. 62 func (a *Audit) Login(entity names.Tag, _ names.ModelTag, _ bool, _ string) { 63 a.state.authenticatedTag = entity.String() 64 } 65 66 // Join implements Observer. 67 func (a *Audit) Join(req *http.Request, _ uint64) { 68 a.state.remoteAddress = req.RemoteAddr 69 } 70 71 // Leave implements Observer. 72 func (a *Audit) Leave() { 73 a.state.remoteAddress = "" 74 a.state.authenticatedTag = "" 75 } 76 77 // RPCObserver implements Observer. 78 func (a *Audit) RPCObserver() rpc.Observer { 79 return &AuditRPCObserver{ 80 jujuServerVersion: a.jujuServerVersion, 81 modelUUID: a.modelUUID, 82 errorHandler: a.errorHandler, 83 handleAuditEntry: a.handleAuditEntry, 84 authenticatedTag: a.state.authenticatedTag, 85 remoteAddress: a.state.remoteAddress, 86 } 87 } 88 89 // AuditRPCObserver is an observer which will log RPC requests using 90 // the function provided. 91 type AuditRPCObserver struct { 92 jujuServerVersion version.Number 93 modelUUID string 94 errorHandler ErrorHandler 95 handleAuditEntry audit.AuditEntrySinkFn 96 authenticatedTag string 97 remoteAddress string 98 } 99 100 // ServerRequest implements Observer. 101 func (a *AuditRPCObserver) ServerRequest(hdr *rpc.Header, body interface{}) { 102 auditEntry := a.boilerplateAuditEntry() 103 auditEntry.OriginName = a.authenticatedTag 104 105 auditEntry.OriginType = "API request" 106 auditEntry.Operation = rpcRequestToOperation(hdr.Request) 107 auditEntry.Data = map[string]interface{}{"request-body": body} 108 err := a.handleAuditEntry(auditEntry) 109 if err != nil { 110 a.errorHandler(errors.Trace(err)) 111 } 112 } 113 114 // ServerReply implements Observer. 115 func (a *AuditRPCObserver) ServerReply(rpc.Request, *rpc.Header, interface{}) {} 116 117 func (a *AuditRPCObserver) boilerplateAuditEntry() audit.AuditEntry { 118 return audit.AuditEntry{ 119 JujuServerVersion: a.jujuServerVersion, 120 ModelUUID: a.modelUUID, 121 Timestamp: time.Now().UTC(), 122 RemoteAddress: a.remoteAddress, 123 OriginName: a.authenticatedTag, 124 } 125 } 126 127 func rpcRequestToOperation(req rpc.Request) string { 128 return fmt.Sprintf("%s:v%d - %s", req.Type, req.Version, req.Action) 129 }