Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 8 additions & 2 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -337,7 +337,10 @@ jobs:
- uses: actions/checkout@v4

- name: setup utils
run: brew tap wix/brew && brew install applesimutils
run: |
brew tap wix/brew
brew trust --formula wix/brew/applesimutils
brew install applesimutils
env:
HOMEBREW_NO_AUTO_UPDATE: 1
HOMEBREW_NO_INSTALL_CLEANUP: 1
Expand Down Expand Up @@ -463,7 +466,10 @@ jobs:
- uses: actions/checkout@v4

- name: setup utils
run: brew tap wix/brew && brew install applesimutils
run: |
brew tap wix/brew
brew trust --formula wix/brew/applesimutils
brew install applesimutils
env:
HOMEBREW_NO_AUTO_UPDATE: 1
HOMEBREW_NO_INSTALL_CLEANUP: 1
Expand Down
7 changes: 7 additions & 0 deletions src/drivers/connection-tls.ts
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,13 @@ export class SQLiteCloudTlsConnection extends SQLiteCloudConnection {
if (this.config.verbose) {
console.debug(`SQLiteCloudTlsConnection - connected to ${this.config.host}, authorized: ${this.socket?.authorized}`)
}
if (!initializationCommands) {
if (this.config.verbose) {
console.debug(`SQLiteCloudTlsConnection - initialized connection`)
}
callback?.call(this, null)
return
}
this.transportCommands(initializationCommands, error => {
if (this.config.verbose) {
console.debug(`SQLiteCloudTlsConnection - initialized connection`)
Expand Down
7 changes: 5 additions & 2 deletions src/drivers/connection-ws.ts
Original file line number Diff line number Diff line change
Expand Up @@ -99,8 +99,11 @@ export class SQLiteCloudWebsocketConnection extends SQLiteCloudConnection {
// (→ crvheg7dhk.g4.gateway.sqlite.cloud). For local development, pass a `gatewayurl`
// containing `localhost` — the driver routes TCP to it and injects the tenant Host header
// (with the gateway marker label) so the gateway still tenant-routes correctly.
const authToken = this.config.apikey || this.config.token
const ioOpts: Record<string, unknown> = { auth: { token: authToken }, parser: createSocketIOParser(websocketMaxAttachments) }
const authToken = this.config.unauthenticated ? undefined : this.config.apikey || this.config.token
const ioOpts: Record<string, unknown> = { parser: createSocketIOParser(websocketMaxAttachments) }
if (authToken) {
ioOpts.auth = { token: authToken }
}
let gatewayUrl: string
if (this.config.gatewayurl?.includes('localhost')) {
const raw = this.config.gatewayurl
Expand Down
2 changes: 2 additions & 0 deletions src/drivers/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,8 @@ export interface SQLiteCloudConfig {
apikey?: string
/** Access Token provided in place of API Key or username/password */
token?: string
/** Connect without sending AUTH. Only commands explicitly allowed pre-auth can run. */
unauthenticated?: boolean

/** Host name is required unless connectionstring is provided, eg: xxx.sqlitecloud.io */
host?: string
Expand Down
22 changes: 13 additions & 9 deletions src/drivers/utilities.ts
Original file line number Diff line number Diff line change
Expand Up @@ -61,12 +61,14 @@ export function getInitializationCommands(config: SQLiteCloudConfig): string {
let commands = 'SET CLIENT KEY NONLINEARIZABLE TO 1;'

// first user authentication, then all other commands
if (config.apikey) {
commands += `AUTH APIKEY ${config.apikey};`
} else if (config.token) {
commands += `AUTH TOKEN ${config.token};`
} else {
commands += `AUTH USER ${config.username || ''} ${config.password_hashed ? 'HASH' : 'PASSWORD'} ${config.password || ''};`
if (!config.unauthenticated) {
if (config.apikey) {
commands += `AUTH APIKEY ${config.apikey};`
} else if (config.token) {
commands += `AUTH TOKEN ${config.token};`
} else {
commands += `AUTH USER ${config.username || ''} ${config.password_hashed ? 'HASH' : 'PASSWORD'} ${config.password || ''};`
}
}

if (config.compression) {
Expand Down Expand Up @@ -94,7 +96,7 @@ export function getInitializationCommands(config: SQLiteCloudConfig): string {
commands += 'SET CLIENT KEY NONLINEARIZABLE TO 0;'
}

if (config.database) {
if (!config.unauthenticated && config.database) {
if (config.create && !config.memory) {
commands += `CREATE DATABASE ${config.database} IF NOT EXISTS;`
}
Expand Down Expand Up @@ -198,14 +200,15 @@ export function validateConfiguration(config: SQLiteCloudConfig): SQLiteCloudCon
config.create = parseBoolean(config.create)
config.non_linearizable = parseBoolean(config.non_linearizable)
config.insecure = parseBoolean(config.insecure)
config.unauthenticated = parseBoolean(config.unauthenticated)

const hasCredentials = (config.username && config.password) || config.apikey || config.token
if (!config.host || !hasCredentials) {
if (!config.host || (!config.unauthenticated && !hasCredentials)) {
console.error('SQLiteCloudConnection.validateConfiguration - missing arguments', config)
throw new SQLiteCloudError('The user, password and host arguments, the ?apikey= or the ?token= must be specified.', { errorCode: 'ERR_MISSING_ARGS' })
}

if (!config.connectionstring) {
if (!config.connectionstring && !config.unauthenticated) {
// build connection string from configuration, values are already validated
config.connectionstring = `sqlitecloud://${config.host}:${config.port}/${config.database || ''}`
if (config.apikey) {
Expand Down Expand Up @@ -258,6 +261,7 @@ export function parseconnectionstring(connectionstring: string): SQLiteCloudConf
memory: options.memory ? parseBoolean(options.memory) : undefined,
compression: options.compression ? parseBoolean(options.compression) : undefined,
non_linearizable: options.non_linearizable ? parseBoolean(options.non_linearizable) : undefined,
unauthenticated: options.unauthenticated ? parseBoolean(options.unauthenticated) : undefined,
noblob: options.noblob ? parseBoolean(options.noblob) : undefined,
maxdata: options.maxdata ? parseInt(options.maxdata) : undefined,
maxrows: options.maxrows ? parseInt(options.maxrows) : undefined,
Expand Down
57 changes: 57 additions & 0 deletions test/utilities.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -263,6 +263,26 @@ describe('validateConfiguration()', () => {
expect(config.websocketBlobFormat).toBe('base64-blobs-v1')
expect(config.websocketMaxAttachments).toBe(100000)
})

it('should allow unauthenticated configuration without credentials', () => {
const config = validateConfiguration({
host: 'host',
unauthenticated: true
})

expect(config.host).toBe('host')
expect(config.unauthenticated).toBe(true)
expect(config.connectionstring).toBeUndefined()
})

it('should parse unauthenticated from connection string params', () => {
const config = validateConfiguration({
connectionstring: 'sqlitecloud://host:1234?unauthenticated=1'
})

expect(config.host).toBe('host')
expect(config.unauthenticated).toBe(true)
})
})

describe('safe integer marker utilities', () => {
Expand Down Expand Up @@ -326,4 +346,41 @@ describe('getInitializationCommands()', () => {
expect(result).toContain('AUTH TOKEN mytoken;')
expect(result).not.toContain('AUTH APIKEY')
})

it('should keep existing authenticated initialization behavior', () => {
const config = {
username: 'admin',
password: 'secret',
database: 'mydb',
compression: true,
non_linearizable: true
}

const result = getInitializationCommands(config)

expect(result).toBe('SET CLIENT KEY NONLINEARIZABLE TO 1;AUTH USER admin PASSWORD secret;SET CLIENT KEY COMPRESSION TO 1;USE DATABASE mydb;')
})

it('should omit auth and database commands for unauthenticated initialization', () => {
const config = {
host: 'host',
database: 'mydb',
unauthenticated: true,
compression: true,
non_linearizable: true,
noblob: true,
maxdata: 128,
maxrows: 256,
maxrowset: 512
}

const result = getInitializationCommands(config)

expect(result).toBe(
'SET CLIENT KEY NONLINEARIZABLE TO 1;SET CLIENT KEY COMPRESSION TO 1;SET CLIENT KEY NOBLOB TO 1;SET CLIENT KEY MAXDATA TO 128;SET CLIENT KEY MAXROWS TO 256;SET CLIENT KEY MAXROWSET TO 512;'
)
expect(result).not.toContain('AUTH ')
expect(result).not.toContain('USE DATABASE')
expect(result).not.toContain('CREATE DATABASE')
})
})
Loading