[Coquelicot] [PATCH] Add LDAP authentication (with uid lookup)

Rowan Thorpe rowan at rowanthorpe.com
Wed Apr 2 13:27:17 CEST 2014


Hi again,

I squashed it to a single commit, which is below (can be applied in git, as it
is "git format-patch" output). You may wish to add in the docs that the
underlying ldap module doesn't validate server certs yet - I just added it as
an in-code comment. Also, the copyright notices will need bumping to 2014 :-)

=====

>From f0a89e471d6387ead50bcb5e5246595e0d88a439 Mon Sep 17 00:00:00 2001
Message-Id: <f0a89e471d6387ead50bcb5e5246595e0d88a439.1396437566.git.rowan at rowanthorpe.com>
In-Reply-To: <20140402102720.GA22057 at loar>
References: <20140402102720.GA22057 at loar>
From: Rowan Thorpe <rowan at rowanthorpe.com>
Date: Mon, 31 Mar 2014 20:01:56 +0300
Subject: [PATCH] Add LDAP authentication (with uid lookup)
To: coquelicot at potager.org

---
 INSTALL                                    |  3 ++
 README                                     |  4 +-
 conf/settings-default.yml                  |  4 +-
 conf/settings-ldap.yml                     | 20 +++++++++
 lib/coquelicot/auth/ldap.rb                | 68 ++++++++++++++++++++++++++++++
 po/coquelicot.pot                          |  6 ++-
 po/de/coquelicot.po                        |  6 ++-
 po/fr/coquelicot.po                        |  6 ++-
 public/javascripts/coquelicot.auth.ldap.js | 45 ++++++++++++++++++++
 spec/coquelicot_spec.rb                    | 18 ++++++++
 views/auth/ldap.haml                       | 24 +++++++++++
 11 files changed, 198 insertions(+), 6 deletions(-)
 create mode 100644 conf/settings-ldap.yml
 create mode 100644 lib/coquelicot/auth/ldap.rb
 create mode 100644 public/javascripts/coquelicot.auth.ldap.js
 create mode 100644 views/auth/ldap.haml

diff --git a/INSTALL b/INSTALL
index 2e571b3..ebd826b 100644
--- a/INSTALL
+++ b/INSTALL
@@ -134,6 +134,9 @@ Further settings example:
  * `conf/settings-imap.yml`: necessary configuration for the "imap"
    authentication mechanism.
 
+ * `conf/settings-ldap.yml`: necessary configuration for the "ldap"
+   authentication mechanism.
+
 You can copy one of these examples to `conf/settings.yml` and adjust
 them according to your environment.
 
diff --git a/README b/README
index 7571d3b..3533d63 100644
--- a/README
+++ b/README
@@ -21,12 +21,14 @@ Features
 
    In order to prevent random Internet users to eat bandwidth and
    disk space, Coquelicot limits upload to authenticated users.
-   It currently ships with two authentication mechanisms:
+   It currently ships with three authentication mechanisms:
 
     - "simplepass": uploading users need to provide a global,
       pre-shared, password;
     - "imap": users will need to provide a login and a password,
       that are used to authenticate against an existing IMAP server.
+    - "ldap": users will need to provide a uid and a password,
+      that are used to authenticate against an existing LDAP server.
 
    It is possible to integrate more authentication mechanisms by
    implementing a single method, some JavaScript, and a partial template
diff --git a/conf/settings-default.yml b/conf/settings-default.yml
index 8077fa9..ebb2012 100644
--- a/conf/settings-default.yml
+++ b/conf/settings-default.yml
@@ -110,8 +110,8 @@ show_exceptions: false
 
 # Authentication method
 #
-#   Please have look at `conf/settings-simplepass.yml` and
-#   `conf/settings-imap.yml` for more details.
+#   Please have a look at `conf/settings-simplepass.yml`,
+#   `conf/settings-imap.yml` and `conf/settings-ldap.yml` for more details.
 #
 # The default password is 'test'.
 authentication_method:
