· 6 years ago · Jun 30, 2019, 07:26 PM
1defmodule MyAppWeb.GraphQL.Types.EncId do
2 use Absinthe.Schema.Notation
3
4 defp secret_key(len \ 32) do
5 Application.get_env(:my_app, MyAppWeb.Endpoint)
6 |> Keyword.get(:secret_key_base, "")
7 |> String.slice(0, len)
8 end
9
10 defp pad_bytes(binary, block \ 16) do
11 padding_bits =
12 case rem(byte_size(binary), block) do
13 0 -> 0
14 r -> (block - r) * 8
15 end
16
17 <<0::size(padding_bits)>> <> binary
18 end
19
20 defp unpad_bytes(<<0, tail::bitstring>>), do: unpad_bytes(tail)
21 defp unpad_bytes(binary), do: binary
22
23 defp encrypt(raw_binary) do
24 padded_binary = pad_bytes(raw_binary)
25
26 :crypto.crypto_one_time(:aes_256_ecb, secret_key(), padded_binary, true)
27 end
28
29 defp decrypt(raw_enc) do
30 :crypto.crypto_one_time(:aes_256_ecb, secret_key(), raw_enc, false)
31 |> unpad_bytes()
32 |> :erlang.binary_to_term()
33 end
34
35 def serialize(id) do
36 id
37 |> :erlang.term_to_binary()
38 |> encrypt()
39 |> Base.url_encode64(padding: false)
40 end
41
42 def parse(%{value: enc_id}) do
43 try do
44 {:ok, raw_enc} = Base.url_decode64(enc_id, padding: false)
45 {:ok, decrypt(raw_enc)}
46 rescue
47 _ -> :error
48 end
49 end
50
51 scalar :enc_id, name: "EncId" do
52 serialize(&__MODULE__.serialize/1)
53
54 parse(&__MODULE__.parse/1)
55 end
56end