Update flavourpathinfo to use Blosxom::Debug.
[blosxom-plugins.git] / general / login
blob534130aa0929a7ff93a96905ae830e0f7f3ddabf
1 # Blosxom Plugin:Login
2 # Author: Fletcher T. Penney
3 # Version: 0.4
5 # NOTE: a functional cookies plugin is required.  Apparently, it has to be named something like
6 # 9999cookies to work properly.
8 # Also, this plugin works by modifying the filter subroutine.  You may need to rename your plugins to insure
9 # the proper order, but it works properly with exclude, and menu to my testing.  I am sure there are possible
10 # conflicts with other plugins though.
13 package login;
15 # --- Configurable variables -----
17 # Where is the passwd file?  Should not be in a public directory nor group/world writeable
18 # This file is created with the htpasswd command (Do a man htpasswd to figure it out - please don't ask
19 # me to explain how to do this - there are plenty of references on the web.
20 # I will work on updating this to allow a cgi program to maintain this file, but can't guarantee
21 # when that will occur.  In the meantime, this provides for better security.
25 my $passwd_file = "$blosxom::datadir/passwd";
27 # Where is your excludefile - this controls which user is able to see which files
28 # the excludefile is built using regular expressions in the format:
29 # user=page
30 # user is a regexp to match allowed user (.* matches all VALIDATED users)
31 # page is a regexp to match pages or directories (.* matches all pages)
33 # Examples:
34 # A blank or nonexistent file allows full access to anyone, logged in or not
35 # myusername=.*
36 #       Only someone logged in as myusername can access any files on the web site
37 # (myuser1|myuser2)=private
38 #       Only these two people can access a file or directory named private, private1, privatestuff, etc
39 # .*=priv$
40 #       Any VALIDATED user can view the directory named priv, but this doesn't affect one named private
42
43 my $excludefile = "$blosxom::datadir/requireuser";
45 my $default_message = "Please log in.";
48 # This section for gui creation of accounts
50 my $pending_file = "$blosxom::plugin_state_dir/pendingaccounts";
53 # --------------------------------
55 # Ideas for improvement
56 # Option to log all succesful logins
57 # Ability to set a sort of exclude file to filter out entries by login name
59 use CGI qw/:standard/;
61 $username = "";         # users login name, if any - set only after password is validated
62 $path_noflavour = "";
65 $message = "";
67 my $passphrase ="";
68 my $validated = 0;
70 sub validate {
71         my ($submituser, $submitpass) = @_;
72         if (open(PASSWD, $passwd_file)) {
73                 $message = "Password file open.";
74                 while (<PASSWD>) {
75                         chop;
76                         ($testuser, $testpass) = split(':',$_);
77                         if ($testuser eq $submituser) {
78                                 if (crypt($submitpass,$testpass) eq $testpass) {
79                                         $message = "Welcome, $submituser.";
80                                         $username = $submituser;
81                                         $validated = 1;
82                                         $loginform = $logoutform;
83                                 } else {
84                                         warn "blosxom : login plugin > Incorrect password for user $submituser.";
85                                 }
86                         }
87                 }
88                 if ($validated eq 0) {
89                         $message = "Login for user $submituser failed.";
90                         warn "blosxom : login plugin > $message";
91                         $username = "";
92                 } 
93                 close PASSWD;
94         } else {
95                 $message =  "Unable to access password file.\n";
96                 warn "blosxom : login plugin > $message";
97                 $username = "";
98         }
100 return $validated;
105 sub start {
106         $path_noflavour = $blosxom::path_info;
107         if ($path_noflavour !~ s/\.[^\.]*$//) {
108                 $path_noflavour =~ s/\/$//;
109                 $path_noflavour .= "\/index";
110                 $path_noflavour =~ s/^([^\/])/$1/;
111         }
112         $path_noflavour =~ s/^\/*//;
114         open (REQUIRE, $excludefile);
115         @requiredlist = <REQUIRE>;
116         close REQUIRE;
118 # HTML code for the login form to be displayed
119 # Converts to a logout button as well?
121 $loginform = qq!<form method="POST" action="$blosxom::url/$path_noflavour.$blosxom::flavour">
122 <table border=0 cellpadding=0 cellspacing=0>
123 <tr>
124 <td align=right>Login:</td><td><input name="user" size="10" value="" ><br></td>
125 </tr><tr>
126 <td align=right>Password:</td><td><input name="pass" size="10" value="" type="password"><br></td>
127 </tr><tr>
128 <td colspan=2 align=center><input type="submit" value="Login"></td>
129 </tr></table>
130 <input type="hidden" name="plugin" value="login">
131 <input type="hidden" name="task" value="login">
132 </form>!;
134 $logoutform = qq!<form method="POST" action="$blosxom::url/$path_noflavour.$blosxom::flavour">
135 <input type="submit" value="Logout">
136 <input type="hidden" name="plugin" value="login">
137 <input type="hidden" name="task" value="logout">
138 </form>!;
140 $signupform = qq!<form method="POST" action="$blosxom::url/$path_noflavour.$blosxom::flavour">
141 <table border=0 cellpadding=0 cellspacing=0>
142 <tr>
143 <td align=right>Login(no spaces):</td><td><input name="user" size="10" value="" ><br></td>
144 </tr><tr>
145 <td align=right>Email:</td><td><input name="email" size="10" value=""><br></td>
146 </tr><tr>
147 <td align=right>Password:</td><td><input name="pass" size="10" value="" type="password"><br></td>
148 </tr><tr>
149 <td align=right>Verify:</td><td><input name="verifypass" size="10" value="" type="password"><br></td>
150 </tr><tr>
151 <td colspan=2 align=center><input type="submit" value="Sign Me Up"></td>
152 </tr></table>
153 <input type="hidden" name="plugin" value="login">
154 <input type="hidden" name="task" value="signup">
155 </form>!;
157         1;
161 sub filter {
162         my ($pkg, $files_ref) = @_;
163         my @files_list = keys %$files_ref;
165         # This handles login requests and creates a cookie, if valid
166         if ( request_method() eq 'POST' and (param('plugin') eq 'login')) {
167                 if (param('task') eq 'logout') {
168                         &cookies::remove('login');      # These don't seem to work
169                         &cookies::clear('login');       # So I overwrite the cookie below
170                                 &cookies::add(
171                                         cookie(
172                                                 -name=>'login',
173                                                 -value=>{ },
174                                                 -domain=>$cookies::domain,
175                                                 -expires=>'now'
176                                         )
177                                 );
178                         $username="";
179                         $validated=-1;
180                 }
182                 if (param('task') eq 'login') {
183                         my $user = param('user');
184                         my $pass = param('pass');
185                         if ((validate($user,$pass)) and $blosxom::plugins{cookies} > 0) {
186                                 # Create a cookie
187                                 &cookies::add(
188                                         cookie(
189                                                 -name=>'login',
190                                                 -value=>{ 'user' => $user, 'pass' => $pass},
191                                                 -domain=>$cookies::domain,
192                                                 -expires=>$cookies::expires
193                                         )
194                                 );
195                         }
196                 }
198                 if (param('task') eq 'signup') {
199                         my $user = param('user');
200                         my $pass = param('pass');
201                         my $verify = param('verifypass');
202                         my $email = param('email');
203                         $validated=-1;
205                         $message= "";
207                         if ($pass ne $verify) {
208                                 $message = "Your passwords were different.  Please try again.";
209                         } 
210                         if (length($pass) < 5) {
211                                 $message = "Come on.... Your password has to be at least 5 characters.";
212                         }
213                         if (length($email) <5) {
214                                 $message = "That's not a real email address... Try again.";
215                         }
217                         if ($message eq "") {
218                                 open(PENDING,">>$pending_file") || ($message = "Error submitting information.  Please try again.");
219                                 if ($message eq "") {
220                                         my @salts = (a..z,A..Z,0..9,'.','/'); 
221                                         my $salt=$salts[rand @salts];
222                                                 $salt.=$salts[rand @salts];
224                                                 my $encrypted=crypt($pass,$salt);
226                                                 my ($dw, $mo, $mo_num, $da, $ti, $yr) = &blosxom::nice_date(time());
227                                         print PENDING "$yr $mo $da $ti $user $email $encrypted\n";
228                                         close PENDING;
229                                         $message="Request submitted.  Don't call us... We'll call you...  ;)";
230                                 }
231                         }
232                 }
233         }
235         # Now, read cookies to see if user is logged in
236         if ($blosxom::plugins{cookies} > 0 and my $cookie = &cookies::get('login') and $validated eq 0) {
237                 my $user = $cookie->{'user'};
238                 my $pass = $cookie->{'pass'};
240                 validate($user,$pass);
241         }
243         $message = $default_message if ($message eq "");
245         # Now filter the files
246         foreach $file (@files_list) {
247                 $localfile = $file;
248                 $localfile =~ s/\/\//\//g;              #An improperly formatted url such as:
249                                                 # /my/document/dir/secret//files would slip through
250                 foreach $required (@requiredlist) {
251                         if ($required =~ /(.*)=(.*)/) {
252                                 $requser=$1;
253                                 $reqfile=$2;
254                                 if ($localfile =~ /^$blosxom::datadir(\/)?$reqfile/) {
255                                         delete $files_ref->{$file} if (($username !~ /$requser/) or $username eq "");
256                                 }
257                         }
258                 }
259         }
260         
267 __END__
269 =head1 NAME
271 Blosxom Plug-in: login
273 =head1 DESCRIPTION
275 Login allows you to create a passwd file that defines usernames and passwords for your web site.  Another file, $excludefile, defines which users can access certain parts of your site.  The format for the files is explained above.
277 $login::username provides the users login, once it has been validated against the database.  It remains "" if someone is not logged in.
279 $login::loginform provides the login/logout box.  Place it in your templates wherever you like.
280 $login::message gives certain status messages.
282 By default, login provides logging messages when certain errors occur, and when a failed login attempt happens.
284 $login::signupform provides html code for a simple account sign up form.  Once a user submits the information, it is added to a text file defined by $pending_file.  If you want to add the account, simply copy the user and password hash to the defined password file in the format "user:hash" as defined by the htpasswd command.
287 PLEASE NOTE:  I am not a security expert.  I can't guarantee that there isn't a huge loophole in this somewhere.  And note - any malicious plug-in could expose a whole by tampering with the $username variable - verify that other plugins do not attempt to manipulate $login::username or access the cookies.  But this is more a matter of being careful which plugins you install.
289 Also, this plugin does not pretend to offer serious security.  Someone could read a restricted file if they have read access to the location that the file is stored.  This could occur if you datadir is in your web servers documentroot.  
291 There are many issues to consider when securing a web site, and I don't pretend to address them all, nor do I guarantee that this plugin even works properly.  It is simply a means to provide some form of control without much effort on your part.  You get what you pay for.  But I will continue to work with the community to improve it as security holes are noted.
293 =head1 BUGS
295 None known; please send bug reports and feedback to the Blosxom
296 development mailing list <blosxom-devel@lists.sourceforge.net>.
298 =head1 AUTHOR
300 Fletcher T. Penney - http://fletcher.freeshell.org
302 This plugin is now maintained by the Blosxom Sourceforge Team,
303 <blosxom-devel@lists.sourceforge.net>.
306 =head1 LICENSE
308 This source is submitted to the public domain.  Feel free to use and modify it.  If you like, a comment in your modified source attributing credit for my original work would be appreciated.
310 THIS SOFTWARE IS PROVIDED AS IS AND WITHOUT ANY WARRANTY OF ANY KIND.  USE AT YOUR OWN RISK!