diff --git a/conf/settings-ldap.yml b/conf/settings-ldap.yml
new file mode 100644
index 0000000..6151bf7
--- /dev/null
+++ b/conf/settings-ldap.yml
@@ -0,0 +1,20 @@
+# Settings for the LDAP authentication method
+# -------------------------------------------
+#
+# When using the LDAP authentication method users will be
+# asked for a login and a password. Those credentials will
+# be tested against the given LDAP server.
+#
+# Connections to the LDAP server are made using SSL/TLS.
+
+authentication_method:
+  name: ldap
+
+  # Hostname of the authenticating LDAP server
+  ldap_server: "ldap.example.com"
+
+  # Port of the authenticating LDAP server
+  ldap_port: 636
+
+  # Search base of the authenticating LDAP server
+  ldap_base: "dc=example,dc=com"
diff --git a/lib/coquelicot/auth/ldap.rb b/lib/coquelicot/auth/ldap.rb
new file mode 100644
index 0000000..fee3783
--- /dev/null
+++ b/lib/coquelicot/auth/ldap.rb
@@ -0,0 +1,68 @@
+# -*- coding: UTF-8 -*-
+# Coquelicot: "one-click" file sharing with a focus on users' privacy.
+# Copyright © 2012-2013 potager.org <jardiniers at potager.org>
+#           © 2011 mh / immerda.ch <mh+coquelicot at immerda.ch>
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as
+# published by the Free Software Foundation, either version 3 of the
+# License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU Affero General Public License for more details.
+#
+# You should have received a copy of the GNU Affero General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+# TODO: set array of multiple ldap servers in settings and loop over them to
+#       find first matching UID to connect as
+
+# TODO: add commented code showing how to direct login by full username,
+#       without lookup
+
+# TODO: add commented code showing how to use starttls as an option instead of
+#       dedicated SSL port, too
+
+# NB:   :simple_tls ensures all communication is encrypted, but it doesn't
+#       verify the server-certificate. A method which does *both* doesn't
+#       seem to exist in Net::LDAP yet...
+
+require 'net/ldap'
+module Coquelicot
+  module Auth
+    class LdapAuthenticator < AbstractAuthenticator
+      def authenticate(params)
+        if params[:ldap_user].empty? or params[:ldap_password].empty?
+          raise Coquelicot::Auth::Error.new(
+                    'Disallowing empty uid/password - might "auth" anonymous user on server')
+        end
+        # connect anonymously & lookup user to do authenticated bind_as() next
+        ldap = Net::LDAP.new( :host => settings.ldap_server,
+                              :port => settings.ldap_port,
+                              :base => settings.ldap_base,
+                              :encryption => :simple_tls,
+                              :auth => { :method => :anonymous } )
+        result = ldap.bind_as( :base => settings.ldap_base,
+                               :filter => "(uid=" + params[:ldap_user] + ")",
+                               :password => params[:ldap_password] )
+        if not(result)
+          raise Coquelicot::Auth::Error.new(
+                    'Failed authentication to LDAP server')
+        end
+        true
+      rescue Errno::ECONNREFUSED
+        raise Coquelicot::Auth::Error.new(
+                  'Unable to connect to LDAP server')
+      rescue NoMethodError => ex
+        if [ :ldap_server, :ldap_port, :ldap_base ].include? ex.name
+          raise Coquelicot::Auth::Error.new(
+                    "Missing '#{ex.name}' attribute in configuration.")
+        else
+          raise
+        end
+      end
+    end
+  end
+end
diff --git a/po/coquelicot.pot b/po/coquelicot.pot
index 503dd26..cf1fadc 100644
--- a/po/coquelicot.pot
+++ b/po/coquelicot.pot
@@ -49,7 +49,11 @@ msgstr ""
 msgid "E-mail User:"
 msgstr ""
 
-#: views/auth/imap.haml:23 views/enter_file_key.haml:22
+#: views/auth/ldap.haml:20
+msgid "LDAP User:"
+msgstr ""
+
+#: views/auth/imap.haml:23 views/auth/ldap.haml:23 views/enter_file_key.haml:22
 msgid "Password:"
 msgstr ""
 
diff --git a/po/de/coquelicot.po b/po/de/coquelicot.po
index c0b45ed..ce9e739 100644
--- a/po/de/coquelicot.po
+++ b/po/de/coquelicot.po
@@ -48,7 +48,11 @@ msgstr "Upload Passwort:"
 msgid "E-mail User:"
 msgstr "Email User:"
 
-#: views/auth/imap.haml:23 views/enter_file_key.haml:22
+#: views/auth/ldap.haml:20
+msgid "LDAP User:"
+msgstr "LDAP User:"
+
+#: views/auth/imap.haml:23 views/auth/ldap.haml:23 views/enter_file_key.haml:22
 msgid "Password:"
 msgstr "Passwort:"
 
