Page MenuHomePhorge

No OneTemporary

Size
19 KB
Referenced Files
None
Subscribers
None
diff --git a/lib/cacophony/message.ex b/lib/cacophony/message.ex
index 8548902..c5a85a4 100644
--- a/lib/cacophony/message.ex
+++ b/lib/cacophony/message.ex
@@ -1,339 +1,359 @@
defmodule Cacophony.Message do
@moduledoc """
Encoding and decoding of ASN.1 LDAP messages to high-level types.
## Known Limitations
* Cannot interpret ExtendedResponse objects. Extended response objects
are encapsulated as an ExtendedResponse type. Matching up OIDs is up
to you.
"""
require Logger
defmodule BindRequest do
@moduledoc "A type representing a BindRequest."
defstruct [:id, :version, :dn, :credentials]
end
defmodule BindResponse do
@moduledoc "A type representing a BindResponse."
defstruct [
:id,
:result_code,
:matched_dn,
{:diagnostic_message, ""},
{:referral, :asn1_NOVALUE},
{:server_sasl_creds, :asn1_NOVALUE}
]
end
defmodule UnbindRequest do
@moduledoc "A type representing an UnbindRequest."
defstruct [:id]
end
@whoami_oid "1.3.6.1.4.1.4203.1.11.3"
defmodule WhoAmIRequest do
@moduledoc "A type representing a Who Am I request (RFC 4532)."
defstruct [:id]
end
defmodule WhoAmIResponse do
@moduledoc "A type representing a Who Am I response (RFC 4532)."
defstruct [
:id,
{:result_code, :success},
{:matched_dn, ""},
{:diag_msg, ""},
{:referral, :asn1_NOVALUE},
:authzid
]
end
defmodule ExtendedRequest do
@moduledoc "A catch-all type for any extended request."
defstruct [:id, :request_name, :request_value]
end
defmodule ExtendedResponse do
@moduledoc "A catch-all type for any extended responses."
defstruct [
:id,
{:result_code, :success},
{:matched_dn, ""},
{:diag_msg, ""},
{:referral, :asn1_NOVALUE},
:response_name,
:response_value
]
end
+ defmodule IntermediateResponse do
+ @moduledoc "A catch-all type for any intermediate responses."
+
+ defstruct [
+ :id,
+ :response_name,
+ :response_value
+ ]
+ end
+
defmodule SearchRequest do
@moduledoc "A type representing a Search request."
defstruct [
:id,
:base,
{:scope, :baseObject},
{:deref_aliases, :neverDerefAliases},
:size_limit,
:time_limit,
:types_only,
:filter,
:attributes
]
end
defmodule SearchResultDone do
@moduledoc "A type representing the end of a set of SearchResultEntry items."
defstruct [
:id,
{:result_code, :success},
{:matched_dn, ""},
{:diag_msg, ""},
{:referral, :asn1_NOVALUE}
]
end
defmodule PartialAttribute do
@moduledoc "A type representing a PartialAttribute: key to 0 or more values."
defstruct [
:name,
{:values, []}
]
end
defmodule SearchResultEntry do
@moduledoc "A type representing a SearchResultEntry: DN to 0 or more PartialAttribute items."
defstruct [
:id,
:object_name,
{:attributes, []}
]
end
defmodule AbandonRequest do
@moduledoc "A type representing an AbandonRequest: an ID of a request to be abandoned."
defstruct [
:id,
:abandon_id
]
end
def interpret(:abandonRequest, abandon_id, message_id),
do: {:ok, %AbandonRequest{id: message_id, abandon_id: abandon_id}}
+ def interpret(:intermediateResponse, {:IntermediateResponse, name, value}, message_id),
+ do: {:ok, %IntermediateResponse{id: message_id, response_name: name, response_value: value}}
+
def interpret(:bindRequest, {:BindRequest, vsn, dn, creds}, message_id),
do: {:ok, %BindRequest{id: message_id, version: vsn, dn: dn, credentials: creds}}
def interpret(
:bindResponse,
{:BindResponse, result, matched_dn, diag_msg, referral, sasl_creds},
message_id
),
do:
{:ok,
%BindResponse{
id: message_id,
result_code: result,
matched_dn: matched_dn,
diagnostic_message: diag_msg,
referral: referral,
server_sasl_creds: sasl_creds
}}
def interpret(:unbindRequest, _, message_id), do: {:ok, %UnbindRequest{id: message_id}}
def interpret(:extendedReq, {:ExtendedRequest, @whoami_oid, :asn1_NOVALUE}, message_id),
do: {:ok, %WhoAmIRequest{id: message_id}}
def interpret(:extendedReq, {:ExtendedRequest, request_name, request_value}, message_id),
do:
{:ok,
%ExtendedRequest{id: message_id, request_name: request_name, request_value: request_value}}
def interpret(
:extendedResp,
{:ExtendedResponse, result, matched_dn, diag_msg, referral, response_name,
response_value},
message_id
),
do:
{:ok,
%ExtendedResponse{
id: message_id,
result_code: result,
matched_dn: matched_dn,
diag_msg: diag_msg,
referral: referral,
response_name: response_name,
response_value: response_value
}}
def interpret(
:searchRequest,
{:SearchRequest, base, scope, deref_aliases, size_limit, time_limit, types_only, filter,
attributes},
message_id
),
do:
{:ok,
%SearchRequest{
id: message_id,
base: base,
scope: scope,
deref_aliases: deref_aliases,
size_limit: size_limit,
time_limit: time_limit,
types_only: types_only,
filter: filter,
attributes: attributes
}}
def interpret(
:searchResDone,
{:LDAPResult, result, matched_dn, diag_msg, referral},
message_id
),
do:
{:ok,
%SearchResultDone{
id: message_id,
result_code: result,
matched_dn: matched_dn,
diag_msg: diag_msg,
referral: referral
}}
def interpret(:searchResEntry, {:SearchResultEntry, matched_dn, attributes}, message_id),
do:
{:ok,
%SearchResultEntry{
id: message_id,
object_name: matched_dn,
attributes:
Enum.map(
attributes,
fn {:PartialAttribute, key, values} ->
%PartialAttribute{name: key, values: values}
end
)
}}
def decode({:LDAPMessage, message_id, {type, payload}, _}),
do: interpret(type, payload, message_id)
def decode({:LDAPMessage, message_id, {type, payload}}),
do: interpret(type, payload, message_id)
def decode(message) when is_binary(message) do
with {:ok, data} <- :LDAP.decode(:LDAPMessage, message),
{:ok, struct} <- decode(data) do
{:ok, struct}
else
e -> {:error, e}
end
end
def decode(_), do: {:error, :badmatch}
def encode({:asn1, message}),
do: :LDAP.encode(:LDAPMessage, message)
def encode({:envelope, message_id, payload}),
do: encode({:asn1, {:LDAPMessage, message_id, payload, :asn1_NOVALUE}})
def encode(%BindRequest{} = msg) do
payload = {:BindRequest, msg.version, msg.dn, msg.credentials}
encode({:envelope, msg.id, {:bindRequest, payload}})
end
def encode(%BindResponse{} = msg) do
payload =
{:BindResponse, msg.result_code, msg.matched_dn, msg.diagnostic_message, msg.referral,
msg.server_sasl_creds}
encode({:envelope, msg.id, {:bindResponse, payload}})
end
def encode(%UnbindRequest{} = msg),
do: encode({:envelope, msg.id, {:unbindRequest, :asn1_NOVALUE}})
def encode(%WhoAmIRequest{} = msg),
do:
encode({:envelope, msg.id, {:extendedReq, {:ExtendedRequest, @whoami_oid, :asn1_NOVALUE}}})
def encode(%WhoAmIResponse{} = msg),
do:
encode(
{:envelope, msg.id,
{:extendedResp,
{:ExtendedResponse, msg.result_code, msg.matched_dn, msg.diag_msg, msg.referral,
:asn1_NOVALUE, msg.authzid}}}
)
def encode(%ExtendedRequest{} = msg),
do:
encode(
{:envelope, msg.id,
{:extendedReq, {:ExtendedRequest, msg.request_name, msg.request_value}}}
)
def encode(%ExtendedResponse{} = msg),
do:
encode(
{:envelope, msg.id,
{:extendedResp,
{:ExtendedResponse, msg.result_code, msg.matched_dn, msg.diag_msg, msg.referral,
msg.response_name, msg.response_value}}}
)
+ def encode(%IntermediateResponse{} = msg),
+ do:
+ encode(
+ {:envelope, msg.id,
+ {:intermediateResponse, {:IntermediateResponse, msg.response_name, msg.response_value}}}
+ )
+
def encode(%SearchRequest{} = msg),
do:
encode(
{:envelope, msg.id,
{:searchRequest,
{:SearchRequest, msg.base, msg.scope, msg.deref_aliases, msg.size_limit, msg.time_limit,
msg.types_only, msg.filter, msg.attributes}}}
)
def encode(%SearchResultDone{} = msg),
do:
encode(
{:envelope, msg.id,
{:searchResDone,
{:LDAPResult, msg.result_code, msg.matched_dn, msg.diag_msg, msg.referral}}}
)
def encode(%SearchResultEntry{} = msg),
do:
encode(
{:envelope, msg.id,
{:searchResEntry,
{:SearchResultEntry, msg.object_name,
Enum.map(
msg.attributes,
fn %PartialAttribute{name: name, values: values} ->
{:PartialAttribute, name, values}
end
)}}}
)
def encode(%AbandonRequest{} = msg),
do: encode({:envelope, msg.id, {:abandonRequest, msg.abandon_id}})
def encode(_), do: {:error, :badmatch}
end
diff --git a/test/cacophony/message_test.exs b/test/cacophony/message_test.exs
index 2fb0b2e..18f1c13 100644
--- a/test/cacophony/message_test.exs
+++ b/test/cacophony/message_test.exs
@@ -1,254 +1,280 @@
defmodule Cacophony.MessageTest do
use ExUnit.Case
@whoami_oid "1.3.6.1.4.1.4203.1.11.3"
@cirno_oid "9.99.999.9999.99999.999999"
describe "decode/1" do
test "successfully decodes a bindRequest" do
bindreq = {:BindRequest, 3, "cn=foo", {:simple, "bar"}}
message = {:LDAPMessage, 1, {:bindRequest, bindreq}, :asn1_NOVALUE}
{:ok, bin} = :LDAP.encode(:LDAPMessage, message)
{:ok, %Cacophony.Message.BindRequest{} = msg} = Cacophony.Message.decode(bin)
assert msg.id == 1
assert msg.dn == "cn=foo"
assert msg.credentials == {:simple, "bar"}
end
test "successfully decodes a bindResponse" do
bindresp = {:BindResponse, :success, "cn=foo", "", :asn1_NOVALUE, :asn1_NOVALUE}
message = {:LDAPMessage, 1, {:bindResponse, bindresp}, :asn1_NOVALUE}
{:ok, bin} = :LDAP.encode(:LDAPMessage, message)
{:ok, %Cacophony.Message.BindResponse{} = msg} = Cacophony.Message.decode(bin)
assert msg.id == 1
assert msg.matched_dn == "cn=foo"
assert msg.result_code == :success
end
test "successfully decodes an unbindRequest" do
message = {:LDAPMessage, 1, {:unbindRequest, :asn1_NOVALUE}, :asn1_NOVALUE}
{:ok, bin} = :LDAP.encode(:LDAPMessage, message)
{:ok, %Cacophony.Message.UnbindRequest{} = msg} = Cacophony.Message.decode(bin)
assert msg.id == 1
end
test "successfully decodes an extended whoAmIRequest" do
message =
{:LDAPMessage, 1, {:extendedReq, {:ExtendedRequest, @whoami_oid, :asn1_NOVALUE}},
:asn1_NOVALUE}
{:ok, bin} = :LDAP.encode(:LDAPMessage, message)
{:ok, %Cacophony.Message.WhoAmIRequest{} = msg} = Cacophony.Message.decode(bin)
assert msg.id == 1
end
test "successfully decodes an unknown extendedRequest" do
message =
{:LDAPMessage, 1, {:extendedReq, {:ExtendedRequest, @cirno_oid, :asn1_NOVALUE}},
:asn1_NOVALUE}
{:ok, bin} = :LDAP.encode(:LDAPMessage, message)
{:ok, %Cacophony.Message.ExtendedRequest{} = msg} = Cacophony.Message.decode(bin)
assert msg.id == 1
end
test "successfully decodes a searchRequest" do
searchreq =
{:SearchRequest, "", :baseObject, :neverDerefAliases, 0, 0, false,
{:present, "objectclass"}, ["supportedSASLMechanisms"]}
message = {:LDAPMessage, 1, {:searchRequest, searchreq}, :asn1_NOVALUE}
{:ok, bin} = :LDAP.encode(:LDAPMessage, message)
{:ok, %Cacophony.Message.SearchRequest{} = msg} = Cacophony.Message.decode(bin)
assert msg.id == 1
assert msg.attributes == ["supportedSASLMechanisms"]
assert msg.filter == {:present, "objectclass"}
assert msg.base == ""
assert msg.scope == :baseObject
assert msg.deref_aliases == :neverDerefAliases
assert msg.time_limit == 0
assert msg.size_limit == 0
refute msg.types_only
end
test "successfully decodes a searchResDone" do
searchresdone = {:SearchResultDone, :success, "", "", :asn1_NOVALUE}
message = {:LDAPMessage, 1, {:searchResDone, searchresdone}, :asn1_NOVALUE}
{:ok, bin} = :LDAP.encode(:LDAPMessage, message)
{:ok, %Cacophony.Message.SearchResultDone{} = msg} = Cacophony.Message.decode(bin)
assert msg.id == 1
end
test "successfully decodes a searchResEntry" do
searchresentry =
{:SearchResultEntry, "cn=test", [{:PartialAttribute, "key", ["value1", "value2"]}]}
message = {:LDAPMessage, 1, {:searchResEntry, searchresentry}, :asn1_NOVALUE}
{:ok, bin} = :LDAP.encode(:LDAPMessage, message)
{:ok, %Cacophony.Message.SearchResultEntry{} = msg} = Cacophony.Message.decode(bin)
assert msg.id == 1
assert msg.object_name == "cn=test"
assert msg.attributes == [
%Cacophony.Message.PartialAttribute{name: "key", values: ["value1", "value2"]}
]
end
test "successfully decodes an abandonRequest" do
message = {:LDAPMessage, 2, {:abandonRequest, 1}, :asn1_NOVALUE}
{:ok, bin} = :LDAP.encode(:LDAPMessage, message)
{:ok, %Cacophony.Message.AbandonRequest{} = msg} = Cacophony.Message.decode(bin)
assert msg.id == 2
assert msg.abandon_id == 1
end
+
+ test "successfully decodes an intermediateResponse" do
+ ir = {:IntermediateResponse, @cirno_oid, "9 is the best number"}
+ message = {:LDAPMessage, 1, {:intermediateResponse, ir}, :asn1_NOVALUE}
+
+ {:ok, bin} = :LDAP.encode(:LDAPMessage, message)
+
+ {:ok, %Cacophony.Message.IntermediateResponse{} = msg} = Cacophony.Message.decode(bin)
+
+ assert msg.id == 1
+ assert msg.response_name == @cirno_oid
+ assert msg.response_value == "9 is the best number"
+ end
end
describe "encode/1" do
test "successfully encodes a bindRequest" do
bindreq = {:BindRequest, 3, "cn=foo", {:simple, "bar"}}
message = {:LDAPMessage, 1, {:bindRequest, bindreq}, :asn1_NOVALUE}
{:ok, bin} = :LDAP.encode(:LDAPMessage, message)
{:ok, %Cacophony.Message.BindRequest{} = msg} = Cacophony.Message.decode(bin)
{:ok, other_bin} = Cacophony.Message.encode(msg)
assert bin == other_bin
end
test "successfully encodes a bindResponse" do
bindresp = {:BindResponse, :success, "cn=foo", "", :asn1_NOVALUE, :asn1_NOVALUE}
message = {:LDAPMessage, 1, {:bindResponse, bindresp}, :asn1_NOVALUE}
{:ok, bin} = :LDAP.encode(:LDAPMessage, message)
{:ok, %Cacophony.Message.BindResponse{} = msg} = Cacophony.Message.decode(bin)
{:ok, other_bin} = Cacophony.Message.encode(msg)
assert bin == other_bin
end
test "successfully encodes an unbindRequest" do
message = {:LDAPMessage, 1, {:unbindRequest, :asn1_NOVALUE}, :asn1_NOVALUE}
{:ok, bin} = :LDAP.encode(:LDAPMessage, message)
{:ok, %Cacophony.Message.UnbindRequest{} = msg} = Cacophony.Message.decode(bin)
{:ok, other_bin} = Cacophony.Message.encode(msg)
assert bin == other_bin
end
test "successfully encodes an extended whoAmIRequest" do
message =
{:LDAPMessage, 1, {:extendedReq, {:ExtendedRequest, @whoami_oid, :asn1_NOVALUE}},
:asn1_NOVALUE}
{:ok, bin} = :LDAP.encode(:LDAPMessage, message)
{:ok, %Cacophony.Message.WhoAmIRequest{} = msg} = Cacophony.Message.decode(bin)
{:ok, other_bin} = Cacophony.Message.encode(msg)
assert bin == other_bin
end
test "successfully encodes an unknown extendedRequest" do
message =
{:LDAPMessage, 1, {:extendedReq, {:ExtendedRequest, @cirno_oid, :asn1_NOVALUE}},
:asn1_NOVALUE}
{:ok, bin} = :LDAP.encode(:LDAPMessage, message)
{:ok, %Cacophony.Message.ExtendedRequest{} = msg} = Cacophony.Message.decode(bin)
{:ok, other_bin} = Cacophony.Message.encode(msg)
assert bin == other_bin
end
test "successfully encodes a searchRequest" do
searchreq =
{:SearchRequest, "", :baseObject, :neverDerefAliases, 0, 0, false,
{:present, "objectclass"}, ["supportedSASLMechanisms"]}
message = {:LDAPMessage, 1, {:searchRequest, searchreq}, :asn1_NOVALUE}
{:ok, bin} = :LDAP.encode(:LDAPMessage, message)
{:ok, %Cacophony.Message.SearchRequest{} = msg} = Cacophony.Message.decode(bin)
{:ok, other_bin} = Cacophony.Message.encode(msg)
assert bin == other_bin
end
test "successfully encodes a searchResDone" do
searchresdone = {:SearchResultDone, :success, "", "", :asn1_NOVALUE}
message = {:LDAPMessage, 1, {:searchResDone, searchresdone}, :asn1_NOVALUE}
{:ok, bin} = :LDAP.encode(:LDAPMessage, message)
{:ok, %Cacophony.Message.SearchResultDone{} = msg} = Cacophony.Message.decode(bin)
{:ok, other_bin} = Cacophony.Message.encode(msg)
assert bin == other_bin
end
test "successfully encodes a searchResEntry" do
searchresentry =
{:SearchResultEntry, "cn=test", [{:PartialAttribute, "key", ["value1", "value2"]}]}
message = {:LDAPMessage, 1, {:searchResEntry, searchresentry}, :asn1_NOVALUE}
{:ok, bin} = :LDAP.encode(:LDAPMessage, message)
{:ok, %Cacophony.Message.SearchResultEntry{} = msg} = Cacophony.Message.decode(bin)
{:ok, other_bin} = Cacophony.Message.encode(msg)
assert bin == other_bin
end
test "successfully encodes an abandonRequest" do
message = {:LDAPMessage, 2, {:abandonRequest, 1}, :asn1_NOVALUE}
{:ok, bin} = :LDAP.encode(:LDAPMessage, message)
{:ok, %Cacophony.Message.AbandonRequest{} = msg} = Cacophony.Message.decode(bin)
{:ok, other_bin} = Cacophony.Message.encode(msg)
assert bin == other_bin
end
+
+ test "successfully encodes an intermediateResponse" do
+ ir = {:IntermediateResponse, @cirno_oid, "9 is the best number"}
+ message = {:LDAPMessage, 1, {:intermediateResponse, ir}, :asn1_NOVALUE}
+
+ {:ok, bin} = :LDAP.encode(:LDAPMessage, message)
+
+ {:ok, %Cacophony.Message.IntermediateResponse{} = msg} = Cacophony.Message.decode(bin)
+
+ {:ok, other_bin} = Cacophony.Message.encode(msg)
+
+ assert bin == other_bin
+ end
end
end

File Metadata

Mime Type
text/x-diff
Expires
Sat, Nov 23, 5:05 AM (1 d, 2 h)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
38849
Default Alt Text
(19 KB)

Event Timeline