Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Raise when sweet_xml dependency is missing while using role based authentication #36

Open
wants to merge 7 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
273 changes: 134 additions & 139 deletions lib/ex_aws/sts/parsers.ex
Original file line number Diff line number Diff line change
@@ -1,142 +1,137 @@
if Code.ensure_loaded?(SweetXml) do
defmodule ExAws.STS.Parsers do
import SweetXml, only: [sigil_x: 2]

def parse({:ok, %{body: xml} = resp}, :assume_role) do
parsed_body =
xml
|> SweetXml.xpath(~x"//AssumeRoleResponse",
access_key_id: ~x"./AssumeRoleResult/Credentials/AccessKeyId/text()"s,
secret_access_key: ~x"./AssumeRoleResult/Credentials/SecretAccessKey/text()"s,
session_token: ~x"./AssumeRoleResult/Credentials/SessionToken/text()"s,
expiration: ~x"./AssumeRoleResult/Credentials/Expiration/text()"s,
assumed_role_id: ~x"./AssumeRoleResult/AssumedRoleUser/AssumedRoleId/text()"s,
assumed_role_arn: ~x"./AssumeRoleResult/AssumedRoleUser/Arn/text()"s,
request_id: request_id_xpath()
)

{:ok, Map.put(resp, :body, parsed_body)}
end

def parse({:ok, %{body: xml} = resp}, :assume_role_with_web_identity) do
parsed_body =
xml
|> SweetXml.xpath(~x"//AssumeRoleWithWebIdentityResponse",
access_key_id: ~x"./AssumeRoleWithWebIdentityResult/Credentials/AccessKeyId/text()"s,
secret_access_key:
~x"./AssumeRoleWithWebIdentityResult/Credentials/SecretAccessKey/text()"s,
session_token: ~x"./AssumeRoleWithWebIdentityResult/Credentials/SessionToken/text()"s,
expiration: ~x"./AssumeRoleWithWebIdentityResult/Credentials/Expiration/text()"s,
assumed_role_id:
~x"./AssumeRoleWithWebIdentityResult/AssumedRoleUser/AssumedRoleId/text()"s,
assumed_role_arn: ~x"./AssumeRoleWithWebIdentityResult/AssumedRoleUser/Arn/text()"s,
request_id: request_id_xpath()
)

{:ok, Map.put(resp, :body, parsed_body)}
end

def parse({:ok, %{body: xml} = resp}, :get_access_key_info) do
parsed_body =
SweetXml.xpath(xml, ~x"//GetAccessKeyInfoResponse",
account: ~x"./GetAccessKeyInfoResult/Account/text()"s,
request_id: request_id_xpath()
)

{:ok, Map.put(resp, :body, parsed_body)}
end

def parse({:ok, %{body: xml} = resp}, :assume_role_with_s_a_m_l) do
parsed_body =
xml
|> SweetXml.xpath(~x"//AssumeRoleWithSAMLResponse",
access_key_id: ~x"./AssumeRoleResult/Credentials/AccessKeyId/text()"s,
secret_access_key: ~x"./AssumeRoleResult/Credentials/SecretAccessKey/text()"s,
session_token: ~x"./AssumeRoleResult/Credentials/SessionToken/text()"s,
expiration: ~x"./AssumeRoleResult/Credentials/Expiration/text()"s,
assumed_role_id: ~x"./AssumeRoleResult/AssumedRoleUser/AssumedRoleId/text()"s,
assumed_role_arn: ~x"./AssumeRoleResult/AssumedRoleUser/Arn/text()"s,
request_id: request_id_xpath()
)

{:ok, Map.put(resp, :body, parsed_body)}
end

def parse({:ok, %{body: xml} = resp}, :get_caller_identity) do
parsed_body =
SweetXml.xpath(xml, ~x"//GetCallerIdentityResponse",
arn: ~x"./GetCallerIdentityResult/Arn/text()"s,
user_id: ~x"./GetCallerIdentityResult/UserId/text()"s,
account: ~x"./GetCallerIdentityResult/Account/text()"s,
request_id: request_id_xpath()
)

{:ok, Map.put(resp, :body, parsed_body)}
end

def parse({:ok, %{body: xml} = resp}, :get_federation_token) do
parsed_body =
xml
|> SweetXml.xpath(~x"//GetFederationTokenResponse",
access_key_id: ~x"./GetFederationTokenResult/Credentials/AccessKeyId/text()"s,
secret_access_key: ~x"./GetFederationTokenResult/Credentials/SecretAccessKey/text()"s,
session_token: ~x"./GetFederationTokenResult/Credentials/SessionToken/text()"s,
expiration: ~x"./GetFederationTokenResult/Credentials/Expiration/text()"s,
request_id: request_id_xpath()
)

{:ok, Map.put(resp, :body, parsed_body)}
end

