gdpr

package module
v0.0.0-...-26f1228 Latest Latest
Warning

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

Go to latest
Published: Jun 8, 2018 License: MIT Imports: 16 Imported by: 0

README

go-gdpr

CircleCI GoDoc

go-gdpr is an implementation of the OpenGDPR specification for use with the EU GDPR regulation.

Disclaimer: Using this library does not imply accordance with GDPR!

Installation

go get github.com/greencase/go-gdpr

Usage

go-gdpr is intended to be used as a library by exposing business logic wrapped as middleware through an HTTP interface that meets the specifications in the OpenGDPR standard. There is no single concise way to achieve GDPR "compliance" per-say without implementing platform specific processes; go-gdpr only provides convenient components for fulfilling requests deemed mandatory by the GDPR legislation.

Concepts

The two major concepts used in this library are the Controller and Processor types. Their definitions are listed below and borrowed directly from the OpenGDPR specification.

Controller

An entity which makes the decision about what personal data will be processed and the types of processing that will be done with respect to that personal data. The Data Controller receives Data Subject requests from the Data Subjects and validates them.

Processor

The organization that processes data pursuant to the instructions of the Controller on behalf of the Controller. The Data Processor receives data subject requests via RESTful endpoints and is responsible for fulfilling requests.

Usage

The primary use case for go-gdpr is wrapping business logic via the Processor and Controller interface with the Server type. There is an additional Client which allows the consumer to access the processor via HTTP calls. This library might also be useful by providing static typing for other server implementations. See the example section for a more thorough introduction.

Simple Processor Example

A basic Processor can be implemented with just three methods:

package main

import (
	"net/http"

	"github.com/greencase/go-gdpr"
)

type Processor struct{}

func (p *Processor) Request(req *gdpr.Request) (*gdpr.Response, error) {
	// Process the request..
	return nil, nil
}

func (p *Processor) Status(id string) (*gdpr.StatusResponse, error) {
	// Check the status of the request..
	return nil, nil
}

func (p *Processor) Cancel(id string) (*gdpr.CancellationResponse, error) {
	// Cancel the request..
	return nil, nil
}

func main() {
	server := gdpr.NewServer(&gdpr.ServerOptions{
		Signer:          gdpr.NoopSigner{},
		ProcessorDomain: "my-processor-domain.com",
		Processor:       &Processor{},
		Identities: []gdpr.Identity{
			gdpr.Identity{
				Type:   gdpr.IDENTITY_EMAIL,
				Format: gdpr.FORMAT_RAW,
			},
		},
		SubjectTypes: []gdpr.SubjectType{
			gdpr.SUBJECT_ACCESS,
			gdpr.SUBJECT_ERASURE,
			gdpr.SUBJECT_PORTABILITY,
		},
	})
	http.ListenAndServe(":4000", server)
}
Simple Controller Example

A Controller only requires a single method, although a Client must also be used to communicate with the Processor:

package main

import (
	"net/http"

	"github.com/greencase/go-gdpr"
)

type Controller struct{}

func (c *Controller) Callback(req *gdpr.CallbackRequest) error {
	// Process the callback..
	return nil
}

func main() {
	server := gdpr.NewServer(&gdpr.ServerOptions{
		Controller: &Controller{},
		Verifier:   gdpr.NoopVerifier{},
	})
	http.ListenAndServe(":4001", server)
}

Contributing

We are open to any and all contributions so long as they improve the library, feel free to open up a new issue!

Documentation

Overview

`go-gdpr` is an experimental implementation of the OpenGDPR specification.

Index

Constants

View Source
const (
	SUBJECT_ACCESS      = SubjectType("access")
	SUBJECT_PORTABILITY = SubjectType("portability")
	SUBJECT_ERASURE     = SubjectType("erasure")
)
View Source
const (
	IDENTITY_CONTROLLER_CUSTOMER_ID   = IdentityType("controller_customer_id")
	IDENTITY_ANDROID_ADVERTISING_ID   = IdentityType("android_advertising_id")
	IDENTITY_ANDROID_ID               = IdentityType("android_id")
	IDENTITY_EMAIL                    = IdentityType("email")
	IDENTITY_FIRE_ADVERTISING_ID      = IdentityType("fire_advertising_id")
	IDENTITY_IOS_ADVERTISING_ID       = IdentityType("ios_advertising_id")
	IDENTITY_IOS_VENDOR_ID            = IdentityType("ios_vendor_id")
	IDENTITY_MICROSOFT_ADVERTISING_ID = IdentityType("microsoft_advertising_id")
	IDENTITY_MICROSOFT_PUBLISHER_ID   = IdentityType("microsoft_publisher_id")
	IDENTITY_ROKU_PUBLISHER_ID        = IdentityType("roku_publisher_id")
	IDENTITY_ROKU_ADVERTISING_ID      = IdentityType("roku_advertising_id")
)
View Source
const (
	FORMAT_RAW    = IdentityFormat("raw")
	FORMAT_SHA1   = IdentityFormat("sha1")
	FORMAT_MD5    = IdentityFormat("md5")
	FORMAT_SHA256 = IdentityFormat("sha256")
)
View Source
const (
	STATUS_PENDING     = RequestStatus("pending")
	STATUS_IN_PROGRESS = RequestStatus("in_progress")
	STATUS_COMPLETED   = RequestStatus("completed")
	STATUS_CANCELLED   = RequestStatus("cancelled")
)
View Source
const ApiVersion = "0.1"

