Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit e0f6bc9

Browse files
authored
Support the sslmode URL query parameter and UDS URLs (vapor#248)
* Add support for the sslmode values accepted by libpq, and support for UDS URLs. * Clean up docs * Improve code coverage info in CI and fix security issues in test workflow
1 parent b69f427 commit e0f6bc9

File tree

9 files changed

+225
-87
lines changed

9 files changed

+225
-87
lines changed

‎.github/workflows/test.yml‎

Lines changed: 35 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,8 @@ concurrency:
33
group: ${{ github.workflow }}-${{ github.ref }}
44
cancel-in-progress: true
55
on:
6-
pull_request: { branches: ['*'] }
7-
push: { branches: ['main'] }
6+
pull_request: { types: [opened, reopened, synchronize, ready_for_review] }
7+
push: { branches: [main] }
88

99
env:
1010
LOG_LEVEL: info
@@ -23,39 +23,9 @@ env:
2323
POSTGRES_PASSWORD_B: 'test_password'
2424

2525
jobs:
26-
# Baseline test run for code coverage stats
27-
codecov:
28-
strategy:
29-
matrix: { dbimage: ['postgres:15'], dbauth: ['scram-sha-256'] }
30-
runs-on: ubuntu-latest
31-
container: swift:5.8-jammy
32-
services:
33-
psql-a:
34-
image: ${{ matrix.dbimage }}
35-
env:
36-
POSTGRES_USER: 'test_username'
37-
POSTGRES_DB: 'test_database'
38-
POSTGRES_PASSWORD: 'test_password'
39-
POSTGRES_HOST_AUTH_METHOD: ${{ matrix.dbauth }}
40-
POSTGRES_INITDB_ARGS: --auth-host=${{ matrix.dbauth }}
41-
steps:
42-
- name: Save Postgres version and method to env
43-
run: |
44-
echo POSTGRES_VERSION='${{ matrix.dbimage }}' >> $GITHUB_ENV
45-
echo POSTGRES_AUTH_METHOD='${{ matrix.dbauth }}' >> $GITHUB_ENV
46-
- name: Check out package
47-
uses: actions/checkout@v3
48-
- name: Run local tests with coverage
49-
run: swift test --enable-code-coverage
50-
- name: Submit coverage report to Codecov.io
51-
uses: vapor/swift-codecov-action@v0.2
52-
with:
53-
cc_env_vars: 'SWIFT_VERSION,SWIFT_PLATFORM,RUNNER_OS,RUNNER_ARCH,POSTGRES_VERSION,POSTGRES_AUTH_METHOD'
54-
cc_fail_ci_if_error: false
55-
5626
# Check for API breakage versus main
5727
api-breakage:
58-
if: github.event_name == 'pull_request'
28+
if: ${{ !(github.event.pull_request.draft || false) }}
5929
runs-on: ubuntu-latest
6030
container: swift:5.8-jammy
6131
steps:
@@ -67,7 +37,7 @@ jobs:
6737

6838
# Run Linux unit tests against various configurations
6939
linux-unit:
70-
if: github.event_name == 'pull_request'
40+
if: ${{ !(github.event.pull_request.draft || false) }}
7141
strategy:
7242
fail-fast: false
7343
matrix:
@@ -82,8 +52,8 @@ jobs:
8252
{dbimage: 'postgres:13', dbauth: 'md5'},
8353
{dbimage: 'postgres:11', dbauth: 'trust'}
8454
]
85-
container: ${{ matrix.swiftver }}
8655
runs-on: ubuntu-latest
56+
container: ${{ matrix.swiftver }}
8757
services:
8858
psql-a:
8959
image: ${{ matrix.dbimage }}
@@ -105,11 +75,23 @@ jobs:
10575
- name: Check out package
10676
uses: actions/checkout@v3
10777
- name: Run local tests
108-
run: swift test
78+
run: swift test --enable-code-coverage
79+
- name: Note Swift version
80+
if: ${{ contains(matrix.swiftver, 'nightly') }}
81+
run: |
82+
echo "SWIFT_PLATFORM=$(. /etc/os-release && echo "${ID}${VERSION_ID}")" >>"${GITHUB_ENV}"
83+
echo "SWIFT_VERSION=$(cat /.swift_tag)" >>"${GITHUB_ENV}"
84+
- name: Upload code coverage
85+
uses: vapor/swift-codecov-action@v0.2
86+
env:
87+
POSTGRES_VERSION: ${{ matrix.dbimage }}
88+
POSTGRES_AUTH_METHOD: ${{ matrix.dbauth }}
89+
with:
90+
cc_env_vars: 'SWIFT_VERSION,SWIFT_PLATFORM,RUNNER_OS,RUNNER_ARCH,POSTGRES_VERSION,POSTGRES_AUTH_METHOD'
10991

