database

package
v0.0.0-...-1064550 Latest Latest
Warning

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

Go to latest
Published: Jun 22, 2026 License: Apache-2.0 Imports: 18 Imported by: 0

Documentation

Overview

Package database provides connection management for the integrated Credentials Config Service database. It supports PostgreSQL, MySQL, and SQLite backends, selected via the config.Database.Type field.

Index

Constants

View Source
const (
	// DriverTypePostgres selects the PostgreSQL driver.
	DriverTypePostgres = "postgres"
	// DriverTypeMySQL selects the MySQL/MariaDB driver.
	DriverTypeMySQL = "mysql"
	// DriverTypeSQLite selects the pure-Go SQLite driver.
	DriverTypeSQLite = "sqlite"
)

Supported database driver type constants.

Variables

View Source
var (
	// ErrRefreshTokenNotFound is returned when a refresh token does not
	// exist in the database or has already been consumed.
	ErrRefreshTokenNotFound = errors.New("refresh token not found")
	// ErrRefreshTokenInvalidIntegrity is returned when the HMAC of a stored
	// row does not match, indicating database-level tampering.
	ErrRefreshTokenInvalidIntegrity = errors.New("refresh token integrity check failed")
)
View Source
var (
	// ErrServiceNotFound is returned when a service ID does not exist.
	ErrServiceNotFound = errors.New("service not found")
	// ErrServiceAlreadyExists is returned on a duplicate service ID insert.
	ErrServiceAlreadyExists = errors.New("service already exists")
)
View Source
var DB_FORMAT_MAP = map[string]string{
	"dc+sd-jwt":   "DC_SD_JWT",
	"vc+sd-jwt":   "VC_SD_JWT",
	"mso_mdoc":    "MSO_MDOC",
	"ldp_vc":      "LDP_VC",
	"jwt_vc_json": "JWT_VC_JSON",
}
View Source
var FORMAT_MAP = map[string]string{
	"DC_SD_JWT":   "dc+sd-jwt",
	"VC_SD_JWT":   "vc+sd-jwt",
	"MSO_MDOC":    "mso_mdoc",
	"LDP_VC":      "ldp_vc",
	"JWT_VC_JSON": "jwt_vc_json",
}

Functions

func Close

func Close(db *sql.DB)

Close gracefully closes the database connection pool. It logs any error but does not return it, making it convenient for deferred calls.

func GenerateSalt

func GenerateSalt() ([]byte, error)

GenerateSalt returns 32 cryptographically random bytes suitable for use with ConfigureHashing.

func InitSchema

func InitSchema(db *sql.DB, dbType string) error

InitSchema creates the service and scope_entry tables if they do not already exist. The DDL is database-type-aware: PostgreSQL uses BIGSERIAL, SQLite uses INTEGER PRIMARY KEY AUTOINCREMENT, and MySQL uses BIGINT AUTO_INCREMENT. The function is idempotent — calling it multiple times is safe.

func NewConnection

func NewConnection(cfg config.Database) (*sql.DB, error)

NewConnection opens a database connection pool based on the provided configuration. The returned *sql.DB is ready to use and has been verified with a ping. Callers are responsible for closing it when done.

func RowToService

func RowToService(row ServiceRow, scopeRows []ScopeEntryRow) (config.ConfiguredService, error)

RowToService assembles a config.ConfiguredService from a ServiceRow and its associated ScopeEntryRow values, unmarshalling JSON text columns back into typed Go structs.

Types

type ClaimsQueryDB

type ClaimsQueryDB struct {
	// REQUIRED if claim_sets is present in the Credential Query; OPTIONAL otherwise. A string identifying the particular claim. The value MUST be a non-empty string consisting of alphanumeric, underscore (_), or hyphen (-) characters. Within the particular claims array, the same id MUST NOT be present more than once.
	Id string `json:"id,omitempty" mapstructure:"id,omitempty"`
	//  The value MUST be a non-empty array representing a claims path pointer that specifies the path to a claim within the Credential. See https://openid.net/specs/openid-4-verifiable-presentations-1_0.html#name-claims-path-pointer
	Path []interface{} `json:"path,omitempty" mapstructure:"path,omitempty"`
	// A non-empty array of strings, integers or boolean values that specifies the expected values of the claim. If the values property is present, the Wallet SHOULD return the claim only if the type and value of the claim both match exactly for at least one of the elements in the array.
	Values []interface{} `json:"values,omitempty" mapstructure:"values,omitempty"`
	// MDoc specific parameter, ignored for all other types. The flag can be set to inform that the reader wishes to keep(store) the data. In case of false, its data is only used to be dispalyed and verified.
	IntentToRetain bool `json:"intent_to_retain,omitempty" mapstructure:"intent_to_retain,omitempty"`
	// MDoc specific parameter, ignored for all other types. Refers to a namespace inside an mdoc.
	Namespace string `json:"namespace,omitempty" mapstructure:"namespace,omitempty"`
	// MDoc specific parameter, ignored for all other types. Identifier for the data-element in the namespace.
	ClaimName string `json:"claimName,omitempty" mapstructure:"claimName,omitempty"`
}

