Skip to content
This repository has been archived by the owner on Jul 12, 2023. It is now read-only.

Commit

Permalink
Authorized App through interface (#361)
Browse files Browse the repository at this point in the history
* Authorized App through interface

Authorized App through interface

* change to calling interfaces models

* remove unrelated pb change

* continue calling it DB, not model

* split db test to util, move mode, database into authorizedapp

* split db test to util, move mode, database into authorizedapp

* remove unneeded alias
  • Loading branch information
crwilcox authored May 20, 2020
1 parent 50ffa9e commit f113c0d
Show file tree
Hide file tree
Showing 24 changed files with 124 additions and 88 deletions.
4 changes: 2 additions & 2 deletions internal/android/safetynet.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,8 @@ import (
"runtime/trace"
"time"

"github.com/google/exposure-notifications-server/internal/authorizedapp/model"
"github.com/google/exposure-notifications-server/internal/base64util"
"github.com/google/exposure-notifications-server/internal/database"
"github.com/google/exposure-notifications-server/internal/logging"

"github.com/dgrijalva/jwt-go"
Expand Down Expand Up @@ -139,7 +139,7 @@ func ValidateAttestation(ctx context.Context, attestation string, opts *VerifyOp

// VerifyOptsFor returns the Android SafetyNet verification options to be used
// based on the AuthorizedApp configuration, request time, and nonce.
func VerifyOptsFor(c *database.AuthorizedApp, from time.Time, nonce string) *VerifyOpts {
func VerifyOptsFor(c *model.AuthorizedApp, from time.Time, nonce string) *VerifyOpts {
digests := make([]string, len(c.SafetyNetApkDigestSHA256))
copy(digests, c.SafetyNetApkDigestSHA256)
rtn := &VerifyOpts{
Expand Down
9 changes: 5 additions & 4 deletions internal/android/safetynet_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import (
"testing"
"time"

"github.com/google/exposure-notifications-server/internal/authorizedapp/model"
"github.com/google/exposure-notifications-server/internal/base64util"
"github.com/google/exposure-notifications-server/internal/database"
"github.com/google/go-cmp/cmp"
Expand Down Expand Up @@ -192,11 +193,11 @@ func TestVerifyOptsFor(t *testing.T) {
testTime := time.Date(2020, 1, 13, 5, 6, 4, 6, time.Local)

cases := []struct {
cfg *database.AuthorizedApp
cfg *model.AuthorizedApp
opts *VerifyOpts
}{
{
cfg: &database.AuthorizedApp{
cfg: &model.AuthorizedApp{
AppPackageName: "foo",
SafetyNetBasicIntegrity: true,
SafetyNetCTSProfileMatch: true,
Expand All @@ -213,7 +214,7 @@ func TestVerifyOptsFor(t *testing.T) {
},
},
{
cfg: &database.AuthorizedApp{
cfg: &model.AuthorizedApp{
AppPackageName: "foo",
SafetyNetBasicIntegrity: true,
SafetyNetCTSProfileMatch: false,
Expand All @@ -230,7 +231,7 @@ func TestVerifyOptsFor(t *testing.T) {
},
},
{
cfg: &database.AuthorizedApp{
cfg: &model.AuthorizedApp{
AppPackageName: "foo",
SafetyNetApkDigestSHA256: []string{"bar"},
SafetyNetBasicIntegrity: true,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,15 +20,27 @@ import (
"fmt"
"time"

"github.com/google/exposure-notifications-server/internal/authorizedapp/model"
"github.com/google/exposure-notifications-server/internal/database"
"github.com/google/exposure-notifications-server/internal/ios"
"github.com/google/exposure-notifications-server/internal/secrets"
pgx "github.com/jackc/pgx/v4"
)

type AuthorizedAppDB struct {
db *database.DB
}

func NewAuthorizedAppDB(db *database.DB) *AuthorizedAppDB {
return &AuthorizedAppDB{
db: db,
}
}

// GetAuthorizedApp loads a single AuthorizedApp for the given name. If no row
// exists, this returns nil.
func (db *DB) GetAuthorizedApp(ctx context.Context, sm secrets.SecretManager, name string) (*AuthorizedApp, error) {
conn, err := db.pool.Acquire(ctx)
func (db *AuthorizedAppDB) GetAuthorizedApp(ctx context.Context, sm secrets.SecretManager, name string) (*model.AuthorizedApp, error) {
conn, err := db.db.Pool.Acquire(ctx)
if err != nil {
return nil, fmt.Errorf("acquiring connection: %v", err)
}
Expand All @@ -45,7 +57,7 @@ func (db *DB) GetAuthorizedApp(ctx context.Context, sm secrets.SecretManager, na

row := conn.QueryRow(ctx, query, name)

config := NewAuthorizedApp()
config := model.NewAuthorizedApp()
var allowedRegions []string
var safetyNetPastSeconds, safetyNetFutureSeconds *int
var deviceCheckTeamID, deviceCheckKeyID, deviceCheckPrivateKeySecret sql.NullString
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,13 @@ import (
"crypto/x509"
"encoding/pem"
"fmt"
"log"
"os"
"testing"
"time"

"github.com/google/exposure-notifications-server/internal/authorizedapp/model"
coredb "github.com/google/exposure-notifications-server/internal/database"
"github.com/google/go-cmp/cmp"
"github.com/google/go-cmp/cmp/cmpopts"
)
Expand All @@ -41,11 +45,25 @@ func (s *testSecretManager) GetSecretValue(ctx context.Context, name string) (st
return v, nil
}

var testDB *coredb.DB

func TestMain(m *testing.M) {
ctx := context.Background()

if os.Getenv("DB_USER") != "" {
var err error
testDB, err = coredb.CreateTestDB(ctx)
if err != nil {
log.Fatalf("creating test DB: %v", err)
}
}
os.Exit(m.Run())
}
func TestGetAuthorizedApp(t *testing.T) {
if testDB == nil {
t.Skip("no test DB")
}
defer resetTestDB(t)
defer coredb.ResetTestDB(t, testDB)
ctx := context.Background()

// Create private key for parsing later
Expand All @@ -72,7 +90,7 @@ func TestGetAuthorizedApp(t *testing.T) {
name string
sql string
args []interface{}
exp *AuthorizedApp
exp *model.AuthorizedApp
err bool
}{
{
Expand All @@ -83,7 +101,7 @@ func TestGetAuthorizedApp(t *testing.T) {
`,

args: []interface{}{"myapp", "android", []string{"US"}},
exp: &AuthorizedApp{
exp: &model.AuthorizedApp{
AppPackageName: "myapp",
Platform: "android",
AllowedRegions: map[string]struct{}{"US": {}},
Expand All @@ -98,7 +116,7 @@ func TestGetAuthorizedApp(t *testing.T) {
VALUES ($1, $2, $3)
`,
args: []interface{}{"myapp", "android", []string{}},
exp: &AuthorizedApp{
exp: &model.AuthorizedApp{
AppPackageName: "myapp",
Platform: "android",
AllowedRegions: map[string]struct{}{},
Expand All @@ -122,7 +140,7 @@ func TestGetAuthorizedApp(t *testing.T) {
"myapp", "android", []string{},
[]string{"092fcfb", "252f10c"}, false, false,
},
exp: &AuthorizedApp{
exp: &model.AuthorizedApp{
AppPackageName: "myapp",
Platform: "android",
AllowedRegions: map[string]struct{}{},
Expand All @@ -141,7 +159,7 @@ func TestGetAuthorizedApp(t *testing.T) {
) VALUES ($1, $2, $3, $4)
`,
args: []interface{}{"myapp", "android", []string{"US"}, 1800},
exp: &AuthorizedApp{
exp: &model.AuthorizedApp{
AppPackageName: "myapp",
Platform: "android",
AllowedRegions: map[string]struct{}{"US": {}},
Expand All @@ -159,7 +177,7 @@ func TestGetAuthorizedApp(t *testing.T) {
) VALUES ($1, $2, $3, $4)
`,
args: []interface{}{"myapp", "android", []string{"US"}, 1800},
exp: &AuthorizedApp{
exp: &model.AuthorizedApp{
AppPackageName: "myapp",
Platform: "android",
AllowedRegions: map[string]struct{}{"US": {}},
Expand All @@ -177,7 +195,7 @@ func TestGetAuthorizedApp(t *testing.T) {
) VALUES ($1, $2, $3, $4, $5, $6)
`,
args: []interface{}{"myapp", "ios", []string{"US"}, "ABCD1234", "DEFG5678", "private_key"},
exp: &AuthorizedApp{
exp: &model.AuthorizedApp{
AppPackageName: "myapp",
Platform: "ios",
AllowedRegions: map[string]struct{}{"US": {}},
Expand All @@ -201,19 +219,19 @@ func TestGetAuthorizedApp(t *testing.T) {

t.Run(c.name, func(t *testing.T) {
// Acquire a connection
conn, err := testDB.pool.Acquire(ctx)
conn, err := testDB.Pool.Acquire(ctx)
if err != nil {
t.Fatal(err)
}
defer conn.Release()
defer resetTestDB(t)
defer coredb.ResetTestDB(t, testDB)

// Insert the data
if _, err := conn.Exec(ctx, c.sql, c.args...); err != nil {
t.Fatal(err)
}

config, err := testDB.GetAuthorizedApp(ctx, sm, "myapp")
config, err := NewAuthorizedAppDB(testDB).GetAuthorizedApp(ctx, sm, "myapp")
if (err != nil) != c.err {
t.Fatal(err)
}
Expand Down
13 changes: 8 additions & 5 deletions internal/authorizedapp/database_provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,10 @@ import (
"sync"
"time"

authorizedappdb "github.com/google/exposure-notifications-server/internal/authorizedapp/database"
"github.com/google/exposure-notifications-server/internal/authorizedapp/model"
"github.com/google/exposure-notifications-server/internal/database"

"github.com/google/exposure-notifications-server/internal/logging"
"github.com/google/exposure-notifications-server/internal/secrets"
)
Expand All @@ -40,7 +43,7 @@ type DatabaseProvider struct {
}

type cacheItem struct {
value *database.AuthorizedApp
value *model.AuthorizedApp
cachedAt time.Time
}

Expand Down Expand Up @@ -74,7 +77,7 @@ func NewDatabaseProvider(ctx context.Context, db *database.DB, config *Config, o
// checkCache checks the local cache within a read lock.
// The bool on return is true if there was a hit (And an error is a valid hit)
// or false if there was a miss (or expiry) and the data source should be queried again.
func (p *DatabaseProvider) checkCache(name string) (*database.AuthorizedApp, bool, error) {
func (p *DatabaseProvider) checkCache(name string) (*model.AuthorizedApp, bool, error) {
// Acquire a read lock first, which allows concurrent readers, to check if
// there's an item in the cache.
p.cacheLock.RLock()
Expand All @@ -91,7 +94,7 @@ func (p *DatabaseProvider) checkCache(name string) (*database.AuthorizedApp, boo
}

// AppConfig returns the config for the given app package name.
func (p *DatabaseProvider) AppConfig(ctx context.Context, name string) (*database.AuthorizedApp, error) {
func (p *DatabaseProvider) AppConfig(ctx context.Context, name string) (*model.AuthorizedApp, error) {
logger := logging.FromContext(ctx)

data, cacheHit, error := p.checkCache(name)
Expand Down Expand Up @@ -136,11 +139,11 @@ func (p *DatabaseProvider) AppConfig(ctx context.Context, name string) (*databas

// loadAuthorizedAppFromDatabase is a lower-level private API that actually loads and parses
// a single AuthorizedApp from the database.
func (p *DatabaseProvider) loadAuthorizedAppFromDatabase(ctx context.Context, name string) (*database.AuthorizedApp, error) {
func (p *DatabaseProvider) loadAuthorizedAppFromDatabase(ctx context.Context, name string) (*model.AuthorizedApp, error) {
logger := logging.FromContext(ctx)

logger.Infof("authorizedapp: loading %v from database", name)
config, err := p.database.GetAuthorizedApp(ctx, p.secretManager, name)
config, err := authorizedappdb.NewAuthorizedAppDB(p.database).GetAuthorizedApp(ctx, p.secretManager, name)
if err != nil {
return nil, fmt.Errorf("failed to read %v from database: %w", name, err)
}
Expand Down
8 changes: 4 additions & 4 deletions internal/authorizedapp/memory_provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ package authorizedapp
import (
"context"

"github.com/google/exposure-notifications-server/internal/database"
"github.com/google/exposure-notifications-server/internal/authorizedapp/model"
)

// Compile-time check to assert implementation.
Expand All @@ -26,20 +26,20 @@ var _ Provider = (*MemoryProvider)(nil)
// MemoryProvider is an Provider that stores values in-memory. It is primarily
// used for testing.
type MemoryProvider struct {
Data map[string]*database.AuthorizedApp
Data map[string]*model.AuthorizedApp
}

// NewMemoryProvider creates a new Provider that reads from a database.
func NewMemoryProvider(ctx context.Context, _ *Config) (Provider, error) {
provider := &MemoryProvider{
Data: make(map[string]*database.AuthorizedApp),
Data: make(map[string]*model.AuthorizedApp),
}

return provider, nil
}

// AppConfig returns the config for the given app package name.
func (p *MemoryProvider) AppConfig(ctx context.Context, name string) (*database.AuthorizedApp, error) {
func (p *MemoryProvider) AppConfig(ctx context.Context, name string) (*model.AuthorizedApp, error) {
val, ok := p.Data[name]
if !ok {
return nil, AppNotFound
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.

package database
package model

import (
"crypto/ecdsa"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.

package database
package model

import (
"testing"
Expand Down
4 changes: 2 additions & 2 deletions internal/authorizedapp/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ import (
"context"
"errors"

"github.com/google/exposure-notifications-server/internal/database"
"github.com/google/exposure-notifications-server/internal/authorizedapp/model"
)

// AppNotFound is the sentinel error returned when AppConfig fails to find an
Expand All @@ -30,5 +30,5 @@ type Provider interface {
// AppConfig returns the application-specific configuration for the given
// name. An error is returned if the configuration fails to load. An error is
// returned if no app with the given name is registered in the system.
AppConfig(ctx context.Context, name string) (*database.AuthorizedApp, error)
AppConfig(ctx context.Context, name string) (*model.AuthorizedApp, error)
}
8 changes: 4 additions & 4 deletions internal/database/connection.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ type config struct {
}

type DB struct {
pool *pgxpool.Pool
Pool *pgxpool.Pool
}

// NewFromEnv sets up the database connections using the configuration in the
Expand All @@ -68,14 +68,14 @@ func NewFromEnv(ctx context.Context, config *Config) (*DB, error) {
return nil, fmt.Errorf("creating connection pool: %v", err)
}

return &DB{pool: pool}, nil
return &DB{Pool: pool}, nil
}

// Close releases database connections.
func (db *DB) Close(ctx context.Context) {
logger := logging.FromContext(ctx)
logger.Infof("Closing connection pool.")
db.pool.Close()
db.Pool.Close()
}

// dbConnectionString builds a connection string suitable for the pgx Postgres driver, using the
Expand All @@ -91,7 +91,7 @@ func dbConnectionString(ctx context.Context, config *Config) (string, error) {

// dbURI builds a Postgres URI suitable for the lib/pq driver, which is used by
// github.com/golang-migrate/migrate.
func dbURI(config *Config) string {
func DbURI(config *Config) string {
return fmt.Sprintf("postgres://%s/%s?sslmode=disable&user=%s&password=%s&port=%s",
config.Host, config.Name, config.User,
url.QueryEscape(config.Password), url.QueryEscape(config.Port))
Expand Down
2 changes: 1 addition & 1 deletion internal/database/database.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ func toNullString(s string) sql.NullString {

// inTx runs the given function f within a transaction with isolation level isoLevel.
func (db *DB) inTx(ctx context.Context, isoLevel pgx.TxIsoLevel, f func(tx pgx.Tx) error) error {
conn, err := db.pool.Acquire(ctx)
conn, err := db.Pool.Acquire(ctx)
if err != nil {
return fmt.Errorf("acquiring connection: %v", err)
}
Expand Down
Loading

0 comments on commit f113c0d

Please sign in to comment.