Variables

View Source
var IdentityFormatMap = map[string]IdentityFormat{
	"raw":    FORMAT_RAW,
	"sha1":   FORMAT_SHA1,
	"md5":    FORMAT_MD5,
	"sha256": FORMAT_SHA256,
}
View Source
var IdentityTypeMap = map[string]IdentityType{
	"controller_customer_id":   IDENTITY_CONTROLLER_CUSTOMER_ID,
	"android_advertising_id":   IDENTITY_ANDROID_ADVERTISING_ID,
	"android_id":               IDENTITY_ANDROID_ID,
	"email":                    IDENTITY_EMAIL,
	"fire_advertising_id":      IDENTITY_FIRE_ADVERTISING_ID,
	"ios_advertising_id":       IDENTITY_IOS_ADVERTISING_ID,
	"ios_vendor_id":            IDENTITY_IOS_VENDOR_ID,
	"microsoft_advertising_id": IDENTITY_MICROSOFT_ADVERTISING_ID,
	"microsoft_publisher_id":   IDENTITY_MICROSOFT_PUBLISHER_ID,
	"roku_publisher_id":        IDENTITY_ROKU_PUBLISHER_ID,
	"roku_advertising_id":      IDENTITY_ROKU_ADVERTISING_ID,
}
View Source
var RequestStatusMap = map[string]RequestStatus{
	"pending":     STATUS_PENDING,
	"in_progress": STATUS_IN_PROGRESS,
	"completed":   STATUS_COMPLETED,
	"cancelled":   STATUS_CANCELLED,
}
View Source
var SubjectTypeMap = map[string]SubjectType{
	"access":      SUBJECT_ACCESS,
	"portability": SUBJECT_PORTABILITY,
	"erasure":     SUBJECT_ERASURE,
}

Functions

func Callback

func Callback(cbReq *CallbackRequest, opts *CallbackOptions) error

Callback sends the CallbackRequest type to the configured StatusCallbackUrl. If it fails to deliver in n attempts or the request is invalid it will return an error.

func ErrInvalidRequestSignature

func ErrInvalidRequestSignature(signature string, err error) error

ErrInvalidRequestSignature indicates the payload could not be verified with the given signature.

func ErrMissingRequiredField

func ErrMissingRequiredField(field string) error

ErrMissingRequiredField indicates the request is missing a required field.

func ErrNotFound

func ErrNotFound(id string) error

ErrNotFound indicates a request could not be found by the processor.

func ErrUnsupportedIdentity

func ErrUnsupportedIdentity(id Identity) error

ErrUnsupportedIdentity indicates the processor does not support the given identity type.

func ErrUnsupportedRequestType

func ErrUnsupportedRequestType(st SubjectType) error

ErrUnsupportedRequestType indicates the processor cannot fullfil a request for the given RequestType.

func SupportedFunc

func SupportedFunc(opts *ServerOptions) func(*Request) error

SupportedFunc returns a function that checks if the server can support a specific request.

func ValidateRequest

func ValidateRequest(opts *ServerOptions) func(*Request) error

Types

type Builder

type Builder func(opts *ServerOptions) Handler

Builder is a functional option to construct a Handler.

type CallbackOptions

type CallbackOptions struct {
	MaxAttempts     int
	Backoff         time.Duration
	ProcessorDomain string
	Client          *http.Client
	Signer          Signer
}

CallBackOptions configure an HTTP callback

type CallbackRequest

type CallbackRequest struct {
	ControllerId           string        `json:"controller_id"`
	ExpectedCompletionTime time.Time     `json:"expected_completion_time"`
	StatusCallbackUrl      string        `json:"status_callback_url"`
	SubjectRequestId       string        `json:"subject_request_id"`
	RequestStatus          RequestStatus `json:"request_status"`
	ResultsUrl             string        `json:"results_url"`
}

type CancellationResponse