func (ClaimsQueryDB) FromVO

func (ClaimsQueryDB) VO

type CredentialDB

type CredentialDB struct {
	// Type of the credential
	Type string `json:"credentialType" mapstructure:"credentialType"`
	// Set if the holder id should be verified
	VerifyHolder bool `json:"verifyHolder" mapstructure:"verifyHolder"`
	// A list of (EBSI Trusted Issuers Registry compatible) endpoints to  retrieve the trusted issuers from. The attributes need to be formated to comply with the verifiers requirements.
	TrustedIssuersLists []config.EndpointEntry `json:"trustedLists,omitempty" mapstructure:"trustedLists,omitempty"`
	// Configuration of Holder Verfification
	HolderVerification config.HolderVerification `json:"holderVerification" mapstructure:"holderVerification"`
	// Does the given credential require a compliancy credential
	RequireCompliance bool `json:"requireCompliance" mapstructure:"requireCompliance"`
	// Configuration for the credential its inclusion into the JWT.
	JwtInclusion config.JwtInclusion `json:"jwtInclusion" mapstructure:"jwtInclusion"`
	// Per-credential configuration for the W3C Bitstring Status List /
	// StatusList2021 revocation-list check. When omitted or disabled no
	// revocation check is performed for credentials of this type, preserving
	// prior behaviour for configurations that do not opt in.
	CredentialStatus config.CredentialStatus `json:"credentialStatus,omitempty" mapstructure:"credentialStatus,omitempty"`
}

func (CredentialDB) FromVO

FromVO converts a config.Credential value object into a CredentialDB, merging the separate participants and issuers lists into a unified EndpointEntry slice.

func (CredentialDB) VO

func (cred CredentialDB) VO() config.Credential

VO converts a CredentialDB into its config.Credential value object, mapping the unified EndpointEntry list back into separate participants and issuers lists.

type CredentialQueryDB

type CredentialQueryDB struct {
	// A string identifying the Credential in the response and, if provided, the constraints in credential_sets. The value MUST be a non-empty string consisting of alphanumeric, underscore (_), or hyphen (-) characters. Within the Authorization Request, the same id MUST NOT be present more than once.
	Id string `json:"id,omitempty" mapstructure:"id,omitempty"`
	// A string that specifies the format of the requested Credential.
	Format string `json:"format,omitempty" mapstructure:"format,omitempty"`
	// A boolean which indicates whether multiple Credentials can be returned for this Credential Query. If omitted, the default value is false.
	Multiple bool `json:"multiple" mapstructure:"multiple" default:"false"`
	// A non-empty array of objects  that specifies claims in the requested Credential. Verifiers MUST NOT point to the same claim more than once in a single query. Wallets SHOULD ignore such duplicate claim queries.
	Claims []ClaimsQueryDB `json:"claims" mapstructure:"claims"`
	// Defines additional properties requested by the Verifier that apply to the metadata and validity data of the Credential. The properties of this object are defined per Credential Format. If empty, no specific constraints are placed on the metadata or validity of the requested Credential.
	Meta *config.MetaDataQuery `json:"meta,omitempty" mapstructure:"meta,omitempty"`
	// A boolean which indicates whether the Verifier requires a Cryptographic Holder Binding proof. The default value is true, i.e., a Verifiable Presentation with Cryptographic Holder Binding is required. If set to false, the Verifier accepts a Credential without Cryptographic Holder Binding proof.
	RequireCryptographicHolderBinding bool `json:"requireCryptographicHolderBinding,omitempty" mapstructure:"requireCryptographicHolderBinding,omitempty" default:"false"`
	// A non-empty array containing arrays of identifiers for elements in claims that specifies which combinations of claims for the Credential are requested.
	ClaimSets [][]string `json:"claimSets,omitempty" mapstructure:"claimSets,omitempty"`
	// A non-empty array of objects  that specifies expected authorities or trust frameworks that certify Issuers, that the Verifier will accept. Every Credential returned by the Wallet SHOULD match at least one of the conditions present in the corresponding trusted_authorities array if present.
	TrustedAuthorities []config.TrustedAuthorityQuery `json:"trustedAuthorities" mapstructure:"trustedAuthorities" default:"[]"`
}

