authstoredb

package module
v0.0.1 Latest Latest
Warning

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

Go to latest
Published: Aug 27, 2026 License: Apache-2.0 Imports: 14 Imported by: 0

README

lib-authstore-db

Implementasi authstorage:db untuk webcore -- pengganti backend adapter/authstore/yaml bawaan webcore, tapi datanya diambil dari PostgreSQL alih-alih file access.yaml.

Isi package

  • entity/entity.go -- model tabel access.auth_users & access.auth_resources
  • backend.go -- DBBackend, implementasi interface auth.IStore (query ke DB)
  • loader.go -- DBLoader, implementasi core.LibraryLoader (di-daftarkan sebagai authstorage:db)
  • migrations/001_create_auth_tables.sql -- DDL tabel yang dipakai entity.go

Cara pakai

1. Jalankan migration

Jalankan migrations/001_create_auth_tables.sql ke database ildki (schema access, sesuai config.yaml).

Jika Anda sudah punya tabel users/permission dengan struktur berbeda, tidak perlu jalankan migration ini -- cukup sesuaikan tag bun:"..." / db:"..." di entity/entity.go supaya cocok dengan tabel existing Anda. backend.go dan loader.go tidak perlu diubah.

2. Daftarkan ke go.work

Tambahkan baris ./libraries/authstoredb ke go.work:

use (
	./libraries/jwt
	./libraries/authstoredb
	./modules/auth
	./webcore
)
3. Daftarkan loader ke APP_LIBRARIES

Edit webcore/auth/libraries.go:

import (
	jwt "github.com/webcore-go/lib-auth-jwt"
	authstoredb "github.com/nersus15/lib-authstoredb"
	memory "github.com/webcore-go/lib-memory"
	postgres "github.com/webcore-go/lib-postgres"
	"github.com/webcore-go/webcore/app/core"
)

var APP_LIBRARIES = map[string]core.LibraryLoader{
	"cache:memory":       &memory.MemoryLoader{},
	"database:postgres":  &postgres.PostgresLoader{},
	"authentication:jwt": &jwt.JWTAuthLoader{},
	"authstorage:db":     &authstoredb.DBLoader{},
}

"cache:memory" wajib terdaftar di sini (bukan cuma authstorage:db) supaya bagian Caching di bawah bisa aktif -- tanpa itu, authstoredb tetap jalan normal, cuma tanpa optimasi cache.

4. Pastikan config.yaml sudah benar

server, auth, database harus top-level (sejajar dengan app:, bukan di dalamnya) -- lihat pembahasan sebelumnya. Yang relevan untuk library ini:

auth:
  type: "jwt"
  control: RBAC
  store: db
  secret_key: your-secret-key-here
  session:
    expires_in: 86400

database:
  driver: postgres
  host: "localhost"
  port: 5432
  user: faskesmanager
  password: jNeOtHQrIPmQsAvHrxB1
  name: ildki
  schema_name: access
  ssl_mode: disable
5. go mod tidy

Karena module ini baru, go.sum-nya belum ada. Jalankan dari root project:

cd libraries/authstoredb && go mod tidy

(butuh akses internet untuk resolve dependency uptrace/bun, golang.org/x/crypto, dst.)

Cara kerja singkat

  • Login (POST /auth/token, hanya relevan untuk auth.type: jwt): GetUserLoginInfo query user by username, verifikasi password dengan bcrypt.CompareHashAndPassword.

  • Request terproteksi: GetUserAuthInfo menentukan 1 kandidat user dulu (query murah, bukan brute-force seperti backend YAML) dengan cara berbeda tergantung validator.Name():

    • "jwt" -- decode klaim user_id dari JWT (tanpa verifikasi signature -- cuma untuk tahu user mana yang perlu di-query), lalu query by username.
    • "apikey" -- key yang dikirim klien dicari di access.auth_apikey (findUserByAPIKey), lalu user pemiliknya diambil dari access.auth_users lewat user_id. UserAuthInfoRBAC.UserId yang dikirim ke validator.VerifyUser diisi dengan nilai API key itu sendiri (bukan user.ID) karena ApiKeyValidator.VerifyUser bawaan webcore membandingkan key ke UserId secara literal -- id asli user tidak pernah keluar dari server.

    Setelah kandidat user ditemukan, verifikasi akhir (signature JWT / kecocokan API key) tetap diserahkan ke validator.VerifyUser (JWTAuthValidator / ApiKeyValidator bawaan webcore) -- kita tidak menduplikasi logic verifikasinya di sini.

  • Authorization (RBAC): GetResourceInfo query access.auth_resources berdasarkan method, lalu cari prefix path yang paling spesifik -- perilakunya disamakan dengan adapter/authstore/yaml/backend.go, dan tidak terpengaruh tipe auth (jwt/apikey) sama sekali.

