From d4afe13f05e9a569a1d54d4b534699f913408081 Mon Sep 17 00:00:00 2001
From: Yann <framagit@perso.ylb.fr>
Date: Feb 03 2017 14:20:20 +0000
Subject: Add htpasswd file support for user authentication


Fixes based on merge request discussion by Luc Didry: https://framagit.org/luc/lufi/merge_requests/7

Coding style

---

diff --git a/cpanfile b/cpanfile
index b006806..6b0481e 100644
--- a/cpanfile
+++ b/cpanfile
@@ -15,3 +15,4 @@ requires 'Filesys::DfPortable';
 requires 'Switch';
 requires 'Data::Entropy';
 requires 'Net::LDAP';
+requires 'Apache::Htpasswd';
diff --git a/lib/Lufi.pm b/lib/Lufi.pm
old mode 100644
new mode 100755
index a27b00f..bb055ad
--- a/lib/Lufi.pm
+++ b/lib/Lufi.pm
@@ -4,6 +4,7 @@ use Mojo::Base 'Mojolicious';
 use LufiDB;
 use Data::Entropy qw(entropy_source);
 use Net::LDAP;
+use Apache::Htpasswd;
 
 $ENV{MOJO_MAX_WEBSOCKET_SIZE} = 100485760; # 10 * 1024 * 1024 = 10MiB
 
