Skip to content

Commit

Permalink
Merge pull request #992 from shinnokdisengir/feature/keys
Browse files Browse the repository at this point in the history
`astarte-dev-tool` Added generating private/public keys feature
  • Loading branch information
Annopaolo authored Sep 27, 2024
2 parents b3c7d10 + 3da8e75 commit 6e7b334
Show file tree
Hide file tree
Showing 6 changed files with 163 additions and 3 deletions.
26 changes: 26 additions & 0 deletions tools/astarte_dev_tool/lib/commands/auth/keys.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
#
# This file is part of Astarte.
#
# Copyright 2024 SECO Mind Srl
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#

defmodule AstarteDevTool.Commands.Auth.Keys do
@moduledoc false
alias AstarteDevTool.Utilities.Auth

def exec(), do: Auth.new_ec_private_key()
def exec(nil), do: exec()
def exec(priv), do: Auth.public_key_from(priv)
end
70 changes: 70 additions & 0 deletions tools/astarte_dev_tool/lib/mix/tasks/astarte_dev_tool/auth/keys.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
#
# This file is part of Astarte.
#
# Copyright 2024 SECO Mind Srl
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#

defmodule Mix.Tasks.AstarteDevTool.Auth.Keys do
use Mix.Task
alias AstarteDevTool.Commands.Auth.Keys

@shortdoc "Astarte keys generation"

@aliases []

@switches [
log_level: :string
]

@moduledoc """
Astarte auth key generation.
If no `arg` is provided, a private key is released
If a private key is provided via `arg`, the linked public key is released.
## Examples
$ mix astarte_dev_tool.auth.key
$ mix astarte_dev_tool.auth.key "-----BEGIN EC PRIVATE KEY-----Base64-----END EC PRIVATE KEY-----"
$ mix astarte_dev_tool.auth.key "$(mix astarte_dev_tool.auth.key)"
## Command line options
* `--log-level` - the level to set for `Logger`. This task
does not start your application, so whatever level you have configured in
your config files will not be used. If this is not provided, no level
will be set, so that if you set it yourself before calling this task
then this won't interfere. Can be any of the `t:Logger.level/0` levels
"""

@impl true
def run(args) do
{opts, args} = OptionParser.parse!(args, strict: @switches, aliases: @aliases)

if Enum.count(args) > 1,
do: Mix.raise("Only one optional argument - the private key - is allowed")

if log_level = opts[:log_level],
do: Logger.configure(level: String.to_existing_atom(log_level))

case Keys.exec(Enum.at(args, 0)) do
{:ok, key} ->
# Result to stdout
IO.puts(key)
{:ok, key}

{:error, reason} ->
Mix.raise("Error generating key: #{reason}")
end
end
end
21 changes: 21 additions & 0 deletions tools/astarte_dev_tool/lib/utilities/auth.ex
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,29 @@

defmodule AstarteDevTool.Utilities.Auth do
alias Astarte.Client.Credentials
alias X509.PrivateKey
alias X509.PublicKey

def gen_auth_token(private_key) when is_bitstring(private_key) do
Credentials.dashboard_credentials() |> Credentials.to_jwt(private_key)
end

def new_ec_private_key(), do: {:ok, PrivateKey.new_ec(:secp256r1) |> PrivateKey.to_pem()}

def public_key_from(priv) do
case PrivateKey.from_pem(priv) do
{:ok, result} -> {:ok, result |> PublicKey.derive() |> PublicKey.to_pem()}
error -> error
end
end

# Useful feature for when `astarte_dev_tool` is a stateful application.
# It will not be used directly by `mix astarte_dev_tool.auth.keys`, as it would not
# make sense to write a key pair to `stdout`
def pem_keys() do
with {:ok, priv} <- new_ec_private_key(),
{:ok, pub} <- public_key_from(priv) do
{:ok, {priv, pub}}
end
end
end
4 changes: 4 additions & 0 deletions tools/astarte_dev_tool/lib/utilities/process.ex
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,10 @@ defmodule AstarteDevTool.Utilities.Process do
end

{:ok, result}
else
:error ->
{:error, :version_badformatted}
end
end

defp clean_version(version), do: {:ok, version |> String.trim() |> String.trim("'")}
Expand Down
1 change: 1 addition & 0 deletions tools/astarte_dev_tool/mix.exs
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ defmodule AstarteDevTool.MixProject do
# Run "mix help deps" to learn about dependencies.
defp deps do
[
{:x509, "~> 0.8"},
{:astarte_client, github: "astarte-platform/astarte-client-elixir"}
]
end
Expand Down
44 changes: 41 additions & 3 deletions tools/astarte_dev_tool/test/astarte_dev_tool_test.exs
Original file line number Diff line number Diff line change
@@ -1,8 +1,46 @@
#
# This file is part of Astarte.
#
# Copyright 2024 SECO Mind Srl
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#

defmodule AstarteDevToolTest do
use ExUnit.Case
# doctest false

test "greets the world" do
assert AstarteDevTool.hello() == :world
@doctest false
alias AstarteDevTool.Utilities.Auth

describe "unit test" do
test "new_ec_private_key/0" do
assert {:ok, private} = Auth.new_ec_private_key()
end

test "public_key_from/1" do
{:ok, private} = Auth.new_ec_private_key()
assert {:ok, public} = Auth.public_key_from(private)
end

test "pem_keys/0" do
assert {:ok, {private, public}} = Auth.pem_keys()
end
end

describe "mix tasks" do
test "auth.keys" do
{:ok, private} = Mix.Tasks.AstarteDevTool.Auth.Keys.run([])
{:ok, public} = Mix.Tasks.AstarteDevTool.Auth.Keys.run(["--", private])
end
end
end

0 comments on commit 6e7b334

Please sign in to comment.