Caching

Berbeda dengan backend YAML (semua data di-load sekali ke memori saat startup), backend DB ini secara default query ke Postgres di setiap request. Untuk kurangi beban itu, DBBackend punya field Cache port.ICacheMemory (opsional -- diisi otomatis oleh loader.go dari instance "cache:memory" yang sudah dimuat, atau nil kalau tidak ada).

Dipanggil dari Method Di-cache? TTL Cache key
GetUserLoginInfo (login) findUserByUsername ❌ Tidak - -
GetUserAuthInfo (jwt) findUserByUsernameCached ✅ Ya cacheTTLUser (30s) authstoredb:user:username:<username>
GetUserAuthInfo (apikey) findUserByAPIKey ✅ Ya cacheTTLUser (30s) authstoredb:user:apikey:<key>
GetResourceInfo (RBAC) findResourcesByMethod ✅ Ya cacheTTLResource (60s) authstoredb:resources:method:<METHOD>

Kenapa login sengaja tidak di-cache: ini titik kontrol paling sensitif (mis. admin menonaktifkan user karena akun dicurigai kompromi) dan jauh lebih jarang dipanggil dibanding pengecekan per-request, jadi manfaat caching-nya kecil dibanding risiko user nonaktif masih lolos beberapa detik.

Trade-off yang perlu disadari: dengan cacheTTLUser = 30s, perubahan active/groups/roles pada user yang sedang login butuh waktu maksimal 30 detik untuk kerasa di endpoint terproteksi (bukan lagi instan). Kalau butuh revoke akses instan tanpa delay sama sekali, turunkan cacheTTLUser (bahkan ke beberapa detik) atau non-aktifkan cache untuk backend ini dengan tidak mendaftarkan "cache:memory" di APP_LIBRARIES -- Cache akan nil dan seluruh lookup otomatis balik ke query DB langsung tanpa cache sama sekali.

Tidak ada invalidasi cache manual (mis. saat admin update user lewat tool lain) -- staleness murni dibatasi oleh TTL. Kalau butuh update near-instant tanpa TTL pendek, perlu mekanisme invalidasi aktif (mis. pub/sub ke semua instance saat ada perubahan), yang belum diimplementasikan di sini.

Tipe auth yang didukung
auth.type Status Catatan
jwt ✅ Didukung Login/Refresh/Logout aktif lewat libraries/jwt
apikey ✅ Didukung Pakai webcore/adapter/auth/apikey bawaan (tidak perlu library custom). Login/Refresh/Logout tidak berlaku (ApiKeyValidator.IsRequireLogin() == false -> Authenticator.Login/RefreshToken/Logout selalu error "operation not supported") -- klien menerima API key di luar jalur ini, lalu kirim di header (X-API-Key atau Authorization: APIKey <key>) tiap request. Wajib tambah "authentication:apikey": &apikey.ApiKeyLoader{} di webcore/auth/libraries.go, dan sebaiknya skip registrasi route /auth/token, /refresh, /logout di module saat auth.type == "apikey". API key disimpan di tabel terpisah access.auth_apikey (relasi 1:1 ke access.auth_users lewat user_id, UNIQUE(user_id)) -- bukan memakai kolom id user sebagai key, jadi id tetap privat/internal. Generate api_key sebagai string acak kuat (32+ byte random).
basic ❌ Belum Perlu tambah case "basic" di GetUserAuthInfo kalau dibutuhkan

Batasan saat ini

  • Hanya mendukung control: RBAC (belum ada mapping ABAC).

Documentation

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type DBBackend

