Skip to content

Commit

Permalink
fix(uri-parsing): handle password with bracket in connection url (#9466)
Browse files Browse the repository at this point in the history
  • Loading branch information
grieve54706 authored Jul 1, 2024
1 parent b35582e commit c73bcf0
Show file tree
Hide file tree
Showing 10 changed files with 46 additions and 16 deletions.
4 changes: 2 additions & 2 deletions ibis/backends/clickhouse/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
from contextlib import closing
from functools import partial
from typing import TYPE_CHECKING, Any, Literal
from urllib.parse import parse_qs, urlparse
from urllib.parse import parse_qs, unquote_plus, urlparse

import clickhouse_connect as cc
import pyarrow as pa
Expand Down Expand Up @@ -82,7 +82,7 @@ def _from_url(self, url: str, **kwargs) -> BaseBackend:

connect_args = {
"user": url.username,
"password": url.password or "",
"password": unquote_plus(url.password or ""),
"host": url.hostname,
"database": database or "",
}
Expand Down
14 changes: 14 additions & 0 deletions ibis/backends/clickhouse/tests/test_client.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
from __future__ import annotations

import os
from urllib.parse import quote_plus

import pandas as pd
import pandas.testing as tm
Expand Down Expand Up @@ -346,3 +347,16 @@ def test_create_table_no_syntax_error(con):
)
t = con.create_table(gen_name("clickouse_temp_table"), schema=schema, temp=True)
assert t.count().execute() == 0


def test_password_with_bracket():
password = f'{os.environ.get("IBIS_TEST_CLICKHOUSE_PASSWORD", "")}['
quoted_pass = quote_plus(password)
with pytest.raises(cc.driver.exceptions.DatabaseError) as e:
ibis.clickhouse.connect(
host=os.environ.get("IBIS_TEST_CLICKHOUSE_HOST", "localhost"),
user=os.environ.get("IBIS_TEST_CLICKHOUSE_USER", "default"),
port=int(os.environ.get("IBIS_TEST_CLICKHOUSE_PORT", 8123)),
password=quoted_pass,
)
assert "password is incorrect" in str(e.value)
6 changes: 4 additions & 2 deletions ibis/backends/druid/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
import contextlib
import json
from typing import TYPE_CHECKING, Any
from urllib.parse import parse_qs, urlparse
from urllib.parse import parse_qs, unquote_plus, urlparse