def parse({:ok, %{body: xml} = resp}, :get_session_token) do
parsed_body =
xml
|> SweetXml.xpath(~x"//GetSessionTokenResponse",
access_key_id: ~x"./GetSessionTokenResult/Credentials/AccessKeyId/text()"s,
secret_access_key: ~x"./GetSessionTokenResult/Credentials/SecretAccessKey/text()"s,
session_token: ~x"./GetSessionTokenResult/Credentials/SessionToken/text()"s,
expiration: ~x"./GetSessionTokenResult/Credentials/Expiration/text()"s,
request_id: request_id_xpath()
)

{:ok, Map.put(resp, :body, parsed_body)}
end

def parse({:error, {type, http_status_code, %{body: xml}}}, _) do
parsed_body =
xml
|> SweetXml.xpath(~x"//ErrorResponse",
request_id: ~x"./RequestId/text()"s,
type: ~x"./Error/Type/text()"s,
code: ~x"./Error/Code/text()"s,
message: ~x"./Error/Message/text()"s,
detail: ~x"./Error/Detail/text()"s
)

{:error, {type, http_status_code, parsed_body}}
end

def parse(val, _), do: val

def parse({:ok, %{body: xml} = resp}, :decode_authorization_message, config) do
parsed_body =
xml
|> SweetXml.xpath(~x"//DecodeAuthorizationMessageResponse",
decoded_message: ~x"./DecodedMessage/text()"s,
request_id: ~x"./RequestId/text()"s
)
|> Map.update!(:decoded_message, fn msg -> config[:json_codec].decode!(msg) end)

{:ok, Map.put(resp, :body, parsed_body)}
end

defp request_id_xpath do
~x"./ResponseMetadata/RequestId/text()"s
end
defmodule ExAws.STS.Parsers do
alias ExAws.STS.Parsers.XML

import XML, only: [sigil_x: 2]

def parse({:ok, %{body: xml} = resp}, :assume_role) do
parsed_body =
xml
|> XML.xpath(~x"//AssumeRoleResponse",
access_key_id: ~x"./AssumeRoleResult/Credentials/AccessKeyId/text()"s,
secret_access_key: ~x"./AssumeRoleResult/Credentials/SecretAccessKey/text()"s,
session_token: ~x"./AssumeRoleResult/Credentials/SessionToken/text()"s,
expiration: ~x"./AssumeRoleResult/Credentials/Expiration/text()"s,
assumed_role_id: ~x"./AssumeRoleResult/AssumedRoleUser/AssumedRoleId/text()"s,
assumed_role_arn: ~x"./AssumeRoleResult/AssumedRoleUser/Arn/text()"s,
request_id: request_id_xpath()
)

{:ok, Map.put(resp, :body, parsed_body)}
end
else
defmodule ExAws.STS.Parsers do
def parse(val, _), do: val
def parse(val, _, _), do: val

def parse({:ok, %{body: xml} = resp}, :assume_role_with_web_identity) do
parsed_body =
xml
|> XML.xpath(~x"//AssumeRoleWithWebIdentityResponse",
access_key_id: ~x"./AssumeRoleWithWebIdentityResult/Credentials/AccessKeyId/text()"s,
secret_access_key:
~x"./AssumeRoleWithWebIdentityResult/Credentials/SecretAccessKey/text()"s,
session_token: ~x"./AssumeRoleWithWebIdentityResult/Credentials/SessionToken/text()"s,
expiration: ~x"./AssumeRoleWithWebIdentityResult/Credentials/Expiration/text()"s,
assumed_role_id:
~x"./AssumeRoleWithWebIdentityResult/AssumedRoleUser/AssumedRoleId/text()"s,
assumed_role_arn: ~x"./AssumeRoleWithWebIdentityResult/AssumedRoleUser/Arn/text()"s,
request_id: request_id_xpath()
)

{:ok, Map.put(resp, :body, parsed_body)}
end

def parse({:ok, %{body: xml} = resp}, :get_access_key_info) do
parsed_body =
XML.xpath(xml, ~x"//GetAccessKeyInfoResponse",
account: ~x"./GetAccessKeyInfoResult/Account/text()"s,
request_id: request_id_xpath()
)

{:ok, Map.put(resp, :body, parsed_body)}
end

def parse({:ok, %{body: xml} = resp}, :assume_role_with_s_a_m_l) do
parsed_body =
xml
|> XML.xpath(~x"//AssumeRoleWithSAMLResponse",
access_key_id: ~x"./AssumeRoleResult/Credentials/AccessKeyId/text()"s,
secret_access_key: ~x"./AssumeRoleResult/Credentials/SecretAccessKey/text()"s,
session_token: ~x"./AssumeRoleResult/Credentials/SessionToken/text()"s,
expiration: ~x"./AssumeRoleResult/Credentials/Expiration/text()"s,
assumed_role_id: ~x"./AssumeRoleResult/AssumedRoleUser/AssumedRoleId/text()"s,
assumed_role_arn: ~x"./AssumeRoleResult/AssumedRoleUser/Arn/text()"s,
request_id: request_id_xpath()
)

