goaudit

package module
v0.0.0-...-3116709 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jan 21, 2018 License: MIT Imports: 10 Imported by: 0

README

goaudit

Build Status Go report card GoDoc

Tested for Go 1.5, 1.6, 1.7, tip

Audits SDK

Goaudit is a set of interceptors to generate and store audits:

And some helpers:

  • GetAudit - Retrieve Audit from context
  • NewTestAudit - Store Audits in memory to allow unit testing

InterceptorAudit

Attach a new audit to the context and populate it automatically.

Typical usage:

a := golax.NewApi()

a.Root.
	Interceptor(goaudit.InterceptorAudit2Log()).
	Interceptor(goaudit.InterceptorAudit(&goaudit.Service{Name:"invented-service"})).
	Method("GET", func(c *golax.Context) {
		// Implement your API here
	})

Custom info

Audit allow to store custom service information:

audit := GetAudit(c)
audit.Custom = map[string]interface{}{
	"a": 20,
	"b": 55,
}
Access

Typically only the user using the service can read the audit but sometimes, a third party is involved (for example, somebody interacting with me).

In that case, the service should add the third auth_id to the access list:

audit := GetAudit(c)
audit.AddReadAccess("other-involved-auth-id")
Sample audit

A sample audit in JSON:

{
	"id": "580a465bce507629a613107c",
	"version": "2.0.0",
	"auth_id": "my-auth-id",
	"origin": "127.0.0.1",
	"session_id": "",
	"service": {
		"name": "invented-service"
	},
	"entry_date": "2016-10-21T18:46:19.820423299+02:00",
	"entry_timestamp": 1.4770683798204234e+09,
	"elapsed_seconds": 1.621246337890625e-05,
	"request": {
		"method": "POST",
		"uri": "/value-1/value-2/test-node?query_a=aaa\u0026query_b=bbb",
		"handler": "/{param1}/{param2}/test-node",
		"args": {
			"query_a": ["aaa"],
			"query_b": ["bbb"]
		},
		"length": 2
	},
	"response": {
		"status_code": 222,
		"length": 5,
		"error": {
			"code": 27,
			"description": "my-error-description"
		}
	},
	"read_access": ["other-involved-auth-id", "my-auth-id"],
	"custom": {
		"a": 20,
		"b": 55
	}
}
TODO: Explain all fields in an Audit

InterceptorAudit2Log

Log Audit to stdout. This interceptor should wrap InterceptorAuth.

Typical usage:

a := golax.NewApi()

a.Root.
	Interceptor(goaudit.InterceptorAudit2Log()).
	Interceptor(goaudit.InterceptorAudit(&goaudit.Service{Name:"invented-service"})).
	Method("GET", func(c *golax.Context) {
		// Implement your API here
	})

InterceptorAudit2Channel

Push Audits to a channel. This interceptor should wrap InterceptorAudit.

Note that this interceptor do not extract the Audits from the channel, that should be done by other task. There is a util included (Chan2Mongo) to do that.

Typical usage:

channel_audits := make(chan *goaudit.Audit, 100)  // Buffered channel, 100 items

// assume `mongo_db` already exists

Chan2Mongo(channel_audits, mongo_db) // do the job: channel -> mongo

a := golax.NewApi()

a.Root.
	Interceptor(goaudit.InterceptorAudit2Channel(channel_audits)). // Pass created channel
	Interceptor(goaudit.InterceptorAudit(&goaudit.Service{Name:"invented-service"})).
	Method("GET", func(c *golax.Context) {
		// Implement your API here
	})

GetAudit

Get Audit object from context.

Typical usage:

func MyHandler(c *golax.Context) {
	// ...
	audit := goaudit.GetAudit(c)
	// ...
}

NewTestAudit

Store Audits in memory to be readed in your tests.

Typical usage:

func Test_ExampleBilling(t *testing.T) {

	audittest := testutils.NewTestAudit() // IMPORTANT

	a := golax.NewApi()
	a.Root.Interceptor(audittest.InterceptorAudit2Memory()) // IMPORTANT

	BuildYourApi(a)

	s := apitest.New(a)
	s.Request("GET", "/my-url-to-test").Do()

	// IMPORTANT: Do things with audittest.Memory[i], for example:
	if 200 != audittest.Memory[0].Response.StatusCode {
		t.Error("blah blah blah...")
	}
}

Dependencies

Dependencies for testing are:

  • github.com/fulldump/apitest
  • github.com/fulldump/golax
  • github.com/satori/go.uuid
  • gopkg.in/mgo.v2

