mirror of
https://github.com/micromdm/micromdm/
synced 2026-08-08 10:45:34 +08:00
Remove methods with signature NewDatastore() from datastores and injection of the logger instance into each datastore because groob is a doodoo head :)
Move NewDatastore methods into unit tests Reinstate NewDB test Create certificates package including a Certificate type and a datastore. Began working on a method ackCertificateList in the connect service. Boilerplate for management service test. Renamed some tables in certificates migration, hopefully nobody notices.
This commit is contained in:
@@ -9,7 +9,7 @@ import (
|
||||
"time"
|
||||
)
|
||||
|
||||
// Datastore manages devices in a database
|
||||
// This Datastore manages a list of known applications, and their relationship to devices.
|
||||
type Datastore interface {
|
||||
New(a *Application) (string, error)
|
||||
Applications(params ...interface{}) ([]Application, error)
|
||||
@@ -19,11 +19,6 @@ type Datastore interface {
|
||||
|
||||
type pgStore struct {
|
||||
*sqlx.DB
|
||||
logger kitlog.Logger
|
||||
}
|
||||
|
||||
func NewDatastore(connection *sqlx.DB, logger kitlog.Logger) (Datastore, error) {
|
||||
return pgStore{DB: connection, logger: logger}, nil
|
||||
}
|
||||
|
||||
func NewDB(driver, conn string, logger kitlog.Logger) (Datastore, error) {
|
||||
|
||||
@@ -42,19 +42,22 @@ var appFixtures []Application = []Application{
|
||||
|
||||
var logger log.Logger = log.NewNopLogger()
|
||||
|
||||
//func TestNewDB(t *testing.T) {
|
||||
// var logger logger.Logger = logger.NewNopLogger()
|
||||
// appsDB, err := NewDB("postgres", "host=localhost", logger)
|
||||
//
|
||||
// if err != nil {
|
||||
// t.Error(err)
|
||||
// }
|
||||
//
|
||||
// if _, ok := appsDB.(Datastore); !ok {
|
||||
// t.Log("Did not get a datastore")
|
||||
// t.Fail()
|
||||
// }
|
||||
//}
|
||||
func NewDatastore(connection *sqlx.DB, logger log.Logger) (Datastore, error) {
|
||||
return pgStore{DB: connection, logger: logger}, nil
|
||||
}
|
||||
|
||||
func TestNewDB(t *testing.T) {
|
||||
appsDB, err := NewDB("postgres", "host=localhost", logger)
|
||||
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
if _, ok := appsDB.(Datastore); !ok {
|
||||
t.Log("Did not get a datastore")
|
||||
t.Fail()
|
||||
}
|
||||
}
|
||||
|
||||
func TestNewDatastore(t *testing.T) {
|
||||
db, _, err := sqlmock.New()
|
||||
|
||||
12
certificates/certificate.go
Normal file
12
certificates/certificate.go
Normal file
@@ -0,0 +1,12 @@
|
||||
package certificates
|
||||
|
||||
import (
|
||||
"crypto/x509"
|
||||
)
|
||||
|
||||
type Certificate struct {
|
||||
UUID string `db:"certificate_uuid" json:"uuid"`
|
||||
Data x509.Certificate `db:"data" json:"data"`
|
||||
CommonName string `db:"common_name" json:"common_nane"`
|
||||
IsIdentity bool `db:"is_identity" json:"is_identity"`
|
||||
}
|
||||
112
certificates/datastore.go
Normal file
112
certificates/datastore.go
Normal file
@@ -0,0 +1,112 @@
|
||||
package certificates
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
kitlog "github.com/go-kit/kit/log"
|
||||
"github.com/jmoiron/sqlx"
|
||||
_ "github.com/lib/pq" // postgres driver
|
||||
"github.com/pkg/errors"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
var (
|
||||
insertCertificateStmt = `INSERT INTO certificates (
|
||||
common_name,
|
||||
data,
|
||||
is_identity
|
||||
) VALUES ($1, $2, $3)
|
||||
RETURNING certificate_uuid;`
|
||||
|
||||
selectCertificatesStmt = `SELECT
|
||||
certificate_uuid,
|
||||
common_name,
|
||||
data,
|
||||
is_identity
|
||||
FROM certificates`
|
||||
)
|
||||
|
||||
// This Datastore manages a list of certificates assigned to devices.
|
||||
type Datastore interface {
|
||||
New(crt *Certificate) (string, error)
|
||||
Certificates(params ...interface{}) ([]Certificate, error)
|
||||
}
|
||||
|
||||
type pgStore struct {
|
||||
*sqlx.DB
|
||||
}
|
||||
|
||||
func NewDB(driver, conn string, logger kitlog.Logger) (Datastore, error) {
|
||||
switch driver {
|
||||
case "postgres":
|
||||
db, err := sqlx.Open(driver, conn)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "certificates datastore")
|
||||
}
|
||||
var dbError error
|
||||
maxAttempts := 20
|
||||
for attempts := 1; attempts <= maxAttempts; attempts++ {
|
||||
dbError = db.Ping()
|
||||
if dbError == nil {
|
||||
break
|
||||
}
|
||||
logger.Log("msg", fmt.Sprintf("could not connect to postgres: %v", dbError))
|
||||
time.Sleep(time.Duration(attempts) * time.Second)
|
||||
}
|
||||
if dbError != nil {
|
||||
return nil, errors.Wrap(dbError, "applications datastore")
|
||||
}
|
||||
return pgStore{DB: db}, nil
|
||||
default:
|
||||
return nil, errors.New("unknown driver")
|
||||
}
|
||||
}
|
||||
|
||||
func (store pgStore) New(c *Certificate) (string, error) {
|
||||
if err := store.QueryRow(insertCertificateStmt, c.CommonName, "", c.IsIdentity).Scan(&c.UUID); err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return c.UUID, nil
|
||||
}
|
||||
|
||||
func (store pgStore) Certificates(params ...interface{}) ([]Certificate, error) {
|
||||
stmt := selectCertificatesStmt
|
||||
stmt = addWhereFilters(stmt, "OR", params...)
|
||||
var certificates []Certificate
|
||||
err := store.Select(&certificates, stmt)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "pgStore Certificates")
|
||||
}
|
||||
return certificates, nil
|
||||
}
|
||||
|
||||
// UUID is a filter that can be added as a parameter to narrow down the list of returned results
|
||||
type UUID struct {
|
||||
UUID string
|
||||
}
|
||||
|
||||
func (p UUID) where() string {
|
||||
return fmt.Sprintf("certificate_uuid = '%s'", p.UUID)
|
||||
}
|
||||
|
||||
// whereer is for building args passed into a method which finds resources
|
||||
type whereer interface {
|
||||
where() string
|
||||
}
|
||||
|
||||
// add WHERE clause from params
|
||||
func addWhereFilters(stmt string, separator string, params ...interface{}) string {
|
||||
var where []string
|
||||
for _, param := range params {
|
||||
if f, ok := param.(whereer); ok {
|
||||
where = append(where, f.where())
|
||||
}
|
||||
}
|
||||
|
||||
if len(where) != 0 {
|
||||
whereFilter := strings.Join(where, " "+separator+" ")
|
||||
stmt = fmt.Sprintf("%s WHERE %s", stmt, whereFilter)
|
||||
}
|
||||
return stmt
|
||||
}
|
||||
41
certificates/datastore_test.go
Normal file
41
certificates/datastore_test.go
Normal file
@@ -0,0 +1,41 @@
|
||||
package certificates
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"github.com/jmoiron/sqlx"
|
||||
"gopkg.in/DATA-DOG/go-sqlmock.v1"
|
||||
"testing"
|
||||
)
|
||||
|
||||
var (
|
||||
db *sql.DB
|
||||
dbx *sqlx.DB
|
||||
mock sqlmock.Sqlmock
|
||||
err error
|
||||
)
|
||||
|
||||
func setup() {
|
||||
db, mock, err = sqlmock.New()
|
||||
if err != nil {
|
||||
panic("an error was not expected when opening a stub database connection")
|
||||
}
|
||||
dbx = sqlx.NewDb(db, "mock")
|
||||
}
|
||||
|
||||
func teardown() {
|
||||
dbx.Close()
|
||||
}
|
||||
|
||||
func NewDatastore(connection *sqlx.DB) (Datastore, error) {
|
||||
return pgStore{DB: connection}, nil
|
||||
}
|
||||
|
||||
func TestNewDatastore(t *testing.T) {
|
||||
setup()
|
||||
defer teardown()
|
||||
|
||||
_, err := NewDatastore(dbx)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
@@ -49,6 +49,8 @@ func (svc service) Acknowledge(ctx context.Context, req mdm.Response) (int, erro
|
||||
fmt.Printf("Got an error acknowledging InstalledApplicationList: %v\n", err)
|
||||
return 0, err
|
||||
}
|
||||
case "CertificateList":
|
||||
return 0, nil
|
||||
default:
|
||||
// Need to handle the absence of RequestType in IOS8 devices
|
||||
if req.QueryResponses.UDID != "" {
|
||||
@@ -232,3 +234,13 @@ skip:
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Acknowledge a response to `CertificateList`.
|
||||
func (svc service) ackCertificateList(req mdm.Response) error {
|
||||
_, err := svc.devices.GetDeviceByUDID(req.UDID, "device_uuid")
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "getting a device record by udid")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
23
management/service_test.go
Normal file
23
management/service_test.go
Normal file
@@ -0,0 +1,23 @@
|
||||
package management
|
||||
|
||||
import "testing"
|
||||
|
||||
func svcSetup() {
|
||||
|
||||
}
|
||||
|
||||
func svcTearDown() {
|
||||
|
||||
}
|
||||
|
||||
func TestService_InstalledApps(t *testing.T) {
|
||||
svcSetup()
|
||||
defer svcTearDown()
|
||||
|
||||
svc := NewService(nil, nil, nil, nil, nil)
|
||||
_, err := svc.InstalledApps("00000000-1111-2222-3333-444455556666")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1 +0,0 @@
|
||||
DROP TABLE IF EXISTS certificates;
|
||||
1
migrations/201607110002_devices_certificates_down.sql
Normal file
1
migrations/201607110002_devices_certificates_down.sql
Normal file
@@ -0,0 +1 @@
|
||||
DROP TABLE IF EXISTS devices_certificates;
|
||||
@@ -1,7 +1,8 @@
|
||||
CREATE EXTENSION IF NOT EXISTS "uuid-ossp";
|
||||
|
||||
CREATE TABLE IF NOT EXISTS certificates (
|
||||
CREATE TABLE IF NOT EXISTS devices_certificates (
|
||||
certificate_uuid uuid PRIMARY KEY DEFAULT uuid_generate_v4(),
|
||||
device_uuid uuid REFERENCES devices(device_uuid) ON DELETE CASCADE,
|
||||
common_name text NOT NULL,
|
||||
data BYTEA NOT NULL,
|
||||
is_identity BOOL DEFAULT false
|
||||
Reference in New Issue
Block a user