{:ok, Map.put(resp, :body, parsed_body)}
end

def parse({:ok, %{body: xml} = resp}, :get_caller_identity) do
parsed_body =
XML.xpath(xml, ~x"//GetCallerIdentityResponse",
arn: ~x"./GetCallerIdentityResult/Arn/text()"s,
user_id: ~x"./GetCallerIdentityResult/UserId/text()"s,
account: ~x"./GetCallerIdentityResult/Account/text()"s,
request_id: request_id_xpath()
)

{:ok, Map.put(resp, :body, parsed_body)}
end

def parse({:ok, %{body: xml} = resp}, :get_federation_token) do
parsed_body =
xml
|> XML.xpath(~x"//GetFederationTokenResponse",
access_key_id: ~x"./GetFederationTokenResult/Credentials/AccessKeyId/text()"s,
secret_access_key: ~x"./GetFederationTokenResult/Credentials/SecretAccessKey/text()"s,
session_token: ~x"./GetFederationTokenResult/Credentials/SessionToken/text()"s,
expiration: ~x"./GetFederationTokenResult/Credentials/Expiration/text()"s,
request_id: request_id_xpath()
)

{:ok, Map.put(resp, :body, parsed_body)}
end

def parse({:ok, %{body: xml} = resp}, :get_session_token) do
parsed_body =
xml
|> XML.xpath(~x"//GetSessionTokenResponse",
access_key_id: ~x"./GetSessionTokenResult/Credentials/AccessKeyId/text()"s,
secret_access_key: ~x"./GetSessionTokenResult/Credentials/SecretAccessKey/text()"s,
session_token: ~x"./GetSessionTokenResult/Credentials/SessionToken/text()"s,
expiration: ~x"./GetSessionTokenResult/Credentials/Expiration/text()"s,
request_id: request_id_xpath()
)

{:ok, Map.put(resp, :body, parsed_body)}
end

def parse({:error, {type, http_status_code, %{body: xml}}}, _) do
parsed_body =
xml
|> XML.xpath(~x"//ErrorResponse",
request_id: ~x"./RequestId/text()"s,
type: ~x"./Error/Type/text()"s,
code: ~x"./Error/Code/text()"s,
message: ~x"./Error/Message/text()"s,
detail: ~x"./Error/Detail/text()"s
)

{:error, {type, http_status_code, parsed_body}}
end

def parse(val, _), do: val

def parse({:ok, %{body: xml} = resp}, :decode_authorization_message, config) do
parsed_body =
xml
|> XML.xpath(~x"//DecodeAuthorizationMessageResponse",
decoded_message: ~x"./DecodedMessage/text()"s,
request_id: ~x"./RequestId/text()"s
)
|> Map.update!(:decoded_message, fn msg -> config[:json_codec].decode!(msg) end)

{:ok, Map.put(resp, :body, parsed_body)}
end

defp request_id_xpath do
~x"./ResponseMetadata/RequestId/text()"s
end
end
19 changes: 19 additions & 0 deletions lib/ex_aws/sts/parsers/xml.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
defmodule ExAws.STS.Parsers.XML do
@moduledoc false

def xpath(parent, spec, subspec) do
xml_module().xpath(parent, spec, subspec)
rescue
_ in UndefinedFunctionError ->
raise "Dependency sweet_xml is required for role based authentication"
end

def sigil_x(path, modifiers) do
xml_module().sigil_x(path, modifiers)
rescue
_ in UndefinedFunctionError ->
raise "Dependency sweet_xml is required for role based authentication"
end

defp xml_module, do: Application.get_env(:ex_aws_sts, :xml_module, SweetXml)
end
20 changes: 19 additions & 1 deletion test/lib/parsers_test.exs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
defmodule ExAws.STS.ParsersTest do
use ExUnit.Case, async: true
use ExUnit.Case, async: false

alias ExAws.STS.Parsers

Expand Down Expand Up @@ -40,4 +40,22 @@ defmodule ExAws.STS.ParsersTest do
end
end
end

describe "when sweet_xml is not installed" do
setup do
Application.put_env(:ex_aws_sts, :xml_module, MissingSweetXml)

on_exit(fn ->
Application.delete_env(:ex_aws_sts, :xml_module)
end)
end

for {action, arity} <- @actions do
test "raises missing sweet_xml error for `:#{action}`" do
assert_raise RuntimeError,
"Dependency sweet_xml is required for role based authentication",
fn -> parse_mock_response(unquote(action), unquote(arity)) end
end
end
end
end