diff --git a/po/fr/coquelicot.po b/po/fr/coquelicot.po
index c5230c3..9e8e9e0 100644
--- a/po/fr/coquelicot.po
+++ b/po/fr/coquelicot.po
@@ -48,7 +48,11 @@ msgstr "Mot de passe pour envoyer :"
 msgid "E-mail User:"
 msgstr "Compte email :"
 
-#: views/auth/imap.haml:23 views/enter_file_key.haml:22
+#: views/auth/ldap.haml:20
+msgid "LDAP User:"
+msgstr "Compte LDAP :"
+
+#: views/auth/imap.haml:23 views/auth/ldap.haml:23 views/enter_file_key.haml:22
 msgid "Password:"
 msgstr "Mot de passe :"
 
diff --git a/public/javascripts/coquelicot.auth.ldap.js b/public/javascripts/coquelicot.auth.ldap.js
new file mode 100644
index 0000000..eca3928
--- /dev/null
+++ b/public/javascripts/coquelicot.auth.ldap.js
@@ -0,0 +1,45 @@
+/*
+ * Coquelicot: "one-click" file sharing with a focus on users' privacy.
+ * Copyright © 2012-2013 potager.org <jardiniers at potager.org>
+ *           © 2011 mh / immerda.ch <mh+coquelicot at immerda.ch>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+var authentication = {
+  getData: function() {
+    return {
+      ldap_user: $('#ldap_user').val(),
+      ldap_password: $('#ldap_password').val()
+    };
+  },
+  focus: function() {
+    $('#ldap_user').focus();
+  },
+  handleReject: function() {
+    $('#ldap_user').val('');
+    $('#ldap_password').val('');
+  },
+};
+
+$(document).ready(function() {
+  $('#ldap-auth-submit').remove();
+  var submit = $('<input type="submit" />');
+  submit.attr('value', 'Login');
+  submit.attr('id', 'ldap-auth-submit');
+  $('#upload-authentication').append(
+    $('<div class="field" />').append(
+      $('<div class="submit" />').append(
+        submit)));
+});
diff --git a/spec/coquelicot_spec.rb b/spec/coquelicot_spec.rb
index 226ab87..a92abaf 100644
--- a/spec/coquelicot_spec.rb
+++ b/spec/coquelicot_spec.rb
@@ -341,4 +341,22 @@ PART
       last_response.should be_ok
     end
   end
+
+  context "when using 'ldap' authentication mechanism" do
+    before(:each) do
+      app.set :authentication_method, :name => 'ldap',
+                                      :ldap_server => 'example.org',
+                                      :ldap_port => 636
+    end
+
+    it "should try to login to the LDAP server when using AJAX" do
+      ldap = stub('Net::LDAP').as_null_object
+      ldap.should_receive(:login).with('user', 'password')
+      Net::LDAP.should_receive(:new).with('example.org', 636, "dc=example, dc=com", :simple_tls, { :method => :anonymous }).and_return(ldap)
+      request "/authenticate", :method => "POST", :xhr => true,
+                               :params => { :ldap_user     => 'user',
+                                            :ldap_password => 'password' }
+      last_response.should be_ok
+    end
+  end
 end
diff --git a/views/auth/ldap.haml b/views/auth/ldap.haml
new file mode 100644
index 0000000..202b158
--- /dev/null
+++ b/views/auth/ldap.haml
@@ -0,0 +1,24 @@
+-# -*- coding: UTF-8 -*-
+-# Coquelicot: "one-click" file sharing with a focus on users' privacy.
+-# Copyright © 2012-2013 potager.org <jardiniers at potager.org>
+-#           © 2011 mh / immerda.ch <mh+coquelicot at immerda.ch>
+-#
+-# This program is free software: you can redistribute it and/or modify
+-# it under the terms of the GNU Affero General Public License as
+-# published by the Free Software Foundation, either version 3 of the
+-# License, or (at your option) any later version.
+-#
+-# This program is distributed in the hope that it will be useful,
+-# but WITHOUT ANY WARRANTY; without even the implied warranty of
+-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+-# GNU Affero General Public License for more details.
+-#
+-# You should have received a copy of the GNU Affero General Public License
+-# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+.field
+  %label{ :for => 'ldap_user' } LDAP User:
+  %input.input{ :type => 'text', :id => 'ldap_user', :name => 'ldap_user' }
+.field
+  %label{ :for => 'ldap_password' } Password:
+  %input.input{ :type => 'password', :id => 'ldap_password', :name => 'ldap_password' }
-- 
1.9.1


More information about the Coquelicot mailing list