Also look in the next-highest directory when detecting VCS; add SVN
[interchange.git] / code / UI_Tag / su.coretag
1 # Copyright 2002-2007 Interchange Development Group and others
2
3 # This program is free software; you can redistribute it and/or modify
4 # it under the terms of the GNU General Public License as published by
5 # the Free Software Foundation; either version 2 of the License, or
6 # (at your option) any later version.  See the LICENSE file for details.
7
8 UserTag su Description  Switch User Tag for catalog superuser
9 UserTag su Order        username
10 UserTag su attrAlias    user username
11 UserTag su addAttr
12 UserTag su Version      1.8
13 UserTag su Routine      <<EOR
14 sub {
15         my ($user, $opt) = @_;
16         use vars qw/$Session $Tag $ready_safe $Scratch/;
17
18         # Note: If adding any new %$opt keys, make sure to also add them to
19         # the list of options to be stripped before passing the remainder
20         # to tag userdb; search below for $new_user.
21
22         $opt->{profile} = 'ui'     if $opt->{admin} and ! $opt->{profile};
23
24         my $u;
25         if($opt->{profile}) {
26                 $u = $Vend::Cfg->{UserDB_repository}{$opt->{profile}};
27         }
28         else {
29                 $u = $Vend::Cfg->{UserDB};
30         }
31
32         if(! $u) {
33                 my $place = $opt->{profile} || 'default';
34                 ::logError("Can't find UserDB repository, profile '%s'", $place);
35                 return undef;
36         }
37         my $table  = $u->{database} || 'userdb';
38         my $ufield = $u->{user_field} || 'username';
39         my $going_to_admin = $u->{admin} || $opt->{admin};
40 #::logDebug("user table=$table ufield=$ufield");
41
42         if ($opt->{create_user}) {
43                 # these settings must be done before any access to the table
44                 $Vend::WriteDatabase{$table} = 1;
45         }
46
47         my $super  = $Tag->if_mm('super');
48         my $former = $Vend::username;
49
50         if($user and $going_to_admin and ! $super) {
51                 ::logError("attempt to su to admin user %s by non-super user %s",
52                                                 $user,
53                                                 $former,
54                                         );
55                 return undef;
56         }
57         elsif($user and ! $Vend::admin) {
58                 ::logError("attempt to su to user %s by non-admin user %s",
59                                                 $user,
60                                                 $former,
61                                         );
62                 return undef;
63         }
64
65         my $dir = "$Global::ConfDir/tmp";
66         if (! -d $dir) {
67                 if(-e $dir) {
68                         logGlobal("Global tmp directory exists as file, aborting su");
69                         return undef;
70                 }
71                 File::Path::mkpath($dir);
72         }
73
74         if($opt->{exit}) {
75                 if(! $Session->{su}) {
76                         logError("attempt to return to superuser without saved session.");
77                         return;
78                 }
79                 my $string = delete $Session->{su};
80                 my $key = $Tag->read_cookie({ name => 'MV_SU_KEY'})
81                         or do {
82                                 logError("no session key in cookie, cannot exit");
83                                 return;
84                         };
85                 my $fn = "$dir/$Session->{id}";
86                 open(MDCHECK, "< $fn")
87                         or do {
88                                 logError("no saved session key in %s, cannot exit", $fn);
89                                 return;
90                         };
91                 my $rand = <MDCHECK>;
92                 close MDCHECK;
93                 if(generate_key($rand . $string) ne $key) {
94                         logError("mismatched session key with saved session, cannot exit");
95                         return;
96                 }
97
98                 my $former = $Session->{username};
99                 ## Authenticated
100                 undef $Vend::Session;
101                 undef $Session;
102                 $Vend::Session = $ready_safe->reval($string);
103                 $Session = $Vend::Session;
104                 delete $Session->{su};
105                 $Vend::admin = $Vend::Session->{admin};
106                 $Vend::username = $Vend::Session->{username};
107                 $Tag->if_mm('logged_in')
108                         and logError(
109                                         "Admin user %s returned from login as %s",
110                                         $Session->{username},
111                                         $former,
112                                 )
113                         and return 1;
114                 return;
115         }
116         elsif ($user) {
117                 my $new_user;
118                 if(! $Tag->data($table, $ufield, $user) ) {
119                         if ($opt->{create_user}) {
120                                 $new_user = 1;
121                         }
122                         else {
123                                 $Scratch->{ui_error} = errmsg("attempt to su to non-existent user %s", $user);
124                                 return undef;
125                         }
126                 }
127
128                 my $rand        = random_string();
129                 my $sess        = uneval_it($Session);
130 #::logDebug("sess is $sess");
131                 my $sesskey     = generate_key($rand . $sess);
132
133                 open(MDIT, "> $dir/$Session->{id}")
134                         or die errmsg("Can't create check file for su: %s\n", $!);
135                 print MDIT $rand;
136                 close MDIT;
137                 $Tag->set_cookie( { name => 'MV_SU_KEY', value => $sesskey } );
138                 my $former = $Session->{username};
139
140                 undef $Vend::admin;
141                 undef $Vend::superuser;
142                 undef $Vend::UI_entry;
143
144                 Vend::Session::init_session();
145                 $Session = $Vend::Session;
146
147                 if ($new_user) {
148                         # pass on any non-su options to userdb tag
149                         my $newopt = { %$opt };
150                         delete @{$newopt}{qw( admin exit create_user )};
151                         $newopt->{username} = $user;
152                         my $result = $Tag->userdb('new_account', $newopt);
153                         unless ($result) {
154                                 my $error = errmsg("Failed to create new user '%s' in su tag", $user);
155                                 logError($error);
156                                 $Scratch->{ui_error} = $error;
157                                 return undef;
158                         }
159                         $Session->{su} = $sess;
160                 }
161                 else {
162                         $Vend::username = $Session->{username} = $user;
163                         $Vend::admin    = $Session->{admin}    = $going_to_admin;
164                         $Session->{logged_in} = 1;
165                         $Session->{su} = $sess;
166                         $Tag->userdb('load');
167                 }
168
169                 ## Reconnect session variables
170                 Vend::Interpolate::init_calc;
171
172                 my $dest = $Tag->if_mm('logged_in') ? 'admin user' : 'regular user';
173                 logError(
174                         "superuser %s switched user to %s %s",
175                         $former,
176                         $dest,
177                         $Session->{username},
178                         );
179                 return 1;
180         }
181         else {
182                 ::logError("unknown su operation: " . uneval_it($opt));
183                 return undef;
184         }
185 }
186 EOR