[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