func (CredentialQueryDB) FromVO

func (CredentialQueryDB) VO

type DCQLDB

type DCQLDB struct {
	// A non-empty array of Credential Queries that specify the requested Credentials.
	Credentials []CredentialQueryDB `json:"credentials" mapstructure:"credentials"`
	// A non-empty array of Credential Set Queries that specifies additional constraints on which of the requested Credentials to return.
	CredentialSets []config.CredentialSetQuery `json:"credentialSets,omitempty" mapstructure:"credentialSets,omitempty"`
}

func (DCQLDB) FromVO

func (d DCQLDB) FromVO(dVO config.DCQL) DCQLDB

func (DCQLDB) VO

func (dcql DCQLDB) VO() config.DCQL

type FormatObjectDB

type FormatObjectDB struct {
	// format of the key
	FormatKey string `json:"formatKey" mapstructure:"formatKey"`
	// list of algorithms to be requested for credential - f.e. ES256
	Alg       []string `json:"alg" mapstructure:"alg"`
	ProofType []string `json:"proofType,omitempty" mapstructure:"proofType"`
}

func (FormatObjectDB) VO

type InputDescriptorDB

type InputDescriptorDB struct {
	// Id of the descriptor
	Id string `json:"id" mapstructure:"id"`
	// defines the information to be requested
	Constraints config.Constraints `json:"constraints" mapstructure:"constraints"`
	// Format of the credential to be requested
	Format []FormatObjectDB `json:"format" mapstructure:"format"`
}

func (InputDescriptorDB) FromVO

func (InputDescriptorDB) VO

type PresentationDefinitionDB

type PresentationDefinitionDB struct {
	// Id of the definition
	Id string `json:"id" mapstructure:"id"`

	// List of requested inputs
	InputDescriptors []InputDescriptorDB `json:"inputDescriptors" mapstructure:"inputDescriptors"`
	// Format of the credential to be requested
	Format []FormatObjectDB `json:"format" mapstructure:"format"`
}

func (PresentationDefinitionDB) FromVO

func (PresentationDefinitionDB) VO

type RefreshTokenRepository

type RefreshTokenRepository interface {
	// StoreRefreshToken persists a new refresh token row.
	StoreRefreshToken(ctx context.Context, row RefreshTokenRow) error

	// GetAndDeleteRefreshToken atomically retrieves and deletes a refresh
	// token (single-use). Returns ErrRefreshTokenNotFound if the token does
	// not exist.
	GetAndDeleteRefreshToken(ctx context.Context, token string) (*RefreshTokenRow, error)

	// DeleteExpiredTokens removes all refresh token rows whose expires_at
	// is in the past. Returns the number of rows deleted.
	DeleteExpiredTokens(ctx context.Context) (int64, error)

	// SetCleanupInterval starts a background goroutine that periodically calls
	// DeleteExpiredTokens at the given interval. If interval is zero or
	// negative, any running cleanup goroutine is cancelled and no new one is
	// started. Calling again with a new interval replaces the previous one.
	// The goroutine stops when ctx is cancelled.
	SetCleanupInterval(ctx context.Context, interval time.Duration)
}

RefreshTokenRepository defines the data-access operations for OAuth2 refresh tokens. Implementations must be safe for concurrent use.

type RefreshTokenRow

type RefreshTokenRow struct {
	// Token is the primary key: the raw token when hashing is disabled, or the
	// HMAC-SHA256 hex digest when hashing is enabled.
	Token string
	// TokenSuffix holds the last 5 characters of the original plaintext token,
	// always stored regardless of hashing, for operational identification.
	TokenSuffix string
	// ClientID identifies the relying party that requested the token.
	ClientID string
	// Claims is the JSON payload extracted from the original access token JWT
	// (base64url-decoded middle segment). On exchange these claims are used to
	// re-issue a new access token without re-applying credential inclusion
	// configurations.
	Claims string
	// Integrity is the HMAC-SHA256 hex digest over the raw refresh token,
	// client ID, and claims. It is computed and verified by the repository;
	// callers of StoreRefreshToken do not need to set this field.
	Integrity string
	// ExpiresAt is the Unix timestamp (seconds) at which this refresh token
	// expires.
	ExpiresAt int64
}

