xaizek / rocketgit (License: AGPLv3+) (since 2018-12-09)
Light and fast Git hosting solution suitable to serve both as a hub or as a personal code storage with its tickets, pull requests, API and much more.
Commit a34c707720da68378086f73f16dd25c5a1ca8072

Lots of bug fixes, especially for cache
Author: Catalin(ux) M. BOIE
Author date (UTC): 2015-09-07 06:06
Committer name: Catalin(ux) M. BOIE
Committer date (UTC): 2015-09-07 06:06
Parent(s): 90eed0aa343f735f717238e3937439737290ecfd
Signing key:
Tree: e405182a033da0b93e56b86d2ba24f48f99d98ed
File Lines added Lines deleted
TODO 57 17
hooks/post-receive 7 26
hooks/pre-commit 2 9
inc/cache.inc.php 133 219
inc/git.inc.php 15 18
inc/log.inc.php 0 7
inc/prof.inc.php 3 0
inc/rights.inc.php 29 23
inc/sess.inc.php 9 5
inc/ssh.inc.php 54 47
inc/struct.inc.php 2 1
inc/totp.inc.php 162 137
inc/user.inc.php 33 28
inc/user/home-page.php 4 5
inc/user/keys/keys.php 1 1
inc/util.inc.php 65 8
root/index.php 2 4
root/themes/default/features.html 2 4
root/themes/default/hints/repo/edit_rights.html 2 1
root/themes/default/mail/user/repo/bug/new.subj.txt 1 1
root/themes/default/mail/user/repo/bug/new_note.subj.txt 1 1
root/themes/default/repo/list/nodata.html 1 0
root/themes/default/user/home.html 3 0
root/themes/default/user/keys/list/header.html 0 1
root/themes/default/user/keys/main.html 1 0
root/themes/extract_texts.sh 13 0
scripts/cache.php 43 23
scripts/cron.php 16 6
scripts/remote.php 25 19
tests/Makefile 4 1
tests/cache.php 20 2
tests/helpers.inc.php 11 1
tests/http.inc.php 12 9
tests/http_settings.php 27 2
tests/http_top.php 2 1
tests/http_totp.php 22 37
tests/rights.php 42 0
tests/totp.php 36 0
File TODO changed (mode: 100644) (index 1d7461a..f9df6b6)
1 1 == Where I stopped last time == == Where I stopped last time ==
2 [ ] retest login by token
2 [ ] totp: login by token in http_totp.php seems to not work.
3 check server logs.
4 [ ] unit test: do not check for "invalid user ..." - it may change
5 test that we landed on the home page!
6 [ ] totp: should we update at once, and verify that the token is really unused?
7 (do not delay till event processing?)
8 [ ] totp: store last ts used, and do not allow reuse.
9 The problem is if both session sends the same token (the attacker and
10 the good user).
11 [ ] totp: update last_use also by ssh!
12 [ ] unit tests fails.
3 13 [ ] [ ]
4 14
5 15 == BEFORE NEXT RELEASE == == BEFORE NEXT RELEASE ==
16 [ ] Extend rg_cache_count for the other rg_cache_* commands.
17 [ ] Restart the cache daemon when an upgrade takes place.
18 Done, test.
19 [ ] Report number of lines of code (and how much it worth) and number of other
20 type of documents.
21 [ ] Add some flags for users: "Coming from GitHub", to be able to give
22 tailored hints.
23 [ ] totp: take care of the race: simultaneously login with the same token.
24 [ ] totp: warn user that if a token is not validated for 1 month will be deleted?
25 [ ] totp: allow prefix for IP addresses.
26 [ ] totp: think about authorizing a push, not the ip (ip may be dynamic).
27 Somethig like, allow the push as pending, but ask for authorization
28 to be sent back:
29 ssh ... totp val-push <push_id> <token>
30 and remove the push from pending.
31 [ ] "repo was changed": nothing interesting?!
32 If nothing changed, do not send the mail.
33 [ ] The URL to the repo presnet in mail is rocketgit.com. We may want to use
34 the http host name in there.
35 [ ] bugs: if none present, just go to 'Add' page.
36 [ ] Seems 'push' is denied for owner!
37 Seems I have the key on both 'admin' user and 'catab'!
38 Do something about this?
39 [ ] features: 'Lightest': Add a note describing that is lightest also with
40 the browser.
41 [ ] Document how to deny non ascii filenames using repo_path.
42 [ ] When a right denies access, also output the description of that right.
43 [ ] Tell clear that rg can be used also for books, articles, documentation etc.
44 [ ] Include uid in namespace path to avoid clashes with other users?
45 [ ] ssh: show the fingerprint of the used key?
46 [ ] Detect when a user is cloning a repo and update stats? Seems I cannot do
47 this easily becasue I just call git-shell.
48 But, at least the fetch can be recorded in stats.
49 [ ] Add cache in rg_git_log.
50 [ ] unit test for login by token
51 [ ] Unit test for fetching by ssh a public repo regarding TOTP
6 52 [ ] Do I need to update last time used for login_tokens when accessing by ssh? [ ] Do I need to update last time used for login_tokens when accessing by ssh?
7 53 Pretty sure, yes. Pretty sure, yes.
8 54 [ ] history: add 2fa ssh validation. [ ] history: add 2fa ssh validation.
9 55 [ ] ionut: Check this to not be send X-PHP-Originating-Script: 0:user.inc.php [ ] ionut: Check this to not be send X-PHP-Originating-Script: 0:user.inc.php
10 [ ] Test if IPv4/IPv6 address is shown at login time.
56 [ ] VM: Test if IPv4/IPv6 address is shown at login time.
11 57 [ ] When listing repos on user homepage, we should not add also the user. [ ] When listing repos on user homepage, we should not add also the user.
12 58 Check rg_repo_list. Check rg_repo_list.
13 59 [ ] totp for ssh is not finished yet. remember, totp for ssh seems to be only [ ] totp for ssh is not finished yet. remember, totp for ssh seems to be only
 
22 68 [ ] Add history for totp enrollment. [ ] Add history for totp enrollment.
23 69 [ ] Add history for logins/logouts/API. [ ] Add history for logins/logouts/API.
24 70 [ ] Add max_requests per hour for plans and enforce them. [ ] Add max_requests per hour for plans and enforce them.
25 [ ] When setting last used, we need to pass the timestamp to the event and
26 we msut not use 'time()' because event may happen at a latter time!
27 71 [ ] Protect login by country/ua? [ ] Protect login by country/ua?
28 72 [ ] Improve input forms to be friendly with mobile phones: give html5 hints. [ ] Improve input forms to be friendly with mobile phones: give html5 hints.
29 73 [ ] Use guestmount when building VM images? [ ] Use guestmount when building VM images?
 
40 84 [ ] totp: update 'utime' for login tokens [ ] totp: update 'utime' for login tokens
41 85 [ ] totp: mark last timestamp used for ok login to not be able to reuse the [ ] totp: mark last timestamp used for ok login to not be able to reuse the
42 86 token; what if the time is in the future? we will not cache it. token; what if the time is in the future? we will not cache it.
43 [ ] totp: login: prevent auto filling/caching of login token
44 switch to 'password' type?
87 Better, do not allow the same ts.
88 [ ] totp: switch to 'password' type for login_token (login page)?
45 89 [ ] totp: hints: [ ] totp: hints:
46 only for admin: install qrencode package...
47 90 AWS asks for two consecutive codes. why? AWS asks for two consecutive codes. why?
48 91 Google also provides a list of backup tokens to be printed. Google also provides a list of backup tokens to be printed.
49 92 "If you want to activate the TOTP extra step, follow the instructions: "If you want to activate the TOTP extra step, follow the instructions:
 