11092
# Test integration with dependent package on Linux
11193
linux-integration:
112-
if: github.event_name == 'pull_request'
94+
if: ${{ !(github.event.pull_request.draft || false) }}
11395
strategy:
11496
fail-fast: false
11597
matrix:
@@ -120,8 +102,8 @@ jobs:
120102
'swiftlang/swift:nightly-5.9-jammy',
121103
'swiftlang/swift:nightly-main-jammy'
122104
]
123-
container: ${{ matrix.swiftver }}
124105
runs-on: ubuntu-latest
106+
container: ${{ matrix.swiftver }}
125107
services:
126108
psql-a:
127109
image: ${{ matrix.dbimage }}
@@ -153,7 +135,7 @@ jobs:
153135

154136
# Run macOS unit tests against various configurations
155137
macos-unit:
156-
if: github.event_name == 'pull_request'
138+
if: ${{ !(github.event.pull_request.draft || false) }}
157139
strategy:
158140
fail-fast: false
159141
matrix:
@@ -165,20 +147,29 @@ jobs:
165147
env:
166148
POSTGRES_HOSTNAME: 127.0.0.1
167149
POSTGRES_DB: postgres
168-
POSTGRES_HOST_AUTH_METHOD: ${{ matrix.dbauth }}
169150
steps:
170151
- name: Select latest available Xcode
171152
uses: maxim-lobanov/setup-xcode@v1
172153
with:
173154
xcode-version: ${{ matrix.xcode }}
174155
- name: Install Postgres, setup DB and auth, and wait for server start
156+
env:
157+
POSTGRES_VERSION: ${{ matrix.dbimage }}
158+
POSTGRES_AUTH_METHOD: ${{ matrix.dbauth }}
175159
run: |
176-
export PATH="$(brew --prefix)/opt/${{ matrix.formula }}/bin:$PATH" PGDATA=/tmp/vapor-postgres-test
177-
(brew unlink postgresql || true) && brew install ${{ matrix.dbimage }} && brew link --force ${{ matrix.dbimage }}
178-
initdb --locale=C --auth-host ${{ matrix.dbauth }} -U $POSTGRES_USER --pwfile=<(echo $POSTGRES_PASSWORD)
160+
export PATH="$(brew --prefix)/opt/${POSTGRES_VERSION}/bin:$PATH" PGDATA=/tmp/vapor-postgres-test
161+
(brew unlink postgresql || true) && brew install "${POSTGRES_VERSION}" && brew link --force "${POSTGRES_VERSION}"
162+
initdb --locale=C --auth-host "${POSTGRES_AUTH_METHOD}" -U "${POSTGRES_USER}" --pwfile=<(echo "${POSTGRES_PASSWORD}")
179163
pg_ctl start --wait
180164
timeout-minutes: 2
181165
- name: Checkout code
182166
uses: actions/checkout@v3
183167
- name: Run local tests
184-
run: swift test
168+
run: swift test --enable-code-coverage
169+
- name: Upload code coverage
170+
uses: vapor/swift-codecov-action@v0.2
171+
env:
172+
POSTGRES_VERSION: ${{ matrix.dbimage }}
173+
POSTGRES_AUTH_METHOD: ${{ matrix.dbauth }}
174+
with:
175+
cc_env_vars: 'MD_APPLE_SDK_ROOT,SWIFT_VERSION,SWIFT_PLATFORM,RUNNER_OS,RUNNER_ARCH,POSTGRES_VERSION,POSTGRES_AUTH_METHOD'

