paddle v0.1.4 Paddle View Source

Module handling ldap requests and translate them to the :eldap syntax.

Configuration

The configuration should be in the dev.secret.exs or prod.secret.exs depending on the environment you’re working on. Here’s an example config:

config :paddle, Paddle,
  host: "ldap.my-organisation.org",
  base: "dc=myorganisation,dc=org",
  ssl: true,
  port: 636,
  ipv6: true,
  tcpopts: [],
  sslopts: [certfile: '/path/to/certificate.crt'],
  timeout: 3000,
  account_subdn: "ou=People",
  schema_files: Path.wildcard("/etc/openldap/schema/*.schema")
OptionDescriptionDefault
:hostThe host(s) containing the LDAP server(s). Can be a bitstring for a single host, or a list of bitstrings, which will make Paddle try to connect to each host in the specified order. See also the :timeout option.Mandatory
:baseThe base DN.""
:sslWhen set to true, use SSL to connect to the LDAP server.false
:portThe port the LDAP server listen to.389
:ipv6When set to true, connect to the LDAP server using IPv6.false
:tcpoptsAdditionnal :gen_tcp.connect/4 / :ssl.connect/4 options. Must not have the :active, :binary, :deliver, :list, :mode or :packet options. See :gen_tcp’s option documentation.[]
:ssloptsAdditionnal :ssl.connect/4 options. Ineffective if the :ssl option is set to false. See :ssl’s option documentation.[]
:timeoutThe timeout in milliseconds, or nil for the default TCP stack timeout value (which may be very long), for each request to the LDAP server.nil
:account_subdnThe DN (without the base) where the accounts are located. Used by the Paddle.authenticate/2 function."ou=People"
:account_identifierThe identifier by which users are identified. Used by the Paddle.authenticate/2 function.:uid
:schema_filesFiles which are to be parsed to help generate classes using Paddle.Class.Helper.[]

Usage

To check a user’s credentials and/or authenticate the connection, simply do:

Paddle.authenticate("username", "password")

You can also specify the partial DN like so:

Paddle.authenticate([cn: "admin"], "adminpassword")

Many functions support passing both a base and a filter via a keyword list or a map like so:

Paddle.get(filter: [uid: "testuser"], base: [ou: "People"])

But you can also use structs which implements the Paddle.Class protocol (called class objects). If we take as example the classes defined in test/support/classes.ex, we could do:

Paddle.get %MyApp.PosixAccount{uid: "user"}

The previous example will return every accounts which are in a given subDN (defined in the Paddle.Class protocol), which have the right objectClass (also defined in the same protocol), and have an uid of “user”.

You can also specify an additional filter as second argument.

Class objects

A class object is simply a struct implementing the Paddle.Class protocol.

If you’re in need of some examples, you can see the test/support/classes.ex file which defines MyApp.PosixAccount, and MyApp.PosixGroup (but only in test mode, so you would have to define your own).

For more informations, see the Paddle.Class module documentation.

Filters

A filter in Paddle is a keyword list or a map.

This is equivalent to a filter where each attribute name (key from the map / keyword list) must have a corresponding value (value from the map / keyword list).

Example:

[uid: "user", cn: "User", homeDirectory: "/home/user"]

If you are missing some filtering capabilities, you can always pass as argument an :eldap filter like so:

Paddle.get(filter: :eldap.substrings('uid', initial: 'b'))

For more informations and examples, see Paddle.Filters.construct_filter/1

Bases

A base in Paddle can be a Keyword list that will be converted to a charlist to be passed on to the :eldap module. A direct string can also be passed.

For more informations and examples, see Paddle.Filters.construct_dn/2

Link to this section Summary

Functions

Add an entry to the LDAP given a class object

Add an entry to the LDAP given a DN and a list of attributes

Check the given credentials and authenticate the current connection

Returns a specification to start this module under a supervisor

Get the whole configuration of the Paddle application

Get the environment configuration of the Paddle application under a certain key

Same as config/1 but allows you to specify a default value

Delete a LDAP entry given a DN or a class object

Same as get/1 but throws in case of an error

Same as get/2 but throws in case of an error

Get one or more LDAP entries given a partial DN and a filter

Get an entry in the LDAP given a class object. You can specify an optional additional filter as second argument

Get the DN of an entry

Get a single LDAP entry given an optional partial DN and an optional filter

Modify an LDAP entry given a DN or a class object and a list of modifications

Closes the current connection and opens a new one

Link to this section Types

Link to this type add_ldap_error() View Source
add_ldap_error() ::
  :undefinedAttributeType
  | :objectClassViolation
  | :invalidAttributeSyntax
  | :noSuchObject
  | :insufficientAccessRights
  | :entryAlreadyExists
Link to this type attributes() View Source
attributes() ::
  keyword() | %{required(binary()) => binary()} | [{binary(), binary()}]