52 95 Instruct user to remove the token if the phone is lost. But, remind Instruct user to remove the token if the phone is lost. But, remind
53 96 user that the account may not be compromised without pass. user that the account may not be compromised without pass.
54 97 [ ] totp: think about loosing the phone. [ ] totp: think about loosing the phone.
55 [ ] totp: how to set it up:
56 press a menu to activate it, show QR code if possible, show base32 key
57 also, ask user to enter the token to finish the activation.
58 warn about time desync; maybe ask the user to press the 'sync' in
98 [ ] totp: warn about time desync; maybe ask the user to press the 'sync' in
59 99 the mobile app. the mobile app.
60 [ ] totp: we may validate the login on one ip and keep ip for x hours.
61 100 [ ] totp: what if I encrypt key with the password and decrypt only at login? [ ] totp: what if I encrypt key with the password and decrypt only at login?
62 101 (If somebody steals the database, will not have the keys). (If somebody steals the database, will not have the keys).
102 Cannot do. We need it also at push by ssh.
63 103 [ ] totp: hints for ssh [ ] totp: hints for ssh
64 104 [ ] totp: Implement 2 factor auth [ ] totp: Implement 2 factor auth
65 105 (check https://korg.wiki.kernel.org/userdoc/gitolite_2fa) (check https://korg.wiki.kernel.org/userdoc/gitolite_2fa)
 
70 110 Change ids to be protected from interference. Change ids to be protected from interference.
71 111 [ ] Use bintray.com to distribute isos? [ ] Use bintray.com to distribute isos?
72 112 [ ] When session expires and I press logout, no message is shown. [ ] When session expires and I press logout, no message is shown.
73 [ ] When creating an account, seems the email is cached, not the username!
113 [ ] When creating an account, seems the email is used as the username in
114 browser cache, not the username!
74 115 Check! Check!
75 [ ] Users should be able to check the plans.
76 116 [ ] After login, show the last ip and date of the last login? [ ] After login, show the last ip and date of the last login?
117 [ ] Users should be able to check the plans.
77 118 [ ] I should show some 'plan' islands when you create the account [ ] I should show some 'plan' islands when you create the account
78 119 so the user will know the disk space and bandwidth. so the user will know the disk space and bandwidth.
79 120 [ ] In a table, if nothing can be deleted, do not show the delete button. [ ] In a table, if nothing can be deleted, do not show the delete button.
80 [ ] Add IP address in /etc/issue (or /etc/motd), somehow to appear, maybe using
81 a systemd script that runs before terminal?
82 [ ] web hooks: start with a http post to a custom server.
121 [ ] web hooks: start with a http post to a user server.
122 warn the user if is not working?
83 123 [ ] When giving some users rights to your repo, do not spam them with [ ] When giving some users rights to your repo, do not spam them with
84 124 messages. The user must agree to be spammed. Best, no notification messages. The user must agree to be spammed. Best, no notification
85 125 is ever issued. User may go to project to activate them if s/he wants. is ever issued. User may go to project to activate them if s/he wants.
File hooks/post-receive changed (mode: 100755) (index 59929df..582ef11)
... ... require_once($INC . "/sql.inc.php");
29 29 require_once($INC . "/repo.inc.php"); require_once($INC . "/repo.inc.php");
30 30 require_once($INC . "/prof.inc.php"); require_once($INC . "/prof.inc.php");
31 31
32 rg_prof_start("post-receive");
32 rg_prof_start('post-receive');
33 33
34 34 rg_log_set_file($rg_log_dir . "/hook_post-receive.log"); rg_log_set_file($rg_log_dir . "/hook_post-receive.log");
35 35
 
... ... rg_log("_SERVER: " . rg_array2string($_SERVER));
41 41
42 42 umask(0022); umask(0022);
43 43
44 $f = @fopen("php://stdin", "r");
45 if ($f === FALSE) {
46 rg_log("Error: Cannot open stdin!");
47 rg_git_fatal("Internal error!");
48 }
49 while (($set = fgets($f))) {
44 while (($set = fgets(STDIN))) {
50 45 $set = trim($set); $set = trim($set);
51 46 if (empty($set)) if (empty($set))
52 47 continue; continue;
53 48
54 $x = explode(" ", $set);
49 $x = explode(' ', $set);
55 50 $old_rev = @rg_git_rev($x[0]); $old_rev = @rg_git_rev($x[0]);
56 51 $new_rev = @rg_git_rev($x[1]); $new_rev = @rg_git_rev($x[1]);
57 52 $refname = @rg_git_reference($x[2]); $refname = @rg_git_reference($x[2]);
 
... ... while (($set = fgets($f))) {
63 58
64 59 rg_log("refname=$refname old_rev=$old_rev new_rev=$new_rev."); rg_log("refname=$refname old_rev=$old_rev new_rev=$new_rev.");
65 60 } }
66 fclose($f);
67
68 /* TODO: replace with event - do not forget to dirty the repo
69 $a = array(
70 "op" => "push",
71 "itime" => getenv("ROCKETGIT_ITIME"),
72 "uid" => getenv("ROCKETGIT_LOGIN_UID"),
73 "repo_id" => getenv("ROCKETGIT_REPO_ID"),
74 "old_rev" => $old_rev,
75 "new_rev" => $new_rev,
76 "refname" => $refname,
77 "elap_ms" => $_start - getenv("ROCKETGIT_ITIME"),
78 "ip" => getenv("ROCKETGIT_IP")
79 );
80 rg_repo_stats_push2file($a);
81 */
82
83 rg_prof_end("post-receive");
61
62 rg_del_tree($repo_path . '/refs/namespaces/' . $namespace);
63
64 rg_prof_end('post-receive');
84 65 rg_prof_log(); rg_prof_log();
85 66 ?> ?>
File hooks/pre-commit changed (mode: 100755) (index b7b8ea0..1abab33)
2 2 <?php <?php
3 3 // This is called by 'pre-commit' hook // This is called by 'pre-commit' hook
4 4 // Inspired by pre-commit.sample in git package // Inspired by pre-commit.sample in git package
5
5 6 error_reporting(E_ALL); error_reporting(E_ALL);
6 7 ini_set("track_errors", "On"); ini_set("track_errors", "On");
7 8
 
... ... else
37 38
38 39 // TODO: Here we can deny non ascii file names // TODO: Here we can deny non ascii file names
39 40 // git diff --cached --name-only --diff-filter=A -z $against | LC_ALL=C tr -d '[ -~]\0') // git diff --cached --name-only --diff-filter=A -z $against | LC_ALL=C tr -d '[ -~]\0')
41 // I think we can do it already with path rights. check.
40 42
41 43
42 44 $diff = sprintf("%u", (microtime(TRUE) - $_start) * 1000); $diff = sprintf("%u", (microtime(TRUE) - $_start) * 1000);
43 45 rg_log("Took " . $diff . "ms."); rg_log("Took " . $diff . "ms.");
44 46
45 /*TODO: replace with event
46 @file_put_contents($repo_path . "/rocketgit/hook-pre-commit",
47 "repo: " . $repo . " ($repo_path)"
48 . "\nat: " . sprintf("%u", $_start)
49 . "\nuid: " . $uid
50 . "\ncmd: against=$against"
51 . "\nTook: " . $diff . "ms");
52 */
53
54 47 ?> ?>
File inc/cache.inc.php changed (mode: 100644) (index 2e5a2c5..f54ff89)
... ... if (!isset($rg_cache_enable))
13 13 // timeout in miliseconds // timeout in miliseconds
14 14 $rg_cache_timeout = 100; $rg_cache_timeout = 100;
15 15
16 $rg_cache_count = 0;
17 $rg_cache_tries = 3;
18
16 19 if (!isset($rg_cache_socket)) if (!isset($rg_cache_socket))
17 20 $rg_cache_socket = "/var/lib/rocketgit/sockets/cache.sock"; $rg_cache_socket = "/var/lib/rocketgit/sockets/cache.sock";
18 21
 
... ... $rg_cache = array();
21 24 $rg_cache_error = ""; $rg_cache_error = "";
22 25
23 26 if (!isset($rg_cache_debug)) if (!isset($rg_cache_debug))
24 $rg_cache_debug = TRUE;
27 $rg_cache_debug = FALSE;
25 28
26 29 function rg_cache_set_error($str) function rg_cache_set_error($str)
27 30 { {
 
... ... function rg_cache_core_adump($ns_var)
281 284 /********************************* Client side functions */ /********************************* Client side functions */
282 285
283 286 /* /*
284 * Returns a variable from the cache daemon
285 * @timeout_in_ms is the connection timeout not variable timeout.
287 * Prepares a string to be send to the wire
286 288 */ */
287 function rg_cache_get($ns_var)
289 function rg_cache_prepare($s)
290 {
291 $x = serialize($s);
292 return addcslashes($x, "\n\r\\");
293 }
294
295 /*
296 * Helps to send and to decode a command
297 */
298 function rg_cache_send($cmd, $para, $flags)
288 299 { {
300 global $rg_cache_enable;
289 301 global $rg_cache_socket; global $rg_cache_socket;
290 302 global $rg_cache_timeout; global $rg_cache_timeout;
291 global $rg_cache_enable;
303 global $rg_cache_tries;
304 global $rg_cache_count;
292 305 global $rg_cache_debug; global $rg_cache_debug;
293 306
294 rg_prof_start("cache_get");
307 if ($rg_cache_enable === FALSE)
308 return FALSE;
309
310 $rg_cache_count++;
311
312 $f = '';
313 if ($flags & RG_SOCKET_NO_WAIT)
314 $f .= 'W';
315
316 $xcmd = $cmd . ' F=' . $f . ' I=' . $rg_cache_count . ' ' . $para;
317 if ($rg_cache_debug)
318 rg_log('Sending [' . $xcmd . ']...');
319 $ret = rg_socket($rg_cache_socket, $xcmd . "\n",
320 $rg_cache_timeout, $rg_cache_tries, $flags);
295 321 if ($rg_cache_debug) if ($rg_cache_debug)
296 rg_log_enter("cache_get: $ns_var");
322 rg_log('Received [' . $ret . ']');
323 if ($ret === FALSE)
324 return FALSE;
297 325
298 $ret = FALSE;
299 while (1) {
300 $ret = rg_cache_core_get($ns_var);
301 if ($ret !== FALSE) {
302 if ($rg_cache_debug)
303 rg_log('Found in core. Good!');
304 break;
326 if ($flags & RG_SOCKET_NO_WAIT)
327 return TRUE;
328
329 $a = explode("\n", $ret);
330 foreach ($a as $line) {
331 if ($rg_cache_debug)
332 rg_log('Parsing line [' . $line . ']');
333
334 $t = explode(' ', $line, 3);
335 if (!isset($t[1]))
336 return FALSE;
337
338 $id = intval($t[1]);
339 if ($id < $rg_cache_count) {
340 rg_log('DEBUG: id: ' . $id . ' < ' . $rg_cache_count);
341 continue;
305 342 } }
343 rg_log('DEBUG: id: ' . $id . ' == ' . $rg_cache_count);
306 344
307 if ($rg_cache_enable === FALSE)
308 break;
345 if (strcmp($t[0], 'OK') != 0) {
346 rg_log('DEBUG: not an OK answer: ' . $t[0]);
347 return FALSE;
348 }
309 349
310 $c = rg_socket($rg_cache_socket,
311 "GET F= " . $ns_var . "\n", $rg_cache_timeout, 1, 0);
312 if ($c === FALSE)
313 break;
350 if (!isset($t[2]))
351 return TRUE;
352
353 return trim(stripcslashes($t[2]));
354 break;
355 }
356 }
314 357
315 $t = explode(" ", $c, 2);
316 if (strcmp($t[0], "OK") != 0)
358 /*
359 * Returns a variable from the cache daemon
360 * @timeout_in_ms is the connection timeout not variable timeout.
361 */
362 function rg_cache_get($ns_var)
363 {
364 rg_prof_start('cache_get');
365
366 $ret = FALSE;
367 while (1) {
368 $ret = rg_cache_core_get($ns_var);
369 if ($ret !== FALSE)
317 370 break; break;
318 371
319 if (!isset($t[1]))
372 $flags = 0;
373 $r = rg_cache_send('GET', $ns_var, $flags);
374 if ($r === FALSE)
320 375 break; break;
321 376
322 $x = trim(stripcslashes($t[1]));
323 $ret = @unserialize($x);
377 $ret = @unserialize($r);
324 378 if ($ret === FALSE) { if ($ret === FALSE) {
325 rg_internal_error("Cannot userialize [$x]!");
379 rg_internal_error("Cannot userialize [$r]!");
326 380 break; break;
327 381 } }
328 382
329 383 rg_cache_core_set($ns_var, $ret); rg_cache_core_set($ns_var, $ret);
330
331 384 break; break;
332 385 } }
333 386
334 if ($rg_cache_debug) {
335 rg_log("ret=" . rg_array2string($ret));
336 rg_log_exit();
337 }
338 rg_prof_end("cache_get");
387 rg_prof_end('cache_get');
339 388 return $ret; return $ret;
340 389 } }
341 390
342 /*
343 * Prepares a string to be send to the wire
344 */
345 function rg_cache_prepare($s)
346 {
347 $x = serialize($s);
348 return addcslashes($x, "\n\r\\");
349 }
350
351 391 /* /*
352 392 * Sets a variable in the cache daemon * Sets a variable in the cache daemon
353 393 */ */
354 394 function rg_cache_set($ns_var, $value, $flags) function rg_cache_set($ns_var, $value, $flags)
355 395 { {
356 global $rg_cache_socket;
357 global $rg_cache_timeout;
358 global $rg_cache_enable;
359 global $rg_cache_debug;
396 rg_prof_start('cache_set');
360 397
361 rg_prof_start("cache_set");
362 if ($rg_cache_debug)
363 rg_log_ml_enter("cache_set: flags=$flags"
364 . " $ns_var = " . print_r($value, TRUE));
365
366 $ret = FALSE;
367 398 while (1) { while (1) {
368 399 rg_cache_core_set($ns_var, $value); rg_cache_core_set($ns_var, $value);
369 400
370 if ($rg_cache_enable === FALSE)
371 break;
372
373 $f = '';
374 if ($flags & RG_SOCKET_NO_WAIT)
375 $f .= 'W';
376
377 $c = rg_socket($rg_cache_socket, "SET F=$f " . $ns_var . "="
378 . rg_cache_prepare($value) . "\n", $rg_cache_timeout, 3, $flags);
379 if ($c === FALSE)
380 break;
381
382 if ($flags & RG_SOCKET_NO_WAIT) {
383 $ret = TRUE;
384 break;
385 }
386
387 if (strncmp($c, "OK", 2) != 0)
401 $para = $ns_var . '=' . rg_cache_prepare($value);
402 $ret = rg_cache_send('SET', $para, $flags);
403 if ($ret === FALSE)
388 404 break; break;
389 405
390 406 $ret = TRUE; $ret = TRUE;
391 407 break; break;
392 408 } }
393 409
394 if ($rg_cache_debug)
395 rg_log_exit();
396 rg_prof_end("cache_set");
410 rg_prof_end('cache_set');
397 411 return $ret; return $ret;
398 412 } }
399 413
 
... ... function rg_cache_set($ns_var, $value, $flags)
402 416 */ */
403 417 function rg_cache_inc($ns_var) function rg_cache_inc($ns_var)
404 418 { {
405 global $rg_cache_socket;
406 global $rg_cache_timeout;
407 global $rg_cache_enable;
408 global $rg_cache_debug;
419 rg_prof_start('cache_inc');
409 420
410 rg_prof_start("cache_inc");
411 if ($rg_cache_debug)
412 rg_log_enter("cache_inc($ns_var)");
421 rg_cache_core_inc($vs_var);
413 422
414 $ret = FALSE;
415 423 while (1) { while (1) {
416 rg_cache_core_inc($vs_var);
417
418 if ($rg_cache_enable === FALSE)
419 break;
420
421 $c = rg_socket($rg_cache_socket,
422 "INC F= " . $ns_var . "\n", $rg_cache_timeout, 1, 0);
423 if ($c === FALSE)
424 break;
425
426 if (strncmp($c, "OK", 2) != 0)
424 $ret = rg_cache_send('INC', $ns_var, $flags);
425 if ($ret === FALSE)
427 426 break; break;
428
429 $v = strstr($c, " v=");
430 if ($v === FALSE)
427 if ($ret === TRUE)
431 428 break; break;
432 429
433 $ret = intval($v);
430 $ret = intval($ret);
434 431 break; break;
435 432 } }
436 433
437 if ($rg_cache_debug)
438 rg_log_exit();
439 rg_prof_end("cache_inc");
434 rg_prof_end('cache_inc');
440 435 return $ret; return $ret;
441 436 } }
442 437
 
... ... function rg_cache_inc($ns_var)
445 440 */ */
446 441 function rg_cache_unset($ns_var, $flags) function rg_cache_unset($ns_var, $flags)
447 442 { {
448 global $rg_cache_socket;
449 global $rg_cache_timeout;
450 global $rg_cache_enable;
451 global $rg_cache_debug;
452
453 rg_prof_start("cache_unset");
454 if ($rg_cache_debug)
455 rg_log_enter("cache_unset($ns_var) flags=$flags"
456 . " enable=" . ($rg_cache_enable ? "true" : "false"));
443 rg_prof_start('cache_unset');
457 444
458 $ret = FALSE;
459 while (1) {
460 rg_cache_core_unset($ns_var);
461
462 if ($rg_cache_enable === FALSE)
463 break;
464
465 $f = '';
466 if ($flags & RG_SOCKET_NO_WAIT)
467 $f .= 'W';
468
469 $ret = rg_socket($rg_cache_socket,
470 "UNSET F=$f " . $ns_var . "\n", $rg_cache_timeout, 1, $flags);
471 if ($ret === FALSE)
472 break;
473
474 if ($flags & RG_SOCKET_NO_WAIT) {
475 $ret = TRUE;
476 break;
477 }
445 rg_cache_core_unset($ns_var);
446 $ret = rg_cache_send('UNSET', $ns_var, $flags);
478 447
479 if (strncmp($ret, "NOT_FOUND", 9) == 0)
480 break;
481
482 // TODO: return old value?
483 if (strncmp($ret, "OK", 2) != 0) {
484 rg_internal_error("Invalid answer: $ret");
485 break;
486 }
487
488 $ret = TRUE;
489 break;
490 }
491
492 if ($rg_cache_debug) {
493 rg_log($ret === TRUE ? "success" : "fail");
494 rg_log_exit();
495 }
496 rg_prof_end("cache_unset");
448 rg_prof_end('cache_unset');
497 449 return $ret; return $ret;
498 450 } }
499 451
 
... ... function rg_cache_unset($ns_var, $flags)
502 454 */ */
503 455 function rg_cache_merge($ns_var, $list, $flags) function rg_cache_merge($ns_var, $list, $flags)
504 456 { {
505 global $rg_cache_socket;
506 global $rg_cache_timeout;
507 global $rg_cache_enable;
508 global $rg_cache_debug;
509
510 rg_prof_start("cache_merge");
511 if ($rg_cache_debug)
512 rg_log_ml_enter("cache_merge: flags=$flags"
513 . " $ns_var = " . print_r($list, TRUE));
514
515 $ret = FALSE;
516 while (1) {
517 rg_cache_core_merge($ns_var, $list);
518
519 if ($rg_cache_enable === FALSE)
520 break;
457 rg_prof_start('cache_merge');
521 458
522 $f = '';
523 if ($flags & RG_SOCKET_NO_WAIT)
524 $f .= 'W';
459 rg_cache_core_merge($ns_var, $list);
525 460
526 $c = rg_socket($rg_cache_socket, "MERGE F=$f " . $ns_var . "="
527 . rg_cache_prepare($list) . "\n", $rg_cache_timeout,
528 1, $flags);
529 if ($c === FALSE)
530 break;
531
532 if ($flags & RG_SOCKET_NO_WAIT) {
533 $ret = TRUE;
534 break;
535 }
536
537 if (strncmp($c, "OK", 2) != 0)
538 break;
461 $para = $ns_var . '=' . rg_cache_prepare($list);
462 $ret = rg_cache_send('MERGE', $para, $flags);
539 463
540 $ret = TRUE;
541 break;
542 }
543
544 if ($rg_cache_debug)
545 rg_log_exit();
546 rg_prof_end("cache_merge");
464 rg_prof_end('cache_merge');
547 465 return $ret; return $ret;
548 466 } }
549 467
 
... ... function rg_cache_merge($ns_var, $list, $flags)
552 470 */ */
553 471 function rg_cache_apush($ns_var, $value, $flags) function rg_cache_apush($ns_var, $value, $flags)
554 472 { {
555 global $rg_cache_socket;
556 global $rg_cache_timeout;
557 global $rg_cache_enable;
558 global $rg_cache_debug;
473 rg_prof_start('cache_apush');
559 474
560 rg_prof_start("cache_apush");
561 if ($rg_cache_debug)
562 rg_log_ml_enter("cache_apush: flags=$flags"
563 . " $ns_var = " . print_r($value, TRUE));
475 rg_cache_core_apush($ns_var, $value);
564 476
565 $ret = FALSE;
566 while (1) {
567 rg_cache_core_apush($ns_var, $value);
568
569 if ($rg_cache_enable === FALSE)
570 break;
477 $para = $ns_var . '=' . rg_cache_prepare($value);
478 $ret = rg_cache_send('APUSH', $para, $flags);
571 479
572 $f = '';
573 if ($flags & RG_SOCKET_NO_WAIT)
574 $f .= 'W';
575
576 $c = rg_socket($rg_cache_socket, "APUSH F=$f " . $ns_var . "="
577 . rg_cache_prepare($value) . "\n", $rg_cache_timeout, 3, $flags);
578 if ($c === FALSE)
579 break;
480 rg_prof_end('cache_apush');
481 return $ret;
482 }
580 483
581 if ($flags & RG_SOCKET_NO_WAIT) {
582 $ret = TRUE;
583 break;
584 }
484 /*
485 * Restarts the cache daemon
486 */
487 function rg_cache_restart()
488 {
489 $flags = 0;
490 $para = '';
491 $ret = rg_cache_send('SHUTDOWN', $para, $flags);
492 if ($ret === FALSE)
493 return FALSE;
585 494
586 if (strncmp($c, "OK", 2) != 0)
587 break;
495 rg_log('Cache restarted.');
496 return TRUE;
497 }
588 498
589 $ret = TRUE;
590 break;
591 }
499 /*
500 * Just to debug stuff
501 */
502 function rg_cache_sleep()
503 {
504 $flags = RG_SOCKET_NO_WAIT;
505 $para = '';
506 $ret = rg_cache_send('SLEEP', $para, $flags);
507 if ($ret === FALSE)
508 return FALSE;
592 509
593 if ($rg_cache_debug)
594 rg_log_exit();
595 rg_prof_end("cache_apush");
596 return $ret;
510 return TRUE;
597 511 } }
598 512
599 513 ?> ?>
File inc/git.inc.php changed (mode: 100644) (index c8dbf68..cc1da62)
... ... function rg_git_files($old, $new)
880 880 rg_prof_start("git_files"); rg_prof_start("git_files");
881 881 rg_log_enter("rg_git_files old=$old new=$new"); rg_log_enter("rg_git_files old=$old new=$new");
882 882
883 // TODO: Here we can deny non ascii file names. Move to update_branch?
884 // git diff --cached --name-only --diff-filter=A -z $against | LC_ALL=C tr -d '[ -~]\0')
885
886 883 $ret = FALSE; $ret = FALSE;
887 884 while (1) { while (1) {
888 885 if (strcmp($old, $rg_git_zero) == 0) if (strcmp($old, $rg_git_zero) == 0)
889 886 $old = $rg_git_empty; $old = $rg_git_empty;
890 887
891 $cmd = "git diff --name-only " . escapeshellarg($old) . " " . escapeshellarg($new);
888 $cmd = 'git diff --name-only ' . escapeshellarg($old)
889 . ' ' . escapeshellarg($new);
892 890 $a = rg_exec($cmd); $a = rg_exec($cmd);
893 891 if ($a['ok'] != 1) { if ($a['ok'] != 1) {
894 892 rg_git_set_error("error on git diff (" . $a['errmsg'] . ")"); rg_git_set_error("error on git diff (" . $a['errmsg'] . ")");
 
... ... function rg_git_update_tag($db, $a)
1143 1141 // If we do not have a namespace, we let git to update the ref. // If we do not have a namespace, we let git to update the ref.
1144 1142 // Not clear when we do not have a namespace. // Not clear when we do not have a namespace.
1145 1143 if (!empty($a['namespace'])) { if (!empty($a['namespace'])) {
1146 // Update the main namespace
1144 // Update the main ref (not a namespace)
1147 1145 $reason = $a['login_username'] . ' pushed tag ' . $a['refname']; $reason = $a['login_username'] . ' pushed tag ' . $a['refname'];
1148 1146 $r = rg_git_update_ref($a['refname'], $a['old_rev'], $r = rg_git_update_ref($a['refname'], $a['old_rev'],
1149 1147 $a['new_rev'], $reason); $a['new_rev'], $reason);
 
... ... function rg_git_update_tag($db, $a)
1151 1149 rg_git_fatal($a['refname'] . "\nCannot update ref (" rg_git_fatal($a['refname'] . "\nCannot update ref ("
1152 1150 . rg_git_error() . ")"); . rg_git_error() . ")");
1153 1151 } }
1154
1155 // We can clean now the tmp namespace - TODO
1156 1152 } }
1157 1153
1158 1154 rg_repo_history_insert($db, $history); rg_repo_history_insert($db, $history);
 
... ... function rg_git_update_branch($db, $a)
1199 1195 break; break;
1200 1196 } }
1201 1197
1202 // If we have 'H' (anonymous push), we have also create branch
1198 // If we have 'H' (anonymous push), we have also 'create branch' right
1203 1199 $check_fast_forward = 1; $check_fast_forward = 1;
1204 1200 if (strcmp($a['old_rev'], $rg_git_zero) == 0) { // create if (strcmp($a['old_rev'], $rg_git_zero) == 0) { // create
1205 1201 $x = $_x; $x = $_x;
 
... ... function rg_git_update_branch($db, $a)
1249 1245 . "\n" . $w); . "\n" . $w);
1250 1246 } }
1251 1247
1252 // Check repo_path rights TODO
1248 rg_log_enter('DEBUG: Checking repo_path rights');
1253 1249 $r = rg_git_files($a['old_rev'], $a['new_rev']); $r = rg_git_files($a['old_rev'], $a['new_rev']);
1254 1250 if ($r === FALSE) if ($r === FALSE)
1255 1251 rg_git_fatal($a['refname'] . "\nInternal error, try again later\n"); rg_git_fatal($a['refname'] . "\nInternal error, try again later\n");
 
... ... function rg_git_update_branch($db, $a)
1273 1269 } }
1274 1270 } }
1275 1271 } }
1272 rg_log_exit();
1276 1273
1277 1274 $x = $_x; $x = $_x;
1278 1275 $x['type'] = 'repo_refs'; $x['type'] = 'repo_refs';
1279 1276 $x['needed_rights'] = 'P'; $x['needed_rights'] = 'P';
1280 1277 $x['misc'] = $a['refname']; $x['misc'] = $a['refname'];
1281 1278 if (rg_rights_allow($db, $x) !== TRUE) { if (rg_rights_allow($db, $x) !== TRUE) {
1282 rg_log("Push is not allowed, let's see the anon one");
1279 rg_log("DEBUG: Push is not allowed, let's see the anon one");
1283 1280 $x['needed_rights'] = 'H'; $x['needed_rights'] = 'H';
1284 1281 if (rg_rights_allow($db, $x) !== TRUE) { if (rg_rights_allow($db, $x) !== TRUE) {
1285 1282 $_z = array(); $_z = array();
 
... ... function rg_git_update_branch($db, $a)
1295 1292 . "_" . str_replace('rg_', '', $a['namespace']); . "_" . str_replace('rg_', '', $a['namespace']);
1296 1293 $reason = $a['login_username'] . ' pushed a merge request' $reason = $a['login_username'] . ' pushed a merge request'
1297 1294 . ' for ref ' . $a['refname'] . ' for ref ' . $a['refname']
1298 . ' into namespace ' . $a['namespace'];
1295 . ' into namespace ' . $mr;
1299 1296 $r = rg_git_update_ref($mr, "", $a['new_rev'], $reason); $r = rg_git_update_ref($mr, "", $a['new_rev'], $reason);
1300 1297 if ($r !== TRUE) { if ($r !== TRUE) {
1301 1298 rg_log("Cannot update-ref: " . rg_git_error()); rg_log("Cannot update-ref: " . rg_git_error());
1302 1299 rg_git_fatal($a['refname'] . ": Cannot set refs/mr/." rg_git_fatal($a['refname'] . ": Cannot set refs/mr/."
1303 1300 . " Try again later."); . " Try again later.");
1304 1301 } }
1305 // TODO: here or inside below function we should record
1306 // a hisotry event that an anon push was done.
1307 1302 $r = rg_mr_queue_add($a['repo_id'], $a['namespace'], $r = rg_mr_queue_add($a['repo_id'], $a['namespace'],
1308 1303 $a['old_rev'], $a['new_rev'], $a['refname'], $a['ip']); $a['old_rev'], $a['new_rev'], $a['refname'], $a['ip']);
1309 1304 if ($r !== TRUE) if ($r !== TRUE)
 
... ... function rg_git_update_branch($db, $a)
1314 1309
1315 1310 $history['history_category'] = REPO_CAT_GIT_BRANCH_ANON_PUSH; $history['history_category'] = REPO_CAT_GIT_BRANCH_ANON_PUSH;
1316 1311 $history['history_message'] = 'Anonymous push to ref ' $history['history_message'] = 'Anonymous push to ref '
1317 . $a['refname'];
1312 . $a['refname'] . ' into namespace ' . $mr;
1318 1313
1319 1314 // TODO: we should notify by e-mail that a merge request is // TODO: we should notify by e-mail that a merge request is
1320 1315 // waiting. // waiting.
1321 1316 } else { } else {
1322 rg_log("We are allowed to push.");
1317 rg_log("DEBUG: We are allowed to push.");
1323 1318
1324 1319 // If we do not have a namespace, we let git to update the ref. // If we do not have a namespace, we let git to update the ref.
1325 1320 // Not clear when we do not have a namespace. // Not clear when we do not have a namespace.
1326 1321 if (!empty($a['namespace'])) { if (!empty($a['namespace'])) {
1327 // Update the main namespace
1322 // Updating main ref (not a namespace)
1328 1323 $reason = $a['login_username'] $reason = $a['login_username']
1329 1324 . ' pushed ref ' . $a['refname']; . ' pushed ref ' . $a['refname'];
1330 1325 $r = rg_git_update_ref($a['refname'], $a['old_rev'], $r = rg_git_update_ref($a['refname'], $a['old_rev'],
1331 1326 $a['new_rev'], $reason); $a['new_rev'], $reason);
1332 1327 if ($r !== TRUE) { if ($r !== TRUE) {
1333 rg_git_fatal($a['refname'] . "\nCannot update ref ("
1328 rg_git_fatal($a['refname']
1329 . "\nCannot update ref ("
1334 1330 . rg_git_error() . ")"); . rg_git_error() . ")");
1335 1331 } }
1336 1332
1337 // We can clean now the tmp namespace - TODO
1333 // Here, the namespace ref is not yet updated
1338 1334 } }
1339 1335
1340 1336 if (strcmp($a['old_rev'], $rg_git_zero) == 0) { if (strcmp($a['old_rev'], $rg_git_zero) == 0) {
 
... ... function rg_git_update_branch($db, $a)
1350 1346 } }
1351 1347 } }
1352 1348 rg_repo_history_insert($db, $history); rg_repo_history_insert($db, $history);
1349 break;
1353 1350 } }
1354 1351
1355 1352 rg_log_exit(); rg_log_exit();
File inc/log.inc.php changed (mode: 100644) (index 1ce3f97..0d41733)
... ... function rg_fatal($msg)
209 209 exit(1); exit(1);
210 210 } }
211 211
212 function rg_fatal_web($msg, $url)
213 {
214 rg_error_core("FATAL: $msg");
215 header("Location: " . $url);
216 exit(1);
217 }
218
219 212 function rg_internal_error($msg) function rg_internal_error($msg)
220 213 { {
221 214 rg_error_core("Internal error: $msg"); rg_error_core("Internal error: $msg");
File inc/prof.inc.php changed (mode: 100644) (index fe5bd78..79ce196)
... ... function rg_prof_text()
167 167
168 168 $add = ""; $add = "";
169 169 foreach ($rg_prof_main as $label => $per_label) { foreach ($rg_prof_main as $label => $per_label) {
170 if (($per_label['time_ms'] < 5) && ($per_label['mem'] < 20))
171 continue;
172
170 173 $ret .= $add; $ret .= $add;
171 174 $add = "\n"; $add = "\n";
172 175
File inc/rights.inc.php changed (mode: 100644) (index 559a846..d9a4d32)
... ... function rg_rights_get($db, $obj_id, $type, $owner, $uid, $right_id)
276 276 global $rg_rights; global $rg_rights;
277 277 global $rg_rights_inject; global $rg_rights_inject;
278 278
279 rg_prof_start("rights_get");
279 rg_prof_start('rights_get');
280 280 rg_log_enter("rg_rights_get: obj_id=$obj_id type=$type owner=$owner" rg_log_enter("rg_rights_get: obj_id=$obj_id type=$type owner=$owner"
281 281 . " uid=$uid right_id=$right_id"); . " uid=$uid right_id=$right_id");
282 282
 
... ... function rg_rights_get($db, $obj_id, $type, $owner, $uid, $right_id)
287 287 $key = "rights_by_obj_id::$obj_id::$type"; $key = "rights_by_obj_id::$obj_id::$type";
288 288 $r = rg_cache_get($key); $r = rg_cache_get($key);
289 289 if ($r === FALSE) { if ($r === FALSE) {
290 rg_log("CHECK: rights_get: key not found in cache! Search in DB.");
290 //rg_log("CHECK: rights_get: key not found in cache! Search in DB.");
291 291
292 292 $r = array(); $r = array();
293 293
 
... ... function rg_rights_get($db, $obj_id, $type, $owner, $uid, $right_id)
309 309 rg_rights_cosmetic($db, $a); rg_rights_cosmetic($db, $a);
310 310
311 311 $r[] = $a; $r[] = $a;
312 rg_log_ml("rights_get: inject all rights for owner");
312 //rg_log_ml("rights_get: inject all rights for owner");
313 313 } }
314 314
315 315 // Inject specific rights // Inject specific rights
316 316 if (isset($rg_rights_inject[$type])) { if (isset($rg_rights_inject[$type])) {
317 317 $f = $rg_rights_inject[$type]; $f = $rg_rights_inject[$type];
318 318 $rows = $f($db, $obj_id, $type, $owner, $uid); $rows = $f($db, $obj_id, $type, $owner, $uid);
319 rg_log_ml("CHECK: inject function for '$type' [$f] returned: " . print_r($rows, TRUE));
319 //rg_log_ml("CHECK: inject function for '$type' [$f] returned: " . print_r($rows, TRUE));
320 320 foreach ($rows as $row) { foreach ($rows as $row) {
321 321 rg_rights_cosmetic($db, $row); rg_rights_cosmetic($db, $row);
322 rg_log_ml("rights_get: inject specific rights: " . print_r($row, TRUE));
322 //rg_log_ml("rights_get: inject specific rights: " . print_r($row, TRUE));
323 323 $r[] = $row; $r[] = $row;
324 324 } }
325 325 } }
 
... ... function rg_rights_get($db, $obj_id, $type, $owner, $uid, $right_id)
333 333 // We store the big list // We store the big list
334 334 rg_cache_set($key, $r, RG_SOCKET_NO_WAIT); rg_cache_set($key, $r, RG_SOCKET_NO_WAIT);
335 335 } else { } else {
336 rg_log("CHECK: rights returned from cache for key $key");
336 //rg_log("CHECK: rights returned from cache for key $key");
337 337 } }
338 338
339 339 // now, filter by uid and right_id // now, filter by uid and right_id
 
... ... function rg_rights_get($db, $obj_id, $type, $owner, $uid, $right_id)
349 349 break; break;
350 350 } }
351 351
352 rg_log("rights_get: rights=" . rg_array2string($ret['list']));
352 //rg_log("rights_get: rights=" . rg_array2string($ret['list']));
353 353
354 354 rg_log_exit(); rg_log_exit();
355 rg_prof_end("rights_get");
355 rg_prof_end('rights_get');
356 356 return $ret; return $ret;
357 357 } }
358 358
 
