Skip to content

Commit 26ef67b

Browse files
committed
fix(apex): one saved password for both spellings of a connection
The account name a password is filed under in the credential store is derived from the connect string, and SQLcl reaches one database through either host:port/service or the JDBC URL for it. Saving from the CLI with one spelling and reading from Settings with the other therefore looked like no password had ever been saved, and the workbench asked for it again -- found while re-running the APEX verification, where apex_render_check.py could not see a password the CLI had stored. account_key() now drops a leading jdbc:oracle:<driver>:@ (and the // of the URL form) before folding the rest, so both spellings name one account. It folds towards the plain form on purpose: every password already in a store stays findable, with no migration and nothing to retype.
1 parent cebcb43 commit 26ef67b

3 files changed

Lines changed: 36 additions & 1 deletion

File tree

CHANGELOG.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
4545

4646
### Fixed
4747

48+
- A saved APEX password is found whether the connection is written as
49+
`host:port/service` or as the JDBC URL for it. The account name in the
50+
credential store is derived from the connection, so the two spellings of
51+
one database used to hide each other's password and the workbench asked
52+
for it again.
4853
- The Diff report labels added and removed program units with their kind,
4954
as modified ones already were, so a package specification and its body
5055
removed together no longer appear as two identical names.

formslang/apeximport.py

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,14 @@
5050

5151
_UNSAFE_ACCOUNT_CHARS = re.compile(r"[^A-Za-z0-9_.:-]")
5252

53+
#: SQLcl reaches one database through either ``host:port/service`` or the
54+
#: JDBC URL for it, and Settings saves whichever was typed. They name the
55+
#: same target, so a password saved under one has to be found under the
56+
#: other: the prefix -- and the ``//`` of the URL form -- comes off before
57+
#: the account name is built. Folding towards the plain form keeps every
58+
#: password already in the store findable.
59+
_JDBC_PREFIX = re.compile(r"^jdbc:oracle:[A-Za-z]+:@(?://)?", re.IGNORECASE)
60+
5361
#: ``apex import``/``apex validate`` print this header (followed by File /
5462
#: Line / Column / Type / Error lines) when the APEXlang package does not
5563
#: compile -- and SQLcl still exits 0, so the exit code alone is not the
@@ -103,8 +111,12 @@ def account_key(username: str, connect_string: str) -> str:
103111
neither of which that validator allows, so unsafe characters are folded to
104112
``_`` -- this only has to be stable and collision-free enough to find the
105113
same saved password again next time, not human-typeable.
114+
115+
A JDBC URL and the plain target inside it are the same database, so both
116+
produce the same account name (see :data:`_JDBC_PREFIX`).
106117
"""
107-
return _UNSAFE_ACCOUNT_CHARS.sub("_", f"{username}@{connect_string}")
118+
target = _JDBC_PREFIX.sub("", str(connect_string or "").strip())
119+
return _UNSAFE_ACCOUNT_CHARS.sub("_", f"{username}@{target}")
108120

109121

110122
def _token(name: str, value: str) -> str:

tests/test_apeximport.py

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,24 @@ def test_account_key_folds_the_unsafe_characters_a_connect_string_carries():
2121
assert key == "FORMSLANG_localhost:1521_FREEPDB1"
2222

2323

24+
def test_the_jdbc_url_and_the_target_inside_it_are_one_saved_password():
25+
"""Settings may hold either form; the store holds one password."""
26+
plain = apeximport.account_key("FORMSLANG", "localhost:1521/FREEPDB1")
27+
for url in (
28+
"jdbc:oracle:thin:@localhost:1521/FREEPDB1",
29+
"jdbc:oracle:thin:@//localhost:1521/FREEPDB1",
30+
"JDBC:ORACLE:OCI:@localhost:1521/FREEPDB1",
31+
" jdbc:oracle:thin:@localhost:1521/FREEPDB1 ",
32+
):
33+
assert apeximport.account_key("FORMSLANG", url) == plain
34+
35+
36+
def test_a_host_that_merely_looks_like_a_prefix_is_still_the_host():
37+
"""Only the real JDBC prefix comes off -- nothing that resembles one."""
38+
key = apeximport.account_key("U", "jdbcserver:1521/S")
39+
assert key == "U_jdbcserver:1521_S"
40+
41+
2442
def test_sqlcl_binary_prefers_the_environment_override(monkeypatch):
2543
monkeypatch.setenv(apeximport.ENV_SQLCL_PATH, "C:/tools/sql.exe")
2644
assert apeximport.sqlcl_binary() == "C:/tools/sql.exe"

0 commit comments

Comments
 (0)