Link to this type auth_status() View Source
auth_status() :: :ok | {:error, atom()}
Link to this type authenticate_ldap_error() View Source
authenticate_ldap_error() ::
  :operationsError
  | :protocolError
  | :authMethodNotSupported
  | :strongAuthRequired
  | :referral
  | :saslBindInProgress
  | :inappropriateAuthentication
  | :invalidCredentials
  | :unavailable
  | :anonymous_auth
Link to this type delete_ldap_error() View Source
delete_ldap_error() ::
  :noSuchObject | :notAllowedOnNonLeaf | :insufficientAccessRights
Link to this type ldap_entry() View Source
ldap_entry() :: %{required(binary()) => binary()}
Link to this type mod() View Source
mod() ::
  {:add, {binary() | atom(), binary() | [binary()]}}
  | {:delete, binary()}
  | {:replace, {binary() | atom(), binary() | [binary()]}}
Link to this type modify_ldap_error() View Source
modify_ldap_error() ::
  :noSuchObject
  | :undefinedAttributeType
  | :namingViolation
  | :attributeOrValueExists
  | :invalidAttributeSyntax
  | :notAllowedOnRDN
  | :objectClassViolation
  | :objectClassModsProhibited
  | :insufficientAccessRights
Link to this type reason() View Source
reason() :: :normal | :shutdown | {:shutdown, term()} | term()
Link to this type search_ldap_error() View Source
search_ldap_error() ::
  :noSuchObject
  | :sizeLimitExceeded
  | :timeLimitExceeded
  | :undefinedAttributeType
  | :insufficientAccessRights

Link to this section Functions

Link to this function add(class_object) View Source
add(Paddle.Class.t()) ::
  :ok
  | {:error, :missing_unique_identifier}
  | {:error, :missing_req_attributes, [atom()]}
  | {:error, add_ldap_error()}

Add an entry to the LDAP given a class object.

Example:

Paddle.add(%MyApp.PosixAccount{uid: "myUser", cn: "My User", gidNumber: "501", homeDirectory: "/home/myUser"})
Link to this function add(kwdn, attributes) View Source
add(dn(), attributes()) :: :ok | {:error, add_ldap_error()}

Add an entry to the LDAP given a DN and a list of attributes.

The first argument is the DN given as a string or keyword list. The second argument is the list of attributes in the new entry as a keyword list like so:

[objectClass: ["account", "posixAccount"],
 cn: "User",
 loginShell: "/bin/bash",
 homeDirectory: "/home/user",
 uidNumber: 501,
 gidNumber: 100]

Please note that due to the limitation of char lists you cannot pass directly a char list as an attribute value. But, you can wrap it in an array like this: homeDirectory: ['/home/user']

Link to this function authenticate(kwdn, password) View Source
authenticate(dn(), binary()) :: :ok | {:error, authenticate_ldap_error()}

Check the given credentials and authenticate the current connection.

When given the wrong credentials, returns {:error, :invalidCredentials}

The user id can be passed as a binary, which will expand to <account_identifier>=<id>,<account subdn>,<base>, or with a keyword list if you want to specify the whole DN (but still without the base DN).

Example:

iex> Paddle.authenticate("testuser", "test")
:ok
iex> Paddle.authenticate("testuser", "wrong password")
{:error, :invalidCredentials}
iex> Paddle.authenticate([cn: "admin"], "test")
:ok

Returns a specification to start this module under a supervisor.

See Supervisor.

Get the whole configuration of the Paddle application.

Link to this function config(atom) View Source
config(atom()) :: any()

Get the environment configuration of the Paddle application under a certain key.

Link to this function config(key, default) View Source
config(atom(), any()) :: any()

Same as config/1 but allows you to specify a default value.

Link to this function delete(kwdn) View Source
delete(Paddle.Class.t() | dn()) :: :ok | {:error, delete_ldap_error()}

Delete a LDAP entry given a DN or a class object.

Examples:

Paddle.delete("uid=testuser,ou=People")
Paddle.delete([uid: "testuser", ou: "People"])
Paddle.delete(%MyApp.PosixAccount{uid: "testuser"})

The three examples above do exactly the same thing (provided that the MyApp.PosixAccount is configured appropriately).

Same as get/1 but throws in case of an error.

Link to this function get!(object, additional_filter \\ []) View Source

Same as get/2 but throws in case of an error.

Link to this function get(kwdn) View Source
get(dn()) :: {:ok, [ldap_entry()]} | {:error, search_ldap_error()}

Get one or more LDAP entries given a partial DN and a filter.

Example:

iex> Paddle.get(base: [uid: "testuser", ou: "People"])
{:ok,
 [%{"cn" => ["Test User"],
   "dn" => "uid=testuser,ou=People",
   "gecos" => ["Test User,,,,"], "gidNumber" => ["120"],
   "homeDirectory" => ["/home/testuser"],
   "loginShell" => ["/bin/bash"],
   "objectClass" => ["account", "posixAccount", "top"],
   "uid" => ["testuser"], "uidNumber" => ["500"],
   "userPassword" => ["{SSHA}AIzygLSXlArhAMzddUriXQxf7UlkqopP"]}]}

