tagging: Allow using titles in for related stories.
[matthijs/upstream/blosxom-plugins.git] / general / login
1 # Blosxom Plugin:Login
2 # Author: Fletcher T. Penney
3 # Version: 0.4
4
5 # NOTE: a functional cookies plugin is required.  Apparently, it has to be named something like
6 # 9999cookies to work properly.
7 #
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.
11
12
13 package login;
14
15 # --- Configurable variables -----
16
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.
22 #
23 #
24
25 my $passwd_file = "$blosxom::datadir/passwd";
26
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)
32 #
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
41 #
42
43 my $excludefile = "$blosxom::datadir/requireuser";
44
45 my $default_message = "Please log in.";
46
47
48 # This section for gui creation of accounts
49
50 my $pending_file = "$blosxom::plugin_state_dir/pendingaccounts";
51
52
53 # --------------------------------
54
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
58
59 use CGI qw/:standard/;
60
61 $username = "";         # users login name, if any - set only after password is validated
62 $path_noflavour = "";
63
64
65 $message = "";
66
67 my $passphrase ="";
68 my $validated = 0;
69
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         }
99
100 return $validated;
101 }
102
103
104
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/^\/*//;
113
114         open (REQUIRE, $excludefile);
115         @requiredlist = <REQUIRE>;
116         close REQUIRE;
117
118 # HTML code for the login form to be displayed
119 # Converts to a logout button as well?
120
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>!;
133
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>!;
139
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>!;
156
157         1;
158 }
159
160
161 sub filter {
162         my ($pkg, $files_ref) = @_;
163         my @files_list = keys %$files_ref;
164
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                 }
181
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                 }
197
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;
204
205                         $message= "";
206
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                         }
216
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];
223
224                                                 my $encrypted=crypt($pass,$salt);
225
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         }
234
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'};
239
240                 validate($user,$pass);
241         }
242
243         $message = $default_message if ($message eq "");
244
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         
261
262 1;
263 }
264
265 1;
266
267 __END__
268
269 =head1 NAME
270
271 Blosxom Plug-in: login
272
273 =head1 DESCRIPTION
274
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.
276
277 $login::username provides the users login, once it has been validated against the database.  It remains "" if someone is not logged in.
278
279 $login::loginform provides the login/logout box.  Place it in your templates wherever you like.
280 $login::message gives certain status messages.
281
282 By default, login provides logging messages when certain errors occur, and when a failed login attempt happens.
283
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.
285
286
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.
288
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.  
290
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.
292
293 =head1 BUGS
294
295 None known; please send bug reports and feedback to the Blosxom
296 development mailing list <blosxom-devel@lists.sourceforge.net>.
297
298 =head1 AUTHOR
299
300 Fletcher T. Penney - http://fletcher.freeshell.org
301
302 This plugin is now maintained by the Blosxom Sourceforge Team,
303 <blosxom-devel@lists.sourceforge.net>.
304
305
306 =head1 LICENSE
307
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.
309
310 THIS SOFTWARE IS PROVIDED AS IS AND WITHOUT ANY WARRANTY OF ANY KIND.  USE AT YOUR OWN RISK!