RefreshTokenRow represents a row in the refresh_token table. Each row stores the raw JWT claims so that access tokens can be re-issued without re-applying credential inclusion configurations.

type ScopeEntryDB

type ScopeEntryDB struct {
	// credential types with their trust configuration
	Credentials []CredentialDB `json:"credentials" mapstructure:"credentials"`
	// 	Proofs to be requested - see https://identity.foundation/presentation-exchange/#presentation-definition
	PresentationDefinition *PresentationDefinitionDB `json:"presentationDefinition" mapstructure:"presentationDefinition"`
	// JSON encoded query to request the credentials to be included in the presentation
	DCQL *DCQLDB `json:"dcql" mapstructure:"dcql"`
	// When set, the claim are flatten to plain JWT-claims before beeing included, instead of keeping the credential/presentation structure, where the claims are under the key vc or vp
	FlatClaims bool `json:"flatClaims" mapstructure:"flatClaims"`
}

DATABASE models

func (ScopeEntryDB) FromVO

func (se ScopeEntryDB) FromVO(seVO config.ScopeEntry) ScopeEntryDB

func (ScopeEntryDB) VO

func (se ScopeEntryDB) VO() config.ScopeEntry

type ScopeEntryRow

type ScopeEntryRow struct {
	// ID is the auto-generated primary key.
	ID int64
	// ServiceID is the foreign key referencing service.id.
	ServiceID string
	// ScopeKey is the OIDC scope name (map key in ServiceScopes).
	ScopeKey string
	// Credentials is a JSON-encoded array of config.Credential objects.
	Credentials string
	// PresentationDefinition is a JSON-encoded config.PresentationDefinition; may be nil.
	PresentationDefinition *string
	// FlatClaims indicates whether claims should be flattened in the JWT.
	FlatClaims bool
	// DcqlQuery is a JSON-encoded config.DCQL object; may be nil.
	DcqlQuery *string
}

ScopeEntryRow represents a row in the scope_entry table.

func ScopeEntryToRows

func ScopeEntryToRows(serviceID string, scopes map[string]config.ScopeEntry) ([]ScopeEntryRow, error)

ScopeEntryToRows converts the ServiceScopes map from a ConfiguredService into a slice of ScopeEntryRow values, marshalling the complex fields to JSON text. An error is returned if any JSON serialisation fails.

type ServiceRepository

type ServiceRepository interface {
	// CreateService persists a new service together with all its scope entries.
	// Returns ErrServiceAlreadyExists if a service with the same ID exists.
	CreateService(ctx context.Context, service config.ConfiguredService) error

	// GetService retrieves a single service by ID, including all scope entries.
	// Returns ErrServiceNotFound if the ID does not exist.
	GetService(ctx context.Context, id string) (config.ConfiguredService, error)

	// GetAllServices returns a page of services ordered by ID and the total
	// count across all pages. page is zero-based.
	GetAllServices(ctx context.Context, page, pageSize int) ([]config.ConfiguredService, int, error)

	// UpdateService replaces the service row and all its scope entries.
	// Returns ErrServiceNotFound if the ID does not exist. Returns the
	// updated service (re-read from DB) for response purposes.
	UpdateService(ctx context.Context, id string, service config.ConfiguredService) (config.ConfiguredService, error)

	// DeleteService removes a service and its scope entries (via CASCADE).
	// Returns ErrServiceNotFound if the ID does not exist.
	DeleteService(ctx context.Context, id string) error

	// GetServiceScopes returns the credential types required for a scope.
	// When oidcScope is nil, the service's default scope is used.
	// Returns ErrServiceNotFound when the service does not exist, or
	// config.ErrorNoSuchScope when the resolved scope is not configured.
	GetServiceScopes(ctx context.Context, id string, oidcScope *string) ([]string, error)

	// ServiceExists checks whether a service with the given ID exists.
	ServiceExists(ctx context.Context, id string) (bool, error)
}

ServiceRepository defines the data-access operations for CCS services and their scope entries. Implementations must be safe for concurrent use.

type ServiceRow

type ServiceRow struct {
	// ID is the unique service identifier (primary key).
	ID string
	// DefaultOidcScope is the default OIDC scope name; may be nil.
	DefaultOidcScope *string
	// AuthorizationType describes the authorization mode; may be nil.
	AuthorizationType *string
}

