1 /*
2 ** Copyright (c) 2002 D. Richard Hipp
3 **
4 ** This program is free software; you can redistribute it and/or
5 ** modify it under the terms of the GNU General Public
6 ** License as published by the Free Software Foundation; either
7 ** version 2 of the License, or (at your option) any later version.
8 **
9 ** This program is distributed in the hope that it will be useful,
10 ** but WITHOUT ANY WARRANTY; without even the implied warranty of
11 ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 ** General Public License for more details.
13 **
14 ** You should have received a copy of the GNU General Public
15 ** License along with this library; if not, write to the
16 ** Free Software Foundation, Inc., 59 Temple Place - Suite 330,
17 ** Boston, MA 02111-1307, USA.
18 **
19 ** Author contact information:
20 ** drh@hwaci.com
21 ** http://www.hwaci.com/drh/
22 **
23 *******************************************************************************
24 **
25 ** Routines for handling user account
26 */
27 #define _XOPEN_SOURCE
28 #include <unistd.h>
29 #include "config.h"
30 #include "user.h"
31
32 /*
33 ** WEBPAGE: /userlist
34 */
user_list(void)35 void user_list(void){
36 char **azResult;
37 int i;
38
39 login_check_credentials();
40 if( !g.okWrite && g.isAnon ){
41 login_needed();
42 return;
43 }
44 common_standard_menu("userlist", 0);
45 common_add_help_item("CvstracAdminUsers");
46 common_add_action_item("useredit", "Add User");
47 common_header("User List");
48 @ <table cellspacing=0 cellpadding=0 border=0>
49 @ <tr>
50 @ <th align="right"><nobr>User ID</nobr></th>
51 @ <th> Permissions </th>
52 @ <th><nobr>In Real Life</nobr></th>
53 @ </tr>
54 azResult = db_query(
55 "SELECT id, name, email, capabilities FROM user ORDER BY id");
56 for(i=0; azResult[i]; i+= 4){
57 @ <tr>
58 @ <td align="right">
59 if( g.okAdmin ){
60 @ <a href="useredit?id=%t(azResult[i])">
61 }
62 @ <nobr>%h(azResult[i])</nobr>
63 if( g.okAdmin ){
64 @ </a>
65 }
66 @ </td>
67 @ <td align="center">%s(azResult[i+3])</td>
68 if( azResult[i+2] && azResult[i+2][0] ){
69 char *zE = azResult[i+2];
70 @ <td align="left"><nobr>%h(azResult[i+1])
71 @ (<a href="mailto:%h(zE)">%h(zE)</a>)</nobr></td>
72 } else {
73 @ <td align="left"><nobr>%h(azResult[i+1])</nobr></td>
74 }
75 @ </tr>
76 }
77 @ </table>
78 @ <p><hr>
79 @ <b>Notes:</b>
80 @ <ol>
81 @ <li><p>The permission flags are as follows:</p>
82 @ <table>
83 @ <tr><td>a</td><td width="10"></td>
84 @ <td>Admin: Create or delete users and ticket report formats</td></tr>
85 @ <tr><td>d</td><td></td>
86 @ <td>Delete: Erase anonymous wiki, tickets, and attachments</td></tr>
87 @ <tr><td>i</td><td></td>
88 @ <td>Check-in: Add new code to the %h(g.scm.zName) repository</td></tr>
89 @ <tr><td>j</td><td></td><td>Read-Wiki: View wiki pages</td></tr>
90 @ <tr><td>k</td><td></td><td>Wiki: Create or modify wiki pages</td></tr>
91 @ <tr><td>n</td><td></td><td>New: Create new tickets</td></tr>
92 @ <tr><td>o</td><td></td>
93 @ <td>Check-out: Read code out of the %h(g.scm.zName) repository</td></tr>
94 @ <tr><td>p</td><td></td><td>Password: Change password</td></tr>
95 @ <tr><td>q</td><td></td><td>Query: Create or edit report formats</td></tr>
96 @ <tr><td>r</td><td></td><td>Read: View tickets and change histories</td></tr>
97 @ <tr><td>s</td><td></td><td>Setup: Change CVSTrac options</td></tr>
98 @ <tr><td>w</td><td></td><td>Write: Edit tickets</td></tr>
99 @ </table>
100 @ </p></li>
101 @
102 @ <li><p>
103 @ If a user named "<b>anonymous</b>" exists, then anyone can access
104 @ the server without having to log in. The permissions on the
105 @ anonymous user determine the access rights for anyone who is not
106 @ logged in.
107 @ </p></li>
108 @
109 if( !strcmp(g.scm.zSCM,"cvs") ){
110 @ <li><p>
111 @ You must be using CVS version 1.11 or later in order to give users
112 @ read-only access to the repository.
113 @ With earlier versions of CVS, all users with check-out
114 @ privileges also automatically get check-in privileges.
115 @ </p></li>
116 @
117 @ <li><p>
118 @ Changing a users ID or password modifies the <b>CVSROOT/passwd</b>,
119 @ <b>CVSROOT/readers</b>, and <b>CVSROOT/writers</b> files in the CVS
120 @ repository, if those files have write permission turned on. Users
121 @ IDs in <b>CVSROOT/passwd</b> that are unknown to CVSTrac are preserved.
122 if( g.okSetup ){
123 @ Use the "Import CVS Users" button on the
124 @ <a href="setup_user">user setup</a> page
125 @ to import CVS users into CVSTrac.
126 }
127 @ </p></li>
128 }
129 @ </ol>
130 common_footer();
131 }
132
133 /*
134 ** WEBPAGE: /useredit
135 */
user_edit(void)136 void user_edit(void){
137 char **azResult;
138 const char *zId, *zName, *zEMail, *zCap;
139 char *oaa, *oas, *oar, *oaw, *oan, *oai, *oaj, *oao, *oap ;
140 char *oak, *oad, *oaq;
141 int doWrite;
142 int higherUser = 0; /* True if user being edited is SETUP and the */
143 /* user doing the editing is ADMIN. Disallow editing */
144
145 /* Must have ADMIN privleges to access this page
146 */
147 login_check_credentials();
148 if( !g.okAdmin ){ login_needed(); return; }
149
150 /* Check to see if an ADMIN user is trying to edit a SETUP account.
151 ** Don't allow that.
152 */
153 zId = P("id");
154 if( zId && !g.okSetup ){
155 char *zOldCaps;
156 zOldCaps = db_short_query(
157 "SELECT capabilities FROM user WHERE id='%q'",zId);
158 higherUser = zOldCaps && strchr(zOldCaps,'s');
159 }
160
161 if( !higherUser ){
162 if( P("delete") ){
163 common_add_action_item("userlist", "Cancel");
164 common_header("Are You Sure?");
165 @ <form action="useredit" method="POST">
166 @ <p>You are about to delete the user <strong>%h(zId)</strong> from
167 @ the database. This is an irreversible operation.</p>
168 @
169 @ <input type="hidden" name="id" value="%t(zId)">
170 @ <input type="hidden" name="nm" value="">
171 @ <input type="hidden" name="em" value="">
172 @ <input type="hidden" name="pw" value="">
173 @ <input type="submit" name="delete2" value="Delete The User">
174 @ <input type="submit" name="can" value="Cancel">
175 @ </form>
176 common_footer();
177 return;
178 }else if( P("can") ){
179 cgi_redirect("userlist");
180 return;
181 }
182 }
183
184 /* If we have all the necessary information, write the new or
185 ** modified user record. After writing the user record, redirect
186 ** to the page that displays a list of users.
187 */
188 doWrite = zId && zId[0] && cgi_all("nm","em","pw") && !higherUser;
189 if( doWrite ){
190 const char *zOldPw;
191 char zCap[20];
192 int i = 0;
193 int aa = P("aa")!=0;
194 int ad = P("ad")!=0;
195 int ai = P("ai")!=0;
196 int aj = P("aj")!=0;
197 int ak = P("ak")!=0;
198 int an = P("an")!=0;
199 int ao = P("ao")!=0;
200 int ap = P("ap")!=0;
201 int aq = P("aq")!=0;
202 int ar = P("ar")!=0;
203 int as = g.okSetup && P("as")!=0;
204 int aw = P("aw")!=0;
205 if( as ) aa = 1;
206 if( aa ) ai = aw = ap = 1;
207 if( aw ) an = ar = 1;
208 if( ai ) ao = 1;
209 if( ak ) aj = 1;
210 if( aa ){ zCap[i++] = 'a'; }
211 if( ad ){ zCap[i++] = 'd'; }
212 if( ai ){ zCap[i++] = 'i'; }
213 if( aj ){ zCap[i++] = 'j'; }
214 if( ak ){ zCap[i++] = 'k'; }
215 if( an ){ zCap[i++] = 'n'; }
216 if( ao ){ zCap[i++] = 'o'; }
217 if( ap ){ zCap[i++] = 'p'; }
218 if( aq ){ zCap[i++] = 'q'; }
219 if( ar ){ zCap[i++] = 'r'; }
220 if( as ){ zCap[i++] = 's'; }
221 if( aw ){ zCap[i++] = 'w'; }
222
223 zCap[i] = 0;
224 zOldPw = db_short_query("SELECT passwd FROM user WHERE id='%q'", zId);
225 db_execute("DELETE FROM user WHERE id='%q'", zId);
226 if( !P("delete2") ){
227 const char *zPw = P("pw");
228 char zBuf[3];
229 if( zOldPw==0 ){
230 char zSeed[100];
231 const char *z;
232 bprintf(zSeed,sizeof(zSeed),"%d%.20s",getpid(),zId);
233 z = crypt(zSeed, "aa");
234 zBuf[0] = z[2];
235 zBuf[1] = z[3];
236 zBuf[2] = 0;
237 zOldPw = zBuf;
238 }
239 db_execute(
240 "INSERT INTO user(id,name,email,passwd,capabilities) "
241 "VALUES('%q','%q','%q','%q','%s')",
242 zId, P("nm"), P("em"), zPw[0] ? crypt(zPw, zOldPw) : zOldPw, zCap
243 );
244 }else{
245 /* User was default assigned user id. Remove the default. */
246 db_execute( "DELETE FROM config WHERE "
247 " name='assignto' AND value='%q'", zId);
248 }
249
250 /*
251 ** The SCM subsystem may be able to replicate the user db somewhere...
252 */
253 if( g.scm.pxUserWrite ) g.scm.pxUserWrite(P("delete2")!=0 ? zId : 0);
254
255 cgi_redirect("userlist");
256 return;
257 }
258
259 /* Load the existing information about the user, if any
260 */
261 zName = "";
262 zEMail = "";
263 zCap = "";
264 oaa = oad = oai = oaj = oak = oan = oao = oap = oaq = oar = oas = oaw = "";
265 if( zId ){
266 azResult = db_query(
267 "SELECT name, email, capabilities FROM user WHERE id='%q'", zId
268 );
269 if( azResult && azResult[0] ){
270 zName = azResult[0];
271 zEMail = azResult[1];
272 zCap = azResult[2];
273 if( strchr(zCap, 'a') ) oaa = " checked";
274 if( strchr(zCap, 'd') ) oad = " checked";
275 if( strchr(zCap, 'i') ) oai = " checked";
276 if( strchr(zCap, 'j') ) oaj = " checked";
277 if( strchr(zCap, 'k') ) oak = " checked";
278 if( strchr(zCap, 'n') ) oan = " checked";
279 if( strchr(zCap, 'o') ) oao = " checked";
280 if( strchr(zCap, 'p') ) oap = " checked";
281 if( strchr(zCap, 'q') ) oaq = " checked";
282 if( strchr(zCap, 'r') ) oar = " checked";
283 if( strchr(zCap, 's') ) oas = " checked";
284 if( strchr(zCap, 'w') ) oaw = " checked";
285 }else{
286 zId = 0;
287 }
288 }
289
290 /* Begin generating the page
291 */
292 common_standard_menu(0,0);
293 common_add_help_item("CvstracAdminUsers");
294 common_add_action_item("userlist", "Cancel");
295 common_add_action_item(mprintf("useredit?delete=1&id=%t",zId), "Delete");
296 if( zId ){
297 common_header("Edit User %s", zId);
298 }else{
299 common_header("Add New User");
300 }
301 @ <form action="%s(g.zPath)" method="POST">
302 @ <table align="left" hspace=10 vspace=10>
303 @ <tr>
304 @ <td align="right"><nobr>User ID:</nobr></td>
305 if( zId ){
306 @ <td>%h(zId) <input type="hidden" name="id" value="%h(zId)"></td>
307 }else{
308 @ <td><input type="text" name="id" size=10></td>
309 }
310 @ </tr>
311 @ <tr>
312 @ <td align="right"><nobr>Full Name:</nobr></td>
313 @ <td><input type="text" name="nm" value="%h(zName)"></td>
314 @ </tr>
315 @ <tr>
316 @ <td align="right"><nobr>E-Mail:</nobr></td>
317 @ <td><input type="text" name="em" value="%h(zEMail)"></td>
318 @ </tr>
319 @ <tr>
320 @ <td align="right" valign="top">Capabilities:</td>
321 @ <td>
322 @ <input type="checkbox" name="aa"%s(oaa)>Admin</input><br>
323 @ <input type="checkbox" name="ad"%s(oad)>Delete</input><br>
324 @ <input type="checkbox" name="ai"%s(oai)>Check-In</input><br>
325 @ <input type="checkbox" name="aj"%s(oaj)>Read Wiki</input><br>
326 @ <input type="checkbox" name="ak"%s(oak)>Write Wiki</input><br>
327 @ <input type="checkbox" name="an"%s(oan)>New Tkt</input><br>
328 @ <input type="checkbox" name="ao"%s(oao)>Check-Out</input><br>
329 @ <input type="checkbox" name="ap"%s(oap)>Password</input><br>
330 @ <input type="checkbox" name="aq"%s(oaq)>Query</input><br>
331 @ <input type="checkbox" name="ar"%s(oar)>Read</input><br>
332 if( g.okSetup ){
333 @ <input type="checkbox" name="as"%s(oas)>Setup</input><br>
334 }
335 @ <input type="checkbox" name="aw"%s(oaw)>Write</input>
336 @ </td>
337 @ </tr>
338 @ <tr>
339 @ <td align="right">Password:</td>
340 @ <td><input type="password" name="pw" value=""></td>
341 @ </tr>
342 if( !higherUser ){
343 @ <tr>
344 @ <td> </td>
345 @ <td><input type="submit" name="submit" value="Apply Changes">
346 @
347 @ <input type="submit" name="delete" value="Delete User"></td>
348 @ </tr>
349 }
350 @ </table>
351 @ <p><b>Notes:</b></p>
352 @ <ol>
353 if( higherUser ){
354 @ <li><p>
355 @ User %h(zId) has Setup privileges and you only have Admin privileges
356 @ so you are not permitted to make changes to %h(zId).
357 @ </p></li>
358 @
359 }
360 if( g.scm.pxUserWrite!=0
361 && !strcmp("yes",db_config("write_cvs_passwd","yes")) ){
362 @ <li><p>
363 @ If the <b>Check-out</b> capability is specified then
364 @ the password entered here will be used to regenerate the
365 @ <b>CVSROOT/passwd</b> file and will thus become the CVS password
366 @ as well as the password for this server.
367 @ </p></li>
368 @
369 @ <li><p>
370 @ The <b>Check-in</b> capability means that the user ID will be written
371 @ into the <b>CVSROOT/writers</b> file and thus allow write access to
372 @ the CVS repository.
373 @ </p></li>
374 @
375 }else{
376 @ <li><p>
377 @ If the <b>Check-out</b> capability is specified then the user will be able
378 @ to browse the %s(g.scm.zName) repository.
379 @ </p></li>
380 @
381 @ <li><p>
382 @ The <b>Check-in</b> capability gives the user the ability to edit check-in
383 @ messages.
384 @ </p></li>
385 @
386 }
387 @ <li><p>
388 @ The <b>Read</b> and <b>Write</b> privileges give the user the ability
389 @ to read and write tickets. The <b>New Tkt</b> capability means that
390 @ the user is able to create new tickets.
391 @ </p></li>
392 @
393 @ <li><p>
394 @ The <b>Delete</b> privilege give the user the ability to erase
395 @ wiki, tickets, and atttachments that have been added by anonymous
396 @ users. This capability is intended for deletion of spam.
397 @ </p></li>
398 @
399 @ <li><p>
400 @ The <b>Query</b> privilege allows the user to create or edit
401 @ report formats by specifying appropriate SQL. Users can run
402 @ existing reports without the Query privilege.
403 @ </p></li>
404 @
405 @ <li><p>
406 @ An <b>Admin</b> user can add other users, create new ticket report
407 @ formats, and change system defaults. But only the <b>Setup</b> user
408 @ is able to change the %h(g.scm.zName) repository to
409 @ which this program is linked.
410 @ </p></li>
411 @
412 if( zId==0 || strcmp(zId,"anonymous")==0 ){
413 @ <li><p>
414 @ No login is required for user "<b>anonymous</b>". The capabilities
415 @ of this user are available to anyone without supplying a username or
416 @ password. To disable anonymous access, make sure there is no user
417 @ with an ID of <b>anonymous</b>.
418 @ </p></li>
419 @
420 @ <li><p>
421 @ The password for the "<b>anonymous</b>" user is used for anonymous
422 @ %h(g.scm.zName) access. The recommended value for the anonymous password
423 @ is "anonymous".
424 @ </p></li>
425 }
426 @ </form>
427 common_footer();
428 }
429
430 /*
431 ** Remove the newline from the end of a string.
432 */
433 void remove_newline(char *z){
434 while( *z && *z!='\n' && *z!='\r' ){ z++; }
435 if( *z ){ *z = 0; }
436 }
437