source: main/trunk/greenstone2/perllib/cgiactions/baseaction.pm@ 27295

Last change on this file since 27295 was 27295, checked in by ak19, 11 years ago

This commit contains bugfixes for authentication within metadata-server.pl and related perl code, and is committed separately before changes in gsajaxapi.js start to make use of it. Another important change is that for adding user comments, a user need not be in the collection's group, so checking the group shouldn't be performed. The bugfixes are to get the authentication to work and are in addition to an earlier commit that corrected the name of the authentication_enable variable in baseaction.pm. The bugfixes are: users.gdb instead of users.db, metadata-server.pl needs to call gsdlCGI's encrypt_password otherwise the password check will fail because it won't match with what's in the db. Also, the calls to authenticate_user had to be through the self variable, since its a method not a function and failed to work correctly otherwise.

File size: 14.9 KB
Line 
1###########################################################################
2#
3# baseaction.pm --
4# A component of the Greenstone digital library software
5# from the New Zealand Digital Library Project at the
6# University of Waikato, New Zealand.
7#
8# Copyright (C) 2009 New Zealand Digital Library Project
9#
10# This program is free software; you can redistr te it and/or modify
11# it under the terms of the GNU General Public License as published by
12# the Free Software Foundation; either version 2 of the License, or
13# (at your option) any later version.
14#
15# This program is distributed in the hope that it will be useful,
16# but WITHOUT ANY WARRANTY; without even the implied warranty of
17# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18# GNU General Public License for more details.
19#
20# You should have received a copy of the GNU General Public License
21# along with this program; if not, write to the Free Software
22# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
23#
24###########################################################################
25
26
27package baseaction;
28
29use strict;
30use util;
31use inexport;
32
33our $authentication_enabled = 0; # debugging flag (can debug without authentication when set to 0)
34our $mail_enabled = 0;
35
36
37# change this to get these values from a config file
38my $mail_to_address = "user\@server"; # Set this appropriately
39my $mail_from_address = "user\@server"; # Set this appropriately
40my $mail_smtp_server = "smtp.server"; # Set this appropriately
41
42
43
44# Required CGI arguments: "a" for action
45# "c" for collection
46# Optional CGI arguemnts: "ts" for timestamp (auto generated is missing)
47# "site" (used by Greenstone3)
48
49# allow "un" for username to be optional for now
50
51sub new
52{
53 my $class = shift (@_);
54 my ($action_table,$gsdl_cgi,$iis6_mode) = @_;
55
56 my $self = { 'gsdl_cgi' => $gsdl_cgi,
57 'iis6_mode' => $iis6_mode,
58 'gsdlhome' => $ENV{'GSDLHOME'} };
59
60 # Retrieve the (required) command CGI argument
61 my $action = $gsdl_cgi->clean_param("a");
62
63 if (!defined $action) {
64 my $err_mess = "No action (a=...) specified.\n";
65 $err_mess .= "\nPossible actions are:\n";
66
67 $err_mess .= " check-installation\n\n";
68
69 foreach my $a (sort keys %$action_table) {
70 $err_mess .= " $a:\n";
71 $err_mess .= " Compulsory args: ";
72 my @comp_args = ("c");
73 push(@comp_args,"un") if ($authentication_enabled);
74 push(@comp_args,@{$action_table->{$a}->{'compulsory-args'}});
75 $err_mess .= join(", ", @comp_args);
76
77 $err_mess .= "\n";
78
79 my @opt_args = ();
80 push(@opt_args,"un") if (!$authentication_enabled);
81 push(@opt_args,@{$action_table->{$a}->{'optional-args'}});
82
83 if (scalar(@opt_args)>0) {
84
85 $err_mess .= " Optional args : ";
86 $err_mess .= join(", ", @opt_args);
87 $err_mess .= "\n";
88 }
89
90 my @help_examples = ();
91 if(defined $action_table->{$a}->{'help-string'}) {
92 push(@help_examples, @{$action_table->{$a}->{'help-string'}});
93 }
94 if (scalar(@help_examples)>0) {
95
96 if (scalar(@help_examples)>1) {
97 $err_mess .= " Example(s) :\n";
98 } else {
99 $err_mess .= " Example :\n";
100 }
101 $err_mess .= join(", \n\n", @help_examples);
102 $err_mess .= "\n\nTo be strictly CGI-compliant special chars like double-quotes,&,?,<,> must be URL encoded.\n";
103 }
104
105 $err_mess .= "\n";
106 }
107
108 $gsdl_cgi->generate_message($err_mess);
109 exit(-1);
110
111 }
112 $gsdl_cgi->delete("a");
113
114 $self = bless $self, $class;
115
116 # The check-installation command has no arguments
117 if ($action eq "check-installation") {
118 $self->check_installation($gsdl_cgi,$iis6_mode);
119 exit 0;
120 }
121
122
123 if (!defined $action_table->{$action}) {
124 my $valid_actions = join(", ", keys %$action_table);
125
126 my $err_mess = "Unrecognised action (a=$action) specified.\n";
127 $err_mess .= "Valid actions are: $valid_actions\n";
128
129 $gsdl_cgi->generate_error($err_mess);
130 }
131
132
133 my $collect = $gsdl_cgi->clean_param("c");
134 if ((!defined $collect) || ($collect =~ m/^\s*$/)) {
135 $gsdl_cgi->generate_error("No collection specified.");
136 }
137 $gsdl_cgi->delete("c");
138
139 # allow un to be optional for now
140 my $username = $gsdl_cgi->clean_param("un");
141
142
143 # Get then remove the ts (timestamp) argument (since this can mess up
144 # other scripts)
145 my $timestamp = $gsdl_cgi->clean_param("ts");
146 if ((!defined $timestamp) || ($timestamp =~ m/^\s*$/)) {
147 # Fall back to using the Perl time() function to generate a timestamp
148 $timestamp = time();
149 }
150 $gsdl_cgi->delete("ts");
151
152 my $site = undef;
153 if($gsdl_cgi->greenstone_version() != 2) {
154 # all GS versions after 2 may define site
155 $site = $gsdl_cgi->clean_param("site");
156 if (!defined $site) {
157 $gsdl_cgi->generate_error("No site specified.");
158 }
159 $gsdl_cgi->delete("site");
160 }
161
162
163 $self->{'action'} = $action;
164 $self->{'collect'} = $collect;
165 $self->{'username'} = $username;
166 $self->{'timestamp'} = $timestamp;
167 $self->{'site'} = $site;
168
169 # Locate and store compulsory arguments
170 my $comp_args = $action_table->{$action}->{'compulsory-args'};
171 foreach my $ca (@$comp_args) {
172 if (!defined $gsdl_cgi->param($ca)) {
173 $gsdl_cgi->generate_error("Compulsory argument '$ca' missing");
174 }
175 else {
176 $self->{$ca} = $gsdl_cgi->clean_param($ca);
177 $gsdl_cgi->delete($ca);
178 }
179 }
180
181 # Locate and store optional args if present
182 my $opt_args = $action_table->{$action}->{'optional-args'};
183 foreach my $oa (@$opt_args) {
184 if (defined $gsdl_cgi->param($oa)) {
185 $self->{$oa} = $gsdl_cgi->clean_param($oa);
186 $gsdl_cgi->delete($oa);
187 }
188 }
189
190
191
192 # Retrieve infodb-type
193 if (defined $collect) {
194
195 my $opt_site = $self->{'site'} || "";
196
197 my $inexport = newCGI inexport(ref $self,$collect,$gsdl_cgi,$opt_site);
198 my ($config_filename,$collect_cfg) = $inexport->read_collection_cfg($collect);
199 $self->{'infodbtype'} = $collect_cfg->{'infodbtype'};
200
201 }
202
203
204 return $self;
205}
206
207
208sub do_action
209{
210 my $self = shift @_;
211 my $action = $self->{'action'};
212
213 $action =~ s/-/_/g;
214
215
216 $self->$action();
217
218}
219
220
221sub authenticate_user
222{
223 my $self = shift @_;
224 my $username = shift(@_);
225 my $collection = shift(@_);
226
227 my $gsdl_cgi = $self->{'gsdl_cgi'};
228
229 # Remove the pw argument (since this can mess up other scripts)
230 my $user_password = $gsdl_cgi->clean_param("pw");
231 $gsdl_cgi->delete("pw");
232
233 if ((!defined $user_password) || ($user_password =~ m/^\s*$/)) {
234 $gsdl_cgi->generate_error("Authentication failed: no password specified.");
235 }
236
237 my $gsdlhome = $ENV{'GSDLHOME'};
238 my $etc_directory = &util::filename_cat($gsdlhome, "etc");
239 my $users_db_file_path = &util::filename_cat($etc_directory, "users.gdb");
240
241 # Use db2txt to get the user accounts information
242 my $users_db_content = "";
243 open(USERS_DB, "db2txt \"$users_db_file_path\" |");
244 while (<USERS_DB>) {
245 $users_db_content .= $_;
246 }
247
248 # Get the user account information from the users.gdb database
249 my %users_db_data = ();
250 foreach my $users_db_entry (split(/-{70}/, $users_db_content)) {
251 if ($users_db_entry =~ /\n?\[(.+)\]\n/) {
252 $users_db_data{$1} = $users_db_entry;
253 }
254 }
255
256 # Check username
257 my $user_data = $users_db_data{$username};
258 if (!defined $user_data) {
259 $gsdl_cgi->generate_error("Authentication failed: no account for user '$username'.");
260 }
261
262 # Check password
263 my ($valid_user_password) = ($user_data =~ /\<password\>(.*)/);
264 if ($user_password ne $valid_user_password) {
265 $gsdl_cgi->generate_error("Authentication failed: incorrect password.");
266 }
267
268 # The following code which tests whether the user is in the required group
269 # seems to have been copied over from gliserver.pl.
270 # But for metadata-server.pl, when user comments are added through the set-metadata functions,
271 # the user doesn't need to be a specific collection's editor in order to add comments to that collection.
272 # So we no longer check the user is in the group here.
273
274 # Check group
275# my ($user_groups) = ($user_data =~ /\<groups\>(.*)/);
276# if ($collection eq "") {
277# # If we're not editing a collection then the user doesn't need to be in a particular group
278# return $user_groups; # Authentication successful
279# }
280# foreach my $user_group (split(/\,/, $user_groups)) {
281 # Does this user have access to all collections?
282# if ($user_group eq "all-collections-editor") {
283# return $user_groups; # Authentication successful
284# }
285 # Does this user have access to personal collections, and is this one?
286# if ($user_group eq "personal-collections-editor" && $collection =~ /^$username\-/) {
287# return $user_groups; # Authentication successful
288# }
289 # Does this user have access to this collection
290# if ($user_group eq "$collection-collection-editor") {
291# return $user_groups; # Authentication successful
292# }
293# }
294#
295# $gsdl_cgi->generate_error("Authentication failed: user is not in the required group.");
296}
297
298
299
300sub check_installation
301{
302 my $self = shift @_;
303 my $iis6_mode = shift(@_);
304
305 my $gsdl_cgi = $self->{'gsdl_cgi'};
306
307 my $installation_ok = 1;
308 my $installation_status = "";
309
310 print STDOUT "Content-type:text/plain\n\n";
311
312 # Check that Java is installed and accessible
313 my $java = $gsdl_cgi->get_java_path();
314 my $java_command = "$java -version 2>&1";
315
316 # IIS 6: redirecting output from STDERR to STDOUT just doesn't work, so we have to let it go
317 # directly out to the page
318 if ($iis6_mode)
319 {
320 $java_command = "java -version";
321 }
322
323 my $java_output = `$java_command`;
324 my $java_status = $?;
325 if ($java_status < 0) {
326 # The Java command failed
327 $installation_status = "Java failed -- do you have the Java run-time installed?\n" . $gsdl_cgi->check_java_home() . "\n";
328 $installation_ok = 0;
329 }
330 else {
331 $installation_status = "Java found: $java_output";
332 }
333
334 # Show the values of some important environment variables
335 $installation_status .= "\n";
336 $installation_status .= "GSDLHOME: " . $ENV{'GSDLHOME'} . "\n";
337 $installation_status .= "GSDLOS: " . $ENV{'GSDLOS'} . "\n";
338 $installation_status .= "PATH: " . $ENV{'PATH'} . "\n";
339
340 if ($installation_ok) {
341 print STDOUT $installation_status . "\nInstallation OK!";
342 }
343 else {
344 print STDOUT $installation_status;
345 }
346}
347
348sub lock_collection
349{
350 my $self = shift @_;
351 my $username = shift(@_);
352 my $collection = shift(@_);
353
354 my $gsdl_cgi = $self->{'gsdl_cgi'};
355
356 my $steal_lock = $gsdl_cgi->clean_param("steal_lock");
357 $gsdl_cgi->delete("steal_lock");
358
359 if (!defined $username) {
360 # don't have any user details for current user to compare with
361 # even if there is a lock file
362 # For now, allow the current user access. Might want to
363 # revisit this in the future.
364 return;
365 }
366
367 #my $gsdlhome = $ENV{'GSDLHOME'};
368 #my $collection_directory = &util::filename_cat($gsdlhome, "collect", $collection);
369 my $site = $self->{'site'};
370 my $collection_directory = $gsdl_cgi->get_collection_dir($site, $collection);
371 $gsdl_cgi->checked_chdir($collection_directory);
372
373 # Check if a lock file already exists for this collection
374 my $lock_file_name = "gli.lck";
375 if (-e $lock_file_name) {
376 # A lock file already exists... check if it's ours
377 my $lock_file_content = "";
378 open(LOCK_FILE, "<$lock_file_name");
379 while (<LOCK_FILE>) {
380 $lock_file_content .= $_;
381 }
382 close(LOCK_FILE);
383
384 # Pick out the owner of the lock file
385 $lock_file_content =~ /\<User\>(.*?)\<\/User\>/;
386 my $lock_file_owner = $1;
387
388 # The lock file is ours, so there is no problem
389 if ($lock_file_owner eq $username) {
390 return;
391 }
392
393 # The lock file is not ours, so throw an error unless "steal_lock" is set
394 unless (defined $steal_lock) {
395 $gsdl_cgi->generate_error("Collection is locked by: $lock_file_owner");
396 }
397 }
398
399 my ($sec, $min, $hour, $mday, $mon, $year, $wday, $yday, $isdst) = localtime(time);
400 my $current_time = sprintf("%02d/%02d/%d %02d:%02d:%02d", $mday, $mon + 1, $year + 1900, $hour, $min, $sec);
401
402 # Create a lock file for us (in the same format as the GLI) and we're done
403 open(LOCK_FILE, ">$lock_file_name");
404 print LOCK_FILE "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n";
405 print LOCK_FILE "<LockFile>\n";
406 print LOCK_FILE " <User>" . $username . "</User>\n";
407 print LOCK_FILE " <Machine>(Remote)</Machine>\n";
408 print LOCK_FILE " <Date>" . $current_time . "</Date>\n";
409 print LOCK_FILE "</LockFile>\n";
410 close(LOCK_FILE);
411}
412
413
414# Release the gli.lck otherwise no one else will be able to use the collection again.
415sub unlock_collection
416{
417 my $self = shift @_;
418 my ($username, $collection) = @_;
419 my $gsdl_cgi = $self->{'gsdl_cgi'};
420
421 # Obtain the path to the collection GLI lock file
422 my $lock_file_path = &util::filename_cat($ENV{'GSDLHOME'}, "collect", $collection, "gli.lck");
423
424 # If the lock file does exist, check if it is ours
425 if (-e $lock_file_path)
426 {
427 my $lock_file_content = "";
428 open(LOCK_FILE, "<$lock_file_path");
429 while (<LOCK_FILE>) {
430 $lock_file_content .= $_;
431 }
432 close(LOCK_FILE);
433
434 # Pick out the owner of the lock file
435 $lock_file_content =~ /\<User\>(.*?)\<\/User\>/;
436 my $lock_file_owner = $1;
437
438 # If we are the owner of this lock, we have the right to delete it
439 if ($lock_file_owner eq $username) {
440 unlink($lock_file_path );
441 }
442 else {
443 $gsdl_cgi->generate_error("Collection is locked by: $lock_file_owner. Cannot be unlocked");
444 }
445 }
446}
447
448
449sub send_mail
450{
451 my $self = shift @_;
452
453 my ($mail_subject,$mail_content) = @_;
454
455 my $gsdl_cgi = $self->{'gsdl_cgi'};
456
457 my $sendmail_command = "\"".&util::get_perl_exec()."\" -S sendmail.pl";
458 $sendmail_command .= " -to \"" . $mail_to_address . "\"";
459 $sendmail_command .= " -from \"" . $mail_from_address . "\"";
460 $sendmail_command .= " -smtp \"" . $mail_smtp_server . "\"";
461 $sendmail_command .= " -subject \"" . $mail_subject . "\"";
462
463 if (!open(POUT, "| $sendmail_command")) {
464 $gsdl_cgi->generate_error("Unable to execute command: $sendmail_command");
465 }
466 print POUT $mail_content . "\n";
467 close(POUT);
468}
469
470
471sub run_script
472{
473 my $self = shift @_;
474
475 my ($collect, $site, $script) = @_;
476
477 my $gsdl_cgi = $self->{'gsdl_cgi'};
478
479 my $perl_args = $collect;
480
481 my $collect_dir = $gsdl_cgi->get_collection_dir($site);
482 $perl_args = "-collectdir \"$collect_dir\" " . $perl_args;
483
484 my $perl_command = "\"".&util::get_perl_exec()."\" -S $script $perl_args";
485
486
487 # IIS 6: redirecting output from STDERR to STDOUT just doesn't work, so
488 # we have to let it go directly out to the page
489
490 if (!$self->{'iis6_mode'})
491 {
492 $perl_command .= " 2>&1";
493 }
494
495 if (!open(PIN, "$perl_command |")) {
496 $gsdl_cgi->generate_error("Unable to execute command: $perl_command");
497 }
498
499 print STDOUT "Content-type:text/plain\n\n";
500 print "$perl_command \n";
501
502 while (defined (my $perl_output_line = <PIN>)) {
503 print STDOUT $perl_output_line;
504 }
505 close(PIN);
506
507 my $perl_status = $?;
508 if ($perl_status > 0) {
509 $gsdl_cgi->generate_error("Perl failed: $perl_command\n--\nExit status: " . ($perl_status / 256));
510 }
511}
512
5131;
Note: See TracBrowser for help on using the repository browser.