... ... function rg_rights_test_ip($list, $ip)
634 634 /* /*
635 635 * Returns TRUE if all 'needed_rights' are included in 'rights' * Returns TRUE if all 'needed_rights' are included in 'rights'
636 636 * @list - an array of rights * @list - an array of rights
637 * needed_rights: rights letters; you can use "ab|cd" = (a AND B) OR (C AND d)
637 * @needed_rights: rights letters; you can use "ab|cd" = (a AND B) OR (C AND d)
638 638 */ */
639 639 function rg_rights_test($list, $needed_rights, $ip, $misc) function rg_rights_test($list, $needed_rights, $ip, $misc)
640 640 { {
 
... ... function rg_rights_test($list, $needed_rights, $ip, $misc)
664 664 continue; continue;
665 665 } }
666 666
667 // Test 'misc' match
668 if (!empty($v['misc'])) {
669 $cmp_func = $rg_rights_cmp_func[$v['type']];
670 $r = $cmp_func($v['misc'], $misc);
671 if ($r !== TRUE) {
672 rg_log("DEBUG: cmp function returned !TRUE");
673 continue;
674 }
675 }
676
677 if (strlen($v['rights']) == 0) {
678 rg_log('DEBUG: rights field is empty'
679 . ', stop processing!');
680 break;
681 }
682
667 683 // Test rights // Test rights
668 684 $have_a_match = FALSE; $have_a_match = FALSE;
669 685 foreach ($needed as $needed1) { foreach ($needed as $needed1) {
670 686 $r = rg_rights_mask($v['rights'], $needed1); $r = rg_rights_mask($v['rights'], $needed1);
671 687 if (strcmp($r, $needed1) != 0) { if (strcmp($r, $needed1) != 0) {
672 rg_log("rights_test: [$r] != [$needed1]! Continue.");
688 rg_log("[$r] != [$needed1]! Continue.");
673 689 continue; continue;
674 690 } }
675 rg_log("rights_test: [$r] = [$needed1]! Allow.");
691 //rg_log("[$r] = [$needed1]! Allow.");
676 692 $have_a_match = TRUE; $have_a_match = TRUE;
677 693 break; break;
678 694 } }
679 695 if ($have_a_match === FALSE) if ($have_a_match === FALSE)
680 696 continue; continue;
681 697
682 // Test 'misc' match
683 if (!empty($v['misc'])) {
684 $cmp_func = $rg_rights_cmp_func[$v['type']];
685 $r = $cmp_func($v['misc'], $misc);
686 if ($r !== TRUE) {
687 rg_log("DEBUG: cmp function returned !TRUE");
688 continue;
689 }
690 }
691
692 698 $ret = TRUE; $ret = TRUE;
693 699 break; break;
694 700 } }
695 701
696 702 break; break;
697 703 } }
698 rg_log("DEBUG: rights_test returns " . ($ret === FALSE ? "!allow" : "allow"));
704 //rg_log("DEBUG: rights_test returns " . ($ret === FALSE ? "!allow" : "allow"));
699 705
700 706 rg_log_exit(); rg_log_exit();
701 707 return $ret; return $ret;
 
