From 3fd3f2c444c148776999a9a07fb9a973d1950668 Mon Sep 17 00:00:00 2001 From: Danylo Negriienko Date: Wed, 26 Jun 2019 00:04:37 +0300 Subject: [PATCH] Testing with JOSE (PS256 signing not working) --- config/config.exs.example | 6 ++ lib/yandex_translate.ex | 39 ++++++++++ lib/yandex_translate/token.ex | 15 ++++ mix.exs | 14 ++-- mix.lock | 4 +- test/yandex_translate_test.exs | 128 +++++++++++++++++++++++++++++++++ 6 files changed, 200 insertions(+), 6 deletions(-) create mode 100755 config/config.exs.example create mode 100644 lib/yandex_translate/token.ex create mode 100755 test/yandex_translate_test.exs diff --git a/config/config.exs.example b/config/config.exs.example new file mode 100755 index 0000000..012c0a0 --- /dev/null +++ b/config/config.exs.example @@ -0,0 +1,6 @@ +# use Mix.Config + +# config :yandex_translate, +# service_account_id: "SERVICE_ACCOUNT_ID" +# authorized_key_id: "AUTHORIZED_KEY_ID" +# private_key: "PRIVATE_KEY" \ No newline at end of file diff --git a/lib/yandex_translate.ex b/lib/yandex_translate.ex index 606aada..1d2410b 100755 --- a/lib/yandex_translate.ex +++ b/lib/yandex_translate.ex @@ -1,2 +1,41 @@ defmodule YandexTranslate do + @iam_token_url "https://iam.api.cloud.yandex.net/iam/v1/tokens" + + def get_key(type, key \\ Application.get_env(:yandex_translate, :private_key)) + + def get_key(:public, key), + do: get_key(:private, key) |> JOSE.JWK.to_public() + + def get_key(:private, key), + do: fetch_key(key) + + defp fetch_key("-----BEGIN" <> _ = key_content), do: JOSE.JWK.from_pem(key_content) + + defp fetch_key(key_file), do: JOSE.JWK.from_pem_file(key_file) + + def get_iam_token(), + do: Application.get_all_env(:yandex_translate) |> Map.new() |> get_iam_token() + + def get_iam_token(%{service_account_id: iss, private_key: private_key, authorized_key_id: kid}) do + now = DateTime.utc_now() |> DateTime.to_unix() + jwk = get_key(:private, private_key) |> JOSE.JWK.merge(%{"kid" => kid}) + + # JSON Web Signature (JWS) + jws = %{ + "alg" => "RS256", + "typ" => "JWT", + "kid" => kid + } + + # JSON Web Token (JWT) + jwt = + JOSE.JWT.from(%{ + "iss" => iss, + "exp" => now + 60 * 60, + "iat" => now, + "aud" => @iam_token_url + }) + + _signed = JOSE.JWT.sign(jwk, jws, jwt) |> JOSE.JWS.compact() |> elem(1) + end end diff --git a/lib/yandex_translate/token.ex b/lib/yandex_translate/token.ex new file mode 100644 index 0000000..01b9b5c --- /dev/null +++ b/lib/yandex_translate/token.ex @@ -0,0 +1,15 @@ +# defmodule YandexTranslate.Token do +# @iam_token_url "https://iam.api.cloud.yandex.net/iam/v1/tokens" + +# use Joken.Config + +# @impl Joken.Config +# def token_config() do +# default_claims( +# iss: Application.get_env(:yandex_translate, :service_account_id), +# default_exp: 60 * 60, +# skip: [:jti, :nbf], +# aud: @iam_token_url +# ) +# end +# end diff --git a/mix.exs b/mix.exs index 668cfbc..ec90484 100755 --- a/mix.exs +++ b/mix.exs @@ -20,17 +20,21 @@ defmodule YandexTranslate.Mixfile do end def application do - [ - extra_applications: [:logger] - ] + [extra_applications: applications(Mix.env())] end + defp applications(:dev), do: applications(:all) ++ [:remix] + defp applications(_all), do: [:logger, :jose] + defp deps do [ - {:joken, "~> 2.1.0"}, + # {:joken, "~> 2.1.0"}, + # {:jose, "~> 1.9.0"}, + {:jose, github: "potatosalad/erlang-jose", branch: "master"}, {:jason, "~> 1.1.2"}, {:mojito, "~> 0.3.0"}, - {:ex_doc, "~> 0.20.2", only: :dev} + {:ex_doc, "~> 0.20.2", only: :dev}, + {:remix, "~> 0.0.2", only: :dev} ] end diff --git a/mix.lock b/mix.lock index a92db2f..55d7f26 100644 --- a/mix.lock +++ b/mix.lock @@ -9,7 +9,7 @@ "idna": {:hex, :idna, "6.0.0", "689c46cbcdf3524c44d5f3dde8001f364cd7608a99556d8fbd8239a5798d4c10", [:rebar3], [{:unicode_util_compat, "0.4.1", [hex: :unicode_util_compat, repo: "hexpm", optional: false]}], "hexpm"}, "jason": {:hex, :jason, "1.1.2", "b03dedea67a99223a2eaf9f1264ce37154564de899fd3d8b9a21b1a6fd64afe7", [:mix], [{:decimal, "~> 1.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm"}, "joken": {:hex, :joken, "2.1.0", "bf21a73105d82649f617c5e59a7f8919aa47013d2519ebcc39d998d8d12adda9", [:mix], [{:jose, "~> 1.9", [hex: :jose, repo: "hexpm", optional: false]}], "hexpm"}, - "jose": {:hex, :jose, "1.9.0", "4167c5f6d06ffaebffd15cdb8da61a108445ef5e85ab8f5a7ad926fdf3ada154", [:mix, :rebar3], [{:base64url, "~> 0.0.1", [hex: :base64url, repo: "hexpm", optional: false]}], "hexpm"}, + "jose": {:git, "https://github.com/potatosalad/erlang-jose.git", "80551fc36abab81b6fac798e112a9c1a644b60f1", [branch: "master"]}, "makeup": {:hex, :makeup, "0.8.0", "9cf32aea71c7fe0a4b2e9246c2c4978f9070257e5c9ce6d4a28ec450a839b55f", [:mix], [{:nimble_parsec, "~> 0.5.0", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm"}, "makeup_elixir": {:hex, :makeup_elixir, "0.13.0", "be7a477997dcac2e48a9d695ec730b2d22418292675c75aa2d34ba0909dcdeda", [:mix], [{:makeup, "~> 0.8", [hex: :makeup, repo: "hexpm", optional: false]}], "hexpm"}, "metrics": {:hex, :metrics, "1.0.1", "25f094dea2cda98213cecc3aeff09e940299d950904393b2a29d191c346a8486", [:rebar3], [], "hexpm"}, @@ -17,9 +17,11 @@ "mint": {:hex, :mint, "0.2.1", "a2ec8729fcad5c8b6460e07dfa64b008b3d9697a9f4604cd5684a87b44677c99", [:mix], [{:castore, "~> 0.1.0", [hex: :castore, repo: "hexpm", optional: true]}], "hexpm"}, "mojito": {:hex, :mojito, "0.3.0", "806cd3c1832333a9ee784e7ea2799863fbe4de55ecb4623a8f4ef870c2844cc6", [:mix], [{:castore, "~> 0.1", [hex: :castore, repo: "hexpm", optional: false]}, {:mint, "~> 0.2.1", [hex: :mint, repo: "hexpm", optional: false]}, {:poolboy, "~> 1.5", [hex: :poolboy, repo: "hexpm", optional: false]}], "hexpm"}, "nimble_parsec": {:hex, :nimble_parsec, "0.5.0", "90e2eca3d0266e5c53f8fbe0079694740b9c91b6747f2b7e3c5d21966bba8300", [:mix], [], "hexpm"}, + "ojson": {:hex, :ojson, "1.0.0", "fd28614eadaec00a15cdb2f53f29d8717a812a508ddb80d202f2f2e2aaeabbcc", [:mix, :rebar3], [], "hexpm"}, "parse_trans": {:hex, :parse_trans, "3.3.0", "09765507a3c7590a784615cfd421d101aec25098d50b89d7aa1d66646bc571c1", [:rebar3], [], "hexpm"}, "poison": {:hex, :poison, "3.1.0", "d9eb636610e096f86f25d9a46f35a9facac35609a7591b3be3326e99a0484665", [:mix], [], "hexpm"}, "poolboy": {:hex, :poolboy, "1.5.2", "392b007a1693a64540cead79830443abf5762f5d30cf50bc95cb2c1aaafa006b", [:rebar3], [], "hexpm"}, + "remix": {:hex, :remix, "0.0.2", "f06115659d8ede8d725fae1708920ef73353a1b39efe6a232d2a38b1f2902109", [:mix], [], "hexpm"}, "ssl_verify_fun": {:hex, :ssl_verify_fun, "1.1.4", "f0eafff810d2041e93f915ef59899c923f4568f4585904d010387ed74988e77b", [:make, :mix, :rebar3], [], "hexpm"}, "unicode_util_compat": {:hex, :unicode_util_compat, "0.4.1", "d869e4c68901dd9531385bb0c8c40444ebf624e60b6962d95952775cac5e90cd", [:rebar3], [], "hexpm"}, } diff --git a/test/yandex_translate_test.exs b/test/yandex_translate_test.exs new file mode 100755 index 0000000..ee8a89b --- /dev/null +++ b/test/yandex_translate_test.exs @@ -0,0 +1,128 @@ +# defmodule YandexTranslatorTest do +# use ExUnit.Case + +# setup_all do +# {:ok, %{"iamToken" => iam_token}} = YandexTranslator.get_iam_token +# {:ok, iam_token: iam_token} +# end + +# describe "cloud api" do +# test "request for getting languages with invalid key" do +# {:error, %{"error_message" => error_message}} = YandexTranslator.langs([iam_token: "1"]) + +# assert error_message == "rpc error: code = Unauthenticated desc = invalid auth data" +# end + +# test "request for detecting language with invalid key" do +# {:error, %{"error_message" => error_message}} = YandexTranslator.detect([iam_token: "1", text: "Hello"]) + +# assert error_message == "rpc error: code = Unauthenticated desc = invalid auth data" +# end + +# test "request for translating text with invalid key" do +# {:error, %{"error_message" => error_message}} = YandexTranslator.translate([iam_token: "1", text: "Hello", lang: "ru"]) + +# assert error_message == "rpc error: code = Unauthenticated desc = invalid auth data" +# end + +# test "request for getting languages", state do +# {:ok, %{"languages" => languages}} = YandexTranslator.langs([iam_token: state[:iam_token]]) + +# assert is_list(languages) == true +# end + +# test "request for detecting language", state do +# {:ok, %{"language" => language}} = YandexTranslator.detect([iam_token: state[:iam_token], text: "Hello"]) + +# assert language == "en" +# end + +# test "request for translating text", state do +# {:ok, %{"translations" => [%{"text" => text}]}} = YandexTranslator.translate([iam_token: state[:iam_token], text: "Hello", target: "es"]) + +# assert text == "Saludar" +# end +# end + +# describe "old api, invalid requests" do +# test "request for getting languages with invalid key" do +# {:error, %{"code" => code, "message" => message}} = YandexTranslator.langs([key: "", format: "json"]) + +# assert code == 401 +# assert message == "API key is invalid" +# end + +# test "request for getting languages with invalid key, xml format" do +# {:error, body} = YandexTranslator.langs([key: ""]) + +# assert String.length(body) != 0 +# end + +# test "request for detecting language with invalid key" do +# {:error, %{"code" => code, "message" => message}} = YandexTranslator.detect([key: "", text: "Hello", format: "json"]) + +# assert code == 401 +# assert message == "API key is invalid" +# end + +# test "request for detecting language with invalid key, xml format" do +# {:error, body} = YandexTranslator.detect([key: "", text: "Hello"]) + +# assert String.length(body) != 0 +# end + +# test "request for translating text with invalid key" do +# {:error, %{"code" => code, "message" => message}} = YandexTranslator.translate([key: "", text: "Hello", lang: "ru", format: "json"]) + +# assert code == 401 +# assert message == "API key is invalid" +# end + +# test "request for translating text with invalid key, xml format" do +# {:error, body} = YandexTranslator.translate([key: "", text: "Hello", lang: "ru"]) + +# assert String.length(body) != 0 +# end +# end + +# describe "old api, valid requests" do +# test "request for getting languages" do +# {:ok, %{"dirs" => dirs}} = YandexTranslator.langs([format: "json"]) + +# assert is_list(dirs) == true +# end + +# test "request for getting languages, xml format" do +# {:ok, body} = YandexTranslator.langs + +# assert String.length(body) != 0 +# end + +# test "request for detecting language" do +# {:ok, %{"code" => code, "lang" => lang}} = YandexTranslator.detect([text: "Hello", format: "json"]) + +# assert code == 200 +# assert lang == "en" +# end + +# test "request for detecting language, xml format" do +# {:ok, body} = YandexTranslator.detect([text: "Hello"]) + +# assert String.length(body) != 0 +# end + +# test "request for translating text" do +# {:ok, %{"code" => code, "lang" => lang, "text" => text}} = YandexTranslator.translate([text: "Hello", lang: "ru", format: "json"]) + +# assert code == 200 +# assert lang == "en-ru" +# assert text == ["Привет"] +# end + +# test "request for translating text, xml format" do +# {:ok, body} = YandexTranslator.translate([text: "Hello", lang: "ru"]) + +# assert String.length(body) != 0 +# end +# end +# end