Skip to content

Commit

Permalink
Support SSL for Postgres/MySQL and accept a custom CA certificates fi…
Browse files Browse the repository at this point in the history
…le (#73)
  • Loading branch information
jonatanklosko authored Jun 12, 2024
1 parent 42d8b4f commit 8b2cdc4
Show file tree
Hide file tree
Showing 5 changed files with 95 additions and 23 deletions.
29 changes: 28 additions & 1 deletion lib/assets/connection_cell/main.js
Original file line number Diff line number Diff line change
Expand Up @@ -381,12 +381,28 @@ export function init(ctx, info) {
:grow
:required
/>
<BaseSwitch
name="use_ssl"
label="SSL"
v-model="fields.use_ssl"
/>
<BaseSwitch
name="use_ipv6"
label="IPv6"
v-model="fields.use_ipv6"
/>
</div>
<div class="row" v-if="fields.use_ssl">
<BaseInput
name="cacertfile"
label="CA certificates file"
type="text"
v-model="fields.cacertfile"
inputClass="input input--xs"
placeholder="By default, uses certificates supplied by the OS"
:grow
/>
</div>
<div class="row">
<BaseInput
name="database"
Expand Down Expand Up @@ -481,6 +497,17 @@ export function init(ctx, info) {
v-model="fields.use_ipv6"
/>
</div>
<div class="row" v-if="fields.use_ssl">
<BaseInput
name="cacertfile"
label="CA certificates file"
type="text"
v-model="fields.cacertfile"
inputClass="input input--xs"
placeholder="By default uses certificates supplied by the OS"
:grow
/>
</div>
<div class="row">
<BaseInput
name="database"
Expand Down Expand Up @@ -870,7 +897,7 @@ export function init(ctx, info) {
},

isSQLServer() {
return this.fields.type === "sqlserver"
return this.fields.type === "sqlserver";
},

isDefaultDatabase() {
Expand Down
63 changes: 46 additions & 17 deletions lib/kino_db/connection_cell.ex
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ defmodule KinoDB.ConnectionCell do
"port" => attrs["port"] || default_port,
"use_ipv6" => Map.get(attrs, "use_ipv6", false),
"use_ssl" => Map.get(attrs, "use_ssl", false),
"cacertfile" => attrs["cacertfile"] || "",
"username" => attrs["username"] || "",
"password" => password,
"use_password_secret" => Map.has_key?(attrs, "password_secret") || password == "",
Expand Down Expand Up @@ -143,13 +144,15 @@ defmodule KinoDB.ConnectionCell do

"sqlserver" ->
if fields["use_password_secret"],
do: ~w|database hostname port use_ipv6 username password_secret use_ssl instance|,
else: ~w|database hostname port use_ipv6 username password use_ssl instance|
do:
~w|database hostname port use_ipv6 username password_secret use_ssl cacertfile instance|,
else:
~w|database hostname port use_ipv6 username password use_ssl cacertfile instance|

type when type in ["postgres", "mysql"] ->
if fields["use_password_secret"],
do: ~w|database hostname port use_ipv6 use_ssl username password_secret|,
else: ~w|database hostname port use_ipv6 use_ssl username password|
do: ~w|database hostname port use_ipv6 use_ssl cacertfile username password_secret|,
else: ~w|database hostname port use_ipv6 use_ssl cacertfile username password|
end

Map.take(fields, @default_keys ++ connection_keys)
Expand Down Expand Up @@ -251,15 +254,15 @@ defmodule KinoDB.ConnectionCell do

defp to_quoted(%{"type" => "postgres"} = attrs) do
quote do
opts = unquote(shared_options(attrs))
opts = unquote(shared_options(attrs) ++ postgres_and_mysql_options(attrs))

{:ok, unquote(quoted_var(attrs["variable"]))} = Kino.start_child({Postgrex, opts})
end
end

defp to_quoted(%{"type" => "mysql"} = attrs) do
quote do
opts = unquote(shared_options(attrs))
opts = unquote(shared_options(attrs) ++ postgres_and_mysql_options(attrs))

{:ok, unquote(quoted_var(attrs["variable"]))} = Kino.start_child({MyXQL, opts})
end
Expand Down Expand Up @@ -362,27 +365,53 @@ defmodule KinoDB.ConnectionCell do
database: attrs["database"]
]

opts =
if attrs["use_ssl"] do
opts ++ [ssl: attrs["use_ssl"]]
else
opts
end

if attrs["use_ipv6"] do
opts ++ [socket_options: [:inet6]]
else
opts
end
end

defp postgres_and_mysql_options(attrs) do
if attrs["use_ssl"] do
cacertfile = attrs["cacertfile"]

ssl_opts =
if cacertfile && cacertfile != "" do
[cacertfile: cacertfile]
else
[cacerts: quote(do: :public_key.cacerts_get())]
end

[ssl: ssl_opts]
else
[]
end
end

defp sqlserver_options(attrs) do
opts =
if attrs["use_ssl"] do
cacertfile = attrs["cacertfile"]

ssl_opts =
if cacertfile && cacertfile != "" do
[cacertfile: cacertfile]
else
[cacerts: quote(do: :public_key.cacerts_get())]
end

[ssl: true, ssl_opts: ssl_opts]
else
[]
end

instance = attrs["instance"]

if instance && instance != "" do
[instance: instance]
opts ++ [instance: instance]
else
[]
opts
end
end

Expand Down Expand Up @@ -413,13 +442,13 @@ defmodule KinoDB.ConnectionCell do

defp missing_dep(%{"type" => "postgres"}) do
unless Code.ensure_loaded?(Postgrex) do
~s/{:postgrex, "~> 0.17"}/
~s/{:postgrex, "~> 0.18"}/
end
end

defp missing_dep(%{"type" => "mysql"}) do
unless Code.ensure_loaded?(MyXQL) do
~s/{:myxql, "~> 0.6"}/
~s/{:myxql, "~> 0.7"}/
end
end

Expand Down
4 changes: 2 additions & 2 deletions mix.exs
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,8 @@ defmodule KinoDB.MixProject do
{:adbc, "~> 0.2", optional: true},
{:db_connection, "~> 2.4.2 or ~> 2.5", optional: true},
{:exqlite, "~> 0.11", optional: true},
{:myxql, "~> 0.6.2 or ~> 0.7", optional: true},
{:postgrex, "~> 0.17.3 or ~> 0.18 or ~> 1.0", optional: true},
{:myxql, "~> 0.7", optional: true},
{:postgrex, "~> 0.18 or ~> 1.0", optional: true},
{:tds, "~> 2.3.4 or ~> 2.4", optional: true},

# Those dependecies are new, so we use stricter versions
Expand Down
4 changes: 2 additions & 2 deletions mix.lock
Original file line number Diff line number Diff line change
Expand Up @@ -23,11 +23,11 @@
"makeup_erlang": {:hex, :makeup_erlang, "0.1.5", "e0ff5a7c708dda34311f7522a8758e23bfcd7d8d8068dc312b5eb41c6fd76eba", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}], "hexpm", "94d2e986428585a21516d7d7149781480013c56e30c6a233534bedf38867a59a"},
"mime": {:hex, :mime, "2.0.5", "dc34c8efd439abe6ae0343edbb8556f4d63f178594894720607772a041b04b02", [:mix], [], "hexpm", "da0d64a365c45bc9935cc5c8a7fc5e49a0e0f9932a761c55d6c52b142780a05c"},
"mint": {:hex, :mint, "1.5.1", "8db5239e56738552d85af398798c80648db0e90f343c8469f6c6d8898944fb6f", [:mix], [{:castore, "~> 0.1.0 or ~> 1.0", [hex: :castore, repo: "hexpm", optional: true]}, {:hpax, "~> 0.1.1", [hex: :hpax, repo: "hexpm", optional: false]}], "hexpm", "4a63e1e76a7c3956abd2c72f370a0d0aecddc3976dea5c27eccbecfa5e7d5b1e"},
"myxql": {:hex, :myxql, "0.6.3", "3d77683a09f1227abb8b73d66b275262235c5cae68182f0cfa5897d72a03700e", [:mix], [{:db_connection, "~> 2.4.1 or ~> 2.5", [hex: :db_connection, repo: "hexpm", optional: false]}, {:decimal, "~> 1.6 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: false]}, {:geo, "~> 3.4", [hex: :geo, repo: "hexpm", optional: true]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:table, "~> 0.1.0", [hex: :table, repo: "hexpm", optional: true]}], "hexpm", "af9eb517ddaced5c5c28e8749015493757fd4413f2cfccea449c466d405d9f51"},
"myxql": {:hex, :myxql, "0.7.0", "3382f139b0b0da977a8fc33c8cded125e20df2e400f8d7b7e674fa62a7e077dd", [:mix], [{:db_connection, "~> 2.4.1 or ~> 2.5", [hex: :db_connection, repo: "hexpm", optional: false]}, {:decimal, "~> 1.6 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: false]}, {:geo, "~> 3.4", [hex: :geo, repo: "hexpm", optional: true]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:table, "~> 0.1.0", [hex: :table, repo: "hexpm", optional: true]}], "hexpm", "40e4b7ad4973c8b895e86a3de04ff7a79c2cf72b9f2bddef7717afb4ab36d8c0"},
"nimble_options": {:hex, :nimble_options, "1.0.2", "92098a74df0072ff37d0c12ace58574d26880e522c22801437151a159392270e", [:mix], [], "hexpm", "fd12a8db2021036ce12a309f26f564ec367373265b53e25403f0ee697380f1b8"},
"nimble_parsec": {:hex, :nimble_parsec, "1.4.0", "51f9b613ea62cfa97b25ccc2c1b4216e81df970acd8e16e8d1bdc58fef21370d", [:mix], [], "hexpm", "9c565862810fb383e9838c1dd2d7d2c437b3d13b267414ba6af33e50d2d1cf28"},
"nimble_pool": {:hex, :nimble_pool, "1.0.0", "5eb82705d138f4dd4423f69ceb19ac667b3b492ae570c9f5c900bb3d2f50a847", [:mix], [], "hexpm", "80be3b882d2d351882256087078e1b1952a28bf98d0a287be87e4a24a710b67a"},
"postgrex": {:hex, :postgrex, "0.17.3", "c92cda8de2033a7585dae8c61b1d420a1a1322421df84da9a82a6764580c503d", [:mix], [{:db_connection, "~> 2.1", [hex: :db_connection, repo: "hexpm", optional: false]}, {:decimal, "~> 1.5 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:table, "~> 0.1.0", [hex: :table, repo: "hexpm", optional: true]}], "hexpm", "946cf46935a4fdca7a81448be76ba3503cff082df42c6ec1ff16a4bdfbfb098d"},
"postgrex": {:hex, :postgrex, "0.18.0", "f34664101eaca11ff24481ed4c378492fed2ff416cd9b06c399e90f321867d7e", [:mix], [{:db_connection, "~> 2.1", [hex: :db_connection, repo: "hexpm", optional: false]}, {:decimal, "~> 1.5 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:table, "~> 0.1.0", [hex: :table, repo: "hexpm", optional: true]}], "hexpm", "a042989ba1bc1cca7383ebb9e461398e3f89f868c92ce6671feb7ef132a252d1"},
"req": {:hex, :req, "0.3.11", "462315e50db6c6e1f61c45e8c0b267b0d22b6bd1f28444c136908dfdca8d515a", [:mix], [{:brotli, "~> 0.3.1", [hex: :brotli, repo: "hexpm", optional: true]}, {:ezstd, "~> 1.0", [hex: :ezstd, repo: "hexpm", optional: true]}, {:finch, "~> 0.9", [hex: :finch, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}, {:mime, "~> 1.6 or ~> 2.0", [hex: :mime, repo: "hexpm", optional: false]}, {:nimble_csv, "~> 1.0", [hex: :nimble_csv, repo: "hexpm", optional: true]}, {:plug, "~> 1.0", [hex: :plug, repo: "hexpm", optional: true]}], "hexpm", "0e4b331627fedcf90b29aa8064cd5a95619ef6134d5ab13919b6e1c4d7cccd4b"},
"req_athena": {:hex, :req_athena, "0.1.4", "8e9e125c265e19982af415d4f2e11ee4bfd411ce6887835675082ebe6fa2cf58", [:mix], [{:aws_signature, "~> 0.3.0", [hex: :aws_signature, repo: "hexpm", optional: false]}, {:req, "~> 0.3.5", [hex: :req, repo: "hexpm", optional: false]}, {:table, "~> 0.1.1", [hex: :table, repo: "hexpm", optional: true]}], "hexpm", "ba241610a30c9c82db0eb9be87ae5e795d60bbed251ff70c4836e9964827ea5f"},
"req_bigquery": {:hex, :req_bigquery, "0.1.2", "46272169724867561335bd54052daf2e43e5673ece3630a1f5ec42aeb5d98257", [:mix], [{:decimal, "~> 2.0", [hex: :decimal, repo: "hexpm", optional: false]}, {:goth, "~> 1.3.0", [hex: :goth, repo: "hexpm", optional: false]}, {:req, "~> 0.3.5", [hex: :req, repo: "hexpm", optional: false]}, {:table, "~> 0.1.1", [hex: :table, repo: "hexpm", optional: true]}], "hexpm", "b1dbac8d5cecaaf7e85217119c035aededcde8f7bc0f64d324b16e15e326c8ea"},
Expand Down
18 changes: 17 additions & 1 deletion test/kino_db/connection_cell_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ defmodule KinoDB.ConnectionCellTest do
"port" => 4444,
"use_ipv6" => false,
"use_ssl" => false,
"cacertfile" => "",
"username" => "admin",
"password" => "pass",
"use_password_secret" => false,
Expand Down Expand Up @@ -97,7 +98,22 @@ defmodule KinoDB.ConnectionCellTest do
username: "admin",
password: "pass",
database: "default",
ssl: true
ssl: [cacerts: :public_key.cacerts_get()]
]
{:ok, db} = Kino.start_child({Postgrex, opts})\
'''

attrs = Map.merge(@attrs, %{"use_ssl" => true, "cacertfile" => "/path/to/cacertfile"})

assert ConnectionCell.to_source(attrs) === ~s'''
opts = [
hostname: "localhost",
port: 4444,
username: "admin",
password: "pass",
database: "default",
ssl: [cacertfile: "/path/to/cacertfile"]
]
{:ok, db} = Kino.start_child({Postgrex, opts})\
Expand Down

0 comments on commit 8b2cdc4

Please sign in to comment.