NOTE: Pinned versions are NOT included in vendor/*.

Transitive dependencies for runtime are:

  • github.com/fulldump/golax

If goaudit.Chan2Mongo is used, as you can expect, it will be needed also:

  • gopkg.in/mgo.v2

Testing

As simple as:

git clone "<this-repo>"
make

Documentation

Index

Constants

View Source
const CONTEXT_KEY = "audit-87c577ea-178c-11e7-ae67-67adfdc1b095"

CONTEXT_KEY is the key used to attach the AUDIT to the golax context. This should be transparent to the user of this library.

View Source
const VERSION = "1.0.0"

VERSION is the model spec version for the AUDIT

Variables

This section is empty.

Functions

func Chan2Mongo

func Chan2Mongo(s chan *Audit, m *mgo.Database)

Chan2Mongo is a util to extract Audits from a channel and store to a MongoDB database. This interceptor should wrap `InterceptorAudit`.

Note that this interceptor do not extract the Audits from the channel, that should be done by other task. There is a util included (`Chan2Mongo`) to do that.

Typical usage:

channel_audits := make(chan *Audit, 100)  // Buffered channel, 100 items

assume `mongo_db` already exists

Chan2Mongo(channel_audits, mongo_db) // do the job: channel -> mongo

a := golax.NewApi()

a.Root.
    Interceptor(InterceptorAudit2Channel(channel_audits)). // Pass created channel
    Interceptor(InterceptorAudit("invented-service")).
    Method("GET", func(c *golax.Context) {
        // Implement your API here
    })

func InterceptorAudit

func InterceptorAudit(service *Service) *golax.Interceptor

InterceptorAudit attach a new Audit to the context and populate it automatically.

Typical usage:

a := golax.NewApi()

s := &model.Service{
    Name: "My service",
    Version: "7.3.0",
    Commit: "0f0710f",
}

a.Root.
    Interceptor(InterceptorAudit2Log()).
    Interceptor(InterceptorAudit(s)).
    Method("GET", func(c *golax.Context) {
        // Implement your API here
    })

func InterceptorAudit2Channel

func InterceptorAudit2Channel(s chan *Audit) *golax.Interceptor

InterceptorAudit2Channel push Audits to a channel. This interceptor should wrap `InterceptorAudit`.

Note that this interceptor do not extract the Audits from the channel, that should be done by other task. There is a util included (`Chan2Mongo`) to do that.

Typical usage:

channel_audits := make(chan *Audit, 100)  // Buffered channel, 100 items

// assume `mongo_db` already exists

Chan2Mongo(channel_audits, mongo_db) // do the job: channel -> mongo

a := golax.NewApi()

a.Root.
    Interceptor(InterceptorAudit2Channel(channel_audits)). // Pass created channel
    Interceptor(InterceptorAudit(&Service{Name: "invented service"}).
    Method("GET", func(c *golax.Context) {
        // Implement your API here
    })

func InterceptorAudit2Log

func InterceptorAudit2Log() *golax.Interceptor

InterceptorAudit2Log log audits to `stdout`. This interceptor should wrap `InterceptorAudit`.

Typical usage:

a := golax.NewApi()

a.Root.
    Interceptor(InterceptorAudit2Log()).
    Interceptor(InterceptorAudit(&Service{Name: "invented-service"})).
    Method("GET", func(c *golax.Context) {
        // Implement your API here
    })

Here is the sample output:

2016/10/24 19:49:19 AUDIT {"id":"580a465bce507629a613107c","version":"1.0.0","auth_id":"my-auth-id","origin":"127.0.0.1","session_id":"","service":"invented-service","entry_date":"2016-10-21T18:46:19.820423299+02:00","entry_timestamp":1.4770683798204234e+09,"elapsed_seconds":1.621246337890625e-05,"request":{"method":"POST","uri":"/value-1/value-2/test-node?query_a=aaa\u0026query_b=bbb","handler":"/{param1}/{param2}/test-node","args":{"query_a":["aaa"],"query_b":["bbb"]},"length":2},"response":{"status_code":222,"length":5,"error":{"code":27,"description":"my-error-description"}},"read_access":["other-involved-auth-id","my-auth-id"],"custom":{"a":20,"b":55}}

Types

type Audit

type Audit struct {

	// Id is a random string to identify a specific Audit.
	Id string `json:"id" bson:"_id,omitempty"`

	// Version is the specification version used in this Audit.
	Version string `json:"version" bson:"version"`

	// AuthId is the authentication identifier in the sistem mapped as `auth_id`.
	AuthId string `json:"auth_id" bson:"auth_id"`

	// SessionId stores cookie session, in case the client was using a session.
	SessionId string `json:"session_id" bson:"session_id"`

	// Origin is the real client IP (v4 and v6 are supported). If the service is
	// behind a proxy, the real IP should be forwarded in the header
	// `X-Forwarded-For`.
	Origin string `json:"origin" bson:"origin"`

	// Service indicates the service/server being provided.
	Service *Service `json:"service" bson:"service"`

	// EntryDate stores a `Time` object with the starting request timestamp.
	EntryDate time.Time `json:"entry_date" bson:"entry_date"`

	// EntryTimestamp stores `EntryDate` in UNIX time format in seconds.
	EntryTimestamp float64 `json:"entry_timestamp" bson:"entry_timestamp,minsize"`

	// ElapsedSeconds is the real time the request has taken in seconds.
	ElapsedSeconds float64 `json:"elapsed_seconds" bson:"elapsed_seconds,minsize"`

	// Request is a struct with standard HTTP client request information
	// (automatically filled up).
	Request Request `json:"request" bson:"request"`

	// Response is a struct with standard HTTP response information.
	Response Response `json:"response" bson:"response"`

	// Array with all authIds allowed to read this Audit.
	ReadAccess []string `json:"read_access" bson:"read_access"`

	Log Log `json:"log" bson:",inline"`

	// Custom stores specific service information that is service-dependent in
	// any format.
	Custom interface{} `json:"custom" bson:"custom"`
	// contains filtered or unexported fields
}

Audit represents Audit Record information. The purpose of this kind of records is auditing and billing.

func GetAudit

func GetAudit(c *golax.Context) *Audit

GetAudit retrieve Audit object from context.

Typical usage:

func MyHandler(c *golax.Context) {
    // ...
    audit := goaudit.GetAudit(c)
    // ...
}

func (*Audit) Abort

func (audit *Audit) Abort()

func (*Audit) AddReadAccess

func (audit *Audit) AddReadAccess(authId string) bool

AddReadAccess add a authId to read access list for this Audit

func (*Audit) SetError

func (audit *Audit) SetError(code int, desc string)

SetError overwrite error code and description in an Audit

type Error

type Error struct {

	// Code is a specific application error code (integer). It could be any
	// integer number: `200`, `396`, `2`, `3213254`, ...
	Code int `json:"code" bson:"code"`

	// Description is a human readable description for the error. It is a
	// specific application domain description, to developers.
	Description string `json:"description" bson:"description"`
}

Error represents a response error object

type Log

type Log struct {
	Entries []*LogEntry `json:"entries" bson:"log"`
}

Log entries for a current request

func (*Log) Debug

func (l *Log) Debug(text ...interface{})

func (*Log) Debugf

func (l *Log) Debugf(format string, text ...interface{})

func (*Log) Error

func (l *Log) Error(text ...interface{})

func (*Log) Errorf

func (l *Log) Errorf(format string, text ...interface{})

func (*Log) Fatal

func (l *Log) Fatal(text ...interface{})

func (*Log) Fatalf

func (l *Log) Fatalf(format string, text ...interface{})

func (*Log) Info

func (l *Log) Info(text ...interface{})

func (*Log) Infof

func (l *Log) Infof(format string, text ...interface{})

func (*Log) Warning

func (l *Log) Warning(text ...interface{})

func (*Log) Warningf

func (l *Log) Warningf(format string, text ...interface{})

type LogEntry

type LogEntry struct {
	Level     string `json:"level" bson:"level"`
	Timestamp int64  `json:"timestamp" bson:"timestamp"`
	Text      string `json:"text" bson:"text"`
	CodeLine  string `json:"code_line" bson:"code_line"`
}

LogEntry entry equivalent to a log line in stdout

type Request

type Request struct {

	// Header is all request headers
	Header http.Header `json:"header" bson:"header"`

	// Method is the HTTP verb
	Method string `json:"method" bson:"method"`

	// URI is the raw URL (the same one sent by the client)
	URI string `json:"uri" bson:"uri"`

	// Handler is the URL before replacing parameters
	Handler string `json:"handler" bson:"handler"`

	// Parameters is the URL parameters sent by client
	Parameters map[string]string `json:"parameters" bson:"parameters"`

	// Query is the URL query params
	Query map[string][]string `json:"query" bson:"query"`

	// Length is the client request body length
	Length int64 `json:"length" bson:"length"`
}

Request represents an Audit Request.

type Response

type Response struct {

	// Header is all request headers
	Header http.Header `json:"header" bson:"header"`

	// StatusCode is the returned status code number.
	StatusCode int `json:"status_code" bson:"status_code"`

	// Length is the response body length.
	Length int64 `json:"length" bson:"length"`

	// Error represents an error object.
	Error *Error `json:"error" bson:"error"`
}

Response represents an Audit response.

type Service

type Service struct {

	// Name is the service name.
	Name string `json:"name" bson:"name"`

	// Version is the service version, for example: 1.2, 0.0.1, 7.3.
	Version string `json:"version" bson:"version"`

	// Commit is the short commit number to identify exactly the code base
	// that is being executed
	Commit string `json:"commit" bson:"commit"`
}

Service identifies the process and other information related to the service.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL