mirror of
https://github.com/bitechdev/ResolveSpec.git
synced 2026-10-02 11:31:57 +00:00
fix(resolvemcp): single transaction for create/update, hook registry mutex, uniform not-found, bounded SSE host cache
This commit is contained in:
@@ -397,7 +397,7 @@ UserInfoParser: func(userInfo map[string]any) (*security.UserContext, error) {
|
||||
|
||||
## Implementation Details
|
||||
|
||||
All database operations use stored procedures for consistency and security:
|
||||
On PostgreSQL, database operations use stored procedures by default (other dialects use direct SQL through `pkg/security/lookup`):
|
||||
- `resolvespec_oauth_getorcreateuser` - Find or create OAuth2 user
|
||||
- `resolvespec_oauth_createsession` - Create OAuth2 session
|
||||
- `resolvespec_oauth_getsession` - Validate and retrieve session
|
||||
|
||||
@@ -276,6 +276,6 @@ authURL += "&access_type=offline&prompt=consent"
|
||||
|
||||
## Complete Example
|
||||
|
||||
See `/pkg/security/oauth2_examples.go` line 250 for full working example.
|
||||
See `/pkg/security/oauth2_examples.go` (`ExampleOAuth2TokenRefresh`) for full working example.
|
||||
|
||||
For detailed documentation see `/pkg/security/OAUTH2_REFRESH_TOKEN_IMPLEMENTATION.md`.
|
||||
|
||||
@@ -43,16 +43,16 @@ CREATE TABLE IF NOT EXISTS user_sessions (
|
||||
**`resolvespec_oauth_getrefreshtoken(p_refresh_token)`**
|
||||
- Gets OAuth2 session data by refresh token
|
||||
- Returns: `{user_id, access_token, token_type, expiry}`
|
||||
- Location: `lookup/database_schema.sql:714`
|
||||
- Location: `lookup/database_schema.sql` (section 15); direct mode: `lookup/direct` `OAuthUserStore.GetByRefreshToken`
|
||||
|
||||
**`resolvespec_oauth_updaterefreshtoken(p_update_data)`**
|
||||
- Updates session with new tokens after refresh
|
||||
- Input: `{user_id, old_refresh_token, new_session_token, new_access_token, new_refresh_token, expires_at}`
|
||||
- Location: `lookup/database_schema.sql:752`
|
||||
- Location: `lookup/database_schema.sql` (section 16); direct mode: `lookup/direct` `OAuthUserStore.UpdateRefreshToken`
|
||||
|
||||
**`resolvespec_oauth_getuser(p_user_id)`**
|
||||
- Gets user data by ID for building UserContext
|
||||
- Location: `lookup/database_schema.sql:791`
|
||||
- Location: `lookup/database_schema.sql` (section 17); direct mode: `lookup/direct` `OAuthUserStore.GetUser`
|
||||
|
||||
---
|
||||
|
||||
@@ -68,7 +68,7 @@ func (a *DatabaseAuthenticator) OAuth2RefreshToken(
|
||||
) (*LoginResponse, error)
|
||||
```
|
||||
|
||||
**Location:** `pkg/security/oauth2_methods.go:375`
|
||||
**Location:** `pkg/security/oauth2_methods.go` (`OAuth2RefreshToken`)
|
||||
|
||||
### Implementation Flow
|
||||
|
||||
@@ -476,7 +476,7 @@ auth.OAuth2RefreshToken(ctx, token, "google") // Must match ProviderName
|
||||
|
||||
## 8. Complete Working Example
|
||||
|
||||
See `pkg/security/oauth2_examples.go:250` for full working example with token refresh.
|
||||
See `pkg/security/oauth2_examples.go` (`ExampleOAuth2TokenRefresh`) for full working example with token refresh.
|
||||
|
||||
---
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@ Passkey authentication (WebAuthn/FIDO2) is now integrated into the DatabaseAuthe
|
||||
### Database Schema
|
||||
Run the passkey SQL schema (in lookup/database_schema.sql):
|
||||
- Creates `user_passkey_credentials` table
|
||||
- Adds stored procedures for passkey operations
|
||||
- Adds stored procedures for passkey operations (Postgres procedure backend; other dialects use `lookup/ddl` tables with direct SQL)
|
||||
|
||||
### Go Code
|
||||
```go
|
||||
|
||||
@@ -16,7 +16,8 @@ rowSec := security.NewDatabaseRowSecurityProvider(db)
|
||||
provider, _ := security.NewCompositeSecurityProvider(auth, colSec, rowSec)
|
||||
|
||||
// Step 3: Setup and apply middleware
|
||||
securityList, _ := security.SetupSecurityProvider(handler, provider)
|
||||
securityList, _ := security.NewSecurityList(provider)
|
||||
restheadspec.RegisterSecurityHooks(handler, securityList)
|
||||
router.Use(security.NewAuthMiddleware(securityList))
|
||||
router.Use(security.SetSecurityMiddleware(securityList))
|
||||
```
|
||||
@@ -25,7 +26,7 @@ router.Use(security.SetSecurityMiddleware(securityList))
|
||||
|
||||
## Stored Procedures
|
||||
|
||||
**All database operations use PostgreSQL stored procedures** with `resolvespec_*` naming:
|
||||
**On PostgreSQL, database operations use stored procedures by default** with `resolvespec_*` naming (other dialects use direct SQL; see `lookup.Config` in README.md):
|
||||
|
||||
### Database Authenticators
|
||||
```go
|
||||
@@ -632,7 +633,8 @@ func main() {
|
||||
|
||||
// Setup security
|
||||
provider := &SimpleProvider{}
|
||||
securityList := security.SetupSecurityProvider(handler, provider)
|
||||
securityList, _ := security.NewSecurityList(provider)
|
||||
restheadspec.RegisterSecurityHooks(handler, securityList)
|
||||
|
||||
// Apply middleware
|
||||
router := mux.NewRouter()
|
||||
@@ -761,7 +763,8 @@ auth := security.NewJWTAuthenticator("secret", db)
|
||||
colSec := security.NewDatabaseColumnSecurityProvider(db)
|
||||
rowSec := security.NewDatabaseRowSecurityProvider(db)
|
||||
provider := security.NewCompositeSecurityProvider(auth, colSec, rowSec)
|
||||
securityList := security.SetupSecurityProvider(handler, provider)
|
||||
securityList, _ := security.NewSecurityList(provider)
|
||||
restheadspec.RegisterSecurityHooks(handler, securityList)
|
||||
|
||||
// ===== INTERFACE METHODS =====
|
||||
Authenticate(r *http.Request) (*UserContext, error)
|
||||
|
||||
+11
-6
@@ -18,7 +18,7 @@ Type-safe, composable security system for ResolveSpec with support for authentic
|
||||
|
||||
## Stored Procedure Architecture
|
||||
|
||||
**All database-backed security providers use PostgreSQL stored procedures exclusively.** No raw SQL queries are executed from Go code.
|
||||
**On PostgreSQL, database-backed security providers use stored procedures by default.** `pkg/security` itself contains no SQL; all database access lives in [`pkg/security/lookup`](lookup), which can also run the same operations as direct SQL on tables (see [Database access (lookup)](#database-access-lookup)).
|
||||
|
||||
### Benefits
|
||||
|
||||
@@ -139,7 +139,7 @@ Read them from Go with `ddl.SQL("sqlite")` or, for drivers that reject multi-sta
|
||||
- Direct mode stores `bytea` / array / `jsonb` values (passkey credentials, OAuth client lists, key meta) as base64 / JSON text; the Go API is unchanged.
|
||||
- OAuth authorization codes are consumed atomically.
|
||||
- Adding a database: implement `dialect.Dialect`, register it with `dialect.Register`, then set `Config.Dialect`.
|
||||
- Backend conformance: `lookup/conformance` is one behavioural suite run against every backend (`go test ./pkg/security/lookup/backends -run TestConformance`). SQLite runs always; Postgres (procedure and direct), MySQL and SQL Server run when `RESOLVESPEC_TEST_PG_DSN`, `RESOLVESPEC_TEST_PG_DIRECT_DSN`, `RESOLVESPEC_TEST_MYSQL_DSN` or `RESOLVESPEC_TEST_MSSQL_DSN` is set (see the comment in `backends/conformance_test.go`). Rows are prefixed and removed afterwards.
|
||||
- Backend conformance: `lookup/conformance` is one behavioural suite run against every backend (`go test ./pkg/security/lookup/backends -run TestConformance`). SQLite runs always; Postgres (procedure and direct), MySQL and SQL Server run when `RESOLVESPEC_TEST_PG_DSN`, `RESOLVESPEC_TEST_PG_DIRECT_DSN`, `RESOLVESPEC_TEST_MYSQL_DSN` or `RESOLVESPEC_TEST_MSSQL_DSN` is set (see the comment in `backends/conformance_test.go`). Rows are prefixed and removed afterwards. With `RESOLVESPEC_TEST_CONTAINERS=1` (and not `-short`) the container tests start a throwaway database with podman or docker (podman first), run the suite and remove the container, so no DSN is needed.
|
||||
- Migration from the old `SQLNames` / `TableNames` / `QueryMode` API: see `breaking_changes.md`.
|
||||
|
||||
## Quick Start
|
||||
@@ -936,7 +936,8 @@ func TestMyHandler(t *testing.T) {
|
||||
&MockRowSecurity{},
|
||||
)
|
||||
|
||||
securityList := security.SetupSecurityProvider(handler, provider)
|
||||
securityList, _ := security.NewSecurityList(provider)
|
||||
restheadspec.RegisterSecurityHooks(handler, securityList)
|
||||
// ... test your handler
|
||||
}
|
||||
```
|
||||
@@ -1227,9 +1228,13 @@ The main changes:
|
||||
| File | Description |
|
||||
|------|-------------|
|
||||
| **QUICK_REFERENCE.md** | Quick reference guide with examples |
|
||||
| **INTERFACE_GUIDE.md** | Complete implementation guide |
|
||||
| **examples.go** | Working provider implementations |
|
||||
| **setup_example.go** | 6 complete integration examples |
|
||||
| **KEYSTORE.md** | Per-user auth keys and key stores |
|
||||
| **OAUTH2.md** | OAuth2 client login and the authorization server |
|
||||
| **OAUTH2_REFRESH_QUICK_REFERENCE.md** / **OAUTH2_REFRESH_TOKEN_IMPLEMENTATION.md** | OAuth2 refresh tokens |
|
||||
| **PASSKEY_QUICK_REFERENCE.md** | WebAuthn passkeys |
|
||||
| **SECURITY_FEATURES.md** | Security feature overview |
|
||||
| **breaking_changes.md** | Migration notes for the `lookup` refactor |
|
||||
| **examples.go**, **examples_funcspec.go**, **oauth2_examples.go**, **passkey_examples.go** | Working provider implementations |
|
||||
|
||||
## API Reference
|
||||
|
||||
|
||||
@@ -27,9 +27,8 @@ Import `github.com/bitechdev/ResolveSpec/pkg/security/totp`. No aliases (import
|
||||
`totp.NewAuthenticator` takes a `totp.BaseAuthenticator` (Login, Logout, Authenticate) instead of
|
||||
`security.Authenticator`; any `security.Authenticator` satisfies it.
|
||||
|
||||
`DatabaseTwoFactorProvider` stays in `security` for now (it uses the core SQL internals) and moves
|
||||
into `totp` once the lookup `TOTPStore` replaces them. Until then core imports `totp`, so `totp`
|
||||
must not import `security`.
|
||||
`DatabaseTwoFactorProvider` stays in `security` (it now calls the lookup `TOTPStore`). Core imports
|
||||
`totp`, so `totp` must not import `security`.
|
||||
|
||||
## Step 0b (providers, first part): moved to `pkg/security/providers`
|
||||
|
||||
@@ -45,7 +44,7 @@ Import `github.com/bitechdev/ResolveSpec/pkg/security/providers`. Names unchange
|
||||
|
||||
The SHA-256 key hash helper is now `sectypes.HashKey`. The database-backed providers
|
||||
(`DatabaseAuthenticator`, `JWTAuthenticator`, `DatabaseKeyStore`, `DatabaseColumn/RowSecurityProvider`)
|
||||
stay in `security` until the lookup stores replace their SQL.
|
||||
stay in `security`; they call the lookup stores (see step 5).
|
||||
|
||||
## Additions (no action needed)
|
||||
|
||||
@@ -67,7 +66,7 @@ stay in `security` until the lookup stores replace their SQL.
|
||||
|
||||
- New `lookup/direct` package: table-backed stores for auth, keys, OAuth (client + user), passkey,
|
||||
TOTP and policy, built from `lookup.Schema` and the dialect. Nothing in `pkg/security` calls it
|
||||
yet (wiring is step 5), so no existing API changes here.
|
||||
yet (wiring happens in step 5), so no existing API changes here.
|
||||
- Direct `LoginAPIKey` is new: `header_api` / `api` keys only; unknown, expired, inactive and
|
||||
wrong-type keys (and inactive users) all return `lookup.ErrInvalidAPIKey`.
|
||||
- Policy tables (`sec_group_members`, `sec_column_rules`, `sec_row_rules`) are required for the
|
||||
|
||||
Reference in New Issue
Block a user