‎Sources/PostgresKit/Deprecations/PostgresColumnType.swift‎

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
import AsyncKit
21
import SQLKit
32

43
/// Postgres-specific column types.
@@ -250,7 +249,7 @@ public struct PostgresColumnType: SQLExpression, Hashable {
250249
case custom(String) /// User-defined type
251250
indirect case array(of: Primitive) /// Array
252251

253-
/// See ``Swift/CustomStringConvertible/description``.
252+
/// See `CustomStringConvertible.description`.
254253
var description: String {
255254
switch self {
256255
case .bigint: return "BIGINT"
@@ -301,7 +300,7 @@ public struct PostgresColumnType: SQLExpression, Hashable {
301300
}
302301
}
303302

304-
/// See ``SQLExpression/serialize(to:)``.
303+
// See `SQLExpression.serialize(to:)`.
305304
public func serialize(to serializer: inout SQLSerializer) {
306305
serializer.write(self.primitive.description)
307306
}

‎Sources/PostgresKit/Deprecations/PostgresConnectionSource+PostgresConfiguration.swift‎

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
import NIOSSL
22
import Atomics
3-
import AsyncKit
43
import Logging
54
import PostgresNIO
65
import NIOCore
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
# ``PostgresKit``
2+
3+
@Metadata {
4+
@TitleHeading(Package)
5+
}
6+
7+
PostgresKit is a library providing an SQLKit driver for PostgresNIO.
8+
9+
## Overview
10+
11+
This package provides the "foundational" level of support for using [Fluent] with PostgreSQL by implementing the requirements of an [SQLKit] driver. It is responsible for:
12+
13+
- Managing the underlying PostgreSQL library ([PostgresNIO]),
14+
- Providing a two-way bridge between PostgresNIO and SQLKit's generic data and metadata formats,
15+
- Presenting an interface for establishing, managing, and interacting with database connections.
16+
17+
> Note: The FluentKit driver for PostgreSQL is provided by the [FluentPostgresDriver] package.
18+
19+
## Version Support
20+
21+
This package uses [PostgresNIO] for all underlying database interactions. It is compatible with all versions of PostgreSQL and all platforms supported by that package.
22+
23+
> Important: There is one exception to the above at the time of this writing: This package requires Swift 5.7 or newer, whereas PostgresNIO continues to support Swift 5.6.
24+
25+
[SQLKit]: https://swiftpackageindex.com/vapor/sql-kit
26+
[PostgresNIO]: https://swiftpackageindex.com/vapor/postgres-nio
27+
[Fluent]: https://swiftpackageindex.com/vapor/fluent-kit
28+
[FluentPostgresDriver]: https://swiftpackageindex.com/vapor/fluent-postgres-driver

‎Sources/PostgresKit/Docs.docc/index.md‎

Lines changed: 0 additions & 3 deletions
This file was deleted.

‎Sources/PostgresKit/SQLPostgresConfiguration.swift‎

Lines changed: 80 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ public struct SQLPostgresConfiguration {
99
/// `UInt16(getservbyname("postgresql", "tcp").pointee.s_port).byteSwapped`
1010
public static var ianaPortNumber: Int { 5432 }
1111

12-
/// See ``PostgresNIO/PostgresConnection/Configuration``.
12+
// See `PostgresNIO.PostgresConnection.Configuration`.
1313
public var coreConfiguration: PostgresConnection.Configuration
1414

1515
/// Optional `search_path` to set on new connections.
@@ -27,40 +27,93 @@ public struct SQLPostgresConfiguration {
2727

2828
/// Create a ``SQLPostgresConfiguration`` from a properly formatted URL.
2929
///
30-
/// The allowed URL format is:
30+
/// The supported URL formats are:
3131
///
32-
/// postgres://username:password@hostname:port/database?tls=mode
32+
/// postgres://username:password@hostname:port/database?tlsmode=mode
33+
/// postgres+tcp://username:password@hostname:port/database?tlsmode=mode
34+
/// postgres+uds://username:password@localhost/path?tlsmode=mode#database
3335
///
34-
/// `hostname` and `username` are required; all other components are optional. For backwards
35-
/// compatibility, `ssl` is treated as an alias of `tls`.
36+
/// The `postgres+tcp` scheme requests a connection over TCP. The `postgres` scheme is an alias
37+
/// for `postgres+tcp`. Only the `hostname` and `username` components are required.
3638
///
37-
/// The allowed `mode` values for `tls` are:
38-
/// - `require` (fail to connect if the server does not support TLS)
39-
/// - `true` (attempt to use TLS but continue anyway if the server doesn't support it)
40-
/// - `false` (do not use TLS even if the server supports it).
41-
/// If `tls` is omitted entirely, the mode defaults to `true`.
39+
/// The `postgres+uds` scheme requests a connection via a UNIX domain socket. The `username` and
40+
/// `path` components are required. The authority must always be empty or `localhost`, and may not
41+
/// specify a port.
42+
///
43+
/// The allowed `mode` values for `tlsmode` are:
44+
///
45+
/// Value|Behavior
46+
/// -|-
47+
/// `disable`|Don't use TLS, even if the server supports it.
48+
/// `prefer`|Use TLS if possible.
49+
/// `require`|Enforce TLS support.
50+
///
51+
/// If no `tlsmode` is specified, the default mode is `prefer` for TCP connections, or `disable`
52+
/// for UDS connections. If more than one mode is specified, the last one wins. Whenever a TLS
53+
/// connection is made, full certificate verification (both chain of trust and hostname match)
54+
/// is always enforced, regardless of the mode used.
55+
///
56+
/// For compatibility with `libpq` and previous versions of this package, any of "`sslmode`",
57+
/// "`tls`", or "`ssl`" may be used instead of "`tlsmode`". There are also various aliases for
58+
/// each of the TLS mode names, as follows:
59+
///
60+
/// - "`disable`": "`false`"
61+
/// - "`prefer`": "`allow`", "`true`"
62+
/// - "`require`": "`verify-ca`", "`verify-full`"
63+
///
64+
/// The aliases always have the same semantics as the "canonical" modes, despite any differences
65+
/// suggested by their names.
66+
///
67+
/// > Note: It is possible to emulate `libpq`'s definitions for `prefer` (TLS if available with
68+
/// > no certificate verification), `require` (TLS enforced, but also without certificate
69+
/// > verification) and `verify-ca` (TLS enforced with no hostname verification) by manually
70+
/// > specifying the TLS configuration instead of using a URL. It is not possible, by design, to
71+
/// > emulate `libpq`'s `allow` mode (TLS only if there is no alternative). It is _strongly_
72+
/// > recommended for both security and privacy reasons to always leave full certificate
73+
/// > verification enabled whenever possible. See NIOSSL's [`TLSConfiguration`](tlsconfig) for
74+
/// > additional information and recommendations.
75+
///
76+
/// [tlsconfig]:
77+
/// https://swiftpackageindex.com/apple/swift-nio-ssl/main/documentation/niossl/tlsconfiguration
4278
public init(url: URL) throws {
43-
guard let comp = URLComponents(url: url, resolvingAgainstBaseURL: true),
44-
comp.scheme?.hasPrefix("postgres") ?? false,
45-
let hostname = comp.host, let username = comp.user
46-
else {
79+
guard let comp = URLComponents(url: url, resolvingAgainstBaseURL: true), let username = comp.user else {
4780
throw URLError(.badURL, userInfo: [NSURLErrorFailingURLErrorKey: url, NSURLErrorFailingURLStringErrorKey: url.absoluteString])
4881
}
49-
let password = comp.password, port = comp.port ?? Self.ianaPortNumber
50-
let tls: PostgresConnection.Configuration.TLS
51-
switch (comp.queryItems ?? []).first(where: { ["ssl", "tls"].contains(0ドル.name.lowercased()) })?.value ?? "true" {
52-
case "require": tls = try .require(.init(configuration: .makeClientConfiguration()))
53-
case "true": tls = try .prefer(.init(configuration: .makeClientConfiguration()))
54-
case "false": tls = .disable
55-
default: throw URLError(.badURL, userInfo: [NSURLErrorFailingURLErrorKey: url, NSURLErrorFailingURLStringErrorKey: url.absoluteString])
82+
83+
func decideTLSConfig(from queryItems: [URLQueryItem], defaultMode: String) throws -> PostgresConnection.Configuration.TLS {
84+
switch queryItems.last(where: { ["tlsmode", "sslmode", "ssl", "tls"].contains(0ドル.name.lowercased()) })?.value ?? defaultMode {
85+
case "verify-full", "verify-ca", "require":
86+
return try .require(.init(configuration: .makeClientConfiguration()))
87+
case "prefer", "allow", "true":
88+
return try .prefer(.init(configuration: .makeClientConfiguration()))
89+
case "disable", "false":
90+
return .disable
91+
default:
92+
throw URLError(.badURL, userInfo: [NSURLErrorFailingURLErrorKey: url, NSURLErrorFailingURLStringErrorKey: url.absoluteString])
93+
}
5694
}
5795

58-
self.init(
59-
hostname: hostname, port: port,
60-
username: username, password: password,
61-
database: url.lastPathComponent,
62-
tls: tls
63-
)
96+
switch comp.scheme {
97+
case "postgres", "postgres+tcp":
98+
guard let hostname = comp.host, !hostname.isEmpty else {
99+
throw URLError(.badURL, userInfo: [NSURLErrorFailingURLErrorKey: url, NSURLErrorFailingURLStringErrorKey: url.absoluteString])
100+
}
101+
self.init(
102+
hostname: hostname, port: comp.port ?? Self.ianaPortNumber,
103+
username: username, password: comp.password,
104+
database: url.lastPathComponent.isEmpty ? nil : url.lastPathComponent,
105+
tls: try decideTLSConfig(from: comp.queryItems ?? [], defaultMode: "prefer")
106+
)
107+
case "postgres+uds":
108+
guard (comp.host?.isEmpty ?? true || comp.host == "localhost"), comp.port == nil, !comp.path.isEmpty, comp.path != "/" else {
109+
throw URLError(.badURL, userInfo: [NSURLErrorFailingURLErrorKey: url, NSURLErrorFailingURLStringErrorKey: url.absoluteString])
110+
}
111+
var coreConfig = PostgresConnection.Configuration(unixSocketPath: comp.path, username: username, password: comp.password, database: comp.fragment)
112+
coreConfig.tls = try decideTLSConfig(from: comp.queryItems ?? [], defaultMode: "disable")
113+
self.init(coreConfiguration: coreConfig)
114+
default:
115+
throw URLError(.badURL, userInfo: [NSURLErrorFailingURLErrorKey: url, NSURLErrorFailingURLStringErrorKey: url.absoluteString])
116+
}
64117
}
65118

66119
/// Create a ``SQLPostgresConfiguration`` for connecting to a server with a hostname and optional port.

‎Tests/PostgresKitTests/PostgresKitTests.swift‎

Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -261,12 +261,3 @@ extension Bar: PostgresNonThrowingEncodable, PostgresArrayEncodable, PostgresDec
261261
static var psqlFormat: PostgresFormat { .binary }
262262
static var psqlArrayType: PostgresDataType { .int8Array }
263263
}
264-
265-
let isLoggingConfigured: Bool = {
266-
LoggingSystem.bootstrap { label in
267-
var handler = StreamLogHandler.standardOutput(label: label)
268-
handler.logLevel = env("LOG_LEVEL").flatMap { Logger.Level(rawValue: 0ドル) } ?? .info
269-
return handler
270-
}
271-
return true
272-
}()

0 commit comments

Comments
(0)

AltStyle によって変換されたページ (->オリジナル) /