type CancellationResponse struct {
	ControllerId     string    `json:"controller_id"`
	SubjectRequestId string    `json:"subject_request_id"`
	ReceivedTime     time.Time `json:"ReceivedTime"`
	EncodedRequest   string    `json:"encoded_request"`
	ApiVersion       string    `json:"api_version"`
}

type Client

type Client struct {
	// contains filtered or unexported fields
}

Client is an HTTP helper client for making requests to an OpenGDPR processor server.

func NewClient

func NewClient(opts *ClientOptions) *Client

NewClient returns a new OpenGDPR client.

func (*Client) Cancel

func (c *Client) Cancel(id string) (*CancellationResponse, error)

Cancel cancels an existing GDPR request.

func (*Client) Discovery

func (c *Client) Discovery() (*DiscoveryResponse, error)

Discovery describes the remote OpenGDPR speciication.

func (*Client) Request

func (c *Client) Request(req *Request) (*Response, error)

Request makes a performs a new GDPR request.

func (*Client) Status

func (c *Client) Status(id string) (*StatusResponse, error)

Status checks the status of an existing GDPR request.

type ClientOptions

type ClientOptions struct {
	Endpoint string
	Verifier Verifier
	Client   *http.Client
}

ClientOptions conifigure a Client.

type Controller

type Controller interface {
	// Process a callback from a remote
	// processor.
	Callback(req *CallbackRequest) error
}

Controller makes new requests to a Processor and processes Callback requests.

type DiscoveryResponse

type DiscoveryResponse struct {
	ApiVersion                   string        `json:"api_version"`
	SupportedIdentities          []Identity    `json:"supported_identities"`
	SupportedSubjectRequestTypes []SubjectType `json:"supported_subject_request_types"`
	ProcessorCertificate         string        `json:"processor_certificate"`
}

type Error

type Error struct {
	Domain  string `json:"domain"`
	Reason  string `json:"reason"`
	Message string `json:"message"`
}

func (Error) Error

func (e Error) Error() string

type ErrorResponse

type ErrorResponse struct {
	Code    int     `json:"-"`
	Message string  `json:"-"`
	Errors  []Error `json:"-"`
}

func (ErrorResponse) Error

func (e ErrorResponse) Error() string

func (ErrorResponse) MarshalJSON

func (e ErrorResponse) MarshalJSON() ([]byte, error)

func (*ErrorResponse) UnmarshalJSON

func (e *ErrorResponse) UnmarshalJSON(raw []byte) error

type Handler

type Handler func(resp io.Writer, req io.Reader, p httprouter.Params) error

Handler reads the incoming request body and encodes a json payload to resp.

type HandlerMap

type HandlerMap map[string]map[string]Builder

HandlerMap is a map of route/methods to Builder.

func (HandlerMap) Merge

func (hm HandlerMap) Merge(other HandlerMap)

Merge merges another HandlerMap into itself.

type Identity

type Identity struct {
	Type   IdentityType   `json:"identity_type"`
	Format IdentityFormat `json:"identity_format"`
	Value  string         `json:"identity_value,omitempty"`
}

type IdentityFormat

type IdentityFormat string

func (*IdentityFormat) UnmarshalJSON

func (i *IdentityFormat) UnmarshalJSON(raw []byte) error

func (IdentityFormat) Valid

func (i IdentityFormat) Valid() bool

type IdentityType

type IdentityType string

func (*IdentityType) UnmarshalJSON

func (i *IdentityType) UnmarshalJSON(raw []byte) error

func (IdentityType) Valid

func (i IdentityType) Valid() bool

type KeyOptions

type KeyOptions struct {
	KeyPath  string
	KeyBytes []byte
	// Optional byte string to decrypt
	// a private key file.
	Password []byte
}

KeyOptions specify the path or bytes of a public or private key.

type NoopSigner

type NoopSigner struct{}

NoopSigner is useful to forgo all signature generation.

func (NoopSigner) Sign

func (s NoopSigner) Sign([]byte) (string, error)

type NoopVerifier

type NoopVerifier struct{}

NoopVerifier is useful to forgo all certificate verification.

func (NoopVerifier) Cert

func (v NoopVerifier) Cert() *x509.Certificate

func (NoopVerifier) Verify

func (v NoopVerifier) Verify([]byte, string) error

type Processor

type Processor interface {
	// Request accepts an incoming Request type
	// and is expected to process it in some way.
	Request(*Request) (*Response, error)
	// Status validates the status of an existing
	// request sent to this processor.
	Status(id string) (*StatusResponse, error)
	// Cancel prevents any further processing of
	// the Request.
	Cancel(id string) (*CancellationResponse, error)
}

