Skip to content

Commit 9346ffd

Browse files
committed
script to setup readonly user on prod deploy
1 parent 637c7c1 commit 9346ffd

3 files changed

Lines changed: 113 additions & 0 deletions

File tree

.github/workflows/release-prod.yml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,3 +44,8 @@ jobs:
4444
env:
4545
GRAFANA_TOKEN: ${{ secrets.GRAFANA_TOKEN }}
4646
run: node scripts/update-grafana-ds.js
47+
48+
- name: setup readonly user
49+
env:
50+
READONLY_USER_PASSWORD: ${{ secrets.READONLY_USER_PASSWORD }}
51+
run: pnpm run setup:readonly-user

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
"process": "sqd process",
2929
"generate:validations": "ts-node --require tsconfig-paths/register scripts/generate-validations.ts",
3030
"log:processing-times": "ts-node --require tsconfig-paths/register scripts/check-processing-times.ts",
31+
"setup:readonly-user": "ts-node --require tsconfig-paths/register scripts/setup-readonly-user.ts",
3132
"postdeploy": "sh -c 'pnpm run log:processing-times $1 && pnpm run generate:validations $1' --",
3233
"lint": "eslint \"src/**/*.ts\"",
3334
"lint:fix": "eslint \"src/**/*.ts\" --fix",

scripts/setup-readonly-user.ts

Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
import { execSync } from 'child_process'
2+
import 'dotenv/config'
3+
import { Pool } from 'pg'
4+
5+
const READONLY_USER_PASSWORD = process.env.READONLY_USER_PASSWORD
6+
7+
interface SqdParams {
8+
host: string
9+
port: number
10+
database: string
11+
user: string
12+
password: string
13+
}
14+
15+
interface SqdOutput {
16+
addons: {
17+
postgres: {
18+
connections: Array<{
19+
params: SqdParams
20+
}>
21+
}
22+
}
23+
}
24+
25+
async function setupReadonlyUser() {
26+
if (!READONLY_USER_PASSWORD) {
27+
console.log('READONLY_USER_PASSWORD not set, skipping readonly user setup')
28+
return
29+
}
30+
31+
// Get SQD credentials
32+
const sqdOutput = execSync('sqd view -o origin -n origin-squid -t prod --json', { encoding: 'utf8' })
33+
const sqd: SqdOutput = JSON.parse(sqdOutput)
34+
const params = sqd.addons.postgres.connections[0].params
35+
36+
const pool = new Pool({
37+
host: params.host,
38+
port: params.port,
39+
database: params.database,
40+
user: params.user,
41+
password: params.password,
42+
})
43+
44+
try {
45+
console.log('Setting up readonly_user...')
46+
47+
// Check if role already exists
48+
const roleExists = await pool.query(`SELECT 1 FROM pg_roles WHERE rolname = 'readonly_user'`)
49+
50+
if (roleExists.rows.length === 0) {
51+
// Create the readonly user role
52+
await pool.query(`
53+
CREATE ROLE readonly_user WITH
54+
LOGIN
55+
NOSUPERUSER
56+
NOCREATEDB
57+
NOCREATEROLE
58+
NOINHERIT
59+
NOREPLICATION
60+
PASSWORD '${READONLY_USER_PASSWORD}'
61+
`)
62+
console.log('Created readonly_user role')
63+
} else {
64+
// Update password if role exists
65+
await pool.query(`ALTER ROLE readonly_user WITH PASSWORD '${READONLY_USER_PASSWORD}'`)
66+
console.log('Updated readonly_user password')
67+
}
68+
69+
// Grant connect privilege on the database
70+
await pool.query(`GRANT CONNECT ON DATABASE "${params.database}" TO readonly_user`)
71+
console.log(`Granted CONNECT on database ${params.database}`)
72+
73+
// Grant usage on the schema
74+
await pool.query(`GRANT USAGE ON SCHEMA public TO readonly_user`)
75+
console.log('Granted USAGE on schema public')
76+
77+
// Grant SELECT on all existing tables in the schema
78+
await pool.query(`GRANT SELECT ON ALL TABLES IN SCHEMA public TO readonly_user`)
79+
console.log('Granted SELECT on all tables')
80+
81+
// Grant SELECT on all existing sequences
82+
await pool.query(`GRANT SELECT ON ALL SEQUENCES IN SCHEMA public TO readonly_user`)
83+
console.log('Granted SELECT on all sequences')
84+
85+
// Set default privileges for future tables
86+
await pool.query(`
87+
ALTER DEFAULT PRIVILEGES IN SCHEMA public
88+
GRANT SELECT ON TABLES TO readonly_user
89+
`)
90+
console.log('Set default privileges for future tables')
91+
92+
await pool.query(`
93+
ALTER DEFAULT PRIVILEGES IN SCHEMA public
94+
GRANT SELECT ON SEQUENCES TO readonly_user
95+
`)
96+
console.log('Set default privileges for future sequences')
97+
98+
console.log('Readonly user setup complete')
99+
} catch (error) {
100+
console.error('Error setting up readonly user:', error)
101+
process.exit(1)
102+
} finally {
103+
await pool.end()
104+
}
105+
}
106+
107+
void setupReadonlyUser()

0 commit comments

Comments
 (0)