Skip to content

Commit 3e0005e

Browse files
committed
test(auth): add tests for SHA256 authentication
- Adjust CI to create a user for SHA256 auth testing - Add new integration tests for the SHA256 - Add new unit tests for SHA256
1 parent 47e9450 commit 3e0005e

File tree

3 files changed

+206
-6
lines changed

3 files changed

+206
-6
lines changed

.github/workflows/ci.yml

Lines changed: 20 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -56,16 +56,30 @@ jobs:
5656
PGHOST: localhost
5757
PGDATABASE: ci_db_test
5858
PGTESTNOSSL: 'true'
59-
# SCRAM_TEST_PGUSER: scram_test
60-
# SCRAM_TEST_PGPASSWORD: test4scram
59+
SHA256_TEST_PGUSER: sha256_test
60+
SHA256_TEST_PGPASSWORD: test4@scram
6161
steps:
6262
- name: Show OS
6363
run: |
6464
uname -a
65-
# - run: |
66-
# psql \
67-
# -c "SET password_encryption = 'scram-sha-256'" \
68-
# -c "CREATE ROLE scram_test LOGIN PASSWORD 'test4scram'"
65+
- name: Wait for GaussDB to be ready
66+
run: |
67+
timeout 60 bash -c 'until pg_isready -h localhost -p 5432; do sleep 2; done'
68+
- name: Setup SHA256 authentication
69+
run: |
70+
# Wait for database to be fully started
71+
sleep 15
72+
73+
# Get container ID
74+
CONTAINER_ID=$(docker ps --filter "ancestor=opengauss/opengauss" --format "{{.ID}}")
75+
docker exec $CONTAINER_ID su - omm -c "gs_guc set -D /var/lib/opengauss/data/ -c 'password_encryption_type = 2'"
76+
77+
# Add SHA256 authentication rule to pg_hba.conf
78+
docker exec $CONTAINER_ID su - omm -c "gs_guc set -D /var/lib/opengauss/data/ -h 'host all sha256_test 0.0.0.0/0 sha256'"
79+
docker exec $CONTAINER_ID su - omm -c "gs_ctl reload -D /var/lib/opengauss/data/"
80+
sleep 5
81+
82+
PGPASSWORD=openGauss@123 psql -h localhost -U ci_user -d ci_db_test -c "CREATE ROLE sha256_test login password 'test4@scram';"
6983
- uses: actions/checkout@v4
7084
with:
7185
persist-credentials: false
Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
'use strict'
2+
const helper = require('./../test-helper')
3+
const pg = helper.pg
4+
const suite = new helper.Suite()
5+
const { native } = helper.args
6+
const assert = require('assert')
7+
8+
/**
9+
* This test only executes if the env variables SHA256_TEST_PGUSER and
10+
* SHA256_TEST_PGPASSWORD are defined. You can override additional values
11+
* for the host, port and database with other SHA256_TEST_ prefixed vars.
12+
* If the variables are not defined the test will be skipped.
13+
*
14+
* SQL to create test role:
15+
*
16+
* SET password_encryption_type = 2;
17+
* CREATE ROLE sha256_test login password 'test4@scram';
18+
*
19+
* Add the following entries to pg_hba.conf:
20+
*
21+
* host all sha256_test ::1/128 sha256
22+
* host all sha256_test 0.0.0.0/0 sha256
23+
*
24+
* Then run this file with after exporting:
25+
*
26+
* SHA256_TEST_PGUSER=sha256_test
27+
* SHA256_TEST_PGPASSWORD=test4@scram
28+
*/
29+
30+
// Base config for SHA256 tests
31+
const config = {
32+
user: process.env.SHA256_TEST_PGUSER,
33+
password: process.env.SHA256_TEST_PGPASSWORD,
34+
host: process.env.SHA256_TEST_PGHOST || 'localhost',
35+
port: process.env.SHA256_TEST_PGPORT || 5432,
36+
database: process.env.SHA256_TEST_PGDATABASE || 'ci_db_test',
37+
}
38+
39+
if (native) {
40+
suite.testAsync('skipping SHA256 tests (on native)', () => {})
41+
return
42+
}
43+
if (!config.user || !config.password) {
44+
suite.testAsync('skipping SHA256 tests (missing env)', () => {})
45+
return
46+
}
47+
48+
suite.testAsync('can connect using sha256 password authentication', async () => {
49+
const client = new pg.Client(config)
50+
let usingSha256 = false
51+
client.connection.once('authenticationSHA256Password', () => {
52+
usingSha256 = true
53+
})
54+
await client.connect()
55+
assert.ok(usingSha256, 'Should be using SHA256 for authentication')
56+
57+
// Test basic query execution
58+
const { rows } = await client.query('SELECT NOW()')
59+
assert.strictEqual(rows.length, 1)
60+
61+
await client.end()
62+
})
63+
64+
suite.testAsync('sha256 authentication fails when password is wrong', async () => {
65+
const client = new pg.Client({
66+
...config,
67+
password: config.password + 'append-something-to-make-it-bad',
68+
})
69+
let usingSha256 = false
70+
client.connection.once('authenticationSHA256Password', () => {
71+
usingSha256 = true
72+
})
73+
await assert.rejects(
74+
() => client.connect(),
75+
{
76+
code: '28P01',
77+
},
78+
'Error code should be for a password error'
79+
)
80+
assert.ok(usingSha256, 'Should be using SHA256 for authentication')
81+
})
82+
83+
suite.testAsync('sha256 authentication fails when password is empty', async () => {
84+
const client = new pg.Client({
85+
...config,
86+
// use a password function to simulate empty password
87+
password: () => '',
88+
})
89+
let usingSha256 = false
90+
client.connection.once('authenticationSHA256Password', () => {
91+
usingSha256 = true
92+
})
93+
await assert.rejects(
94+
() => client.connect(),
95+
(err) => {
96+
// Should fail with authentication error
97+
return err.code === '28P01' || err.message.includes('password')
98+
},
99+
'Should fail with password-related error'
100+
)
101+
assert.ok(usingSha256, 'Should be using SHA256 for authentication')
102+
})
Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
'use strict'
2+
const helper = require('./test-helper')
3+
const BufferList = require('../../buffer-list')
4+
const crypto = require('../../../lib/crypto/utils')
5+
const assert = require('assert')
6+
const suite = new helper.Suite()
7+
const test = suite.test.bind(suite)
8+
9+
test('sha256 authentication', async function () {
10+
const client = helper.createClient()
11+
client.password = '!'
12+
// Mock SHA256 authentication data following the format expected by postgresSha256PasswordHash, similar to the MD5 test
13+
// Structure: [4 bytes method][64 bytes random code][8 bytes token][4 bytes iteration]
14+
const data = Buffer.alloc(80)
15+
data.writeInt32BE(1, 0) // password method. in fact this data is not used in the hashing
16+
data.write('A'.repeat(64), 4, 'ascii') // 64-byte random code
17+
data.write('B'.repeat(8), 68, 'ascii') // 8-byte token
18+
data.writeInt32BE(1000, 76) // iteration count
19+
20+
await client.connection.emit('authenticationSHA256Password', { data: data })
21+
22+
setTimeout(() =>
23+
test('responds', function () {
24+
assert.lengthIs(client.connection.stream.packets, 1)
25+
test('should have correct encrypted data', async function () {
26+
const hashedPassword = await crypto.postgresSha256PasswordHash(client.user, client.password, data)
27+
assert.equalBuffers(
28+
client.connection.stream.packets[0],
29+
new BufferList().addCString(hashedPassword).join(true, 'p')
30+
)
31+
})
32+
})
33+
)
34+
})
35+
36+
test('sha256 authentication with empty password', async function () {
37+
const client = helper.createClient()
38+
client.password = ''
39+
const data = Buffer.alloc(80)
40+
data.writeInt32BE(1, 0)
41+
data.write('A'.repeat(64), 4, 'ascii')
42+
data.write('B'.repeat(8), 68, 'ascii')
43+
data.writeInt32BE(1000, 76)
44+
45+
await client.connection.emit('authenticationSHA256Password', { data: data })
46+
47+
setTimeout(() =>
48+
test('responds with empty password', function () {
49+
assert.lengthIs(client.connection.stream.packets, 1)
50+
test('should have correct encrypted data for empty password', async function () {
51+
const hashedPassword = await crypto.postgresSha256PasswordHash(client.user, client.password, data)
52+
assert.equalBuffers(
53+
client.connection.stream.packets[0],
54+
new BufferList().addCString(hashedPassword).join(true, 'p')
55+
)
56+
})
57+
})
58+
)
59+
})
60+
61+
test('sha256 authentication with utf-8 password', async function () {
62+
const client = helper.createClient()
63+
client.password = 'test_password_123'
64+
const data = Buffer.alloc(80)
65+
data.writeInt32BE(1, 0)
66+
data.write('A'.repeat(64), 4, 'ascii')
67+
data.write('B'.repeat(8), 68, 'ascii')
68+
data.writeInt32BE(1000, 76)
69+
70+
await client.connection.emit('authenticationSHA256Password', { data: data })
71+
72+
setTimeout(() =>
73+
test('responds with utf-8 password', function () {
74+
assert.lengthIs(client.connection.stream.packets, 1)
75+
test('should have correct encrypted data for utf-8 password', async function () {
76+
const hashedPassword = await crypto.postgresSha256PasswordHash(client.user, client.password, data)
77+
assert.equalBuffers(
78+
client.connection.stream.packets[0],
79+
new BufferList().addCString(hashedPassword).join(true, 'p')
80+
)
81+
})
82+
})
83+
)
84+
})

0 commit comments

Comments
 (0)