... ... function rg_rights_test($list, $needed_rights, $ip, $misc)
703 709
704 710 /* /*
705 711 * Returns TRUE if all 'needed_rights' are included in 'rights' * Returns TRUE if all 'needed_rights' are included in 'rights'
706 * needed_rights: rights letters; you can use "ab|cd" = (a AND B) OR (C AND d)
712 * @a[needed_rights]: rights letters; you can use "ab|cd" = (a AND B) OR (C AND d)
707 713 */ */
708 714 function rg_rights_allow($db, $a) function rg_rights_allow($db, $a)
709 715 { {
File inc/sess.inc.php changed (mode: 100644) (index b7916a4..00d60b0)
... ... function rg_sess_add($db, $uid, $sid, $session_time, $lock_ip)
37 37 rg_sql_free_result($res); rg_sql_free_result($res);
38 38
39 39 $params['last_db_write'] = $now; $params['last_db_write'] = $now;
40 rg_cache_set("sess::" . $sid, $params, RG_SOCKET_NO_WAIT);
40 rg_cache_set('sess' . '::' . $sid . '::' . 'info',
41 $params, RG_SOCKET_NO_WAIT);
41 42
42 43 $ret = TRUE; $ret = TRUE;
43 44 break; break;
 
... ... function rg_sess_valid($db, $sid)
59 60
60 61 $ret = FALSE; $ret = FALSE;
61 62 while (1) { while (1) {
62 $r = rg_cache_get("sess::" . $sid);
63 $r = rg_cache_get('sess' . '::' . $sid . '::' . 'info');
63 64 if ($r === FALSE) { if ($r === FALSE) {
64 65 $params = array("sid" => $sid); $params = array("sid" => $sid);
65 66 $sql = "SELECT * FROM sess WHERE sid = @@sid@@"; $sql = "SELECT * FROM sess WHERE sid = @@sid@@";
 
... ... function rg_sess_valid($db, $sid)
72 73 if ($rows > 0) { if ($rows > 0) {
73 74 $r = rg_sql_fetch_array($res); $r = rg_sql_fetch_array($res);
74 75 $r['last_db_write'] = $r['expire'] - $r['session_time']; $r['last_db_write'] = $r['expire'] - $r['session_time'];
75 rg_cache_set("sess::" . $sid, $r, RG_SOCKET_NO_WAIT);
76 rg_cache_set('sess' . '::' . $sid . '::' . 'info',
77 $r, RG_SOCKET_NO_WAIT);
76 78 } }
77 79 rg_sql_free_result($res); rg_sql_free_result($res);
78 80 } }
 
... ... function rg_sess_update($db, $sess)
140 142 rg_sql_free_result($res); rg_sql_free_result($res);
141 143 } }
142 144
143 rg_cache_set("sess::" . $sess['sid'], $sess, RG_SOCKET_NO_WAIT);
145 rg_cache_set('sess' . '::' . $sess['sid'] . '::' . 'info',
146 $sess, RG_SOCKET_NO_WAIT);
144 147
145 148 $ret = TRUE; $ret = TRUE;
146 149 break; break;
 
... ... function rg_sess_destroy($db, $sid, &$ui)
177 180 $ui['uid'] = 0; $ui['uid'] = 0;
178 181 $ui['is_admin'] = 0; $ui['is_admin'] = 0;
179 182
180 rg_cache_unset('sess' . '::' . $sid, RG_SOCKET_NO_WAIT);
183 rg_cache_unset('sess' . '::' . $sid . '::'. 'info',
184 RG_SOCKET_NO_WAIT);
181 185
182 186 $ret = TRUE; $ret = TRUE;
183 187 break; break;
File inc/ssh.inc.php changed (mode: 100644) (index 9a4be66..5f2797c)
... ... function rg_ssh_repo($db, $uid, $paras)
88 88 /* /*
89 89 * Deal with TOTP stuff * Deal with TOTP stuff
90 90 */ */
91 function rg_ssh_totp($db, $uid, $paras)
91 function rg_ssh_totp($db, $ip, $uid, $paras)
92 92 { {
93 93 rg_prof_start('ssh_totp'); rg_prof_start('ssh_totp');
94 rg_log_enter('ssh_totp uid=' . $uid . ' paras=' . rg_array2string($paras));
95
96 $ssh_client = getenv("SSH_CLIENT");
97 $_t = explode(" ", $ssh_client);
98 $ip = $_t[0];
94 rg_log_enter('ssh_totp ip=' . $ip . ' uid=' . $uid
95 . ' paras=' . rg_array2string($paras));
99 96
100 97 $cmd = array_shift($paras); $cmd = array_shift($paras);
101 98 switch ($cmd) { switch ($cmd) {
 
... ... function rg_ssh_totp($db, $uid, $paras)
178 175 break; break;
179 176
180 177 case 'list-val': case 'list-val':
181 $r = rg_totp_list_ip($db, $uid);
178 $r = rg_totp_verify_ip($db, $uid, $ip);
182 179 if ($r['ok'] != 1) { if ($r['ok'] != 1) {
183 echo "An internal error occured; please try again later.\n";
180 echo 'RocketGit: Error: ' . rg_totp_error() . ".\n";
184 181 break; break;
185 182 } }
186
187 // we provide the list only from a validated ip
188 $found = FALSE;
189 foreach ($r['list'] as $t) {
190 if (strcmp($t['ip'], $ip) == 0) {
191 $found = TRUE;
192 break;
193 }
194 }
195
196 if (!$found || empty($r['list'])) {
197 echo "No validated IPs are pesent.\n";
183 if ($r['enrolled'] == 0) {
184 echo 'RocketGit: Info: You are not enrolled.' . ".\n";
198 185 break; break;
199 186 } }
200 187
 
... ... function rg_ssh_totp($db, $uid, $paras)
224 211 $a = array($v['id'] => ''); $a = array($v['id'] => '');
225 212 $r = rg_totp_remove($db, $uid, $a); $r = rg_totp_remove($db, $uid, $a);
226 213 if ($r !== TRUE) { if ($r !== TRUE) {
227 echo "Error: " . rg_totp_error() . "\n";
214 echo "Error: " . rg_totp_error() . ".\n";
228 215 break; break;
229 216 } }
230 217
 
... ... function rg_ssh_totp($db, $uid, $paras)
239 226
240 227 $del_ip = array_shift($paras); $del_ip = array_shift($paras);
241 228
242 $r = rg_totp_list_ip($db, $uid);
229 $r = rg_totp_verify_ip($db, $uid, $ip);
243 230 if ($r['ok'] != 1) { if ($r['ok'] != 1) {
244 echo "An internal error occured; please try again later.\n";
231 echo 'RocketGit: Error: ' . rg_totp_error() . ".\n";
245 232 break; break;
246 233 } }
247
248 // we allow the deletion only from a validated ip
249 $found = FALSE;
250 foreach ($r['list'] as $t) {
251 if (strcmp($t['ip'], $ip) == 0) {
252 $found = TRUE;
253 break;
254 }
255 }
256 if (!$found) {
257 echo "You are not allowed to delete the IPs."
258 . " Try 'val' command first.\n";
234 if ($r['enrolled'] == 0) {
235 echo 'RocketGit: Info: You are not enrolled.' . ".\n";
259 236 break; break;
260 237 } }
261 238
262 239 $r = rg_totp_del_ip($db, $uid, $del_ip); $r = rg_totp_del_ip($db, $uid, $del_ip);
263 240 if ($r !== TRUE) { if ($r !== TRUE) {
264 echo "Error: " . rg_totp_error() . "!\n";
241 echo "Error: " . rg_totp_error() . ".\n";
265 242 break; break;
266 243 } }
267 244
 
... ... function rg_ssh_totp($db, $uid, $paras)
270 247
271 248 default: default:
272 249 echo "Posible TOTP commands:\n"; echo "Posible TOTP commands:\n";
273 echo " enroll [token] - adds a new device in the system\n";
250 echo " enroll <token> - adds a new device in the system\n";
274 251 echo " val [X(m|h|d)] - adds your IP to the allow list for X time\n"; echo " val [X(m|h|d)] - adds your IP to the allow list for X time\n";
275 252 echo " default is 24 hours;\n"; echo " default is 24 hours;\n";
276 253 echo " you can specify m for minutes, h for hours and d for days\n"; echo " you can specify m for minutes, h for hours and d for days\n";
277 254 echo " list-val - list the already validated IPs\n"; echo " list-val - list the already validated IPs\n";
278 255 echo " inval ip|all - Invalidate IP address(es)\n"; echo " inval ip|all - Invalidate IP address(es)\n";
279 echo " unenroll token - remove a device from TOTP system\n";
256 echo " unenroll <token> - remove a device from TOTP system\n";
280 257 break; break;
281 258 } }
282 259
 
... ... function rg_ssh_totp($db, $uid, $paras)
284 261 rg_prof_end('ssh_totp'); rg_prof_end('ssh_totp');
285 262 } }
286 263
287 function rg_ssh_dispatch($db, $uid, $cmd)
264 function rg_ssh_dispatch($db, $ip, $uid, $orig_cmd)
288 265 { {
289 $paras = explode(' ', $cmd);
266 rg_log('ssh_dispatch orig_cmd=[' . $orig_cmd . ']');
267
268 $paras = explode(' ', $orig_cmd);
290 269 $cmd = array_shift($paras); $cmd = array_shift($paras);
291 270
271 // some commands are not executed here
272 switch ($cmd) {
273 case 'git-upload-pack'; return;
274 case 'git-receive-pack': return;
275 }
276
292 277 echo "\n== Welcome to RocketGit! ==\n"; echo "\n== Welcome to RocketGit! ==\n";
293 278
279 $ui = rg_user_info($db, $uid, '', '');
280 if ($ui['exists'] == 1)
281 echo 'RocketGit: You are connecting as user'
282 . ' \'' . $ui['username'] . '\'.' . "\n";
283
284 echo 'RocketGit: You are connecting from IP ' . $ip . ".\n";
285
286 // First, test if the IP is validated
287 switch ($cmd) {
288 case '': break;
289 case 'totp': break; // totp will verify the ip only for some commands
290 default:
291 $r = rg_totp_verify_ip($db, $uid, $ip);
292 if ($r['ok'] != 1) {
293 echo 'RocketGit: Error: ' . rg_totp_error() . ".\n";
294 exit(0);
295 }
296 break;
297 }
298
299 // Now, we can safely execute the command
294 300 switch ($cmd) { switch ($cmd) {
295 case 'status': rg_ssh_status($db, $uid); break;
296 case 'repos': rg_ssh_repos($db, $uid); break;
297 case 'repo': rg_ssh_repo($db, $uid, $paras); break;
298 case 'totp': rg_ssh_totp($db, $uid, $paras); break;
299 default:
301 case 'status': rg_ssh_status($db, $uid);
302 case 'repos': rg_ssh_repos($db, $uid); exit(0);
303 case 'repo': rg_ssh_repo($db, $uid, $paras); exit(0);
304 case 'totp': rg_ssh_totp($db, $ip, $uid, $paras); exit(0);
305 case '':
300 306 echo "Available commmands:\n" echo "Available commmands:\n"
301 307 . " status - show some status about the user\n" . " status - show some status about the user\n"
302 308 . " repos - list repos and information about them\n" . " repos - list repos and information about them\n"
303 309 . " repo - list info about a repo\n" . " repo - list info about a repo\n"
304 310 . " totp - two-factor authentication commands\n"; . " totp - two-factor authentication commands\n";
305 break;
311 exit(0);
306 312 } }
307 313
308 exit(0);
314 // Do not exit(0) from this function because a fetch or push will be
315 // executed after this function's exit.
309 316 } }
310 317
311 318 ?> ?>
File inc/struct.inc.php changed (mode: 100644) (index b3b984c..24b34ec)
... ... $rg_sql_struct[33]['other'] = array(
427 427 . ", name TEXT NOT NULL DEFAULT ''" . ", name TEXT NOT NULL DEFAULT ''"
428 428 . ", secret TEXT NOT NULL DEFAULT ''" . ", secret TEXT NOT NULL DEFAULT ''"
429 429 . ", ip TEXT NOT NULL DEFAULT ''" . ", ip TEXT NOT NULL DEFAULT ''"
430 . ", conf BOOLEAN NOT NULL DEFAULT 't')",
430 . ", conf BOOLEAN NOT NULL DEFAULT 't'"
431 . ", last_used_tc INT NOT NULL DEFAULT 0)",
431 432 "login_tokens_i_uid" => "login_tokens_i_uid" =>
432 433 "CREATE INDEX login_tokens_i_uid ON login_tokens(uid)", "CREATE INDEX login_tokens_i_uid ON login_tokens(uid)",
433 434 'login_tokens_ip' => "CREATE TABLE login_tokens_ip (" 'login_tokens_ip' => "CREATE TABLE login_tokens_ip ("
File inc/totp.inc.php changed (mode: 100644) (index b3006ab..57048d2)
1 1 <?php <?php
2 $INC = isset($INC) ? $INC : dirname(__FILE__);
3 require_once($INC . "/util.inc.php");
4 require_once($INC . "/log.inc.php");
5 require_once($INC . "/sql.inc.php");
6 require_once($INC . "/events.inc.php");
2 7
3 8 $rg_totp_error = ''; $rg_totp_error = '';
4 9
 
... ... function rg_totp_set_error($str)
6 11 { {
7 12 global $rg_totp_error; global $rg_totp_error;
8 13 $rg_totp_error = $str; $rg_totp_error = $str;
9 rg_log('set_error: ' . $str);
14 rg_log('totp_set_error: ' . $str);
10 15 } }
11 16
12 17 function rg_totp_error() function rg_totp_error()
 
... ... function rg_totp_compute($key, $tc, $digits)
93 98 return sprintf("%0" . $digits . "u", $i % pow(10, $digits)); return sprintf("%0" . $digits . "u", $i % pow(10, $digits));
94 99 } }
95 100
101 /*
102 * Verifies a tokens based on a specified 'tc'
103 * Returns 'tc' if ok, FALSE on error
104 */
96 105 function rg_totp_verify_tc($key, $tc, $token) function rg_totp_verify_tc($key, $tc, $token)
97 106 { {
98 107 $t = rg_totp_compute($key, $tc, 6); $t = rg_totp_compute($key, $tc, 6);
108 //rg_log('DEBUG: compute[tc=' . $tc . ']=' . $t);
99 109 if (strcmp($token, $t) == 0) if (strcmp($token, $t) == 0)
100 return TRUE;
110 return $tc;
101 111
102 112 return FALSE; return FALSE;
103 113 } }
104 114
105 115 /* /*
106 116 * Verifies if a login token is valid * Verifies if a login token is valid
107 * We try 2 tcs before currend ts and after current ts
117 * We try 2 tcs before current ts and after current ts
118 * Returns FALSE if token is not valid or 'tc' of the token if is valid.
108 119 */ */
109 function rg_totp_verify($key, $token)
120 function rg_totp_verify($key, $ts, $token)
110 121 { {
111 122 rg_prof_start('totp_verify'); rg_prof_start('totp_verify');
123 rg_log_enter('totp_verify ts=' . $ts . ', token=' . $token);
112 124
113 $tc = intval(time() / 30);
114 while (1) {
125 $tc = intval($ts / 30);
126 $list = array($tc, $tc - 1, $tc - 2, $tc + 1, $tc + 2);
127 foreach ($list as $tc) {
115 128 $ret = rg_totp_verify_tc($key, $tc, $token); $ret = rg_totp_verify_tc($key, $tc, $token);
116 if ($ret === TRUE)
117 break;
118
119 $ret = rg_totp_verify_tc($key, $tc - 1, $token);
120 if ($ret === TRUE)
121 break;
122
123 $ret = rg_totp_verify_tc($key, $tc - 2, $token);
124 if ($ret === TRUE)
125 break;
126
127 $ret = rg_totp_verify_tc($key, $tc + 1, $token);
128 if ($ret === TRUE)
129 break;
130
131 $ret = rg_totp_verify_tc($key, $tc + 2, $token);
132 if ($ret === TRUE)
129 //rg_log('DEBUG: using tc ' . $tc . ', ret=' . $ret);
130 if ($ret !== FALSE)
133 131 break; break;
134
135 $ret = FALSE;
136 break;
137 132 } }
138 133
134 rg_log_exit();
139 135 rg_prof_end('totp_verify'); rg_prof_end('totp_verify');
140 136 return $ret; return $ret;
141 137 } }
 
... ... function rg_totp_cosmetic(&$row)
201 197 /* /*
202 198 * Sets when a login token was last used * Sets when a login token was last used
203 199 */ */
204 function rg_totp_set_last_use($db, $uid, $id, $ts)
200 function rg_totp_set_last_use($db, $uid, $id, $tc, $ts)
205 201 { {
206 202 rg_prof_start('totp_set_last_use'); rg_prof_start('totp_set_last_use');
207 rg_log_enter('totp_set_last_use uid=' . $uid . ' id=' . $id);
203 rg_log_enter('totp_set_last_use uid=' . $uid . ' id=' . $id
204 . ' tc=' . $tc);
208 205
209 206 $ret = FALSE; $ret = FALSE;
210 207 while (1) { while (1) {
211 208 $params = array('uid' => $uid, $params = array('uid' => $uid,
212 209 'id' => $id, 'id' => $id,
213 'used' => $ts);
210 'used' => $ts,
211 'last_used_tc' => $tc,
212 'conf' => 't');
214 213 $sql = 'UPDATE login_tokens SET used = @@used@@' $sql = 'UPDATE login_tokens SET used = @@used@@'
214 . ', last_used_tc = @@last_used_tc@@'
215 . ', conf = @@conf@@'
215 216 . ' WHERE uid = @@uid@@' . ' WHERE uid = @@uid@@'
216 . ' AND id = @@id@@'
217 . ' AND used != @@used@@';
217 . ' AND id = @@id@@';
218 218 $res = rg_sql_query_params($db, $sql, $params); $res = rg_sql_query_params($db, $sql, $params);
219 219 if ($res === FALSE) { if ($res === FALSE) {
220 220 rg_user_set_error('cannot update last used (' . rg_sql_error()); rg_user_set_error('cannot update last used (' . rg_sql_error());
 
... ... function rg_totp_set_last_use($db, $uid, $id, $ts)
223 223 rg_sql_free_result($res); rg_sql_free_result($res);
224 224
225 225 $key = 'user' . '::' . $uid . '::' . 'login_tokens' $key = 'user' . '::' . $uid . '::' . 'login_tokens'
226 . '::' . 'list' . '::' . $id . '::' . 'used';
227 $a = array('used' => $ts);
226 . '::' . 'list' . '::' . $id;
227 $a = array('used' => $ts, 'last_used_tc' => $tc, 'conf' => 't');
228 228 rg_totp_cosmetic($a); rg_totp_cosmetic($a);
229 229 rg_cache_merge($key, $a, RG_SOCKET_NO_WAIT); rg_cache_merge($key, $a, RG_SOCKET_NO_WAIT);
230 230
 
... ... function rg_totp_set_last_use($db, $uid, $id, $ts)
237 237 return $ret; return $ret;
238 238 } }
239 239
240 /*
241 * Returns a list of login tokens from database
242 */
243 function rg_totp_list($db, $uid)
244 {
245 rg_prof_start('totp_list');
246 rg_log_enter('totp_list');
247
248 while (1) {
249 $key = 'user' . '::' . $uid . '::' . 'login_tokens';
250 $ret = rg_cache_get($key);
251 if ($ret !== FALSE) {
252 $ret['ok'] = 1;
253 break;
254 }
255
256 $ret = array();
257 $ret['ok'] = 0;
258
259 $params = array('uid' => $uid);
260 $sql = 'SELECT * FROM login_tokens'
261 . ' WHERE uid = @@uid@@'
262 . ' ORDER BY itime';
263 $res = rg_sql_query_params($db, $sql, $params);
264 if ($res === FALSE) {
265 rg_totp_set_error('cannot load login tokens');
266 break;
267 }
268
269 $ret['list'] = array();
270 while (($row = rg_sql_fetch_array($res))) {
271 $id = $row['id'];
272
273 rg_totp_cosmetic($row);
274
275 $ret['list'][$id] = $row;
276 }
277 rg_sql_free_result($res);
278
279 unset($ret['ok']); // we do not need it in cache
280 rg_cache_set($key, $ret, RG_SOCKET_NO_WAIT);
281 $ret['ok'] = 1;
282 break;
283 }
284
285 rg_log_exit();
286 rg_prof_end('totp_list');
287 return $ret;
288 }
289
240 290 /* /*
241 291 * Validates a token * Validates a token
242 292 * Also, it marks the tokens as 'confirmed' if needed * Also, it marks the tokens as 'confirmed' if needed
 
... ... function rg_totp_verify_all($db, $uid, $token)
246 296 rg_prof_start('totp_verify_all'); rg_prof_start('totp_verify_all');
247 297 rg_log_enter('totp_verify_all token=' . $token); rg_log_enter('totp_verify_all token=' . $token);
248 298
299 $now = time();
300
249 301 $ret = array(); $ret = array();
250 302 $ret['ok'] = 0; $ret['ok'] = 0;
251 303 while (1) { while (1) {
252 if (strlen($token) < 6)
253 break;
254
255 304 $lt = rg_totp_list($db, $uid); $lt = rg_totp_list($db, $uid);
256 305 if ($lt['ok'] != 1) if ($lt['ok'] != 1)
257 306 break; break;
258 307
259 308 $ret['ok'] = 1; $ret['ok'] = 1;
260 $ret['enrolled'] = 0;
309 $ret['enrolled'] = empty($lt['list']) ? 0 : 1;
261 310 $ret['id'] = 0; $ret['id'] = 0;
262 311 $ret['conf_error'] = 0; $ret['conf_error'] = 0;
263 foreach ($lt['list'] as $_id => $t) {
264 $ret['enrolled'] = 1;
312 $ret['tc'] = 0;
313
314 if (strlen($token) < 6)
315 break;
265 316
266 $_r = rg_totp_verify($t['secret'], $token);
267 if ($_r !== TRUE)
317 foreach ($lt['list'] as $_id => $t) {
318 $tc = rg_totp_verify($t['secret'], $now, $token);
319 if ($tc === FALSE)
268 320 continue; continue;
269 321
322 if ($tc <= $t['last_used_tc']) {
323 rg_totp_set_error('cannot reuse the login token');
324 break;
325 }
326
270 327 $ret['id'] = $t['id']; $ret['id'] = $t['id'];
271 if (strcmp($t['conf'], 't') != 0) {
272 $x = rg_totp_conf($db, $uid, $t['id']);
273 if ($x !== TRUE) {
274 $ret['conf_error'] = 1;
275 break;
276 }
328 $ret['tc'] = $tc;
329
330 // Mark it as used and update 'conf' status
331 // TODO: Should we give error if we cannot update it?
332 $r = rg_totp_set_last_use($db, $uid, $t['id'], $tc, $now);
333 if ($r !== TRUE) {
334 $ret['ok'] = 0;
335 break;
277 336 } }
337
278 338 break; break;
279 339 } }
280 340
 
... ... function rg_totp_enroll($db, $uid, $name, $secret, $ip, $conf)
298 358 while (1) { while (1) {
299 359 $params = array('uid' => $uid, 'name' => $name, $params = array('uid' => $uid, 'name' => $name,
300 360 'secret' => $secret, 'secret' => $secret,
301 'itime' => time(), 'ip' => $ip, 'conf' => $conf);
361 'itime' => time(), 'ip' => $ip, 'conf' => $conf,
362 'last_used_tc' => 0);
302 363 $sql = 'INSERT INTO login_tokens (uid, itime, name, secret, ip' $sql = 'INSERT INTO login_tokens (uid, itime, name, secret, ip'
303 . ', conf)'
364 . ', conf, last_used_tc)'
304 365 . ' VALUES (@@uid@@, @@itime@@, @@name@@' . ' VALUES (@@uid@@, @@itime@@, @@name@@'
305 . ', @@secret@@, @@ip@@, @@conf@@)'
366 . ', @@secret@@, @@ip@@, @@conf@@, @@last_used_tc@@)'
306 367 . ' RETURNING id'; . ' RETURNING id';
307 368 $res = rg_sql_query_params($db, $sql, $params); $res = rg_sql_query_params($db, $sql, $params);
308 369 if ($res === FALSE) { if ($res === FALSE) {
 
... ... function rg_totp_del_ip($db, $uid, $ip)
409 470 return $ret; return $ret;
410 471 } }
411 472
412 /*
413 * Confirm a SSH enrollment process
414 */
415 function rg_totp_conf($db, $uid, $id)
416 {
417 rg_prof_start('totp_conf');
418 rg_log_enter('totp_conf id=' . $id);
419
420 $ret = FALSE;
421 while (1) {
422 $params = array('uid' => $uid, 'id' => $id, 'conf' => 't');
423 $sql = 'UPDATE login_tokens SET conf = @@conf@@'
424 . ' WHERE uid = @@uid@@'
425 . ' AND id = @@id@@'
426 . ' AND conf != @@conf@@';
427 $res = rg_sql_query_params($db, $sql, $params);
428 if ($res === FALSE) {
429 rg_totp_set_error('cannot confirm login token; try again later');
430 break;
431 }
432 rg_sql_free_result($res);
433
434 $key = 'user' . '::' . $uid . '::' . 'login_tokens'
435 . '::' . 'list' . '::' . $params['id'];
436 $a = array('conf' => 't');
437 rg_totp_cosmetic($params);
438 rg_cache_merge($key, $a, RG_SOCKET_NO_WAIT);
439
440 $ret = TRUE;
441 break;
442 }
443
444 rg_log_exit();
445 rg_prof_end('totp_conf');
446 return $ret;
447 }
448
449 /*
450 * Returns a list of login tokens from database
451 */
452 function rg_totp_list($db, $uid)
453 {
454 rg_prof_start('totp_list');
455 rg_log_enter('totp_list');
456
457 while (1) {
458 $key = 'user' . '::' . $uid . '::' . 'login_tokens';
459 $ret = rg_cache_get($key);
460 if ($ret !== FALSE) {
461 $ret['ok'] = 1;
462 break;
463 }
464
465 $ret = array();
466 $ret['ok'] = 0;
467
468 $params = array('uid' => $uid);
469 $sql = 'SELECT * FROM login_tokens'
470 . ' WHERE uid = @@uid@@'
471 . ' ORDER BY itime';
472 $res = rg_sql_query_params($db, $sql, $params);
473 if ($res === FALSE) {
474 rg_totp_set_error('cannot load login tokens');
475 break;
476 }
477
478 $ret['list'] = array();
479 while (($row = rg_sql_fetch_array($res))) {
480 $id = $row['id'];
481
482 rg_totp_cosmetic($row);
483
484 $ret['list'][$id] = $row;
485 }
486 rg_sql_free_result($res);
487
488 rg_cache_set($key, $ret, RG_SOCKET_NO_WAIT);
489 $ret['ok'] = 1;
490 break;
491 }
492
493 rg_log_exit();
494 rg_prof_end('totp_list');
495 return $ret;
496 }
497
498 473 /* /*
499 474 * Remove a list of login tokens * Remove a list of login tokens
500 475 */ */
 
... ... function rg_totp_list_ip($db, $uid)
601 576 return $ret; return $ret;
602 577 } }
603 578
579 /*
580 * Verifies that an IP is in the 'validated' list
581 * Returns -1 on error, 0 if no IP were present and 1 if all is OK
582 */
583 function rg_totp_verify_ip($db, $uid, $ip)
584 {
585 rg_prof_start('totp_verify_ip');
586 rg_log_enter('totp_verify_ip ip=' . $ip);
587
588 $ret = array();
589 $ret['ok'] = -1;
590 $ret['enrolled'] = 1;
591 while (1) {
592 // we will return OK if user is not enrolled
593 $r = rg_totp_list($db, $uid);
594 if ($r['ok'] != 1)
595 break;
596 if (empty($r['list'])) {
597 $ret['ok'] = 1;
598 $ret['enrolled'] = 0;
599 break;
600 }
601
602 $r = rg_totp_list_ip($db, $uid);
603 if ($r['ok'] != 1)
604 break;
605
606 $ret['ok'] = 0;
607 foreach ($r['list'] as $t) {
608 if (strcasecmp($t['ip'], $ip) == 0) {
609 $ret['list'] = $r['list'];
610 $ret['ok'] = 1;
611 break;
612 }
613 }
614
615 if ($ret['ok'] == 0)
616 rg_totp_set_error('you have no IP validated;'
617 . ' run \'ssh ... totp\' for help');
618
619 break;
620 }
621
622 rg_log_exit();
623 rg_prof_end('totp_verify_ip');
624 return $ret;
625 }
626
604 627 /* /*
605 628 * High-level function for listing tokens * High-level function for listing tokens
606 629 */ */
 
... ... function rg_totp_high_level($db, $rg)
658 681 rg_prof_start('totp_high_level'); rg_prof_start('totp_high_level');
659 682 rg_log_enter('totp_high_level'); rg_log_enter('totp_high_level');
660 683
684 $now = time();
685
661 686 $rg['totp'] = array(); $rg['totp'] = array();
662 687
663 688 $ret = ''; $ret = '';
 
... ... function rg_totp_high_level($db, $rg)
691 716 break; break;
692 717 } }
693 718
694 $r = rg_totp_verify($secret, $ver);
719 $r = rg_totp_verify($secret, $now, $ver);
695 720 if ($r === FALSE) { if ($r === FALSE) {
696 721 $errmsg[] = rg_template('user/settings/totp/ver_error.html', $errmsg[] = rg_template('user/settings/totp/ver_error.html',
697 722 $rg, TRUE /*xss*/); $rg, TRUE /*xss*/);
File inc/user.inc.php changed (mode: 100644) (index 0874f6b..6ca3783)
... ... function rg_user_event_login($db, $event)
78 78 if ($r !== TRUE) if ($r !== TRUE)
79 79 break; break;
80 80
81 if ($event['used_login_token_id'] > 0) {
82 $r = rg_totp_set_last_use($db, $event['uid'],
83 $event['used_login_token_id'], $event['ts']);
84 if ($r !== TRUE)
85 break;
86 }
87
88 81 $ret = array(); $ret = array();
89 82 break; break;
90 83 } }
 
... ... function rg_user_edit($db, $d)
517 510 rg_sql_free_result($res); rg_sql_free_result($res);
518 511
519 512 if ($d['uid'] == 0) { // add if ($d['uid'] == 0) { // add
520 rg_cache_set('user::' . $d['uid'], $d, RG_SOCKET_NO_WAIT);
513 rg_cache_set('user' . '::' . $d['uid'] . '::' . ':info',
514 $d, RG_SOCKET_NO_WAIT);
521 515
522 516 $event = array('category' => 2000, 'prio' => 50, $event = array('category' => 2000, 'prio' => 50,
523 517 'ui' => array( 'ui' => array(
 
... ... function rg_user_edit($db, $d)
540 534 if (!$update_pass) if (!$update_pass)
541 535 unset($d['pass']); unset($d['pass']);
542 536 unset($d['pass2']); // not needed in cache unset($d['pass2']); // not needed in cache
543 rg_cache_merge('user::' . $d['uid'], $d, RG_SOCKET_NO_WAIT);
537 rg_cache_merge('user' . '::' . $d['uid'] . '::' . 'info',
538 $d, RG_SOCKET_NO_WAIT);
544 539 } }
545 540
546 541 // TODO: should we cache here the user_by_uid and user_by_name // TODO: should we cache here the user_by_uid and user_by_name
 
... ... function rg_user_remove($db, $rg, $uid)
586 581 rg_sql_free_result($res); rg_sql_free_result($res);
587 582
588 583 // invalidate cache // invalidate cache
589 rg_cache_unset('user::' . $uid, RG_SOCKET_NO_WAIT);
584 rg_cache_unset('user' . '::' . $uid . 'info', RG_SOCKET_NO_WAIT);
590 585
591 586 $ret = TRUE; $ret = TRUE;
592 587 break; break;
 
... ... function rg_user_info($db, $uid, $user, $email)
617 612 "email" => $email); "email" => $email);
618 613
619 614 if ($uid > 0) { if ($uid > 0) {
620 $c = rg_cache_get("user::" . $uid);
615 // We will get all info about fthe user not only 'info'
616 // to populate the cache with all info.
617 $c = rg_cache_get('user' . '::' . $uid);
621 618 if ($c !== FALSE) { if ($c !== FALSE) {
622 $ret = $c;
623 break;
619 if (isset($c['info'])) {
620 $ret = $c['info'];
621 break;
622 }
624 623 } }
625 624
626 625 $sql = "SELECT * FROM users WHERE uid = @@uid@@"; $sql = "SELECT * FROM users WHERE uid = @@uid@@";
 
... ... function rg_user_info($db, $uid, $user, $email)
668 667 $ret = array_merge($ret, $row); $ret = array_merge($ret, $row);
669 668 $ret['exists'] = 1; $ret['exists'] = 1;
670 669
671 rg_cache_set("user::" . $ret['uid'], $ret, RG_SOCKET_NO_WAIT);
670 rg_cache_set('user' . '::' . $ret['uid'] . '::' . 'info',
671 $ret, RG_SOCKET_NO_WAIT);
672 672 rg_cache_set('username_to_uid::' . $ret['username'], rg_cache_set('username_to_uid::' . $ret['username'],
673 673 $ret['uid'], RG_SOCKET_NO_WAIT); $ret['uid'], RG_SOCKET_NO_WAIT);
674 674 rg_cache_set('email_to_uid::' . $ret['email'], $ret['uid'], rg_cache_set('email_to_uid::' . $ret['email'], $ret['uid'],
 
... ... function rg_user_set_last_seen($db, $uid, $ts, $ip)
704 704 } }
705 705 rg_sql_free_result($res); rg_sql_free_result($res);
706 706
707 rg_cache_merge("user::" . $uid, $params, RG_SOCKET_NO_WAIT);
707 rg_cache_merge('user' . '::' . $uid . '::' . 'info',
708 $params, RG_SOCKET_NO_WAIT);
708 709
709 710 $ret = TRUE; $ret = TRUE;
710 711 break; break;
 
... ... function rg_user_login_by_sid($db, &$rg)
767 768 */ */
768 769 function rg_user_pass_valid($db, $uid, $pass) function rg_user_pass_valid($db, $uid, $pass)
769 770 { {
770 rg_log_enter("user_pass_valid: uid=$uid, pass=$pass...");
771 rg_log_enter("user_pass_valid: uid=$uid");
771 772
772 773 $ret = FALSE; $ret = FALSE;
773 774 while (1) { while (1) {
 
... ... function rg_user_login_by_user_pass($db, $user, $pass, $login_token, $lock_ip,
857 858 &$ui) &$ui)
858 859 { {
859 860 rg_prof_start("user_login_by_user_pass"); rg_prof_start("user_login_by_user_pass");
860 rg_log_enter("user_login_by_user_pass: user=$user, pass=$pass"
861 rg_log_enter("user_login_by_user_pass: user=$user"
861 862 . " login_token=$login_token lock_ip=$lock_ip"); . " login_token=$login_token lock_ip=$lock_ip");
862 863
863 864 $ui = array(); $ui = array();
 
... ... function rg_user_login_by_user_pass($db, $user, $pass, $login_token, $lock_ip,
868 869 $ret = FALSE; $ret = FALSE;
869 870 while (1) { while (1) {
870 871 if (empty($user) || empty($pass)) { if (empty($user) || empty($pass)) {
871 rg_user_set_error("invalid user, pass or login_token");
872 rg_user_set_error("invalid user, pass or login token");
872 873 rg_log("user or pass are empty"); rg_log("user or pass are empty");
873 874 break; break;
874 875 } }
 
... ... function rg_user_login_by_user_pass($db, $user, $pass, $login_token, $lock_ip,
877 878 if ($ui0['ok'] != 1) if ($ui0['ok'] != 1)
878 879 break; break;
879 880 if ($ui0['exists'] != 1) { if ($ui0['exists'] != 1) {
880 rg_user_set_error("invalid user, pass or login_token");
881 rg_user_set_error("invalid user, pass or login token");
881 882 rg_log("user doesn't exists"); rg_log("user doesn't exists");
882 883 break; break;
883 884 } }
884 885
885 886 if ($ui0['suspended'] > 0) { if ($ui0['suspended'] > 0) {
886 rg_user_set_error("invalid user, pass or login_token");
887 rg_user_set_error("invalid user, pass or login token");
887 888 rg_log("account is suspended"); rg_log("account is suspended");
888 889 break; break;
889 890 } }
890 891
891 892 if ($ui0['confirmed'] == 0) { if ($ui0['confirmed'] == 0) {
892 rg_user_set_error("invalid user, pass or login_token");
893 rg_user_set_error("invalid user, pass or login token");
893 894 rg_log("account is not confirmed"); rg_log("account is not confirmed");
894 895 break; break;
895 896 } }
 
... ... function rg_user_login_by_user_pass($db, $user, $pass, $login_token, $lock_ip,
906 907 rg_user_set_error('login token error: ' . rg_totp_error()); rg_user_set_error('login token error: ' . rg_totp_error());
907 908 break; break;
908 909 } }
910 //rg_log_ml('DEBUG: vi: ' . print_r($vi, TRUE));
909 911 if (($vi['enrolled'] == 1) && ($vi['id'] == 0)) { if (($vi['enrolled'] == 1) && ($vi['id'] == 0)) {
910 rg_user_set_error('invalid user, pass or login_token');
912 rg_user_set_error('invalid user, pass or login token');
911 913 rg_log('invalid token'); rg_log('invalid token');
912 914 break; break;
913 915 } }
 
... ... function rg_user_login_by_user_pass($db, $user, $pass, $login_token, $lock_ip,
916 918 'category' => 2001, 'category' => 2001,
917 919 'prio' => 100, 'prio' => 100,
918 920 'uid' => $ui0['uid'], 'uid' => $ui0['uid'],
919 'used_login_token_id' => $vi['id'],
920 921 'ts' => time()); 'ts' => time());
921 922 $r = rg_event_add($db, $event); $r = rg_event_add($db, $event);
922 923 if ($r !== TRUE) { if ($r !== TRUE) {
 
... ... function rg_user_suspend($db, $rg, $uid, $op)
979 980
980 981 // update cache // update cache
981 982 // TODO: what if we cannot update? // TODO: what if we cannot update?
982 rg_cache_set("user::" . $uid . "::suspended", $v, RG_SOCKET_NO_WAIT);
983 rg_cache_set('user' . '::' . $uid . '::' . 'info'
984 . '::' . 'suspended', $v, RG_SOCKET_NO_WAIT);
983 985
984 986 break; break;
985 987 } }
 
... ... function rg_user_make_admin($db, $rg, $uid, $op)
1021 1023 } }
1022 1024 rg_sql_free_result($res); rg_sql_free_result($res);
1023 1025
1024 rg_cache_set("user::" . $uid . "::is_admin", 1, RG_SOCKET_NO_WAIT);
1026 rg_cache_set('user' . '::' . $uid . '::' . 'info'
1027 . '::' . 'is_admin', 1, RG_SOCKET_NO_WAIT);
1025 1028
1026 1029 $ret = TRUE; $ret = TRUE;
1027 1030 break; break;
 
... ... function rg_user_forgot_pass_destroy($db, $uid)
1306 1309 function rg_user_set_pass($db, $uid, $pass) function rg_user_set_pass($db, $uid, $pass)
1307 1310 { {
1308 1311 rg_prof_start("user_set_pass"); rg_prof_start("user_set_pass");
1309 rg_log_enter("user_set_pass: uid=$uid pass=$pass");
1312 rg_log_enter("user_set_pass: uid=$uid");
1310 1313
1311 1314 $ret = FALSE; $ret = FALSE;
1312 1315 while (1) { while (1) {
 
... ... function rg_user_set_pass($db, $uid, $pass)
1327 1330 } }
1328 1331 rg_sql_free_result($res); rg_sql_free_result($res);
1329 1332
1330 rg_cache_merge('user::' . $uid, $params, RG_SOCKET_NO_WAIT);
1333 rg_cache_merge('user' . '::' . $uid . '::' . 'info',
1334 $params, RG_SOCKET_NO_WAIT);
1331 1335
1332 1336 $ret = TRUE; $ret = TRUE;
1333 1337 break; break;
 
... ... function rg_user_confirm($db, $token)
1385 1389 } }
1386 1390 rg_sql_free_result($res); rg_sql_free_result($res);
1387 1391
1388 rg_cache_merge('user::' . $uid, $params, RG_SOCKET_NO_WAIT);
1392 rg_cache_merge('user' . '::' . $uid . '::' . 'info',
1393 $params, RG_SOCKET_NO_WAIT);
1389 1394
1390 1395 $ret = $uid; $ret = $uid;
1391 1396 break; break;
File inc/user/home-page.php changed (mode: 100644) (index c15e96b..7540b39)
... ... rg_log("FILE: /inc/user/home-page");
3 3
4 4 $_home = ''; $_home = '';
5 5
6 $page_ui = rg_user_info($db, 0, $user, "");
7 if ($page_ui['exists'] == 0) {
6 $rg['page_ui'] = rg_user_info($db, 0, $user, "");
7 if ($rg['page_ui']['exists'] == 0) {
8 8 $_home .= rg_template("user/invalid.html", $rg, TRUE /* xss */); $_home .= rg_template("user/invalid.html", $rg, TRUE /* xss */);
9 9 return; return;
10 10 } }
11 11
12 $_home .= '<div class="main_title">Home page of user '
13 . $page_ui['username'] . ' (#' . $page_ui['uid'] . ')</div>';
12 $_home .= rg_template('user/home.html', $rg, TRUE /*xss*/);
14 13
15 14 // list of repositories // list of repositories
16 $_home .= rg_repo_list($db, $rg, "", $page_ui['uid']);
15 $_home .= rg_repo_list($db, $rg, '', $rg['page_ui']['uid']);
17 16 ?> ?>
File inc/user/keys/keys.php changed (mode: 100644) (index eb6ec36..2c9c6f2)
... ... while (rg_var_uint("delete") == 1) {
49 49 } }
50 50
51 51 if (empty($key_delete_ids)) { if (empty($key_delete_ids)) {
52 $del_errmsg[] = "No key selected.";
52 $del_errmsg[] = "No keys selected.";
53 53 break; break;
54 54 } }
55 55
File inc/util.inc.php changed (mode: 100644) (index 1671051..94d70e8)
... ... function rg_copy_tree($src, $dst, $mask)
1147 1147 global $php_errormsg; global $php_errormsg;
1148 1148
1149 1149 rg_prof_start("copy_tree"); rg_prof_start("copy_tree");
1150 rg_log_enter("rg_copy_tree($src, $dst, $mask)");
1150 rg_log_enter("copy_tree($src, $dst, $mask)");
1151 1151
1152 1152 $ret = FALSE; $ret = FALSE;
1153 1153 while (1) { while (1) {
1154 1154 if (!is_dir($dst)) { if (!is_dir($dst)) {
1155 $r = @mkdir($dst, $mask);
1155 $r = @mkdir($dst, $mask, TRUE);
1156 1156 if ($r !== TRUE) { if ($r !== TRUE) {
1157 1157 rg_log("ERROR: Cannot mkdir [$dst] ($php_errormsg)."); rg_log("ERROR: Cannot mkdir [$dst] ($php_errormsg).");
1158 1158 break; break;
 
... ... function rg_copy_tree($src, $dst, $mask)
1198 1198 return $ret; return $ret;
1199 1199 } }
1200 1200
1201 /*
1202 * Recursively deletes a tree
1203 */
1204 function rg_del_tree($dst)
1205 {
1206 global $php_errormsg;
1207
1208 rg_prof_start('del_tree');
1209 rg_log_enter('del_tree(' . $dst . ')');
1210
1211 $ret = FALSE;
1212 while (1) {
1213 if (!is_dir($dst))
1214 break;
1215
1216 $d = rg_dir_load($dst);
1217 $err = FALSE;
1218 foreach ($d as $obj) {
1219 if (is_dir($dst . '/' . $obj)) {
1220 $r = rg_del_tree($dst . '/' . $obj);
1221 if ($r !== TRUE) {
1222 $err = TRUE;
1223 break;
1224 }
1225 } else {
1226 $r = @unlink($dst . '/' . $obj);
1227 if ($r !== TRUE) {
1228 rg_log("ERROR: Cannot del file ($php_errormsg).");
1229 $err = TRUE;
1230 break;
1231 }
1232 }
1233 }
1234
1235 $r = @rmdir($dst);
1236 if ($r !== TRUE) {
1237 rg_log("ERROR: Cannot del dir ($php_errormsg).");
1238 $err = TRUE;
1239 break;
1240 }
1241
1242 if (!$err)
1243 $ret = TRUE;
1244 break;
1245 }
1246
1247 rg_log_exit();
1248 rg_prof_end('del_tree');
1249 return $ret;
1250 }
1251
1201 1252 /* /*
1202 1253 * Called by PHP in case of error * Called by PHP in case of error
1203 1254 */ */
 
... ... function rg_mail_template($template, $more)
1290 1341 . base64_encode($rg_admin_name) . "?="; . base64_encode($rg_admin_name) . "?=";
1291 1342
1292 1343 $subject = rg_template($template . ".subj.txt", $more, FALSE /* xss */); $subject = rg_template($template . ".subj.txt", $more, FALSE /* xss */);
1293 $subject = "=?UTF-8?B?" . base64_encode(trim($subject)) . "?=";
1344 $subject = trim($subject);
1345 $subject = str_replace("\r", '', $subject);
1346 $subject = str_replace("\n", '', $subject);
1347 // TODO: do not encode it as UTF-8 if not needed
1348 $subject = "=?UTF-8?B?" . base64_encode($subject) . "?=";
1349
1294 1350 $header = rg_template("mail/common.head.txt", $more, FALSE /* xss */); $header = rg_template("mail/common.head.txt", $more, FALSE /* xss */);
1295 1351 $header .= rg_template($template . ".head.txt", $more, FALSE /* xss */); $header .= rg_template($template . ".head.txt", $more, FALSE /* xss */);
1296 1352 $header = trim($header); $header = trim($header);
1353
1297 1354 $body = rg_template($template . ".body.txt", $more, FALSE /* xss */); $body = rg_template($template . ".body.txt", $more, FALSE /* xss */);
1298 1355
1299 1356 rg_log("CHECK: mail_template(" . $more['ui']['email'] . ", rg_log("CHECK: mail_template(" . $more['ui']['email'] . ",
 
... ... function rg_socket_recv_wait($socket, $wait, $timeout)
1349 1406 $tv_usec = ($timeout % 1000) * 1000; $tv_usec = ($timeout % 1000) * 1000;
1350 1407 } }
1351 1408
1352 $ret_buf = "";
1409 $ret_buf = '';
1353 1410 while (1) { while (1) {
1354 1411 $reads = array($socket); $writes = array(); $ex = array(); $reads = array($socket); $writes = array(); $ex = array();
1355 1412 $r = @socket_select($reads, $writes, $ex, $tv_sec, $tv_usec); $r = @socket_select($reads, $writes, $ex, $tv_sec, $tv_usec);
1356 1413 if ($r === FALSE) { if ($r === FALSE) {
1357 rg_log("Cannot select(" . socket_strerror(socket_last_error()) . ")!");
1414 rg_log('Cannot select(' . socket_strerror(socket_last_error()) . ')!');
1358 1415 break; break;
1359 1416 } }
1360 1417
1361 1418 if ($r === 0) { // timeout if ($r === 0) { // timeout
1362 rg_log("Timeout in reading!");
1419 rg_log('Timeout in reading!');
1363 1420 break; break;
1364 1421 } }
1365 1422
1366 1423 if (!in_array($socket, $reads)) { if (!in_array($socket, $reads)) {
1367 rg_log("Select returned > 0 and my socket is not in reads");
1424 rg_log('Select returned > 0 and my socket is not in reads');
1368 1425 break; break;
1369 1426 } }
1370 1427
1371 1428 $r = @socket_recv($socket, $buf, 32 * 4096, 0); $r = @socket_recv($socket, $buf, 32 * 4096, 0);
1372 1429 if ($r === FALSE) { if ($r === FALSE) {
1373 rg_log("Cannot receive(" . socket_strerror(socket_last_error()) . ")!");
1430 rg_log('Cannot receive (' . socket_strerror(socket_last_error()) . ')!');
1374 1431 break; break;
1375 1432 } }
1376 1433 //rg_log("Received [$buf]"); //rg_log("Received [$buf]");
File root/index.php changed (mode: 100644) (index 726cced..149704e)
... ... while ($tries > 0) {
104 104 sleep(1); sleep(1);
105 105 } }
106 106 if ($good == 0) { if ($good == 0) {
107 // TODO: we must let it go to dispatcher instead of redirecting =>
108 // another connection
109 $url = rg_re_url("fatal");
110 rg_fatal_web("Internal error", $url);
107 echo "Internal error; please try again later.";
108 exit(0);
111 109 } }
112 110
113 111 if (strcmp($service, "git-upload-packXXX") == 0) { if (strcmp($service, "git-upload-packXXX") == 0) {
File root/themes/default/features.html changed (mode: 100644) (index c5d2ab1..205c13e)
58 58 <div class="island_cell"> <div class="island_cell">
59 59 <div class="island"> <div class="island">
60 60 <div class="island_title">Corporate friendly</div> <div class="island_title">Corporate friendly</div>
61 RocketGit is SELinux aware (we wrote the policy),
61 RocketGit is SELinux protected (we wrote the policy),
62 62 therefore you can keep your system secure when therefore you can keep your system secure when
63 using RocketGit.
63 using RocketGit. Also, we implemented two-factor authentication.
64 64 </div> </div>
65 65 </div> </div>
66 66 </div> </div>
 
69 69 <div class="island_cell"> <div class="island_cell">
70 70 <div class="island"> <div class="island">
71 71 <div class="island_title">Platform independent</div> <div class="island_title">Platform independent</div>
72 RocketGit is the fastest and lightest platform independent Git hosting
73 solution currently available on the market.
74 72 You can use RocketGit in your web browser (no matter what You can use RocketGit in your web browser (no matter what
75 73 operating system is running on your machine) or you can choose to operating system is running on your machine) or you can choose to
76 74 install RocketGit on your machine (natively, if you are install RocketGit on your machine (natively, if you are
File root/themes/default/hints/repo/edit_rights.html changed (mode: 100644) (index efb304f..dfbe1b3)
... ... You can use both IPv4 and IPv6. Some examples: 4.4.4.4, 1.2.3.0/24,
10 10 Priority is used to order the rights. They are evaluated top to bottom.<br /> Priority is used to order the rights. They are evaluated top to bottom.<br />
11 11 <br /> <br />
12 12
13 To add a deny rule, just do not check any right.<br />
13 To add a deny rule, just do not select any right; if matched, the evaluation
14 of the next rules will stop.<br />
File root/themes/default/mail/user/repo/bug/new.subj.txt changed (mode: 100644) (index e610874..fc447fc)
1 New bug: '@@bug::title@@' (@@repo::name@@)
1 New bug: '@@bug::title@@' (@@ri::name@@ repo)
File root/themes/default/mail/user/repo/bug/new_note.subj.txt changed (mode: 100644) (index 524b277..6c42034)
1 New note for '@@bug::title@@' (@@repo::name@@)
1 New note for '@@bug::title@@' bug (@@ri::name@@ repo)
File root/themes/default/repo/list/nodata.html changed (mode: 100644) (index d752a71..fec6095)
1 1 <div class="mess ok"> <div class="mess ok">
2 2 No repositories found. No repositories found.
3 Go to <a href="/op/repo">My repositories</a> to create one.
3 4 </div> </div>
File root/themes/default/user/home.html added (mode: 100644) (index 0000000..e6fd4ab)
1 <div class="main_title">
2 Home page of user @@page_ui::username@@ (#@@page_ui::uid@@)
3 </div>
File root/themes/default/user/keys/list/header.html changed (mode: 100644) (index 4a9921a..4287641)
1 1 <div class="rg_keys_list"> <div class="rg_keys_list">
2 2
3 3 @@del_errmsg@@ @@del_errmsg@@
4 @@del_status@@
5 4
6 5 <form method="post" action="/op/settings/keys"> <form method="post" action="/op/settings/keys">
7 6 <input type="hidden" name="delete" value="1" /> <input type="hidden" name="delete" value="1" />
File root/themes/default/user/keys/main.html changed (mode: 100644) (index aa18d41..8a433e6)
1 1 @@add_status@@ @@add_status@@
2 2 @@add_form@@ @@add_form@@
3 3
4 @@del_status@@
4 5 @@keys@@ @@keys@@
5 6
6 7 @@hints@@ @@hints@@
File root/themes/extract_texts.sh added (mode: 100755) (index 0000000..8367450)
1 #!/bin/bash
2
3 if [ "${1}" = "" ]; then
4 dir="default"
5 else
6 dir="${1}"
7 fi
8
9 (
10 cd "${dir}"
11 find . -type f -name '*.txt' -exec cat {} \;
12 //find . -type f -name '*.html' -exec lynx -dump {} \;
13 ) | sort | uniq > extract_texts.out
File scripts/cache.php changed (mode: 100644) (index bd7e87d..6acc008)
... ... function rg_handle_command($k, &$conn_table, $cmd)
47 47
48 48 $s = &$conn_table['conns'][$k]; $s = &$conn_table['conns'][$k];
49 49
50 $a = explode(" ", $cmd, 3);
50 $a = explode(' ', $cmd, 4);
51 51 $buf = "ER Invalid command\n"; $buf = "ER Invalid command\n";
52 52 $no_wait = FALSE; $no_wait = FALSE;
53 53 while (1) { while (1) {
54 // We must have at least 2 parameters: cmd and flags
55 if (!isset($a[1]))
54 // We must have at least 2 parameters: cmd and flags and I
55 if (!isset($a[2]))
56 56 break; break;
57 57
58 58 $cmd = trim($a[0]); $cmd = trim($a[0]);
59
59 60 $flags = trim($a[1]); $flags = trim($a[1]);
60 61 if (strncmp($flags, 'F=', 2) != 0) { if (strncmp($flags, 'F=', 2) != 0) {
61 62 rg_log('Invalid command (no flags): $cmd'); rg_log('Invalid command (no flags): $cmd');
 
... ... function rg_handle_command($k, &$conn_table, $cmd)
65 66 if (strstr($flags, 'W')) if (strstr($flags, 'W'))
66 67 $no_wait = TRUE; $no_wait = TRUE;
67 68
69 $id = trim($a[2]);
70 if (strncmp($id, 'I=', 2) != 0) {
71 rg_log('Invalid command (no id): $cmd');
72 break;
73 }
74 $id = substr($id, 2);
75
68 76 /* From here, commands with no parameters */ /* From here, commands with no parameters */
77 /* none yet */
69 78
70 79 /* From here, at least 1 para */ /* From here, at least 1 para */
71 if (!isset($a[2]))
80 if (!isset($a[3]))
72 81 break; break;
73 $para1 = trim($a[2]);
82 $para1 = trim($a[3]);
74 83
75 84 if (strcmp($cmd, "SET") == 0) { if (strcmp($cmd, "SET") == 0) {
76 85 $ns_var_value = explode("=", $para1, 2); $ns_var_value = explode("=", $para1, 2);
 
... ... function rg_handle_command($k, &$conn_table, $cmd)
81 90 $value = unserialize(stripcslashes($value)); $value = unserialize(stripcslashes($value));
82 91 if ($value !== FALSE) { if ($value !== FALSE) {
83 92 rg_cache_core_set("normal::" . $ns_var, $value); rg_cache_core_set("normal::" . $ns_var, $value);
84 $buf = "OK\n";
93 $buf = 'OK ' . $id . "\n";
85 94 } else { } else {
86 $buf = "ER cannot unserialize data\n";
95 $buf = 'ER ' . $id . ' cannot unserialize data' . "\n";
87 96 } }
88 97 break; break;
89 98 } }
 
... ... function rg_handle_command($k, &$conn_table, $cmd)
99 108 $ret = rg_cache_core_merge("normal::" . $ns_var, $ret = rg_cache_core_merge("normal::" . $ns_var,
100 109 $value); $value);
101 110 if ($ret === FALSE) if ($ret === FALSE)
102 $buf = "NOT_FOUND\n";
111 $buf = 'ER ' . $id . ' NOT_FOUND' . "\n";
103 112 else else
104 $buf = "OK\n";
113 $buf = 'OK ' . $id . "\n";
105 114 } else { } else {
106 $buf = "ER cannot unserialize data\n";
115 $buf = 'ER ' . $id . 'cannot unserialize data' . "\n";
107 116 } }
108 117 break; break;
109 118 } }
110 119
111 120 if (strcmp($cmd, "INC") == 0) { if (strcmp($cmd, "INC") == 0) {
112 121 $v = rg_cache_core_inc("normal::" . $para1); $v = rg_cache_core_inc("normal::" . $para1);
113 $buf = "OK v=$v\n";
122 $buf = 'OK ' . $id . ' v=' . $v . "\n";
114 123 break; break;
115 124 } }
116 125
117 126 if (strcmp($cmd, "GET") == 0) { if (strcmp($cmd, "GET") == 0) {
118 127 $ret = rg_cache_core_get("normal::" . $para1); $ret = rg_cache_core_get("normal::" . $para1);
119 128 if ($ret === FALSE) if ($ret === FALSE)
120 $buf = "NOT_FOUND\n";
129 $buf = 'ER ' . $id . ' NOT_FOUND' . "\n";
121 130 else else
122 $buf = "OK " . rg_cache_prepare($ret) . "\n";
131 $buf = 'OK ' . $id . ' ' . rg_cache_prepare($ret) . "\n";
123 132 break; break;
124 133 } }
125 134
126 135 if (strcmp($cmd, "UNSET") == 0) { if (strcmp($cmd, "UNSET") == 0) {
127 136 $ret = rg_cache_core_unset("normal::" . $para1); $ret = rg_cache_core_unset("normal::" . $para1);
128 137 if ($ret === FALSE) if ($ret === FALSE)
129 $buf = "NOT_FOUND\n";
138 $buf = 'ER ' . $id . ' NOT_FOUND' . "\n";
130 139 else else
131 $buf = "OK\n";
140 $buf = 'OK ' . $id . "\n";
132 141 break; break;
133 142 } }
134 143
135 144 if (strcmp($cmd, "ADUMP") == 0) { if (strcmp($cmd, "ADUMP") == 0) {
136 145 $ret = rg_cache_core_adump("normal::" . $para1); $ret = rg_cache_core_adump("normal::" . $para1);
137 146 if ($ret === FALSE) if ($ret === FALSE)
138 $buf = "NOT_FOUND\n";
147 $buf = 'ER ' . $id . ' NOT_FOUND' . "\n";
139 148 else else
140 $buf = "OK " . rg_cache_prepare($ret) . "\n";
149 $buf = 'OK ' . $id . ' ' . rg_cache_prepare($ret) . "\n";
141 150 break; break;
142 151 } }
143 152
 
... ... function rg_handle_command($k, &$conn_table, $cmd)
150 159 $value = unserialize(stripcslashes($value)); $value = unserialize(stripcslashes($value));
151 160 if ($value !== FALSE) { if ($value !== FALSE) {
152 161 rg_cache_core_apush("normal::" . $ns_var, $value); rg_cache_core_apush("normal::" . $ns_var, $value);
153 $buf = "OK\n";
162 $buf = 'OK ' . $id . "\n";
154 163 } else { } else {
155 $buf = "ER cannot unserialize data\n";
164 $buf = 'ER ' . $id . ' cannot unserialize data' . "\n";
156 165 } }
157 166 break; break;
158 167 } }
 
... ... function rg_handle_command($k, &$conn_table, $cmd)
160 169 if (strcmp($cmd, "APOP") == 0) { if (strcmp($cmd, "APOP") == 0) {
161 170 $ret = rg_cache_core_apop("normal::" . $para1); $ret = rg_cache_core_apop("normal::" . $para1);
162 171 if ($ret === FALSE) if ($ret === FALSE)
163 $buf = "NOT_FOUND\n";
172 $buf = 'ER ' . $id . ' NOT_FOUND' . "\n";
164 173 else else
165 $buf = "OK " . $ret . "\n";
174 $buf = 'OK ' . $id . ' ' . $ret . "\n";
166 175 break; break;
167 176 } }
168 177
169 178 if (strcmp($cmd, "ASHIFT") == 0) { if (strcmp($cmd, "ASHIFT") == 0) {
170 179 $ret = rg_cache_core_ashift("normal::" . $para1); $ret = rg_cache_core_ashift("normal::" . $para1);
171 180 if ($ret === FALSE) if ($ret === FALSE)
172 $buf = "NOT_FOUND\n";
181 $buf = 'ER ' . $id . ' NOT_FOUND' . "\n";
173 182 else else
174 $buf = "OK " . $ret . "\n";
183 $buf = 'OK ' . $id . ' ' . $ret . "\n";
175 184 break; break;
176 185 } }
177 186
187 if (strcmp($cmd, "SHUTDOWN") == 0) {
188 rg_log('Shutting down...');
189 exit(0);
190 }
191
192 if (strcmp($cmd, "SLEEP") == 0) {
193 rg_log('Sleeping...');
194 $buf = 'OK ' . $id . "\n";
195 sleep(1);
196 }
197
178 198 break; break;
179 199 } }
180 200
File scripts/cron.php changed (mode: 100644) (index 76587ef..76d38eb)
... ... if ($db === FALSE) {
54 54 exit(1); exit(1);
55 55 } }
56 56
57 $r = rg_sql_struct_update($db, 0);
58 if ($r !== TRUE)
59 exit(1);
57 $restart_cache = FALSE;
58 if (rg_sql_struct_update_needed($db) == 1) {
59 $r = rg_sql_struct_update($db, 0);
60 if ($r !== TRUE)
61 exit(1);
62 $restart_cache = TRUE;
63 }
60 64
61 $r = rg_fixes_update($db);
62 if ($r !== TRUE)
63 exit(1);
65 if (rg_fixes_needed($db) == 1) {
66 $r = rg_fixes_update($db);
67 if ($r !== TRUE)
68 exit(1);
69 $restart_cache = TRUE;
70 }
71
72 if ($restart_cache)
73 rg_cache_restart();
64 74
65 75 $install_id = rg_state_get($db, "install_id"); $install_id = rg_state_get($db, "install_id");
66 76 if ($install_id === FALSE) if ($install_id === FALSE)
File scripts/remote.php changed (mode: 100644) (index abcc609..75d88ff)
1 1 <?php <?php
2 // This is called by a remote client that does push or fetch
2 // It is called by a remote client that does a push/fetch by git/ssh.
3 3 error_reporting(E_ALL); error_reporting(E_ALL);
4 4 ini_set("track_errors", "On"); ini_set("track_errors", "On");
5 5
 
... ... function info($str)
40 40
41 41 function fatal($str) function fatal($str)
42 42 { {
43 info("FATAL ERROR: $str");
43 info("Error: $str");
44 44 exit(1); exit(1);
45 45 } }
46 46
 
... ... if (isset($_SERVER['SSH_CONNECTION'])) {
67 67 rg_log("SSH connection: " . $_SERVER['SSH_CONNECTION']); rg_log("SSH connection: " . $_SERVER['SSH_CONNECTION']);
68 68
69 69 // we do not have host info // we do not have host info
70 $host = "";
70 $host = '';
71 71
72 72 // first parameter must be uid of the user // first parameter must be uid of the user
73 73 $login_uid = isset($_SERVER['argv'][1]) ? $_SERVER['argv'][1] : 0; $login_uid = isset($_SERVER['argv'][1]) ? $_SERVER['argv'][1] : 0;
 
... ... if (isset($_SERVER['SSH_CONNECTION'])) {
85 85 $cmd_repo = ""; $cmd_repo = "";
86 86 else else
87 87 $cmd_repo = trim($_SERVER['SSH_ORIGINAL_COMMAND']); $cmd_repo = trim($_SERVER['SSH_ORIGINAL_COMMAND']);
88 rg_ssh_dispatch($db, $login_uid, $cmd_repo);
89 88
90 89 $ssh_client = getenv("SSH_CLIENT"); $ssh_client = getenv("SSH_CLIENT");
91 90 $_t = explode(" ", $ssh_client); $_t = explode(" ", $ssh_client);
92 91 $ip = $_t[0]; $ip = $_t[0];
93 92
93 rg_ssh_dispatch($db, $ip, $login_uid, $cmd_repo);
94
94 95 // TODO: This should be put in a queue for performance reasons // TODO: This should be put in a queue for performance reasons
96 // At the same time, update stats?
95 97 $_r = rg_keys_update_use($db, $key_id, $ip); $_r = rg_keys_update_use($db, $key_id, $ip);
96 98 if ($_r !== TRUE) if ($_r !== TRUE)
97 99 rg_internal_error("Cannot update key last_use!"); rg_internal_error("Cannot update key last_use!");
 
... ... if (isset($_SERVER['SSH_CONNECTION'])) {
102 104 $login_uid = 0; $login_uid = 0;
103 105 $key_id = 0; $key_id = 0;
104 106
105 $f = @fopen("php://stdin", "r");
106 if ($f === FALSE)
107 fatal("Cannot open stdin!");
108 $line = @fread($f, 8000);
109 if ($line === FALSE)
110 fatal("Cannot read!");
111 fclose($f);
107 $line = @fread(STDIN, 8000);
112 108 $line_len = strlen($line); $line_len = strlen($line);
113
114 109 rg_log("line=[$line]"); rg_log("line=[$line]");
115 110 if ($line_len < 4) if ($line_len < 4)
116 111 fatal("Line is too short!"); fatal("Line is too short!");
 
... ... if (isset($_SERVER['SSH_CONNECTION'])) {
124 119 $cmd_repo = trim($v[0]); $cmd_repo = trim($v[0]);
125 120 $host = isset($v[1]) ? trim(substr($v[1], 5)) : ""; $host = isset($v[1]) ? trim(substr($v[1], 5)) : "";
126 121
122 if (strcasecmp($host, $rg_git_host) != 0)
123 info('Warn: Please use ' . $rg_git_host
124 . ' instead of ' . $host . '.');
125
127 126 $ip = getenv("REMOTE_HOST"); $ip = getenv("REMOTE_HOST");
128 127 } }
129 128
 
... ... if (strcmp($_t[0], "user") == 0) {
159 158
160 159 rg_log("host=[$host] cmd=[$cmd] prefix=[$prefix] user=[$user] repo=[$repo]."); rg_log("host=[$host] cmd=[$cmd] prefix=[$prefix] user=[$user] repo=[$repo].");
161 160
162 // TODO: if $host does not match $rg_git_host, give a warning to the user to
163 // update the config.
164
165 161 // validity/security checks // validity/security checks
166 162 // Load info about the owner // Load info about the owner
167 163 if (rg_user_ok($user) !== TRUE) if (rg_user_ok($user) !== TRUE)
 
... ... if ($login_uid > 0) {
177 173 $conn_ui = rg_user_info($db, $login_uid, "", ""); $conn_ui = rg_user_info($db, $login_uid, "", "");
178 174 if ($conn_ui['exists'] != 1) if ($conn_ui['exists'] != 1)
179 175 fatal("User does not exists (conn)."); fatal("User does not exists (conn).");
176 info('you are connecting as user \'' . $conn_ui['username'] . '\'.');
180 177 } else { } else {
181 178 $conn_ui = array('uid' => 0, 'username' => ''); $conn_ui = array('uid' => 0, 'username' => '');
179 info('you are connecting as anonymous.');
182 180 } }
183 181
184 182 // Loading info about the repository // Loading info about the repository
 
... ... $ret = rg_rights_allow($db, $x);
210 208 if ($ret !== TRUE) if ($ret !== TRUE)
211 209 fatal("You have no rights to access this repo!"); fatal("You have no rights to access this repo!");
212 210
211 // If we are enrolled, ask for login token
212 // For push we always ask for it, for fetch only if repo is NOT public
213 // And we can ask only if we have a ssh connection. For git protocol we cannot
214 // because we do not have the username/uid of the connecting user.
215 if (isset($_SERVER['SSH_CONNECTION'])) {
216 if (($ri['public'] == 0) || ($push == 1)) {
217 $r = rg_totp_verify_ip($db, $conn_ui['uid'], $ip);
218 if ($r['ok'] != 1)
219 fatal(rg_totp_error() . '.');
220 }
221 }
222
213 223 // TODO: limit per connection // TODO: limit per connection
214 224 // TODO: limit time and/or cpu // TODO: limit time and/or cpu
215 225 // TODO: limit cpuset // TODO: limit cpuset
 
... ... if ($push == 1) {
239 249 // "No refs in common and none specified; doing nothing. // "No refs in common and none specified; doing nothing.
240 250 // Perhaps you should specify a branch such as 'master'." // Perhaps you should specify a branch such as 'master'."
241 251 $dst = $repo_path . "/refs/namespaces/" . $namespace . "/refs/heads"; $dst = $repo_path . "/refs/namespaces/" . $namespace . "/refs/heads";
242 $ret = @mkdir($dst, 0755, TRUE);
243 if ($ret === FALSE)
244 fatal("Internal error (namespace dir)");
245 // copy refs
246 252 $r = rg_copy_tree($repo_path . "/refs/heads", $dst . "/", 0755); $r = rg_copy_tree($repo_path . "/refs/heads", $dst . "/", 0755);
247 253 if ($r !== TRUE) if ($r !== TRUE)
248 254 fatal("Internal error (cannot copy refs)"); fatal("Internal error (cannot copy refs)");
File tests/Makefile changed (mode: 100644) (index 5249830..2c556db)
1 tests := git_log1.sh \
1 tests := totp git_log1.sh \
2 2 http_admin http_bug \ http_admin http_bug \
3 3 http_create_account http_login http_settings http_csrf http_top \ http_create_account http_login http_settings http_csrf http_top \
4 4 token util log state cache prof db event rights keys user repo git \ token util log state cache prof db event rights keys user repo git \
 
... ... all: $(tests)
10 10 @-ls -l err-* @-ls -l err-*
11 11 @echo "Do not forget to check for errors in /var/log/rocketgit!" @echo "Do not forget to check for errors in /var/log/rocketgit!"
12 12
13 totp:
14 php totp.php
15
13 16 git_log1.sh: git_log1.sh:
14 17 ./git_log1.sh ./git_log1.sh
15 18
File tests/cache.php changed (mode: 100644) (index 94e431e..bd4cc48)
... ... rg_log_set_file("cache.log");
14 14 $rg_no_db = TRUE; $rg_no_db = TRUE;
15 15 require_once("common.php"); require_once("common.php");
16 16
17 $rg_cache_enable = TRUE;
18 $rg_cache_debug = TRUE;
19
17 20 rg_cache_core_set("a::b::c", "1"); rg_cache_core_set("a::b::c", "1");
18 21
19 22 $e = "1"; $e = "1";
 
... ... if ($r !== $e) {
46 49 rg_cache_core_apush("v", "1"); rg_cache_core_apush("v", "1");
47 50 rg_cache_core_apush("v", "2"); rg_cache_core_apush("v", "2");
48 51 rg_cache_core_apush("v", "aa"); rg_cache_core_apush("v", "aa");
49 $e = "1,2,aa";
52 $e = "0=[1] 1=[2] 2=[aa]";
50 53 $r = rg_cache_core_adump("v"); $r = rg_cache_core_adump("v");
51 54 if ($r !== $e) { if ($r !== $e) {
52 55 print_r($rg_cache); print_r($rg_cache);
 
... ... if ($r !== $e) {
79 82 exit(1); exit(1);
80 83 } }
81 84
82 rg_log("OK!");
83 85
86 rg_log('');
87 rg_log('Testing set/get with a small timeout');
88 $rg_cache_timeout = 1;
89 rg_cache_set('test::A', 'A', 0);
90 rg_cache_set('test::B', 'B', 0);
91 rg_cache_sleep();
92 $rg_cache = array();
93 rg_cache_get('test::B');
94 $rg_cache_timeout = 3000;
95 $r = rg_cache_get('test::A');
96 if ($r !== 'A') {
97 rg_log('Small timeout test failed!');
98 exit(1);
99 }
100
101 rg_log("OK!");
84 102 ?> ?>
File tests/helpers.inc.php changed (mode: 100644) (index 1773bb5..bcbf5b2)
... ... function rg_test_create_user($db, &$rg_ui)
79 79 $res = rg_sql_query($db, $sql); $res = rg_sql_query($db, $sql);
80 80 rg_sql_free_result($res); rg_sql_free_result($res);
81 81
82 // delete given rights
82 // Delete given rights
83 83 $sql = "DELETE FROM rights WHERE uid = " . $rg_ui['uid']; $sql = "DELETE FROM rights WHERE uid = " . $rg_ui['uid'];
84 84 $res = rg_sql_query($db, $sql); $res = rg_sql_query($db, $sql);
85 85 rg_sql_free_result($res); rg_sql_free_result($res);
86 86
87 // Delete login tokens
88 $sql = "DELETE FROM login_tokens WHERE uid = " . $rg_ui['uid'];
89 $res = rg_sql_query($db, $sql);
90 rg_sql_free_result($res);
91
92 // Delete login tokens ips
93 $sql = "DELETE FROM login_tokens_ip WHERE uid = " . $rg_ui['uid'];
94 $res = rg_sql_query($db, $sql);
95 rg_sql_free_result($res);
96
87 97 return TRUE; return TRUE;
88 98 } }
89 99
File tests/http.inc.php changed (mode: 100644) (index fcc73ba..d87fb47)
... ... function do_req($url, &$data, &$headers)
54 54 $ret['body'] = substr($r, $header_size); $ret['body'] = substr($r, $header_size);
55 55 curl_close($c); curl_close($c);
56 56
57 // Check for XSS
58 if (stristr($ret['body'], '<xss>')) {
59 file_put_contents('http_xss.out', $ret['body']);
60 rg_log("Found <xss> token! Check http_xss.out. Not good!");
61 exit(1);
62 }
63
57 64 // Check with tidy // Check with tidy
58 65 if (!empty($ret['body'])) { // we may have a redirect if (!empty($ret['body'])) { // we may have a redirect
66 // some fixes
67 $ret['body'] = str_replace('autocomplete="off"', '', $ret['body']);
68 $ret['body'] = str_replace('<xss>', '|xss|', $ret['body']);
59 69 file_put_contents("http.tidy.in", $ret['body']); file_put_contents("http.tidy.in", $ret['body']);
60 70 $cmd = "tidy -errors -utf8 -file http.tidy.out http.tidy.in"; $cmd = "tidy -errors -utf8 -file http.tidy.out http.tidy.in";
61 71 system($cmd, $ec); system($cmd, $ec);
 
... ... function do_req($url, &$data, &$headers)
81 91 $ret['sid'] = $matches[1]; $ret['sid'] = $matches[1];
82 92 } }
83 93
84 // Check for XSS
85 if (stristr($ret['body'], '<xss>')) {
86 file_put_contents('http_xss.out', $ret['body']);
87 rg_log("Found <xss> token! Check http_xss.out. Not good!");
88 exit(1);
89 }
90
91 94 $ret['tokens'] = array(); $ret['tokens'] = array();
92 95 $x = preg_match_all('/ name="token" value="([a-zA-Z0-9_:]*)"/', $ret['body'], $matches); $x = preg_match_all('/ name="token" value="([a-zA-Z0-9_:]*)"/', $ret['body'], $matches);
93 96 //rg_log_ml('DEBUG: matches: ' . print_r($matches, TRUE)); //rg_log_ml('DEBUG: matches: ' . print_r($matches, TRUE));
 
... ... function test_login($url, $rg_ui, &$good_sid)
179 182 } }
180 183 $good_sid = $r['sid']; $good_sid = $r['sid'];
181 184
182 if (strstr($r['body'], "invalid user or pass")) {
185 if (strstr($r['body'], "invalid user")) {
183 186 rg_log_ml(print_r($r, TRUE)); rg_log_ml(print_r($r, TRUE));
184 187 rg_log("Login invalid. Check above!"); rg_log("Login invalid. Check above!");
185 188 return FALSE; return FALSE;
 
... ... function test_restore($db)
207 210 } }
208 211 rg_sql_free_result($res); rg_sql_free_result($res);
209 212
210 rg_cache_unset('user::4', RG_SOCKET_NO_WAIT);
213 rg_cache_unset('user::4::info', RG_SOCKET_NO_WAIT);
211 214 } }
212 215
213 216 /* /*
File tests/http_settings.php changed (mode: 100644) (index 931bcb2..93a3b0d)
... ... $rg_cache_enable = TRUE;
22 22
23 23 $now = time(); $now = time();
24 24
25 rg_log("Test if caching works cache_enable=" . ($rg_cache_enable ? "true" : "false"));
25 rg_log("Testing if caching works: cache_enable=" . ($rg_cache_enable ? "true" : "false"));
26 26 rg_cache_set("test::a", "1", 0); rg_cache_set("test::a", "1", 0);
27 27 $r = rg_cache_get("test::a"); $r = rg_cache_get("test::a");
28 28 if (strcmp($r, "1") != 0) { if (strcmp($r, "1") != 0) {
 
... ... if ($r === FALSE) {
45 45 exit(1); exit(1);
46 46 } }
47 47
48 rg_log('');
48 49 rg_log("Loading change pass form"); rg_log("Loading change pass form");
49 50 $data = array(); $data = array();
50 51 $headers = array("Cookie: sid=" . $good_sid); $headers = array("Cookie: sid=" . $good_sid);
51 52 $r = do_req($test_url . "/op/settings/change_pass?t=load_change_pass_form", $data, $headers); $r = do_req($test_url . "/op/settings/change_pass?t=load_change_pass_form", $data, $headers);
52 53 if (!strstr($r['body'], "action=\"/op/settings/change_pass\"")) { if (!strstr($r['body'], "action=\"/op/settings/change_pass\"")) {
54 rg_log_ml('r: ' . print_r($r, TRUE));
53 55 rg_log("Cannot load change pass form!"); rg_log("Cannot load change pass form!");
54 56 exit(1); exit(1);
55 57 } }
56 58 $good_token = $r['tokens']['set_pass']; $good_token = $r['tokens']['set_pass'];
57 59
60 rg_log('');
58 61 rg_log("Posting change pass form"); rg_log("Posting change pass form");
59 62 $data = array( $data = array(
60 63 "doit" => 1, "doit" => 1,
 
... ... $data = array(
66 69 $headers = array("Cookie: sid=" . $good_sid); $headers = array("Cookie: sid=" . $good_sid);
67 70 $r = do_req($test_url . "/op/settings/change_pass?t=post_change_pass_form", $data, $headers); $r = do_req($test_url . "/op/settings/change_pass?t=post_change_pass_form", $data, $headers);
68 71 if (!strstr($r['body'], "Password was updated with success")) { if (!strstr($r['body'], "Password was updated with success")) {
72 rg_log_ml('r: ' . print_r($r, TRUE));
69 73 rg_log("Cannot change pass!"); rg_log("Cannot change pass!");
70 74 exit(1); exit(1);
71 75 } }
72 76
73 77
78 rg_log('');
74 79 rg_log("Now, try to login with the old password"); rg_log("Now, try to login with the old password");
75 80 $r2 = test_login($test_url, $rg_ui, $junk); $r2 = test_login($test_url, $rg_ui, $junk);
76 81 if ($r2 !== FALSE) { if ($r2 !== FALSE) {
77 82 rg_log("Seems we were able to login with the old password!"); rg_log("Seems we were able to login with the old password!");
78 83 exit(1); exit(1);
84
79 85 } }
80 86
81 87
88 rg_log('');
82 89 rg_log("Change back the password"); rg_log("Change back the password");
83 90 $data = array(); $data = array();
84 91 $headers = array("Cookie: sid=" . $good_sid); $headers = array("Cookie: sid=" . $good_sid);
 
... ... $data = array(
98 105 $headers = array("Cookie: sid=" . $good_sid); $headers = array("Cookie: sid=" . $good_sid);
99 106 $r = do_req($test_url . "/op/settings/change_pass?t=change_back_the_password", $data, $headers); $r = do_req($test_url . "/op/settings/change_pass?t=change_back_the_password", $data, $headers);
100 107 if ($r === FALSE) { if ($r === FALSE) {
108 rg_log_ml('r: ' . print_r($r, TRUE));
101 109 rg_log("Cannot change back the pass to aaaa!"); rg_log("Cannot change back the pass to aaaa!");
102 110 exit(1); exit(1);
103 111 } }
104 112
105 113
114 rg_log('');
106 115 rg_log("Testing edit info section"); rg_log("Testing edit info section");
107 116
117 rg_log('');
108 118 rg_log("Loading edit info form"); rg_log("Loading edit info form");
109 119 $data = array(); $data = array();
110 120 $headers = array("Cookie: sid=" . $good_sid); $headers = array("Cookie: sid=" . $good_sid);
111 121 $r = do_req($test_url . "/op/settings/edit_info?t=load_edit_info_form", $data, $headers); $r = do_req($test_url . "/op/settings/edit_info?t=load_edit_info_form", $data, $headers);
112 122 if ($r === FALSE) { if ($r === FALSE) {
123 rg_log_ml('r: ' . print_r($r, TRUE));
113 124 rg_log("Cannot load form!"); rg_log("Cannot load form!");
114 125 exit(1); exit(1);
115 126 } }
116 127
128 rg_log('');
117 129 rg_log("Posting edit info form"); rg_log("Posting edit info form");
118 130 $session_time = intval($now / 393956); $session_time = intval($now / 393956);
119 131 $data = array( $data = array(
 
... ... $data = array(
128 140 $headers = array("Cookie: sid=" . $good_sid); $headers = array("Cookie: sid=" . $good_sid);
129 141 $r = do_req($test_url . "/op/settings/edit_info?t=post_edit_info_form", $data, $headers); $r = do_req($test_url . "/op/settings/edit_info?t=post_edit_info_form", $data, $headers);
130 142 if (!strstr($r['body'], "Information was updated with success")) { if (!strstr($r['body'], "Information was updated with success")) {
143 rg_log_ml('r: ' . print_r($r, TRUE));
131 144 rg_log("Cannot change back the pass to aaaa!"); rg_log("Cannot change back the pass to aaaa!");
132 145 exit(1); exit(1);
133 146 } }
134 147
148 rg_log('');
135 149 rg_log("Verify against database"); rg_log("Verify against database");
136 150 $sql = "SELECT * FROM users WHERE username = '" . $rg_ui['username'] . "'"; $sql = "SELECT * FROM users WHERE username = '" . $rg_ui['username'] . "'";
137 151 $res = rg_sql_query($db, $sql); $res = rg_sql_query($db, $sql);
138 152 $row = rg_sql_fetch_array($res); $row = rg_sql_fetch_array($res);
139 153 rg_sql_free_result($res); rg_sql_free_result($res);
140 154 if (strcmp($rg_ui['realname'], $row['realname']) != 0) { if (strcmp($rg_ui['realname'], $row['realname']) != 0) {
155 rg_log_ml('r: ' . print_r($r, TRUE));
141 156 rg_log_ml("realname was not changed: " . print_r($row, TRUE)); rg_log_ml("realname was not changed: " . print_r($row, TRUE));
142 157 exit(1); exit(1);
143 158 } }
144 159 if ($row['plan_id'] != 5) { if ($row['plan_id'] != 5) {
160 rg_log_ml('r: ' . print_r($r, TRUE));
145 161 rg_log_ml("plan_id was not changed: " . print_r($row, TRUE)); rg_log_ml("plan_id was not changed: " . print_r($row, TRUE));
146 162 exit(1); exit(1);
147 163 } }
148 164 if ($row['session_time'] != $session_time) { if ($row['session_time'] != $session_time) {
165 rg_log_ml('r: ' . print_r($r, TRUE));
149 166 rg_log_ml("session_time was not changed: " . print_r($row, TRUE)); rg_log_ml("session_time was not changed: " . print_r($row, TRUE));
150 167 exit(1); exit(1);
151 168 } }
152 169
153 170
171 rg_log('');
154 172 rg_log("Testing SSH keys"); rg_log("Testing SSH keys");
155 173 rg_log("Loading ssh keys form"); rg_log("Loading ssh keys form");
156 174 $data = array(); $data = array();
157 175 $headers = array("Cookie: sid=" . $good_sid); $headers = array("Cookie: sid=" . $good_sid);
158 176 $r = do_req($test_url . "/op/settings/keys?t=load_key_form_add", $data, $headers); $r = do_req($test_url . "/op/settings/keys?t=load_key_form_add", $data, $headers);
159 177 if ($r === FALSE) { if ($r === FALSE) {
178 rg_log_ml('r: ' . print_r($r, TRUE));
160 179 rg_log("Cannot load form!"); rg_log("Cannot load form!");
161 180 exit(1); exit(1);
162 181 } }
163 182 if (empty($r['tokens']['keys'])) { if (empty($r['tokens']['keys'])) {
183 rg_log_ml('r: ' . print_r($r, TRUE));
164 184 rg_log("token not found!"); rg_log("token not found!");
165 185 exit(1); exit(1);
166 186 } }
 
... ... if ($r === FALSE) {
175 195 exit(1); exit(1);
176 196 } }
177 197 // the key upload stuff will change < and > to empty. // the key upload stuff will change < and > to empty.
178 $sql = "SELECT * FROM keys WHERE key = '" . $key . " xss" . $rg_ui['uid'] . "'";
198 $sql = "SELECT * FROM keys WHERE key = '" . $key . " " . $comment . "'";
179 199 $res = rg_sql_query($db, $sql); $res = rg_sql_query($db, $sql);
180 200 $rows = rg_sql_num_rows($res); $rows = rg_sql_num_rows($res);
181 201 if ($rows > 0) if ($rows > 0)
182 202 $row = rg_sql_fetch_array($res); $row = rg_sql_fetch_array($res);
183 203 rg_sql_free_result($res); rg_sql_free_result($res);
184 204 if ($rows == 0) { if ($rows == 0) {
205 rg_log_ml('r: ' . print_r($r, TRUE));
185 206 rg_log("Key was not uploaded!"); rg_log("Key was not uploaded!");
186 207 exit(1); exit(1);
187 208 } }
188 209 $key_id = $row['key_id']; $key_id = $row['key_id'];
189 210
211 rg_log('');
190 212 rg_log("Now, testing deletion: key_id=$key_id"); rg_log("Now, testing deletion: key_id=$key_id");
191 213 rg_log("Loading ssh keys form"); rg_log("Loading ssh keys form");
192 214 $data = array(); $data = array();
193 215 $headers = array("Cookie: sid=" . $good_sid); $headers = array("Cookie: sid=" . $good_sid);
194 216 $r = do_req($test_url . "/op/settings/keys?t=load_key_form_del", $data, $headers); $r = do_req($test_url . "/op/settings/keys?t=load_key_form_del", $data, $headers);
195 217 if ($r === FALSE) { if ($r === FALSE) {
218 rg_log_ml('r: ' . print_r($r, TRUE));
196 219 rg_log("Cannot load ssh key form!"); rg_log("Cannot load ssh key form!");
197 220 exit(1); exit(1);
198 221 } }
 
... ... $data = array("delete" => 1, "token" => $r['tokens']['keys'], "key_delete_ids[$k
201 224 $headers = array("Cookie: sid=" . $good_sid); $headers = array("Cookie: sid=" . $good_sid);
202 225 $r = do_req($test_url . "/op/settings/keys?t=post_key_form_del", $data, $headers); $r = do_req($test_url . "/op/settings/keys?t=post_key_form_del", $data, $headers);
203 226 if (!strstr($r['body'], "Selected keys were removed with success.")) { if (!strstr($r['body'], "Selected keys were removed with success.")) {
227 rg_log_ml('r: ' . print_r($r, TRUE));
204 228 rg_log("Cannot delete key!"); rg_log("Cannot delete key!");
205 229 exit(1); exit(1);
206 230 } }
 
... ... $res = rg_sql_query($db, $sql);
209 233 $rows = rg_sql_num_rows($res); $rows = rg_sql_num_rows($res);
210 234 rg_sql_free_result($res); rg_sql_free_result($res);
211 235 if ($rows == 1) { if ($rows == 1) {
236 rg_log_ml('r: ' . print_r($r, TRUE));
212 237 rg_log("key $key_id was not deleted!"); rg_log("key $key_id was not deleted!");
213 238 exit(1); exit(1);
214 239 } }
File tests/http_top.php changed (mode: 100644) (index db046dc..b2cd64e)
... ... $rg_cache_enable = TRUE;
23 23 $now = time(); $now = time();
24 24
25 25 // TODO: donate has some problems with input + border! // TODO: donate has some problems with input + border!
26 $_list = array("discover", "download", "features", "pricing", "tos");
26 $_list = array("features", "discover", "download", "pricing", "tos",
27 "create_account", "login");
27 28 foreach ($_list as $op) { foreach ($_list as $op) {
28 29 rg_log("Loading $op page..."); rg_log("Loading $op page...");
29 30 $data = array(); $data = array();
File tests/http_totp.php copied from file tests/http_admin.php (similarity 54%) (mode: 100644) (index 789ce9a..9ca337f)
1 1 <?php <?php
2 //
3 // Will test the login by TOTP
4 //
5
2 6 error_reporting(E_ALL | E_STRICT); error_reporting(E_ALL | E_STRICT);
3 7 ini_set("track_errors", "On"); ini_set("track_errors", "On");
4 8
 
... ... require_once($INC . "/util.inc.php");
9 13 require_once("helpers.inc.php"); require_once("helpers.inc.php");
10 14 require_once("http.inc.php"); require_once("http.inc.php");
11 15
12 rg_log_set_file("http_admin.log");
16 rg_log_set_file("http_totp.log");
13 17
14 18 $rg_sql = "host=localhost user=rocketgit dbname=rocketgit connect_timeout=10"; $rg_sql = "host=localhost user=rocketgit dbname=rocketgit connect_timeout=10";
15 19 $rg_no_db = TRUE; $rg_no_db = TRUE;
16 20 require_once("common.php"); require_once("common.php");
17 21
18 $_testns = 'http_admin';
22 $_testns = 'http_totp';
19 23 $rg_cache_enable = TRUE; $rg_cache_enable = TRUE;
20 24
21 25 $rg_user_max_len = 60; $rg_user_max_len = 60;
22 26
23 $rg_ui = array('is_admin' => 1);
24 27 rg_test_create_user($db, $rg_ui); rg_test_create_user($db, $rg_ui);
25 28
29 // Add an totp token to this account
30 $key = 'ACHCBCCVQ7AK4RGM';
31
32 $r = rg_totp_enroll($db, $rg_ui['uid'], 'test', $key, '127.0.0.1', TRUE);
33 if ($r !== TRUE) {
34 rg_log('cannot enroll!');
35 exit(1);
36 }
37
38 // Now test the login without and with login_token
39 $lt = rg_totp_compute($key, time() / 30, 6);
40
26 41 // First we need to load the form so we can get the token // First we need to load the form so we can get the token
27 42 // We provide an old cookie to test if we generate a new pre-login one // We provide an old cookie to test if we generate a new pre-login one
28 43 $r = do_req($test_url . "/op/login", $data, $headers); $r = do_req($test_url . "/op/login", $data, $headers);
 
... ... $good_sid = $r['sid'];
34 49 $good_token = $r['tokens']['login']; $good_token = $r['tokens']['login'];
35 50
36 51
37 rg_log("Do the login (sid=$good_sid token=$good_token)...");
52 rg_log("Do the login (sid=$good_sid token=$good_token"
53 . " login_token=$lt)...");
38 54 $data = array( $data = array(
39 55 "doit" => 1, "doit" => 1,
40 56 "token" => $good_token, "token" => $good_token,
41 57 "user" => $rg_ui['username'], "user" => $rg_ui['username'],
42 58 "pass" => $rg_ui['pass'], "pass" => $rg_ui['pass'],
59 "login_token" => $lt,
43 60 "lock_ip" => 0); "lock_ip" => 0);
44 61 $headers = array("Cookie: sid=" . $good_sid); $headers = array("Cookie: sid=" . $good_sid);
45 62 $r = do_req($test_url . "/op/login", $data, $headers); $r = do_req($test_url . "/op/login", $data, $headers);
 
... ... if ($r === FALSE) {
47 64 rg_log_ml("Cannot login: " . print_r($r, TRUE)); rg_log_ml("Cannot login: " . print_r($r, TRUE));
48 65 exit(1); exit(1);
49 66 } }
50 if (strstr($r['body'], "invalid user or pass")) {
67 if (strstr($r['body'], "invalid user")) {
51 68 rg_log_ml("Login invalid. r=" . print_r($r, TRUE)); rg_log_ml("Login invalid. r=" . print_r($r, TRUE));
52 69 exit(1); exit(1);
53 70 } }
54 71
55 rg_log("Loading invites form...");
56 $url = "/op/admin/invites";
57 $data = array();
58 $r = do_req($test_url . $url, $data, $headers);
59 if ($r === FALSE) {
60 rg_log("Cannot load add bug form.");
61 exit(1);
62 }
63 if (!isset($r['tokens']['admin_invites_hl'])) {
64 rg_log("No admin_invites_hl token!");
65 exit(1);
66 }
67 $token = $r['tokens']['admin_invites_hl'];
68
69 rg_log("Posting invites form (token=$token)...");
70 $data = array('doit' => 1, 'token' => $token,
71 'inv::list' => "a@embedromix.ro|a\nb@embedromix.ro|b b2 b3<xss>\n",
72 'inv::subject' => 'Invite 1 - hello {NAME}<xss>',
73 'inv::body' => "Hello {NAME}!\n\nYou are invited, {NAME}!<xss>");
74 $r = do_req($test_url . $url, $data, $headers);
75 if ($r === FALSE) {
76 rg_log("Cannot post bug request.");
77 exit(1);
78 }
79 // test invites here
80 /*
81 if ($row['state'] != 1) {
82 rg_log("State is not 1 but " . $row['state']);
83 exit(1);
84 }
85 */
86
87 72 rg_prof_log(); rg_prof_log();
88 73 rg_log("OK!"); rg_log("OK!");
89 74 ?> ?>
File tests/rights.php changed (mode: 100644) (index 8673c28..707a4d1)
... ... $sql = "DELETE FROM rights";
25 25 $res = rg_sql_query($db, $sql); $res = rg_sql_query($db, $sql);
26 26 rg_sql_free_result($res); rg_sql_free_result($res);
27 27
28 rg_log('');
28 29 rg_log("test if combine works correctly (1)"); rg_log("test if combine works correctly (1)");
29 30 $a = "AF"; $b = "AD"; $e = "AFD"; $a = "AF"; $b = "AD"; $e = "AFD";
30 31 $r = rg_rights_combine($a, $b); $r = rg_rights_combine($a, $b);
 
... ... if (strcmp($r, $e) != 0) {
33 34 exit(1); exit(1);
34 35 } }
35 36
37 rg_log('');
36 38 rg_log("test if combine works correctly (2)"); rg_log("test if combine works correctly (2)");
37 39 $a = ""; $b = ""; $e = ""; $a = ""; $b = ""; $e = "";
38 40 $r = rg_rights_combine($a, $b); $r = rg_rights_combine($a, $b);
 
... ... if (strcmp($r, $e) != 0) {
41 43 exit(1); exit(1);
42 44 } }
43 45
46 rg_log('');
44 47 rg_log("test if combine works correctly (3)"); rg_log("test if combine works correctly (3)");
45 48 $a = "AXUJUNFUUFU"; $b = ""; $e = $a; $a = "AXUJUNFUUFU"; $b = ""; $e = $a;
46 49 $r = rg_rights_combine($a, $b); $r = rg_rights_combine($a, $b);
 
... ... if (strcmp($r, $e) != 0) {
49 52 exit(1); exit(1);
50 53 } }
51 54
55 rg_log('');
52 56 rg_log("testing mask..."); rg_log("testing mask...");
53 57 $a = "ABCDE"; $mask = "AEZ"; $e = "AE"; $a = "ABCDE"; $mask = "AEZ"; $e = "AE";
54 58 $r = rg_rights_mask($a, $mask); $r = rg_rights_mask($a, $mask);
 
... ... if (strcmp($e, $e) != 0) {
57 61 exit(1); exit(1);
58 62 } }
59 63
64 rg_log('');
60 65 rg_log("rights: testing 'test'..."); rg_log("rights: testing 'test'...");
61 66 $rights = array(array("rights" => "ABC", "ip" => "")); $rights = array(array("rights" => "ABC", "ip" => ""));
62 67 $needed_rights = "BCD"; $needed_rights = "BCD";
 
... ... if ($r !== FALSE) {
68 73 exit(1); exit(1);
69 74 } }
70 75
76 rg_log('');
71 77 rg_log("rights: testing rg_rights_set..."); rg_log("rights: testing rg_rights_set...");
72 78 $a = array(); $a = array();
73 79 $a['right_id'] = 0; $a['right_id'] = 0;
 
... ... if ($r !== TRUE) {
98 104 exit(1); exit(1);
99 105 } }
100 106
107 rg_log('');
101 108 rg_log("Testing rg_rights_get..."); rg_log("Testing rg_rights_get...");
102 109 $right_id = 0; $right_id = 0;
103 110 $r = rg_rights_get($db, $a['obj_id'], "type1", $a['who'], $a['uid'], $right_id); $r = rg_rights_get($db, $a['obj_id'], "type1", $a['who'], $a['uid'], $right_id);
 
... ... if (($r['ok'] !== 1) || (strcmp($r['list'][1]['rights'], "d") != 0)) {
115 122 } }
116 123 $for_delete_list = $r['list']; $for_delete_list = $r['list'];
117 124
125 rg_log('');
118 126 rg_log("Testing allow with @USER@ token..."); rg_log("Testing allow with @USER@ token...");
119 127 $x = array(); $x = array();
120 128 $x['obj_id'] = $a['obj_id']; $x['obj_id'] = $a['obj_id'];
 
... ... if ($r === FALSE) {
131 139 exit(1); exit(1);
132 140 } }
133 141
142
143 rg_log('');
144 rg_log('Testing if we can found out if anybody can fetch - deny');
145 $list = array();
146 $list[] = array('rights' => 'PH', 'ip' => '', 'misc' => '');
147 $list[] = array('rights' => '', 'ip' => '', 'misc' => '');
148 $list[] = array('rights' => 'F', 'ip' => '', 'misc' => '');
149 $needed_rights = 'F';
150 $ip = '1.2.3.4';
151 $misc = '';
152 $r = rg_rights_test($list, $needed_rights, $ip, $misc);
153 if ($r !== FALSE) {
154 rg_log('We must not be allowed because rule 2 denies everything!');
155 exit(1);
156 }
157
158
159 rg_log('');
160 rg_log('Testing if we can found out if anybody can fetch - allow');
161 $list = array();
162 $list[] = array('rights' => 'PH', 'ip' => '', 'misc' => '');
163 $list[] = array('rights' => 'F', 'ip' => '', 'misc' => '');
164 $needed_rights = 'F';
165 $ip = '1.2.3.4';
166 $misc = '';
167 $r = rg_rights_test($list, $needed_rights, $ip, $misc);
168 if ($r !== TRUE) {
169 rg_log('We must be allowed because rule 2 allows fetch!');
170 exit(1);
171 }
172
173
174 rg_log('');
134 175 rg_log("Testing delete_list..."); rg_log("Testing delete_list...");
135 176 $list = array(); $list = array();
136 177 foreach ($for_delete_list as $junk => $i) foreach ($for_delete_list as $junk => $i)
 
... ... if (($r['ok'] !== 1) || (count($r['list']) > 0)) {
148 189 exit (1); exit (1);
149 190 } }
150 191
192 rg_log('');
151 193 rg_log("Testing IP match part - test1"); rg_log("Testing IP match part - test1");
152 194 $list = "1.2.3.4/24 10.0.0.0/8 fd00::/64" $list = "1.2.3.4/24 10.0.0.0/8 fd00::/64"
153 195 . " 1234:5678:aaaa:bbbb:cccc:dddd:eeee::/120" . " 1234:5678:aaaa:bbbb:cccc:dddd:eeee::/120"
File tests/totp.php added (mode: 100644) (index 0000000..b532012)
1 <?php
2 //
3 // Will test core TOTP functions
4 //
5 error_reporting(E_ALL | E_STRICT);
6 ini_set("track_errors", "On");
7
8 $INC = dirname(__FILE__) . "/../inc";
9 require_once(dirname(__FILE__) . "/config.php");
10 require_once($INC . "/init.inc.php");
11 require_once($INC . "/util.inc.php");
12 require_once($INC . "/log.inc.php");
13 require_once($INC . "/totp.inc.php");
14
15 rg_log_set_file("totp.log");
16
17 $rg_no_db = TRUE;
18 require_once("common.php");
19
20 rg_log('');
21 rg_log('Testing rg_top_verify...');
22 $key = 'ACHCBCCVQ7AK4RGM';
23 $now = 1441527846; $tc = intval($now / 30);
24 $token = 215840;
25 $r = rg_totp_verify($key, $now, $token);
26 if ($r === FALSE) {
27 rg_log('verifying token ' . $token . ' with key ' . $key . ' failed!');
28 exit(1);
29 }
30 if ($r !== $tc) {
31 rg_log('verifying token with key ' . $key . ' returned wrong tc: ' . $tc . '!');
32 exit(1);
33 }
34
35 rg_log("OK!");
36 ?>
Hints

Before first commit, do not forget to setup your git environment:
git config --global user.name "your_name_here"
git config --global user.email "your@email_here"

Clone this repository using HTTP(S):
git clone https://code.reversed.top/user/xaizek/rocketgit

Clone this repository using ssh (do not forget to upload a key first):
git clone ssh://rocketgit@code.reversed.top/user/xaizek/rocketgit

You are allowed to anonymously push to this repository.
This means that your pushed commits will automatically be transformed into a pull request:
... clone the repository ...
... make some changes and some commits ...
git push origin master