Documentation
¶
Overview ¶
Package sqlite は authengine.Store の SQLite Adapter である。
modernc.org/sqlite(CGO 不要)を使い、スキーマは埋め込んだ SQL マイグレーションを goose ライブラリで適用する。goose CLI は不要。
原子性の保証(docs/adr/0005-storage-port.md):
- 書き込みは 1 つの DB トランザクションで行い、プロセス内では Mutex で直列化する。 BEGIN IMMEDIATE(_txlock=immediate)で開始するので、複数プロセスからの書き込みは SQLite のロックと busy_timeout で直列化される。
- 読み取りは WAL により書き込みと並行できる。
- 単一行の作成は主キーへの INSERT ... ON CONFLICT DO NOTHING の更新行数で重複を判定する。
時刻は UTC の Unix ナノ秒で保存し、配列・マップは JSON で保存する。 `:memory:` は接続ごとに別 DB になるので対応しない。ファイル DB を使うこと。
Index ¶
- func Migrate(ctx context.Context, db *sql.DB) error
- type Store
- func (s *Store) Close() error
- func (s *Store) ConsumeAuthorizationCode(ctx context.Context, codeHash string, now time.Time) (*authorization.AuthorizationCode, error)
- func (s *Store) CreateAuthorizationCode(ctx context.Context, code *authorization.AuthorizationCode) error
- func (s *Store) CreateClient(ctx context.Context, c *client.Client) error
- func (s *Store) CreateTransaction(ctx context.Context, txn *authorization.Transaction) error
- func (s *Store) DB() *sql.DB
- func (s *Store) FindGrant(ctx context.Context, clientID, subject string) (*grant.Grant, error)
- func (s *Store) GetClient(ctx context.Context, id string) (*client.Client, error)
- func (s *Store) GetGrant(ctx context.Context, id string) (*grant.Grant, error)
- func (s *Store) GetTokenFamily(ctx context.Context, id string) (*token.Family, error)
- func (s *Store) GetTransaction(ctx context.Context, id string) (*authorization.Transaction, error)
- func (s *Store) IssueTokens(ctx context.Context, family *token.Family, access *token.AccessToken, ...) error
- func (s *Store) ListGrantsBySubject(ctx context.Context, subject string) ([]*grant.Grant, error)
- func (s *Store) LookupAccessToken(ctx context.Context, hash string) (*token.AccessToken, *grant.Grant, error)
- func (s *Store) LookupRefreshToken(ctx context.Context, hash string) (*token.RefreshToken, error)
- func (s *Store) Purge(ctx context.Context, now time.Time) error
- func (s *Store) RevokeAccessToken(ctx context.Context, hash string, now time.Time) error
- func (s *Store) RevokeGrant(ctx context.Context, id string, now time.Time) error
- func (s *Store) RevokeTokenFamily(ctx context.Context, familyID string, now time.Time) error
- func (s *Store) RotateRefreshToken(ctx context.Context, oldHash string, next *token.RefreshToken, ...) error
- func (s *Store) TransitionTransaction(ctx context.Context, id string, from, to authorization.Status, ...) error
- func (s *Store) UpsertGrant(ctx context.Context, candidate *grant.Grant) (*grant.Grant, error)
Examples ¶
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
Types ¶
type Store ¶
type Store struct {
// contains filtered or unexported fields
}
Store は SQLite 上の authengine.Store 実装。
func New ¶
New は利用者が用意した *sql.DB から Store を作る。スキーマは Migrate で適用済みであること。 DSN に Open と同じ pragma と _txlock=immediate を含めることを推奨する。
func Open ¶
Open はファイル DB を開き、マイグレーションを適用して Store を返す。
DSN には次を固定する: journal_mode=WAL、busy_timeout=5000ms、foreign_keys=ON、 書き込みトランザクションは BEGIN IMMEDIATE。
Example ¶
ExampleOpen はファイル DB を開いて Engine に渡す。Open はマイグレーションを適用してから返すので、 goose CLI などで事前にスキーマを作る必要はない。`:memory:` は使えない(接続ごとに別 DB になる)。 再び開いても登録済みの Client は残る。
package main
import (
"context"
"fmt"
"log"
"os"
"path/filepath"
"time"
authengine "github.com/u-masato/auth-engine"
"github.com/u-masato/auth-engine/adapters/sqlite"
"github.com/u-masato/auth-engine/client"
"github.com/u-masato/auth-engine/keys"
)
func main() {
ctx := context.Background()
dir, err := os.MkdirTemp("", "auth-engine-example")
if err != nil {
log.Fatal(err)
}
defer os.RemoveAll(dir)
path := filepath.Join(dir, "auth.db")
store, err := sqlite.Open(ctx, path)
if err != nil {
log.Fatal(err)
}
c, err := client.NewNative(client.NativeParams{ID: "cli", Name: "CLI", RedirectURIs: []string{"http://127.0.0.1/callback"}, Scopes: []string{"openid"}}, time.Now())
if err != nil {
log.Fatal(err)
}
if err := store.CreateClient(ctx, c); err != nil {
log.Fatal(err)
}
if err := store.Close(); err != nil {
log.Fatal(err)
}
// プロセスの再起動に相当する。
store, err = sqlite.Open(ctx, path)
if err != nil {
log.Fatal(err)
}
defer store.Close()
ks := keys.NewSet()
k, err := keys.Generate(keys.AlgRS256, time.Now())
if err != nil {
log.Fatal(err)
}
if err := ks.AddSigning(k); err != nil {
log.Fatal(err)
}
engine, err := authengine.New(authengine.Config{Issuer: "https://as.example.com", Store: store, Keys: ks})
if err != nil {
log.Fatal(err)
}
got, err := engine.Store.GetClient(ctx, "cli")
if err != nil {
log.Fatal(err)
}
fmt.Println(got.ID, got.Profile)
// 期限切れの Transaction・Code・トークンの削除はエンジンが自動では行わない。利用者のスケジューラから呼ぶ。
if err := engine.Store.Purge(ctx, time.Now()); err != nil {
log.Fatal(err)
}
}
Output: cli native
func (*Store) ConsumeAuthorizationCode ¶
func (s *Store) ConsumeAuthorizationCode(ctx context.Context, codeHash string, now time.Time) (*authorization.AuthorizationCode, error)
ConsumeAuthorizationCode は 1 文の UPDATE ... RETURNING で原子的に消費する。 0 行なら SELECT で「消費済み」と「不明・期限切れ」を区別する。
func (*Store) CreateAuthorizationCode ¶
func (s *Store) CreateAuthorizationCode(ctx context.Context, code *authorization.AuthorizationCode) error
func (*Store) CreateClient ¶
func (*Store) CreateTransaction ¶
func (s *Store) CreateTransaction(ctx context.Context, txn *authorization.Transaction) error
func (*Store) GetTokenFamily ¶
func (*Store) GetTransaction ¶
func (s *Store) GetTransaction(ctx context.Context, id string) (*authorization.Transaction, error)
func (*Store) IssueTokens ¶
func (s *Store) IssueTokens(ctx context.Context, family *token.Family, access *token.AccessToken, refresh *token.RefreshToken) error
IssueTokens は Grant が active であることを同じトランザクション内で確認してから挿入する。
func (*Store) ListGrantsBySubject ¶
func (*Store) LookupAccessToken ¶
func (*Store) LookupRefreshToken ¶
func (*Store) RevokeAccessToken ¶
func (*Store) RevokeGrant ¶
RevokeGrant は Grant と配下の Family・全トークンを 1 トランザクションで失効させる。
func (*Store) RevokeTokenFamily ¶
func (*Store) RotateRefreshToken ¶
func (s *Store) RotateRefreshToken(ctx context.Context, oldHash string, next *token.RefreshToken, access *token.AccessToken, now time.Time) error
RotateRefreshToken は旧トークンが active のときだけ rotated にし、後継と Access Token を挿入する。
func (*Store) TransitionTransaction ¶
func (s *Store) TransitionTransaction(ctx context.Context, id string, from, to authorization.Status, patch authorization.Patch) error
TransitionTransaction は CAS。UPDATE の WHERE に from を含め、更新行数で成否を判定する。