ServiceRow represents a row in the service table.

func ServiceToRow

func ServiceToRow(service config.ConfiguredService) ServiceRow

ServiceToRow converts a config.ConfiguredService into a ServiceRow. The scope entries are handled separately via ScopeEntryToRows.

type SqlRefreshTokenRepository

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

SqlRefreshTokenRepository is a RefreshTokenRepository backed by database/sql.

func NewRefreshTokenRepository

func NewRefreshTokenRepository(db *sql.DB, dbType string) *SqlRefreshTokenRepository

NewRefreshTokenRepository creates a new SqlRefreshTokenRepository for the provided database connection and driver type.

func (*SqlRefreshTokenRepository) ConfigureHashing

func (r *SqlRefreshTokenRepository) ConfigureHashing(salt []byte)

ConfigureHashing enables HMAC-SHA256 hashing of tokens before storage. Must be called before any tokens are stored or retrieved. The salt must not be empty.

func (*SqlRefreshTokenRepository) DeleteExpiredTokens

func (r *SqlRefreshTokenRepository) DeleteExpiredTokens(ctx context.Context) (int64, error)

DeleteExpiredTokens removes all refresh token rows whose expiration time has passed. Returns the number of rows deleted.

func (*SqlRefreshTokenRepository) GetAndDeleteRefreshToken

func (r *SqlRefreshTokenRepository) GetAndDeleteRefreshToken(ctx context.Context, token string) (*RefreshTokenRow, error)

GetAndDeleteRefreshToken atomically retrieves and deletes a refresh token within a transaction, ensuring single-use semantics. Returns ErrRefreshTokenNotFound if the token does not exist, or ErrRefreshTokenInvalidIntegrity if the stored HMAC does not match (possible database-level tampering). The token is consumed regardless of the integrity outcome to prevent repeated use of a tampered row.

func (*SqlRefreshTokenRepository) SetCleanupInterval

func (r *SqlRefreshTokenRepository) SetCleanupInterval(ctx context.Context, interval time.Duration)

SetCleanupInterval starts a background goroutine that periodically deletes expired refresh token rows. If interval is zero or negative, any running cleanup goroutine is cancelled and no new one is started. Calling again replaces the previous interval. The goroutine stops when ctx is cancelled.

func (*SqlRefreshTokenRepository) StoreRefreshToken

func (r *SqlRefreshTokenRepository) StoreRefreshToken(ctx context.Context, row RefreshTokenRow) error

StoreRefreshToken persists a new refresh token row in the database. The token primary key is hashed when hashing is configured; the last 5 characters of the raw token are always stored in token_suffix. The integrity HMAC is computed from the configured salt and stored alongside the claims.

type SqlServiceRepository

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

SqlServiceRepository is a ServiceRepository backed by database/sql.

func NewServiceRepository

func NewServiceRepository(db *sql.DB, dbType string) *SqlServiceRepository

NewServiceRepository creates a new SqlServiceRepository for the provided database connection and driver type. The dbType must be one of the DriverType* constants and is used to adapt SQL placeholder syntax.

func (*SqlServiceRepository) CreateService

func (r *SqlServiceRepository) CreateService(ctx context.Context, service config.ConfiguredService) error

CreateService persists a new service and its scope entries within a single transaction. Returns ErrServiceAlreadyExists on duplicate ID.

func (*SqlServiceRepository) DeleteService

func (r *SqlServiceRepository) DeleteService(ctx context.Context, id string) error

DeleteService removes a service. Scope entries are cascade-deleted.

func (*SqlServiceRepository) GetAllServices

func (r *SqlServiceRepository) GetAllServices(ctx context.Context, page, pageSize int) ([]config.ConfiguredService, int, error)

GetAllServices returns a page of services and the total service count.

func (*SqlServiceRepository) GetService

GetService retrieves a single service by ID.

func (*SqlServiceRepository) GetServiceScopes

func (r *SqlServiceRepository) GetServiceScopes(ctx context.Context, id string, oidcScope *string) ([]string, error)

GetServiceScopes returns the credential type names required for the given scope. When oidcScope is nil the service's default scope is used.

func (*SqlServiceRepository) ServiceExists

func (r *SqlServiceRepository) ServiceExists(ctx context.Context, id string) (bool, error)

ServiceExists returns true if a service with the given ID exists.

func (*SqlServiceRepository) UpdateService

UpdateService replaces a service's data and all its scope entries.

Jump to

Keyboard shortcuts

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