Mix.install([
{:req, "~> 0.5.8"}
])
defmodule XMLXPath do
def keyword_list_to_namespace(kwl) do
kwl
|> Enum.map(fn {k, v} -> {Atom.to_charlist(k), String.to_charlist(v)} end)
|> then(&[namespace: &1])
end
@doc """
Evaluates an XPath expression on an XML document.
"""
@spec xpath(String.t(), String.t(), Keyword.t()) :: term()
def xpath(document, xpath, namespaces) do
try do
{doc, ~c[]} =
document
|> String.to_charlist()
|> :xmerl_scan.string(namespace_conformant: true)
:xmerl_xpath.string(String.to_charlist(xpath), doc, keyword_list_to_namespace(namespaces))
rescue
e -> {:error, e}
end
end
require Record
for {name, fields} <- Record.extract_all(from_lib: "xmerl/include/xmerl.hrl") do
Record.defrecordp(name, fields)
end
@spec text([term()]) :: [String.t()]
def text(list) do
list
|> Enum.map(fn xmlText(value: value) ->
List.to_string(value)
end)
end
end
defmodule Entra.Discovery do
def get_all_domains(domain) do
request_body_xml = fn domain ->
"""
<?xml version="1.0" encoding="utf-8"?>
<soap:Envelope
xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:a="http://www.w3.org/2005/08/addressing"
xmlns="http://schemas.microsoft.com/exchange/2010/Autodiscover">
<soap:Header>
<a:Action soap:mustUnderstand="1">
http://schemas.microsoft.com/exchange/2010/Autodiscover/Autodiscover/GetFederationInformation
</a:Action>
<a:To soap:mustUnderstand="1">
https://autodiscover-s.outlook.com/autodiscover/autodiscover.svc
</a:To>
<a:ReplyTo>
<a:Address>
http://www.w3.org/2005/08/addressing/anonymous
</a:Address>
</a:ReplyTo>
</soap:Header>
<soap:Body>
<GetFederationInformationRequestMessage>
<Request>
<Domain>#{domain}</Domain>
</Request>
</GetFederationInformationRequestMessage>
</soap:Body>
</soap:Envelope>
"""
end
%Req.Response{
status: 200,
body: xml_response
} = Req.new(
method: :post,
headers: [
{ "Content-Type", "text/xml; charset=utf-8"},
{ "SOAPAction", ~s{"http://schemas.microsoft.com/exchange/2010/Autodiscover/Autodiscover/GetFederationInformation"}}
],
url: "https://autodiscover-s.outlook.com/autodiscover/autodiscover.svc",
body: request_body_xml.(domain)
)
|> Req.request!()
[error] =
xml_response
|> XMLXPath.xpath("//discover:ErrorCode/text()",
discover: "http://schemas.microsoft.com/exchange/2010/Autodiscover"
)
|> XMLXPath.text()
error
|> case do
"NotFederated" ->
{:error, :not_federated}
"InternalServerError" ->
{:error, :internal_server_error}
"InvalidDomain" ->
{:error, :invalid_domain}
"NoError" ->
domain_list =
xml_response
|> XMLXPath.xpath("//discover:Domain/text()",
discover: "http://schemas.microsoft.com/exchange/2010/Autodiscover"
)
|> XMLXPath.text()
{:ok, domain_list}
end
end
end
Entra.Discovery.get_all_domains("chgeuercxpisv03.onmicrosoft.com")