| 1 |
|
// Package remoteapi assembles dolt.sr.ht's remotesapi subsystem: the gRPC |
| 2 |
|
// authentication/authorization interceptors, the importable remotesrv server |
| 3 |
|
// that serves the bare NBS chunk stores, and the small separate |
| 4 |
|
// CredentialsService (WhoAmI) server that backs `dolt login`. |
| 5 |
|
// |
| 6 |
|
// The two gRPC surfaces run on two ports (nginx path-routes between them): |
| 7 |
|
// |
| 8 |
|
// - the remotesapi ChunkStoreService (clone/push data plane) on ListenAddr, |
| 9 |
|
// wrapped by our per-repo authz interceptors (see interceptors.go); |
| 10 |
|
// - the CredentialsService.WhoAmI RPC on CredsListenAddr (see credsvc.go), |
| 11 |
|
// which lets the dolt CLI discover which SourceHut user a keypair maps to |
| 12 |
|
// while it polls for the key to be associated through the web UI. |
| 13 |
|
package remoteapi |
| 14 |
|
|
| 15 |
|
import ( |
| 16 |
|
"context" |
| 17 |
|
"database/sql" |
| 18 |
|
|
| 19 |
|
"sourcecraft.dev/bigbes/sr-ht-dolt/authn" |
| 20 |
|
"sourcecraft.dev/bigbes/sr-ht-dolt/db" |
| 21 |
|
) |
| 22 |
|
|
| 23 |
|
// keyStore adapts the SQL persistence layer (db.Store over the shared *sql.DB |
| 24 |
|
// pool) to the authn.KeyStore interface the Bearer-JWT resolver consumes. It |
| 25 |
|
// wraps the pool rather than a context-bound Store so it can be constructed once |
| 26 |
|
// at startup and shared by both the interceptor path and the credentials |
| 27 |
|
// service; each call binds a fresh db.Store to the pool (which manages |
| 28 |
|
// connection lifetime) and threads ctx for cancellation. |
| 29 |
|
type keyStore struct { |
| 30 |
|
db *sql.DB |
| 31 |
|
} |
| 32 |
|
|
| 33 |
|
var _ authn.KeyStore = (*keyStore)(nil) |
| 34 |
|
|
| 35 |
|
// newKeyStore builds a keyStore over the shared database pool. pool must be |
| 36 |
|
// non-nil. |
| 37 |
0 |
func newKeyStore(pool *sql.DB) *keyStore { |
| 38 |
0 |
if pool == nil { |
| 39 |
0 |
panic("remoteapi: newKeyStore requires a non-nil *sql.DB") |
| 40 |
|
} |
| 41 |
0 |
return &keyStore{db: pool} |
| 42 |
|
} |
| 43 |
|
|
| 44 |
|
// ByKID resolves a dolt key id to the owner's raw Ed25519 public key and |
| 45 |
|
// username, mapping the db layer's KeyAuth onto authn.KeyStore's contract. A |
| 46 |
|
// missing key surfaces as db.ErrNotFound (returned unchanged), which the JWT |
| 47 |
|
// resolver wraps into an invalid-token rejection. |
| 48 |
0 |
func (k *keyStore) ByKID(ctx context.Context, kid string) (pubkey []byte, username string, err error) { |
| 49 |
0 |
ka, err := db.NewStore(k.db).KeyByKID(ctx, kid) |
| 50 |
0 |
if err != nil { |
| 51 |
0 |
return nil, "", err |
| 52 |
0 |
} |
| 53 |
0 |
return ka.PubKey, ka.Username, nil |
| 54 |
|
} |
| 55 |
|
|
| 56 |
|
// TouchLastUsed stamps the key's last_used column after a successful keypair |
| 57 |
|
// authentication. |
| 58 |
0 |
func (k *keyStore) TouchLastUsed(ctx context.Context, kid string) error { |
| 59 |
0 |
return db.NewStore(k.db).TouchKeyLastUsed(ctx, kid) |
| 60 |
0 |
} |