Processor implements the business logic for processing GDPR requests. The Processor interface is intended to be wrapped by the Server type and provide an HTTP REST server. Any method may return an ErrorResponse type which will be serialized as JSON and handled in accordance to the OpenGDPR specification.

type Request

type Request struct {
	SubjectRequestId   string      `json:"subject_request_id"`
	SubjectRequestType SubjectType `json:"subject_request_type"`
	SubmittedTime      time.Time   `json:"submitted_time"`
	ApiVersion         string      `json:"api_version"`
	StatusCallbackUrls []string    `json:"status_callback_urls"`
	SubjectIdentities  []Identity  `json:"subject_identities"`
	// TODO
	Extensions json.RawMessage `json:"extensions"`
}

Request represents a GDPR request

func (Request) Base64

func (r Request) Base64() string

type RequestStatus

type RequestStatus string

RequestStatus represents the status of a GDPR request

func (*RequestStatus) UnmarshalJSON

func (r *RequestStatus) UnmarshalJSON(raw []byte) error

func (RequestStatus) Valid

func (r RequestStatus) Valid() bool

type Response

type Response struct {
	ControllerId           string    `json:"controller_id"`
	ExpectedCompletionTime time.Time `json:"expected_completion_time"`
	ReceivedTime           time.Time `json:"received_time"`
	EncodedRequest         string    `json:"encoded_request"`
	SubjectRequestId       string    `json:"subject_request_id"`
}

type Server

type Server struct {
	// contains filtered or unexported fields
}

Server exposes an HTTP interface to an underlying Processor or Controller. Server relies on httprouter for route matching, in the future we might expand this to support other mux and frameworks.

func NewServer

func NewServer(opts *ServerOptions) *Server

NewServer returns a server type that statisfies the http.Handler interface.

func (*Server) After

func (s *Server) After(handlers ...http.HandlerFunc)

After applys any http.HandlerFunc to the request after it has been handled by the controller/processor.

func (*Server) Before

func (s *Server) Before(handlers ...http.HandlerFunc)

Before applys any http.HandlerFunc to the request before sending it on to the controller/processor. Note that if a handler incecepts the body of the request via request.Body they must ensure it's contents are added back to request or signature verification will fail!

func (Server) ServeHTTP

func (s Server) ServeHTTP(w http.ResponseWriter, r *http.Request)

type ServerOptions

type ServerOptions struct {
	// Controller to process callback requests.
	Controller Controller
	// Processor to handle GDPR requests.
	Processor Processor
	// Signs all responses.
	Signer Signer
	// Verifies any incoming callbacks.
	Verifier Verifier
	// Array of identity types supported by
	// the server.
	Identities []Identity
	// Array of subject types supported by
	// the server.
	SubjectTypes []SubjectType
	// Optional map allowing the user to
	// override specific routes.
	HandlerMap HandlerMap
	// Processor domain of this server.
	ProcessorDomain string
	// Remote URL where the public certificate
	// of this server can be downloaded and used
	// to verify subsequent response payload
	ProcessorCertificateUrl string
}

ServerOptions contains configuration options that effect the operation of the HTTP server.

type Signer

type Signer interface {
	// Generate a signature for the digest
	Sign(body []byte) (string, error)
}

Signer accepts a byte array which it creates a hash from and generates a signature which it base64 encodes as a string.

func MustNewSigner

func MustNewSigner(opts *KeyOptions) Signer

func NewSigner

func NewSigner(opts *KeyOptions) (Signer, error)

NewSigner creates a new RSA backed Signer

type StatusResponse

type StatusResponse struct {
	ControllerId           string        `json:"controller_id"`
	ExpectedCompletionTime time.Time     `json:"expected_completion_time"`
	SubjectRequestId       string        `json:"subject_request_id"`
	RequestStatus          RequestStatus `json:"request_status"`
	ApiVersion             string        `json:"api_version"`
	ResultsUrl             string        `json:"results_url"`
}

type SubjectType

type SubjectType string

SubjectType is the type of request that is being made.

func (*SubjectType) UnmarshalJSON

func (s *SubjectType) UnmarshalJSON(raw []byte) error

func (SubjectType) Valid

func (s SubjectType) Valid() bool

type Verifier

type Verifier interface {
	// return the underlying cerificate
	Cert() *x509.Certificate
	// verify the digest of the signature
	Verify(body []byte, signature string) error
}

Verifier accepts a byte array and base64 encoded signature. It hashes the byte array and compares it's decoded value.

func MustNewVerifier

func MustNewVerifier(opts *KeyOptions) Verifier

func NewVerifier

func NewVerifier(opts *KeyOptions) (Verifier, error)

NewVerifier creates a new RSA backed Verifier

Directories

Path Synopsis

Jump to

Keyboard shortcuts

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