Page Menu
Home
Phorge
Search
Configure Global Search
Log In
Files
F112219
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Award Token
Flag For Later
Size
19 KB
Referenced Files
None
Subscribers
None
View Options
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
Details
Attached
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)
Attached To
Mode
R11 cacophony
Attached
Detach File
Event Timeline
Log In to Comment