d1gorm

package module
v1.0.2 Latest Latest
Warning

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

Go to latest
Published: Sep 27, 2022 License: Apache-2.0 Imports: 6 Imported by: 0

README

CYBERCRYPT D1 GORM

This integration can be used to encrypt and decrypt data transparently when reading and writing to the database, using CYBERCRYPT D1 Generic. The data is encrypted in the application layer in such a way that the database itself never receives the data in plain text.

This protects the data in the database from being read by third parties and tampering.

Supported databases

All databases supported by GORM are supported by the d1gorm package. These include:

  • MySQL
  • PostgreSQL
  • SQL Server
  • SQLite
  • any database that is compatible with the mysql or postgres dialects.

Installation

You can install the d1gorm package in your go project by running:

go get github.com/cybercryptio/d1-gorm

Usage

For examples of how to use the integration see our examples in the godoc.

Limitations

  • Currently only string and []byte data fields can be encrypted.
  • Encrypted data is not searchable by the database.

License

The software in the CYBERCRYPT d1-gorm repository is licensed under the Apache License 2.0.

Documentation

Overview

Example
package main

import (
	"fmt"
	"log"
	"os"

	client "github.com/cybercryptio/d1-client-go/v2/d1-generic"
	d1gorm "github.com/cybercryptio/d1-gorm"
	"github.com/cybercryptio/d1-gorm/crypto"
	"google.golang.org/grpc"
	"google.golang.org/grpc/credentials/insecure"
	"gorm.io/driver/sqlite"
	"gorm.io/gorm"
	"gorm.io/gorm/logger"
	"gorm.io/gorm/schema"
)

var endpoint = os.Getenv("D1_ENDPOINT")
var uid = os.Getenv("D1_UID")
var password = os.Getenv("D1_PASS")
var creds = insecure.NewCredentials()

type Person struct {
	ID        string
	FirstName string
	LastName  string `gorm:"serializer:D1"`
}

func main() {
	// Create a new D1 Generic client
	client, err := client.NewGenericClient(endpoint,
		client.WithGrpcOption(grpc.WithTransportCredentials(creds)),
		client.WithTokenRefresh(uid, password),
	)
	if err != nil {
		log.Fatal(err)
	}

	// Instantiate the D1Serializer with a Cryptor that uses the created client
	d1Serializer := d1gorm.NewD1Serializer(crypto.NewD1Cryptor(client))

	// Register the D1Serializer to be used for your database schema
	schema.RegisterSerializer("D1", d1Serializer)

	// Create a connection to your database
	db, err := gorm.Open(sqlite.Open(":memory:"), &gorm.Config{Logger: logger.Default.LogMode(logger.Info)})
	if err != nil {
		log.Fatal(err)
	}
	_ = db.AutoMigrate(&Person{})

	// Now all the created Persons will have their fields tagged with "serializer:D1" encrypted before being written to the database
	michael := &Person{"1", "Michael", "Jackson"}
	// Michael's last name will be encrypted
	db.Create(michael)

	// The encrypted data is transparently decrypted when reading from the database
	ret := &Person{}
	db.Where("id = ?", "1").First(ret)

	fmt.Printf("First Name: %s Last Name: %s", ret.FirstName, ret.LastName)
	// Out: First Name: Michael Last Name: Jackson
}
Example (PassTokenInCtx)
package main

import (
	"context"
	"fmt"
	"log"

	client "github.com/cybercryptio/d1-client-go/v2/d1-generic"
	pb "github.com/cybercryptio/d1-client-go/v2/d1-generic/protobuf/authn"
	d1gorm "github.com/cybercryptio/d1-gorm"
	"github.com/cybercryptio/d1-gorm/crypto"
	"google.golang.org/grpc"
	"gorm.io/driver/sqlite"
	"gorm.io/gorm"
	"gorm.io/gorm/logger"
	"gorm.io/gorm/schema"
)

type ctxKeyType string
type perRPCToken struct{}

var tokenKey = ctxKeyType("token")

func (p perRPCToken) GetRequestMetadata(ctx context.Context, uri ...string) (map[string]string, error) {
	token, found := ctx.Value(tokenKey).(string)
	if !found {
		return nil, fmt.Errorf("token not found in context")
	}
	return map[string]string{"authorization": "bearer " + token}, nil
}

func (p perRPCToken) RequireTransportSecurity() bool {
	return false
}

func getToken() string {
	client, err := client.NewGenericClient(endpoint,
		client.WithGrpcOption(grpc.WithTransportCredentials(creds)),
	)
	if err != nil {
		log.Fatal(err)
	}

	res, err := client.Authn.LoginUser(
		context.Background(), &pb.LoginUserRequest{UserId: uid, Password: password},
	)
	if err != nil {
		log.Fatal(err)
	}
	return res.AccessToken
}

func main() {
	// Create a new D1 Generic client which fetches the token from the context on each request
	client, err := client.NewGenericClient(endpoint,
		client.WithGrpcOption(grpc.WithTransportCredentials(creds)),
		client.WithGrpcOption(grpc.WithPerRPCCredentials(perRPCToken{})),
	)
	if err != nil {
		log.Fatal(err)
	}

	// Instantiate the D1Serializer with a Cryptor that uses the created client
	d1Serializer := d1gorm.NewD1Serializer(crypto.NewD1Cryptor(client))

	// Register the D1Serializer to be used for your database schema
	schema.RegisterSerializer("D1", d1Serializer)

	// Create a connection to your database
	db, err := gorm.Open(sqlite.Open(":memory:"), &gorm.Config{Logger: logger.Default.LogMode(logger.Info)})
	if err != nil {
		log.Fatal(err)
	}
	_ = db.AutoMigrate(&Person{})

	// Now all the created Persons will have their fields tagged with "serializer:D1" encrypted before being written to the database
	michael := &Person{"1", "Michael", "Jackson"}
	// Michael's last name will be encrypted
	// Note that we pass in the context the access token to be used by the D1 Generic client
	db.WithContext(context.WithValue(context.Background(), tokenKey, getToken())).Create(michael)
}

Index

Examples

Constants

This section is empty.

Variables

View Source
var ErrDecryptUnsupported = fmt.Errorf("supported decryption field types: string, []byte")

Error returned when trying to decrypt a field of an unsupported type.

View Source
var ErrEncryptUnsupported = fmt.Errorf("supported encryption field types: string, []byte")

Error returned when trying to encrypt a field of an unsupported type.

Functions

This section is empty.

Types

type D1Serializer

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

D1Serializer is used to transparently encrypt and decrypt data when reading/writing to the database. To use it you must instantiate it, register it to be used for your gorm schema with schema.RegisterSerializer("D1", d1Serializer), and tag the model fields to be serialized with `gorm:"serializer:D1"`. Currently only the serialization of string and []byte data types is supported.

func NewD1Serializer

func NewD1Serializer(cryptor crypto.Cryptor) D1Serializer

NewD1Serializer creates a new D1Serializer that uses the provided Cryptor to encrypt and decrypt data.

func (D1Serializer) Scan

func (s D1Serializer) Scan(ctx context.Context, field *schema.Field, dst reflect.Value, dbValue interface{}) error

Scan is called by gorm to deserialize the value of a field after it has been read from the database.

func (D1Serializer) Value

func (s D1Serializer) Value(ctx context.Context, field *schema.Field, dst reflect.Value, fieldValue interface{}) (interface{}, error)

Value is called by gorm to serialize the value of a field before being written to the database.

Directories

Path Synopsis

Jump to

Keyboard shortcuts

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