Set search_path on each new connection, and role on each acquisition

The whole application will need the same search_path, so it is wasteful
to do that in each handler.

It is possible to pass the search path as a parameter to the database’s
connection string, but then everyone would need to remember to do that,
and update the configuration in case i add another schema.

Similarly, i need to change the user’s role to match her
permissions—which are not in yet—, but this time i need it each time a
handler requests a connection from the pool, because each time the
connection is returned to the pool i reset the role back to the initial,
that hopefully will be authenticator.
This commit is contained in:
jordi fita mas 2023-01-17 14:46:22 +01:00
parent 97ac586a3b
commit ab6c0079c9
3 changed files with 42 additions and 11 deletions

View File

@ -9,13 +9,11 @@ import (
"syscall"
"time"
"github.com/jackc/pgx/v4/pgxpool"
numerus "dev.tandem.ws/tandem/numerus/pkg"
)
func main() {
dbpool, err := pgxpool.Connect(context.Background(), os.Getenv("DATABASE_URL"))
dbpool, err := numerus.ConnectToDatabase(context.Background(), os.Getenv("NUMERUS_DATABASE_URL"))
if err != nil {
log.Fatal(err)
}

39
pkg/db.go Normal file
View File

@ -0,0 +1,39 @@
package pkg
import (
"context"
"log"
"github.com/jackc/pgx/v4"
"github.com/jackc/pgx/v4/pgxpool"
)
func ConnectToDatabase(ctx context.Context, connString string) (*pgxpool.Pool, error) {
config, err := pgxpool.ParseConfig(connString)
if err != nil {
log.Fatal(err)
}
config.AfterConnect = func(ctx context.Context, conn *pgx.Conn) error {
_, err := conn.Exec(context.Background(), "SET search_path TO numerus, public")
return err
}
config.BeforeAcquire = func(ctx context.Context, conn *pgx.Conn) bool {
if _, err := conn.Exec(ctx, "select set_config('role', $1, false)", "guest"); err != nil {
log.Printf("ERROR - Failed to set role: %v", err)
return false
}
return true
}
config.AfterRelease = func(conn *pgx.Conn) bool {
if _, err := conn.Exec(context.Background(), "RESET ROLE"); err != nil {
log.Printf("ERROR - Failed to reset role: %v", err)
return false
}
return true
}
return pgxpool.ConnectConfig(ctx, config)
}

View File

@ -1,7 +1,6 @@
package pkg
import (
"context"
"html/template"
"log"
"net/http"
@ -17,14 +16,9 @@ func NewRouter(db *pgxpool.Pool) http.Handler {
email := r.FormValue("email")
password := r.FormValue("password")
var role string
if _, err := db.Exec(context.Background(), "select set_config('search_path', 'numerus, public', false)"); err != nil {
log.Printf("ERROR - %s", err.Error())
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
err := db.QueryRow(context.Background(), "select login($1, $2)", email, password).Scan(&role)
err := db.QueryRow(r.Context(), "select login($1, $2)", email, password).Scan(&role)
if err != nil {
log.Printf("ERROR - %s", err.Error())
log.Printf("ERROR - %v for %q", err, email)
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}