@@ -61,6 +62,9 @@ sub startup {
     # Debug
     $self->plugin('DebugDumperHelper');
 
+    # Check htpasswd file existence
+    die 'Unable to read '.$self->config('htpasswd') if (defined($self->config('htpasswd')) && !-r $self->config('htpasswd'));
+
     # Authentication (if configured)
     $self->plugin('authentication' =>
         {
@@ -74,41 +78,51 @@ sub startup {
             validate_user => sub {
                 my ($c, $username, $password, $extradata) = @_;
 
-                my $ldap = Net::LDAP->new($c->config->{ldap}->{uri});
-                my $mesg = $ldap->bind($c->config->{ldap}->{bind_user}.$c->config->{ldap}->{bind_dn},
-                    password => $c->config->{ldap}->{bind_pwd}
-                );
-
-                $mesg->code && die $mesg->error;
-
-                $mesg = $ldap->search(
-                    base   => $c->config->{ldap}->{user_tree},
-                    filter => "(&(uid=$username)".$c->config->{ldap}->{user_filter}.")"
-                );
-
-                if ($mesg->code) {
-                    $c->app->log->error($mesg->error);
-                    return undef;
-                }
-
-                # Now we know that the user exists
-                $mesg = $ldap->bind('uid='.$username.$c->config->{ldap}->{bind_dn},
-                    password => $password
-                );
-
-                if ($mesg->code) {
-                    $c->app->log->info("[LDAP authentication failed] login: $username, IP: ".$c->ip);
-                    $c->app->log->error("[LDAP authentication failed] ".$mesg->error);
-                    return undef;
+                if (defined($c->config('ldap'))) {
+                    my $ldap = Net::LDAP->new($c->config->{ldap}->{uri});
+                    my $mesg = $ldap->bind($c->config->{ldap}->{bind_user}.$c->config->{ldap}->{bind_dn},
+                        password => $c->config->{ldap}->{bind_pwd}
+                    );
+    
+                    $mesg->code && die $mesg->error;
+    
+                    $mesg = $ldap->search(
+                        base   => $c->config->{ldap}->{user_tree},
+                        filter => "(&(uid=$username)".$c->config->{ldap}->{user_filter}.")"
+                    );
+    
+                    if ($mesg->code) {
+                        $c->app->log->error($mesg->error);
+                        return undef;
+                    }
+    
+                    # Now we know that the user exists
+                    $mesg = $ldap->bind('uid='.$username.$c->config->{ldap}->{bind_dn},
+                        password => $password
+                    );
+    
+                    if ($mesg->code) {
+                        $c->app->log->info("[LDAP authentication failed] login: $username, IP: ".$c->ip);
+                        $c->app->log->error("[LDAP authentication failed] ".$mesg->error);
+                        return undef;
+                    }
+    
+                    $c->app->log->info("[LDAP authentication successful] login: $username, IP: ".$c->ip);
+                } elsif (defined($c->config('htpasswd'))) {
+                    my $htpasswd = new Apache::Htpasswd({passwdFile => $c->config->{htpasswd},
+                                                 ReadOnly   => 1}
+                                                );
+                    if (!$htpasswd->htCheckPassword($username, $password)) {
+                        return undef;
+                    }
+                    $c->app->log->info("[Simple authentication successful] login: $username, IP: ".$c->ip);
                 }
 
-                $c->app->log->info("[LDAP authentication successful] login: $username, IP: ".$c->ip);
-
                 return $username;
             }
         }
     );
-    if (defined($self->config('ldap'))) {
+    if (defined($self->config('ldap')) || defined($self->config('htpasswd'))) {
         $self->app->sessions->default_expiration($self->config('session_duration'));
     }
 
@@ -238,14 +252,14 @@ sub startup {
     # Page for files uploading
     $r->get('/' => sub {
         my $c = shift;
-        if (!defined($c->config('ldap')) || $c->is_user_authenticated) {
+        if ((!defined($c->config('ldap')) && !defined($c->config('htpasswd'))) || $c->is_user_authenticated) {
             $c->render(template => 'index');
         } else {
             $c->redirect_to('login');
         }
     })->name('index');
 
-    if (defined $self->config('ldap')) {
+    if (defined $self->config('ldap') || defined $self->config('htpasswd')) {
         # Login page
         $r->get('/login' => sub {
             my $c = shift;
@@ -291,7 +305,7 @@ sub startup {
     # List of files (use localstorage, so the server know nothing about files)
     $r->get('/files' => sub {
         my $c = shift;
-        if (!defined($c->config('ldap')) || $c->is_user_authenticated) {
+        if ((!defined($c->config('ldap')) && !defined($c->config('htpasswd'))) || $c->is_user_authenticated) {
             $c->render(template => 'files');
         } else {
             $c->redirect_to('login');
diff --git a/lib/Lufi/Controller/Files.pm b/lib/Lufi/Controller/Files.pm
index 077bb52..eb6cdb4 100644
--- a/lib/Lufi/Controller/Files.pm
+++ b/lib/Lufi/Controller/Files.pm
@@ -13,7 +13,7 @@ use Filesys::DfPortable;
 sub upload {
     my $c = shift;
 
-    if (!defined($c->config('ldap')) || $c->is_user_authenticated) {
+    if ((!defined($c->config('ldap')) && !defined($c->config('htpasswd'))) || $c->is_user_authenticated) {
         $c->inactivity_timeout(30000000);
 
         $c->app->log->debug('Client connected');
@@ -97,7 +97,7 @@ sub upload {
                         }
 
                         my $creator = $c->ip;
-                        if (defined($c->config('ldap'))) {
+                        if (defined($c->config('ldap')) || defined($c->config('htpasswd'))) {
                             $creator = 'User: '.$c->current_user.', IP: '.$creator;
                         }
                         $f = Lufi::File->new(
@@ -303,7 +303,7 @@ sub get_counter {
     my $short = $c->param('short');
     my $token = $c->param('token');
 
-    if (!defined($c->config('ldap')) || $c->is_user_authenticated) {
+    if ((!defined($c->config('ldap')) && !defined($c->config('htpasswd'))) || $c->is_user_authenticated) {
         my @records = LufiDB::Files->select('WHERE short = ?', $short);
         if (scalar(@records)) {
             if ($records[0]->mod_token eq $token) {
@@ -352,7 +352,7 @@ sub delete {
     my $short = $c->param('short');
     my $token = $c->param('token');
 
-    if (!defined($c->config('ldap')) || $c->is_user_authenticated) {
+    if ((!defined($c->config('ldap')) && !defined($c->config('htpasswd'))) || $c->is_user_authenticated) {
         my @records = LufiDB::Files->select('WHERE short = ? AND mod_token = ?', ($short, $token));
         if (scalar(@records)) {
             my $f   = Lufi::File->new(record => $records[0]);
diff --git a/lufi.conf.template b/lufi.conf.template
old mode 100644
new mode 100755
index a337d1c..e07139d
--- a/lufi.conf.template
+++ b/lufi.conf.template
@@ -132,6 +132,10 @@
     #    user_filter => '!(uid=ldap_user)'
     #},
 
+    # set `htpasswd` if you want to use an htpasswd file instead of ldap
+    # see 'man htpasswd' to know how to create such file
+    #htpasswd => 'lufi.passwd',
+
     # if you've set ldap above, the session will last `session_duration` seconds before
     # the user needs to reauthenticate
     # optional, default is 3600
diff --git a/themes/default/templates/layouts/default.html.ep b/themes/default/templates/layouts/default.html.ep
index 67b7ddc..faf03be 100644
--- a/themes/default/templates/layouts/default.html.ep
+++ b/themes/default/templates/layouts/default.html.ep
@@ -31,26 +31,26 @@
                 <a href="<%= url_for('/') %>" class="brand-logo">&nbsp;<img src="<%= url_for('/img/lufi-min.png') %>" alt="logo"> Lufi</a>
                 <a href="#" data-activates="mobile-demo" class="button-collapse"><i class="mdi-navigation-menu"></i></a>
                 <ul id="nav-mobile" class="right hide-on-med-and-down">
-                % if (!defined(config('ldap')) || is_user_authenticated()) {
+                % if ((!defined(config('ldap')) && !defined(config('htpasswd'))) || is_user_authenticated()) {
                     <li<%== ' class="active"' if (current_route eq 'index') %>><a href="<%= url_for('/') %>"><%= l('Upload files') %></a></li>
                     <li<%== ' class="active"' if (current_route eq 'files') %>><a href="<%= url_for('/files') %>"><%= l('My files') %></a></li>
                 % } else {
                     <li><a href="<%= url_for('/login') %>"><%= l('Signin') %></a></li>
                 % }
                     <li<%== ' class="active"' if (current_route eq 'about') %>><a href="<%= url_for('/about') %>"><%= l('About') %></a></li>
-                % if (defined(config('ldap')) && is_user_authenticated()) {
+                % if ((defined(config('ldap')) || defined(config('htpasswd'))) && is_user_authenticated()) {
                     <li><a href="<%= url_for('/logout') %>"><%= l('Logout') %></a></li>
                 % }
                 </ul>
                 <ul id="mobile-demo" class="side-nav">
-                % if (!defined(config('ldap')) || is_user_authenticated()) {
+                % if ((!defined(config('ldap')) && !defined(config('htpasswd'))) || is_user_authenticated()) {
                     <li<%== ' class="active"' if (current_route eq 'index') %>><a href="<%= url_for('/') %>"><%= l('Upload files') %></a></li>
                     <li<%== ' class="active"' if (current_route eq 'files') %>><a href="<%= url_for('/files') %>"><%= l('My files') %></a></li>
                 % } else {
                     <li><a href="<%= url_for('/login') %>"><%= l('Signin') %></a></li>
                 % }
                     <li<%== ' class="active"' if (current_route eq 'about') %>><a href="<%= url_for('/about') %>"><%= l('About') %></a></li>
-                % if (defined(config('ldap')) && is_user_authenticated()) {
+                % if ((defined(config('ldap')) || defined(config('htpasswd'))) && is_user_authenticated()) {
                     <li><a href="<%= url_for('/logout') %>"><%= l('Logout') %></a></li>
                 % }
                 </ul>