iex> Paddle.get(base: "uid=testuser,ou=People")
{:ok,
 [%{"cn" => ["Test User"],
   "dn" => "uid=testuser,ou=People",
   "gecos" => ["Test User,,,,"], "gidNumber" => ["120"],
   "homeDirectory" => ["/home/testuser"],
   "loginShell" => ["/bin/bash"],
   "objectClass" => ["account", "posixAccount", "top"],
   "uid" => ["testuser"], "uidNumber" => ["500"],
   "userPassword" => ["{SSHA}AIzygLSXlArhAMzddUriXQxf7UlkqopP"]}]}

iex> Paddle.get(base: [uid: "nothing"])
{:error, :noSuchObject}

iex> Paddle.get(filter: [uid: "testuser"], base: [ou: "People"])
{:ok,
 [%{"cn" => ["Test User"],
   "dn" => "uid=testuser,ou=People",
   "gecos" => ["Test User,,,,"], "gidNumber" => ["120"],
   "homeDirectory" => ["/home/testuser"],
   "loginShell" => ["/bin/bash"],
   "objectClass" => ["account", "posixAccount", "top"],
   "uid" => ["testuser"], "uidNumber" => ["500"],
   "userPassword" => ["{SSHA}AIzygLSXlArhAMzddUriXQxf7UlkqopP"]}]}
Link to this function get(object, additional_filter \\ nil) View Source
get(Paddle.Class.t(), Paddle.Filters.t()) ::
  {:ok, [Paddle.Class.t()]} | {:error, search_ldap_error()}

Get an entry in the LDAP given a class object. You can specify an optional additional filter as second argument.

Example:

iex> Paddle.get(%MyApp.PosixAccount{})
{:ok,
 [%MyApp.PosixAccount{cn: ["Test User"], description: nil,
   gecos: ["Test User,,,,"], gidNumber: ["120"],
   homeDirectory: ["/home/testuser"], host: nil, l: nil,
   loginShell: ["/bin/bash"], o: nil,
   ou: nil, seeAlso: nil, uid: ["testuser"],
   uidNumber: ["500"],
   userPassword: ["{SSHA}AIzygLSXlArhAMzddUriXQxf7UlkqopP"]}]}

iex> Paddle.get(%MyApp.PosixGroup{cn: "users"})
{:ok,
 [%MyApp.PosixGroup{cn: ["users"], description: nil, gidNumber: ["2"],
   memberUid: ["testuser"], userPassword: nil}]}

iex> Paddle.get(%MyApp.PosixGroup{}, :eldap.substrings('cn', initial: 'a'))
{:ok,
 [%MyApp.PosixGroup{cn: ["adm"], description: nil, gidNumber: ["3"],
   memberUid: nil, userPassword: nil}]}
Link to this function get_dn(object) View Source
get_dn(Paddle.Class.t()) ::
  {:ok, binary()} | {:error, :missing_unique_identifier}

Get the DN of an entry.

Example:

iex> Paddle.get_dn(%MyApp.PosixAccount{uid: "testuser"})
{:ok, "uid=testuser,ou=People"}
Link to this function get_single(kwdn) View Source
get_single(dn()) :: {:ok, ldap_entry()} | {:error, search_ldap_error()}

Get a single LDAP entry given an optional partial DN and an optional filter.

Example:

iex> Paddle.get_single(base: [ou: "People"])
{:ok,
 %{"dn" => "ou=People",
  "objectClass" => ["top", "organizationalUnit"], "ou" => ["People"]}}

iex> Paddle.get_single(filter: [uid: "nothing"])
{:error, :noSuchObject}
Link to this function modify(kwdn, mods) View Source
modify(Paddle.Class.t() | dn(), [mod()]) ::
  :ok | {:error, modify_ldap_error()}

Modify an LDAP entry given a DN or a class object and a list of modifications.

A modification is specified like so:

{action, {parameters...}}

Available modifications:

  • {:add, {field, value}}
  • {:delete, field}
  • {:replace, {field, value}}

For example, adding a “description” field:

{:add, {"description", "This is a description"}}

This allows you to do things like this:

Paddle.modify([uid: "testuser", ou: "People"],
              add: {"description", "This is a description"},
              delete: "gecos",
              replace: {"o", ["Club *Nix", "Linux Foundation"]})

Or, using class objects:

Paddle.modify(%MyApp.PosixAccount{uid: "testuser"},
              add: {"description", "This is a description"},
              delete: "gecos",
              replace: {"o", ["Club *Nix", "Linux Foundation"]})

Closes the current connection and opens a new one.

Accepts connection information as arguments. Not specified values will be fetched from the config.

Example:

iex> Paddle.reconnect(host: ['example.com'])
{:error, {:not_connected, "connect failed"}}
iex> Paddle.reconnect()
{:ok, :connected}