import pydruid.db
import sqlglot as sg
Expand Down Expand Up @@ -61,7 +61,9 @@ def _from_url(self, url: str, **kwargs):
query_params = parse_qs(url.query)
kwargs = {
"user": url.username,
"password": url.password,
"password": unquote_plus(url.password)
if url.password is not None
else None,
"host": url.hostname,
"path": url.path,
"port": url.port,
Expand Down
6 changes: 4 additions & 2 deletions ibis/backends/exasol/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
import datetime
import re
from typing import TYPE_CHECKING, Any
from urllib.parse import parse_qs, urlparse
from urllib.parse import parse_qs, unquote_plus, urlparse

import pyexasol
import sqlglot as sg
Expand Down Expand Up @@ -105,7 +105,9 @@ def _from_url(self, url: str, **kwargs) -> BaseBackend:
query_params = parse_qs(url.query)
kwargs = {
"user": url.username,
"password": url.password,
"password": unquote_plus(url.password)
if url.password is not None
else None,
"schema": url.path[1:] or None,
"host": url.hostname,
"port": url.port,
Expand Down
4 changes: 2 additions & 2 deletions ibis/backends/mysql/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
from functools import cached_property
from operator import itemgetter
from typing import TYPE_CHECKING, Any
from urllib.parse import parse_qs, urlparse
from urllib.parse import parse_qs, unquote_plus, urlparse

import numpy as np
import pymysql
Expand Down Expand Up @@ -62,7 +62,7 @@ def _from_url(self, url: str, **kwargs):
query_params = parse_qs(url.query)
connect_args = {
"user": url.username,
"password": url.password or "",
"password": unquote_plus(url.password or ""),
"host": url.hostname,
"database": database or "",
"port": url.port or None,
Expand Down
4 changes: 2 additions & 2 deletions ibis/backends/oracle/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
from functools import cached_property
from operator import itemgetter
from typing import TYPE_CHECKING, Any
from urllib.parse import urlparse
from urllib.parse import unquote_plus, urlparse

import numpy as np
import oracledb
Expand Down Expand Up @@ -164,7 +164,7 @@ def _from_url(self, url: str, **kwargs):
url = urlparse(url)
self.do_connect(
user=url.username,
password=url.password,
password=unquote_plus(url.password) if url.password is not None else None,
database=url.path.removeprefix("/"),
)

Expand Down
4 changes: 2 additions & 2 deletions ibis/backends/postgres/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
from itertools import takewhile
from operator import itemgetter
from typing import TYPE_CHECKING, Any
from urllib.parse import parse_qs, urlparse
from urllib.parse import parse_qs, unquote_plus, urlparse

import numpy as np
import pandas as pd
Expand Down Expand Up @@ -71,7 +71,7 @@ def _from_url(self, url: str, **kwargs):
query_params = parse_qs(url.query)
connect_args = {
"user": url.username,
"password": url.password or "",
"password": unquote_plus(url.password or ""),
"host": url.hostname,
"database": database or "",
"schema": schema[0] if schema else "",
Expand Down
12 changes: 12 additions & 0 deletions ibis/backends/postgres/tests/test_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
from __future__ import annotations

import os
from urllib.parse import quote_plus

import hypothesis as h
import hypothesis.strategies as st
Expand Down Expand Up @@ -378,3 +379,14 @@ def test_infoschema_dtypes(con):
con.table("triggers", database="information_schema").select("created").schema()
== triggers_created_schema
)


def test_password_with_bracket():
password = f"{IBIS_POSTGRES_PASS}["
quoted_pass = quote_plus(password)
url = f"postgres://{IBIS_POSTGRES_USER}:{quoted_pass}@{IBIS_POSTGRES_HOST}:{IBIS_POSTGRES_PORT}/{POSTGRES_TEST_DB}"
with pytest.raises(PsycoPg2OperationalError) as e:
ibis.connect(url)
assert f'password authentication failed for user "{IBIS_POSTGRES_USER}"' in str(
e.value
)
4 changes: 2 additions & 2 deletions ibis/backends/snowflake/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
from operator import itemgetter
from pathlib import Path
from typing import TYPE_CHECKING, Any
from urllib.parse import parse_qs, urlparse
from urllib.parse import parse_qs, unquote_plus, urlparse
from urllib.request import urlretrieve

import pyarrow as pa
Expand Down Expand Up @@ -116,7 +116,7 @@ def _from_url(self, url: str, **kwargs):
(warehouse,) = query_params.pop("warehouse", (None,))
connect_args = {
"user": url.username,
"password": url.password or "",
"password": unquote_plus(url.password or ""),
"account": url.hostname,
"warehouse": warehouse,
"database": database or "",
Expand Down
4 changes: 2 additions & 2 deletions ibis/backends/trino/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
from functools import cached_property
from operator import itemgetter
from typing import TYPE_CHECKING, Any
from urllib.parse import urlparse
from urllib.parse import unquote_plus, urlparse

import sqlglot as sg
import sqlglot.expressions as sge
Expand Down Expand Up @@ -44,7 +44,7 @@ def _from_url(self, url: str, **kwargs):
catalog, db = url.path.strip("/").split("/")
self.do_connect(
user=url.username or None,
auth=url.password or None,
auth=unquote_plus(url.password) if url.password is not None else None,
host=url.hostname or None,
port=url.port or None,
database=catalog,
Expand Down

0 comments on commit c73bcf0

Please sign in to comment.