Documentation ¶
Overview ¶
Package database manages database connections and ORM integration.
Index ¶
- Constants
- Variables
- func NewTestDatabaseWithConfig(tb testing.TB) (*Database, *Config)
- type APIUserType
- type AuthorizedApp
- type CleanupStatus
- type Config
- type Database
- func (db *Database) ClaimCleanup(current *CleanupStatus, lockTime time.Duration) (*CleanupStatus, error)
- func (db *Database) ClaimToken(realmID uint, tokenID string, subject *Subject) error
- func (db *Database) Close() error
- func (db *Database) CreateAuthorizedApp(realmID uint, name string, apiUserType APIUserType) (*AuthorizedApp, error)
- func (db *Database) CreateCleanup(cType string) (*CleanupStatus, error)
- func (db *Database) CreateRealm(name string) (*Realm, error)
- func (db *Database) CreateUser(email string, name string, admin bool) (*User, error)
- func (db *Database) DeleteSMSConfig(s *SMSConfig) error
- func (db *Database) DeleteUser(u *User) error
- func (db *Database) DeleteVerificationCode(code string) error
- func (db *Database) FindAuthorizedAppByAPIKey(apiKey string) (*AuthorizedApp, error)
- func (db *Database) FindCleanupStatus(cType string) (*CleanupStatus, error)
- func (db *Database) FindTokenByID(tokenID string) (*Token, error)
- func (db *Database) FindUser(email string) (*User, error)
- func (db *Database) FindVerificationCode(code string) (*VerificationCode, error)
- func (db *Database) GetRealm(realmID uint) (*Realm, error)
- func (db *Database) GetRealmByName(name string) (*Realm, error)
- func (db *Database) GetUser(id uint) (*User, error)
- func (db *Database) ListAuthorizedApps(includeDeleted bool) ([]*AuthorizedApp, error)
- func (db *Database) ListUsers(includeDeleted bool) ([]*User, error)
- func (db *Database) MigrateTo(ctx context.Context, target string, rollback bool) error
- func (db *Database) PurgeTokens(maxAge time.Duration) (int64, error)
- func (db *Database) PurgeVerificationCodes(maxAge time.Duration) (int64, error)
- func (db *Database) RunMigrations(ctx context.Context) error
- func (db *Database) SaveRealm(r *Realm) error
- func (db *Database) SaveSMSConfig(s *SMSConfig) error
- func (db *Database) SaveUser(u *User) error
- func (db *Database) SaveVerificationCode(vc *VerificationCode, maxAge time.Duration) error
- func (db *Database) VerifyCodeAndIssueToken(realmID uint, verCode string, expireAfter time.Duration) (*Token, error)
- type Realm
- func (r *Realm) AddAdminUser(u *User)
- func (r *Realm) AddAuthorizedApp(a *AuthorizedApp)
- func (r *Realm) AddUser(u *User)
- func (r *Realm) DeleteUserFromRealm(db *Database, u *User) error
- func (r *Realm) GetAuthorizedApps(db *Database, includeDeleted bool) ([]*AuthorizedApp, error)
- func (r *Realm) GetSMSConfig(ctx context.Context, db *Database) (*SMSConfig, error)
- func (r *Realm) GetSMSProvider(ctx context.Context, db *Database) (sms.Provider, error)
- func (r *Realm) LoadRealmUsers(db *Database, includeDeleted bool) error
- type SMSConfig
- type Subject
- type Token
- type User
- type VerificationCode
Constants ¶
const (
// MinCodeLength defines the minimum number of digits in a code.
MinCodeLength = 6
)
Variables ¶
var ( ErrVerificationCodeExpired = errors.New("verification code expired") ErrVerificationCodeUsed = errors.New("verification code used") ErrTokenExpired = errors.New("verification token expired") ErrTokenUsed = errors.New("verification token used") ErrTokenMetadataMismatch = errors.New("verification token test metadata mismatch") )
var ( // ValidTestTypes is a map containing the valid test types. ValidTestTypes = map[string]struct{}{ "confirmed": {}, "likely": {}, "negative": {}, } ErrInvalidTestType = errors.New("invalid test type, must be confirmed, likely, or negative") ErrCodeAlreadyExpired = errors.New("code already expired") ErrCodeTooShort = errors.New("verification code must be at least 6 digits") ErrTestTooOld = errors.New("test date is more than 14 day ago") )
var CleanupName = "cleanup"
var (
ErrCleanupWrongGeneration = errors.New("cleanup wrong generation")
)
var (
ErrNoSMSConfig = errors.New("no sms config for realm")
)
Functions ¶
func NewTestDatabaseWithConfig ¶
NewTestDatabaseWithConfig creates a new database suitable for use in testing. This should not be used outside of testing, but it is exposed in the main package so it can be shared with other packages.
All database tests can be skipped by running `go test -short` or by setting the `SKIP_DATABASE_TESTS` environment variable.
Types ¶
type APIUserType ¶
type APIUserType int
const ( APIUserTypeDevice APIUserType = 0 APIUserTypeAdmin APIUserType = 1 )
type AuthorizedApp ¶
type AuthorizedApp struct { gorm.Model // AuthorizedApps belong to exactly one realm. RealmID uint `gorm:"unique_index:realm_apikey_name"` Realm *Realm // for loading the owning realm. Name string `gorm:"type:varchar(100);unique_index:realm_apikey_name"` APIKey string `gorm:"type:varchar(100);unique_index"` APIKeyType APIUserType `gorm:"default:0"` }
AuthorizedApp represents an application that is authorized to verify verification codes and perform token exchanges. This is controlled via a generated API key.
Admin Keys are able to issue diagnosis keys and are not able to perticipate the verification protocol.
func (*AuthorizedApp) GetRealm ¶
func (a *AuthorizedApp) GetRealm(db *Database) (*Realm, error)
GetRealm does a lazy load read of the realm associated with this authorized app.
func (*AuthorizedApp) IsAdminType ¶
func (a *AuthorizedApp) IsAdminType() bool
func (*AuthorizedApp) IsDeviceType ¶
func (a *AuthorizedApp) IsDeviceType() bool
func (AuthorizedApp) TableName ¶
func (AuthorizedApp) TableName() string
TableName definition for the authorized apps relation.
type CleanupStatus ¶
type CleanupStatus struct { gorm.Model Type string `gorm:"type:varchar(50);unique_index"` Generation uint NotBefore time.Time }
func (CleanupStatus) TableName ¶
func (CleanupStatus) TableName() string
TableName sets the CleanupStatus table name
type Config ¶
type Config struct { Name string `env:"DB_NAME" json:",omitempty"` User string `env:"DB_USER" json:",omitempty"` Host string `env:"DB_HOST, default=localhost" json:",omitempty"` Port string `env:"DB_PORT, default=5432" json:",omitempty"` SSLMode string `env:"DB_SSLMODE, default=require" json:",omitempty"` ConnectionTimeout uint `env:"DB_CONNECT_TIMEOUT" json:",omitempty"` Password string `env:"DB_PASSWORD" json:"-"` // ignored by zap's JSON formatter SSLCertPath string `env:"DB_SSLCERT" json:",omitempty"` SSLKeyPath string `env:"DB_SSLKEY" json:",omitempty"` SSLRootCertPath string `env:"DB_SSLROOTCERT" json:",omitempty"` // Debug is a boolean that indicates whether the database should log SQL // commands. Debug bool `env:"DB_DEBUG,default=false"` // CacheTTL is the amount of time to cache values. This is enabled on a // per-query basis. Not all query results are cached. CacheTTL time.Duration `env:"DB_CACHE_TTL, default=5m" json:",omitempty"` // Secrets is the secret configuration. This is used to resolve values that // are actually pointers to secrets before returning them to the caller. The // table implementation is the source of truth for which values are secrets // and which are plaintext. Secrets secrets.Config }
Config represents the env var based configuration for database connections.
func (*Config) ConnectionString ¶
ConnectionString returns the postgresql connection string based on this config.
While this package could be adapted to different databases easily, this file and method in particular would need to change.
type Database ¶
type Database struct {
// contains filtered or unexported fields
}
Database is a handle to the database layer for the Exposure Notifications Verification Server.
func NewTestDatabase ¶
func (*Database) ClaimCleanup ¶
func (db *Database) ClaimCleanup(current *CleanupStatus, lockTime time.Duration) (*CleanupStatus, error)
ClaimCleanup attempts to obtain a lock for the specified `lockTime` so that that type of cleanup can be perofmed exclusively by the owner.
func (*Database) ClaimToken ¶
ClaimToken looks up the token by ID, verifies that it is not expired and that the specified subject matches the parameters that were configured when issued.
func (*Database) Close ¶
Close will close the database connection. Should be deferred right after Open.
func (*Database) CreateAuthorizedApp ¶
func (db *Database) CreateAuthorizedApp(realmID uint, name string, apiUserType APIUserType) (*AuthorizedApp, error)
CreateAuthorizedApp generates a new APIKey and assigns it to the specified name.
func (*Database) CreateCleanup ¶
func (db *Database) CreateCleanup(cType string) (*CleanupStatus, error)
CreateCleanup is used to create a new 'cleanup' type/row in the database.
func (*Database) CreateUser ¶
CreateUser creates a user record.
func (*Database) DeleteSMSConfig ¶
DeleteSMSConfig removes an SMS configuration record.
func (*Database) DeleteUser ¶
DeleteUser removes a user record.
func (*Database) DeleteVerificationCode ¶
DeleteVerificationCode deletes the code if it exists. This is a hard delete.
func (*Database) FindAuthorizedAppByAPIKey ¶
func (db *Database) FindAuthorizedAppByAPIKey(apiKey string) (*AuthorizedApp, error)
FindAuthorizedAppByAPIKey located an authorized app based on API key. If no app exists for the given API key, it returns nil.
func (*Database) FindCleanupStatus ¶
func (db *Database) FindCleanupStatus(cType string) (*CleanupStatus, error)
FindCleanupStatus looks up the current cleanup state in the database by cleanup type.
func (*Database) FindVerificationCode ¶
func (db *Database) FindVerificationCode(code string) (*VerificationCode, error)
FindVerificationCode find a verification code by the code number.
func (*Database) ListAuthorizedApps ¶
func (db *Database) ListAuthorizedApps(includeDeleted bool) ([]*AuthorizedApp, error)
ListAuthorizedApps retrieves all of the configured authorized apps. Done without pagination, as the expected number of authorized apps is low signal digits.
func (*Database) ListUsers ¶
ListUsers retrieves all of the configured users. Done without pagination. This is not scoped to realms.
func (*Database) PurgeTokens ¶
PurgeTokens will delete tokens that have expired since at least the provided maxAge ago. This is a hard delete, not a soft delete.
func (*Database) PurgeVerificationCodes ¶
PurgeVerificationCodes will delete verifications that have expired since at least the provided maxAge ago. This is a hard delete, not a soft delete.
func (*Database) RunMigrations ¶
RunMigrations will apply sequential, transactional migrations to the database
func (*Database) SaveSMSConfig ¶
SaveSMSConfig creates or updates an SMS configuration record.
func (*Database) SaveVerificationCode ¶
func (db *Database) SaveVerificationCode(vc *VerificationCode, maxAge time.Duration) error
SaveVerificationCode created or updates a verification code in the database. Max age represents the maximum age of the test date [optional] in the record.
func (*Database) VerifyCodeAndIssueToken ¶
func (db *Database) VerifyCodeAndIssueToken(realmID uint, verCode string, expireAfter time.Duration) (*Token, error)
VerifyCodeAndIssueToken takes a previously issed verification code and exchanges it for a long term token. The verification code must not have expired and must not have been previously used. Both acctions are done in a single database transaction.
The long term token can be used later to sign keys when they are submitted.
type Realm ¶
type Realm struct { gorm.Model Name string `gorm:"type:varchar(200);unique_index"` // Use GetSMSConfig to lazy load this property. SMSConfig *SMSConfig AuthorizedApps []*AuthorizedApp RealmUsers []*User `gorm:"many2many:user_realms"` RealmAdmins []*User `gorm:"many2many:admin_realms"` // Relations to items that blong to a realm. Codes []*VerificationCode Tokens []*Token }
Realm represents a tenant in the system. Typically this corresponds to a geography or a public health authority scope. This is used to manage user logins.
func (*Realm) AddAdminUser ¶
AddAdminUser adds to the in memory structure, but does not save. Use SaveRealm to persist.
func (*Realm) AddAuthorizedApp ¶
func (r *Realm) AddAuthorizedApp(a *AuthorizedApp)
AddAuthorizedApp adds to the in memory structure, but does not save. Use SaveRealm to persist.
func (*Realm) AddUser ¶
AddUser add to the in memory structure, but does not save. Use SaveRealm to persist.
func (*Realm) DeleteUserFromRealm ¶
func (*Realm) GetAuthorizedApps ¶
func (r *Realm) GetAuthorizedApps(db *Database, includeDeleted bool) ([]*AuthorizedApp, error)
GetAuthorizedApps does a lazy load on a realm's authorized apps if they are not already loaded.
func (*Realm) GetSMSConfig ¶
GetSMSConfig returns the currently associates SMSConfig for the realm. This is a lazy load over the the SMSConfig attached to the realm.
func (*Realm) GetSMSProvider ¶
GetSMSProvider returns the configured provider of SMS dispatch for the realm.
type SMSConfig ¶
type SMSConfig struct { gorm.Model // SMS Config belongs to exactly one realm. RealmID uint `gorm:"unique_index"` // ProviderType is the SMS provider type - it's used to determine the // underlying configuration. ProviderType sms.ProviderType `gorm:"type:varchar(100)"` // Twilio configuration options. TwilioAccountSid string `gorm:"type:varchar(250)"` TwilioAuthToken string `gorm:"type:varchar(250)"` // secret reference TwilioFromNumber string `gorm:"type:varchar(16)"` // contains filtered or unexported fields }
SMSConfig represents and SMS configuration.
type Subject ¶
Subject represents the data that is used in the 'sub' field of the token JWT.
func ParseSubject ¶
func (*Subject) SymptomInterval ¶
type Token ¶
type Token struct { gorm.Model // Tokens belong to one realm. RealmID uint TokenID string `gorm:"type:varchar(200); unique_index"` TestType string `gorm:"type:varchar(20)"` SymptomDate *time.Time Used bool `gorm:"default:false"` ExpiresAt time.Time }
Token represents an issued "long term" from a validated verification code.
func (*Token) FormatSymptomDate ¶
FormatSymptomDate returns YYYY-MM-DD formatted test date, or "" if nil.
type User ¶
type User struct { gorm.Model Email string `gorm:"type:varchar(250);unique_index"` Name string `gorm:"type:varchar(100)"` Admin bool `gorm:"default:false"` LastRevokeCheck time.Time Realms []*Realm `gorm:"many2many:user_realms;PRELOAD:true"` AdminRealms []*Realm `gorm:"many2many:admin_realms;PRELOAD:true"` }
User represents a user of the system
func (*User) CanAdminRealm ¶
func (*User) CanViewRealm ¶
func (*User) MultipleRealms ¶
type VerificationCode ¶
type VerificationCode struct { gorm.Model RealmID uint // VerificationCodes belong to exactly one realm when issued. Code string `gorm:"type:varchar(20);unique_index"` Claimed bool `gorm:"default:false"` TestType string `gorm:"type:varchar(20)"` SymptomDate *time.Time ExpiresAt time.Time IssuingUser *User IssuingApp *AuthorizedApp }
VerificationCode represnts a verification code in the database.
func (*VerificationCode) FormatSymptomDate ¶
func (v *VerificationCode) FormatSymptomDate() string
FormatSymptomDate returns YYYY-MM-DD formatted test date, or "" if nil.
func (*VerificationCode) IsExpired ¶
func (v *VerificationCode) IsExpired() bool
IsExpired returns ture if a verification code has expired.
func (VerificationCode) TableName ¶
func (VerificationCode) TableName() string
TableName sets the VerificationCode table name