type DBBackend struct {
	Connection  port.IDatabase
	Ctx         context.Context
	ControlType string // "RBAC" atau "ABAC" (config.Control) -- saat ini hanya RBAC yang diimplementasikan

	// Cache opsional (port.ICacheMemory, bisa nil kalau "cache:memory" tidak
	// dimuat -- lihat loader.go). Dipakai di findUserByUsername,
	// findUserByAPIKey, dan GetResourceInfo untuk kurangi query berulang ke
	// DB tiap request. GetUserLoginInfo (login) sengaja TIDAK memakai cache
	// ini -- lihat komentar TTL di atas.
	Cache port.ICacheMemory
}

DBBackend mengimplementasikan auth.IStore (port/auth/store.go) dengan backend PostgreSQL, sebagai pengganti adapter/authstore/yaml bawaan webcore.

Method yang wajib diimplementasikan:

  • GetUserLoginInfo : dipakai saat login (POST /auth/token)
  • GetUserAuthInfo : dipakai saat verifikasi token pada request terproteksi
  • GetResourceInfo : dipakai untuk pengecekan RBAC (route mana butuh role apa)

func NewDBBackend

func NewDBBackend(ctx context.Context, conn port.IDatabase, controlType string, cache port.ICacheMemory) *DBBackend

NewDBBackend membuat instance DBBackend baru. cache boleh nil (backend tetap berfungsi tanpa cache, cuma tanpa optimasi -- selalu query DB langsung seperti sebelumnya).

func (*DBBackend) GetResourceInfo

func (b *DBBackend) GetResourceInfo(method string, path string) (auth.IResourceInfo, error)

GetResourceInfo mencari aturan akses (path+method) yang paling spesifik (prefix match terpanjang), sama seperti perilaku backend YAML.

Daftar resource per `method` di-cache (cacheTTLResource) supaya tidak query ulang ke access.auth_resources tiap request -- pencocokan prefix path tetap dilakukan di memori setiap kali (murah, cuma loop string), yang di-cache hanya hasil query DB-nya.

func (*DBBackend) GetUserAuthInfo

func (b *DBBackend) GetUserAuthInfo(ctx *fiber.Ctx, validator auth.IAuthValidator) (auth.IUserAuthInfo, error)

GetUserAuthInfo dipanggil saat verifikasi token/key pada request terproteksi.

Berbeda dengan backend YAML (yang loop ke semua user in-memory lalu memanggil validator.VerifyUser satu-satu -- boleh karena datanya kecil & in-memory), backend DB ini query 1 kandidat user saja dulu (lebih murah untuk tabel besar), baru menyerahkan verifikasi akhir ke validator.VerifyUser.

Cara menentukan kandidat user berbeda tergantung tipe auth aktif (validator.Name()):

  • "jwt" : userKey adalah JWT. Decode klaim "user_id" TANPA verifikasi signature dulu (hanya untuk tahu user mana yang perlu di-query), signature sesungguhnya diverifikasi oleh JWTAuthValidator.VerifyUser (pegang secret_key).
  • "apikey" : userKey adalah API key mentah. Dicari di access.auth_apikey (findUserByAPIKey), lalu user pemiliknya diambil dari access.auth_users lewat user_id. `id` user TIDAK pernah dikirim ke klien -- tetap rahasia internal.

Tambah case baru di switch ini kalau menambah tipe auth lain (mis. "basic").

func (*DBBackend) GetUserLoginInfo

func (b *DBBackend) GetUserLoginInfo(ctx *fiber.Ctx, username string, password string) (auth.IUserAuthInfo, error)

GetUserLoginInfo dipanggil saat proses login (username + password).

type DBLoader

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

DBLoader mendaftarkan backend authstorage berbasis database (PostgreSQL). Didaftarkan sebagai loader "authstorage:db" di APP_LIBRARIES (lihat webcore/auth/libraries.go), dipanggil otomatis oleh webcore/adapter/auth/authn/authn.go via:

context.StartDefaultSingletonInstance("authstorage", context, config)

yang secara internal me-resolve nama loader menjadi "authstorage:" + config.Auth.Store (lihat webcore/app/core/core.go -> getDefaultName).

func (*DBLoader) Init

func (l *DBLoader) Init(args ...any) (port.Library, error)

Init dipanggil oleh LibraryManager dengan args:

args[0] = *core.AppContext
args[1] = config.AuthConfig (dari section `auth:` di config.yaml)

func (*DBLoader) Name

func (l *DBLoader) Name() string

func (*DBLoader) SetName

func (l *DBLoader) SetName(name string)

Directories

Path Synopsis

Jump to

Keyboard shortcuts

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