File TODO changed (mode: 100644) (index aea65d9..3fe8a47) |
1 |
1 |
== Where I stopped last time == |
== Where I stopped last time == |
2 |
2 |
[ ] |
[ ] |
3 |
3 |
|
|
4 |
|
== CSRF logic == |
|
5 |
|
- Generate a token for a specific form (call rg_token_get with an $op) |
|
6 |
|
Why I do not use a key per user not a master key?! Maybe because I have |
|
7 |
|
to store it in db. Why not? Because of caching of the pages... |
|
8 |
|
- For a form, we create a token based on sess, ua, tag and a random string. |
|
9 |
|
We store it in sess::SID::token::tag to be reused next time we |
|
10 |
|
load that specific form. |
|
11 |
|
Also, we store it in sess::SID::used_tokens::TOKEN=0 to optimize |
|
12 |
|
for when we receive the POST request (to not lookup in db). |
|
13 |
|
- When checking the validity, we look it up in sess::SID::used_tokens and |
|
14 |
|
compare with 0. If true, we know that token is valid and not used. |
|
15 |
|
|
|
16 |
|
|
|
17 |
4 |
== BEFORE NEXT RELEASE == |
== BEFORE NEXT RELEASE == |
|
5 |
|
[ ] Add history for totp enrollment. |
|
6 |
|
[ ] Add history for logins/logouts/API. |
|
7 |
|
[ ] Add, next to the user, the enroll position for rocketgit.com. |
|
8 |
|
Something like 'user number 454'. |
|
9 |
|
[ ] Add max_requests per hour for plans and enforce them. |
|
10 |
|
[ ] When setting last used, we need to pass the timestamp to the event and |
|
11 |
|
we msut not use 'time()' because event may happen at a latter time! |
|
12 |
|
[ ] Protect login by country/ua? |
|
13 |
|
[ ] Improve input forms to be friendly with mobile phones: give html5 hints. |
|
14 |
|
[ ] Use guestmount when building VM images? |
|
15 |
|
[ ] Main web application must not have access to e-mail column, maybe others. |
|
16 |
|
Only the queue processor must be able to use it. This is to avoid |
|
17 |
|
XSS and the stealing of sensitive information. |
|
18 |
|
Check http://www.postgresql.org/docs/9.4/interactive/sql-grant.html |
|
19 |
|
web user must not be able to create roles/tables/databases/etc. |
|
20 |
|
Hm. What about the settings?! I must be able to do a select... |
|
21 |
|
[ ] totp: Build a Android application which will be able to authenticate also |
|
22 |
|
the server to the user. |
|
23 |
|
[ ] totp: update 'utime' for login tokens |
|
24 |
|
[ ] totp: mark last timestamp used for ok login to not be able to reuse the |
|
25 |
|
token; what if the time is in the future? we will not cache it. |
|
26 |
|
[ ] totp: login: prevent auto filling/caching of login token |
|
27 |
|
switch to 'password' type? |
|
28 |
|
[ ] totp: hints: |
|
29 |
|
only for admin: install qrencode package... |
|
30 |
|
AWS asks for two consecutive codes. why? |
|
31 |
|
Google also provides a list of backup tokens to be printed. |
|
32 |
|
"If you want to activate the TOTP extra step, follow the instructions: |
|
33 |
|
show the qr code or the key, wait for user to enter it |
|
34 |
|
and if everything is ok, just activate it. |
|
35 |
|
Instruct user to remove the token if the phone is lost. But, remind |
|
36 |
|
user that the account may not be compromised without pass. |
|
37 |
|
[ ] totp: think about loosing the phone. |
|
38 |
|
[ ] totp: how to set it up: |
|
39 |
|
press a menu to activate it, show QR code if possible, show base32 key |
|
40 |
|
also, ask user to enter the token to finish the activation. |
|
41 |
|
warn about time desync; maybe ask the user to press the 'sync' in |
|
42 |
|
the mobile app. |
|
43 |
|
[ ] totp: we may validate the login on one ip and keep ip for x hours. |
|
44 |
|
[ ] totp: what if I encrypt key with the password and decrypt only at login? |
|
45 |
|
(If somebody steals the database, will not have the keys). |
|
46 |
|
[ ] totp: hints for ssh |
|
47 |
|
[ ] totp: Implement 2 factor auth |
|
48 |
|
(check https://korg.wiki.kernel.org/userdoc/gitolite_2fa) |
|
49 |
|
[ ] Use PAM (man pam_start) to be able to use any type of auth, including LDAP. |
|
50 |
|
[ ] http://www.cybertec.at/shrinking-the-storage-footprint-of-data/ |
18 |
51 |
[ ] Allow repo admins to delete notes/bugs/etc. |
[ ] Allow repo admins to delete notes/bugs/etc. |
19 |
52 |
[ ] Seems that some other unit test is messing with repo.php ids. |
[ ] Seems that some other unit test is messing with repo.php ids. |
20 |
53 |
Change ids to be protected from interference. |
Change ids to be protected from interference. |
21 |
54 |
[ ] Use bintray.com to distribute isos? |
[ ] Use bintray.com to distribute isos? |
|
55 |
|
[ ] When session expires and I press logout, no message is shown. |
22 |
56 |
[ ] When creating an account, seems the email is cached, not the username! |
[ ] When creating an account, seems the email is cached, not the username! |
23 |
57 |
Check! |
Check! |
24 |
58 |
[ ] Users should be able to check the plans. |
[ ] Users should be able to check the plans. |
|
... |
... |
to store it in db. Why not? Because of caching of the pages... |
33 |
67 |
messages. The user must agree to be spammed. Best, no notification |
messages. The user must agree to be spammed. Best, no notification |
34 |
68 |
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. |
35 |
69 |
Better, show some notifications in the top bar? |
Better, show some notifications in the top bar? |
|
70 |
|
Or in a weekly e-mail with the status. |
36 |
71 |
(see Linus Torvalds post about GitHub) |
(see Linus Torvalds post about GitHub) |
37 |
72 |
[ ] Add a cache based on content. For example, if a repo was last changed |
[ ] Add a cache based on content. For example, if a repo was last changed |
38 |
73 |
at timestamp t1, add a cache entry 'history''t1' with the content |
at timestamp t1, add a cache entry 'history''t1' with the content |
|
... |
... |
to store it in db. Why not? Because of caching of the pages... |
64 |
99 |
logout token. What if the user is not logged in?! Yep, we can do it. |
logout token. What if the user is not logged in?! Yep, we can do it. |
65 |
100 |
ETag! What about the cookies?! |
ETag! What about the cookies?! |
66 |
101 |
Also, we may want to reuse the logout token? |
Also, we may want to reuse the logout token? |
67 |
|
[ ] When sending mail with the new key, append also the fingerprints. |
|
68 |
102 |
[ ] Why 'not github' articles, should be integrated somewhere: |
[ ] Why 'not github' articles, should be integrated somewhere: |
69 |
103 |
http://www.valdyas.org/fading/index.cgi/2015/05/29#no-github |
http://www.valdyas.org/fading/index.cgi/2015/05/29#no-github |
70 |
104 |
http://www.adamhyde.net/why-github-is-bad-for-open-source/ |
http://www.adamhyde.net/why-github-is-bad-for-open-source/ |
|
... |
... |
to store it in db. Why not? Because of caching of the pages... |
89 |
123 |
[ ] Add a random token in header to prevent watermarking (this is the name?). |
[ ] Add a random token in header to prevent watermarking (this is the name?). |
90 |
124 |
[ ] User home page link is missing from top bar! use login_ui::homepage. |
[ ] User home page link is missing from top bar! use login_ui::homepage. |
91 |
125 |
[ ] Add "Spread the word!" on website. |
[ ] Add "Spread the word!" on website. |
92 |
|
[ ] Give up on submenu1/2 and integrate them when needed? |
|
93 |
126 |
[ ] https://www.kernel.org/pub/software/scm/git/docs/gitworkflows.html |
[ ] https://www.kernel.org/pub/software/scm/git/docs/gitworkflows.html |
94 |
127 |
[ ] git-name-rev is nice. |
[ ] git-name-rev is nice. |
95 |
128 |
[ ] git pack-redundant should be called after git gc? And then prune-packed? |
[ ] git pack-redundant should be called after git gc? And then prune-packed? |
|
... |
... |
But, we have a problem with the expiration time! |
276 |
309 |
[ ] Warning if user has not enabled cookies? |
[ ] Warning if user has not enabled cookies? |
277 |
310 |
[ ] Seems that Etag is not working for main.css!!! At least. |
[ ] Seems that Etag is not working for main.css!!! At least. |
278 |
311 |
[ ] bad_token.html must not be in user/ |
[ ] bad_token.html must not be in user/ |
279 |
|
[ ] On create_account page, submenu1 and submenu2 are with @@...@@! |
|
280 |
312 |
[ ] The merge request name is not so good. Maybe include also the user? |
[ ] The merge request name is not so good. Maybe include also the user? |
281 |
313 |
[ ] I do at least two times a request to database for uid 22 in hook_update.log |
[ ] I do at least two times a request to database for uid 22 in hook_update.log |
282 |
314 |
[ ] git update-ref supports "ref:" to update a ref. Should we? |
[ ] git update-ref supports "ref:" to update a ref. Should we? |
File inc/git.inc.php changed (mode: 100644) (index 5a441f9..c8dbf68) |
... |
... |
function rg_git_install_hooks($dst) |
74 |
74 |
|
|
75 |
75 |
} |
} |
76 |
76 |
|
|
77 |
|
rg_log("\tRemoving original hooks dir..."); |
|
|
77 |
|
rg_log("Removing original hooks dir..."); |
78 |
78 |
if (!rg_rmdir($dst . "/hooks")) { |
if (!rg_rmdir($dst . "/hooks")) { |
79 |
79 |
rg_git_set_error("cannot remove hooks dir" |
rg_git_set_error("cannot remove hooks dir" |
80 |
80 |
. " (" . rg_util_error() . ")"); |
. " (" . rg_util_error() . ")"); |
81 |
81 |
break; |
break; |
82 |
82 |
} |
} |
83 |
83 |
|
|
84 |
|
rg_log("\tLink hooks dir..."); |
|
|
84 |
|
rg_log("Link hooks dir..."); |
85 |
85 |
if (symlink($rg_scripts . "/hooks", $dst . "/hooks") === FALSE) { |
if (symlink($rg_scripts . "/hooks", $dst . "/hooks") === FALSE) { |
86 |
86 |
rg_git_set_error("cannot make symlink [$rg_scripts/hooks]" |
rg_git_set_error("cannot make symlink [$rg_scripts/hooks]" |
87 |
87 |
. "->[$dst/] ($php_errormsg)."); |
. "->[$dst/] ($php_errormsg)."); |
|
... |
... |
function rg_git_whitespace_ok($old, $new) |
351 |
351 |
. " " . escapeshellarg($old) |
. " " . escapeshellarg($old) |
352 |
352 |
. " " . escapeshellarg($new); |
. " " . escapeshellarg($new); |
353 |
353 |
$a = rg_exec($cmd); |
$a = rg_exec($cmd); |
354 |
|
rg_log("\ta:" . rg_array2string($a)); |
|
|
354 |
|
rg_log("a:" . rg_array2string($a)); |
355 |
355 |
if ($a['ok'] != 1) { |
if ($a['ok'] != 1) { |
356 |
356 |
rg_git_set_error("error on diff (" . $a['errmsg'] . ")"); |
rg_git_set_error("error on diff (" . $a['errmsg'] . ")"); |
357 |
357 |
$ret = $a['data']; // TODO: should we return FALSE?! |
$ret = $a['data']; // TODO: should we return FALSE?! |
|
... |
... |
function rg_git_diff2array($diff, &$extra) |
639 |
639 |
} |
} |
640 |
640 |
|
|
641 |
641 |
if (empty($line)) { |
if (empty($line)) { |
642 |
|
//rg_log("\tWARN: empty line [$line]!"); |
|
|
642 |
|
//rg_log("WARN: empty line [$line]!"); |
643 |
643 |
continue; |
continue; |
644 |
644 |
} |
} |
645 |
645 |
|
|
|
... |
... |
function rg_git_diff2array($diff, &$extra) |
666 |
666 |
|
|
667 |
667 |
// Ignore, for now, "\ No newline at end of file" (TODO) |
// Ignore, for now, "\ No newline at end of file" (TODO) |
668 |
668 |
if (strncmp($line, "\\", 1) == 0) { |
if (strncmp($line, "\\", 1) == 0) { |
669 |
|
rg_log("\tINFO: warn line: [$line]."); |
|
|
669 |
|
rg_log("INFO: warn line: [$line]."); |
670 |
670 |
continue; |
continue; |
671 |
671 |
} |
} |
672 |
672 |
|
|
|
... |
... |
function rg_git_log($path, $max, $from, $to, $also_patch) |
692 |
692 |
$ret = FALSE; |
$ret = FALSE; |
693 |
693 |
while (1) { |
while (1) { |
694 |
694 |
if (!file_exists($path . "/refs/heads/master")) { |
if (!file_exists($path . "/refs/heads/master")) { |
695 |
|
rg_log("\tRepo is empty."); |
|
|
695 |
|
rg_log("Repo is empty."); |
696 |
696 |
break; |
break; |
697 |
697 |
} |
} |
698 |
698 |
|
|
|
... |
... |
function rg_git_update_branch($db, $a) |
1169 |
1169 |
global $rg_git_zero; |
global $rg_git_zero; |
1170 |
1170 |
|
|
1171 |
1171 |
rg_prof_start("git_update_branch"); |
rg_prof_start("git_update_branch"); |
1172 |
|
rg_log("git_update_branch: " . rg_array2string($a)); |
|
|
1172 |
|
rg_log_enter("git_update_branch: " . rg_array2string($a)); |
1173 |
1173 |
|
|
|
1174 |
|
while (1) { |
1174 |
1175 |
$_x = array(); |
$_x = array(); |
1175 |
1176 |
$_x['obj_id'] = $a['repo_id']; |
$_x['obj_id'] = $a['repo_id']; |
1176 |
1177 |
$_x['type'] = 'repo_refs'; |
$_x['type'] = 'repo_refs'; |
|
... |
... |
function rg_git_update_branch($db, $a) |
1189 |
1190 |
$x = $_x; |
$x = $_x; |
1190 |
1191 |
$x['needed_rights'] = 'D'; |
$x['needed_rights'] = 'D'; |
1191 |
1192 |
if (rg_rights_allow($db, $x) !== TRUE) |
if (rg_rights_allow($db, $x) !== TRUE) |
1192 |
|
rg_git_fatal($a['refname'] . "\nNo rights to delete" |
|
1193 |
|
. " a branch."); |
|
|
1193 |
|
rg_git_fatal($a['refname'] |
|
1194 |
|
. "\nYou have no rights to delete a branch."); |
1194 |
1195 |
$history['history_category'] = REPO_CAT_GIT_BRANCH_DELETE; |
$history['history_category'] = REPO_CAT_GIT_BRANCH_DELETE; |
1195 |
1196 |
$history['history_message'] = 'Reference ' . $a['refname'] |
$history['history_message'] = 'Reference ' . $a['refname'] |
1196 |
1197 |
. ' deleted'; |
. ' deleted'; |
1197 |
1198 |
rg_repo_history_insert($db, $history); |
rg_repo_history_insert($db, $history); |
1198 |
|
return; |
|
|
1199 |
|
break; |
1199 |
1200 |
} |
} |
1200 |
1201 |
|
|
1201 |
1202 |
// If we have 'H' (anonymous push), we have also create branch |
// If we have 'H' (anonymous push), we have also create branch |
|
... |
... |
function rg_git_update_branch($db, $a) |
1204 |
1205 |
$x = $_x; |
$x = $_x; |
1205 |
1206 |
$x['needed_rights'] = 'H|C'; |
$x['needed_rights'] = 'H|C'; |
1206 |
1207 |
if (rg_rights_allow($db, $x) !== TRUE) |
if (rg_rights_allow($db, $x) !== TRUE) |
1207 |
|
rg_git_fatal($a['refname'] . "\nYou have no rights" |
|
1208 |
|
. " to create a branch."); |
|
|
1208 |
|
rg_git_fatal($a['refname'] |
|
1209 |
|
. "\nYou have no rights to create a branch."); |
1209 |
1210 |
$check_fast_forward = 0; |
$check_fast_forward = 0; |
1210 |
1211 |
} |
} |
1211 |
1212 |
|
|
|
... |
... |
function rg_git_update_branch($db, $a) |
1222 |
1223 |
} |
} |
1223 |
1224 |
|
|
1224 |
1225 |
if (strcmp($merge_base, $a['old_rev']) != 0) |
if (strcmp($merge_base, $a['old_rev']) != 0) |
1225 |
|
rg_git_fatal($a['refname'] . "\nNon fast-forward is" |
|
1226 |
|
. " not allowed."); |
|
|
1226 |
|
rg_git_fatal($a['refname'] |
|
1227 |
|
. "\nYou have no rights to do a non fast-forward push"); |
1227 |
1228 |
} |
} |
1228 |
1229 |
|
|
1229 |
1230 |
// Check if user pushes a merge commit |
// Check if user pushes a merge commit |
|
... |
... |
function rg_git_update_branch($db, $a) |
1232 |
1233 |
$x['needed_rights'] = 'M'; |
$x['needed_rights'] = 'M'; |
1233 |
1234 |
if (rg_rights_allow($db, $x) !== TRUE) { |
if (rg_rights_allow($db, $x) !== TRUE) { |
1234 |
1235 |
if (rg_git_rev_ok($a['new_rev'] . "^2")) |
if (rg_git_rev_ok($a['new_rev'] . "^2")) |
1235 |
|
rg_git_fatal($a['refname'] . "\nNo rights to push merges."); |
|
|
1236 |
|
rg_git_fatal($a['refname'] |
|
1237 |
|
. "\nYou have no rights to push merges."); |
1236 |
1238 |
} |
} |
1237 |
1239 |
|
|
1238 |
1240 |
// Check for bad whitespace |
// Check for bad whitespace |
|
... |
... |
function rg_git_update_branch($db, $a) |
1243 |
1245 |
$w = rg_git_whitespace_ok($a['old_rev'], $a['new_rev']); |
$w = rg_git_whitespace_ok($a['old_rev'], $a['new_rev']); |
1244 |
1246 |
if ($w !== TRUE) |
if ($w !== TRUE) |
1245 |
1247 |
rg_git_fatal($a['refname'] |
rg_git_fatal($a['refname'] |
1246 |
|
. "\nNo rights to push bad whitespace:" |
|
|
1248 |
|
. "\nYou have no rights to push bad whitespace:" |
1247 |
1249 |
. "\n" . $w); |
. "\n" . $w); |
1248 |
1250 |
} |
} |
1249 |
1251 |
|
|
|
... |
... |
function rg_git_update_branch($db, $a) |
1266 |
1268 |
$w = rg_git_whitespace_ok($a['old_rev'], $a['new_rev']); |
$w = rg_git_whitespace_ok($a['old_rev'], $a['new_rev']); |
1267 |
1269 |
if ($w !== TRUE) { |
if ($w !== TRUE) { |
1268 |
1270 |
rg_git_fatal($a['refname'] |
rg_git_fatal($a['refname'] |
1269 |
|
. "\nNo rights to push bad whitespace on path [$file]:" |
|
|
1271 |
|
. "\nYou have no rights to push bad whitespace on path [$file]:" |
1270 |
1272 |
. "\n" . $w); |
. "\n" . $w); |
1271 |
1273 |
} |
} |
1272 |
1274 |
} |
} |
|
... |
... |
function rg_git_update_branch($db, $a) |
1277 |
1279 |
$x['needed_rights'] = 'P'; |
$x['needed_rights'] = 'P'; |
1278 |
1280 |
$x['misc'] = $a['refname']; |
$x['misc'] = $a['refname']; |
1279 |
1281 |
if (rg_rights_allow($db, $x) !== TRUE) { |
if (rg_rights_allow($db, $x) !== TRUE) { |
1280 |
|
rg_log("\tPush is not allowed, let's see the anon one"); |
|
|
1282 |
|
rg_log("Push is not allowed, let's see the anon one"); |
1281 |
1283 |
$x['needed_rights'] = 'H'; |
$x['needed_rights'] = 'H'; |
1282 |
1284 |
if (rg_rights_allow($db, $x) !== TRUE) { |
if (rg_rights_allow($db, $x) !== TRUE) { |
1283 |
1285 |
$_z = array(); |
$_z = array(); |
|
... |
... |
function rg_git_update_branch($db, $a) |
1314 |
1316 |
$history['history_message'] = 'Anonymous push to ref ' |
$history['history_message'] = 'Anonymous push to ref ' |
1315 |
1317 |
. $a['refname']; |
. $a['refname']; |
1316 |
1318 |
|
|
1317 |
|
// TODO: we shoult notify by e-mail that a merge request is |
|
|
1319 |
|
// TODO: we should notify by e-mail that a merge request is |
1318 |
1320 |
// waiting. |
// waiting. |
1319 |
1321 |
} else { |
} else { |
1320 |
1322 |
rg_log("We are allowed to push."); |
rg_log("We are allowed to push."); |
|
... |
... |
function rg_git_update_branch($db, $a) |
1348 |
1350 |
} |
} |
1349 |
1351 |
} |
} |
1350 |
1352 |
rg_repo_history_insert($db, $history); |
rg_repo_history_insert($db, $history); |
|
1353 |
|
} |
1351 |
1354 |
|
|
|
1355 |
|
rg_log_exit(); |
1352 |
1356 |
rg_prof_end("git_update_branch"); |
rg_prof_end("git_update_branch"); |
1353 |
1357 |
} |
} |
1354 |
1358 |
|
|
|
... |
... |
function rg_git_log2listing($log, $rg, $commit_table) |
1559 |
1563 |
$ret .= "<br />\n" |
$ret .= "<br />\n" |
1560 |
1564 |
. nl2br(rg_xss_safe($i['vars']['body'])); |
. nl2br(rg_xss_safe($i['vars']['body'])); |
1561 |
1565 |
|
|
1562 |
|
$ret .= "<br />\n" |
|
|
1566 |
|
$ret .= "<br />\n" |
1563 |
1567 |
. "<b>Author</b>: " . rg_xss_safe($i['vars']['author name']); |
. "<b>Author</b>: " . rg_xss_safe($i['vars']['author name']); |
1564 |
1568 |
|
|
1565 |
1569 |
if (!empty($i['vars']['commiter name'])) |
if (!empty($i['vars']['commiter name'])) |
1566 |
1570 |
$ret .= "<br />\n" |
$ret .= "<br />\n" |
1567 |
1571 |
. "<b>Commiter</b>: " . rg_xss_safe($i['vars']['commiter name']); |
. "<b>Commiter</b>: " . rg_xss_safe($i['vars']['commiter name']); |
1568 |
1572 |
|
|
1569 |
|
$ret .= "<br />\n" |
|
|
1573 |
|
$ret .= "<br />\n" |
1570 |
1574 |
. "<b>Date (UTC)</b>: " . gmdate("Y-m-d H:i", $i['vars']['author date']); |
. "<b>Date (UTC)</b>: " . gmdate("Y-m-d H:i", $i['vars']['author date']); |
1571 |
1575 |
$ret .= "<br />\n"; |
$ret .= "<br />\n"; |
1572 |
1576 |
|
|
File inc/keys.inc.php changed (mode: 100644) (index ecdb7b8..2999cea) |
... |
... |
function rg_keys_event_notify_user($db, $event) |
96 |
96 |
rg_prof_start("keys_event_notify_user"); |
rg_prof_start("keys_event_notify_user"); |
97 |
97 |
rg_log("keys_event_notify_user: event=" . rg_array2string($event)); |
rg_log("keys_event_notify_user: event=" . rg_array2string($event)); |
98 |
98 |
|
|
99 |
|
// TODO: load info about the keys and put the fingerprint in e-mail |
|
100 |
|
// TODO: Maybe put also the body in the e-mail, so it can be re-uploaded. |
|
101 |
|
// TODO: Maybe add also the statistics. |
|
102 |
|
// TODO: Do not forget that here we have a list |
|
103 |
|
// TODO: Take care: we already deleted the keys. We cannot inspect |
|
|
99 |
|
// TODO: del: Maybe add also the statistics. |
|
100 |
|
// TODO: del: Do not forget that here we have a list |
|
101 |
|
// TODO: del: Take care: we already deleted the keys. We cannot inspect |
104 |
102 |
// them anymore! Maybe put info in the event. |
// them anymore! Maybe put info in the event. |
105 |
103 |
|
|
106 |
104 |
$r = rg_mail_template("mail/user/key/" . $event['op'], $event); |
$r = rg_mail_template("mail/user/key/" . $event['op'], $event); |
|
... |
... |
function rg_keys_event_notify_user($db, $event) |
117 |
115 |
function rg_keys_info($key) |
function rg_keys_info($key) |
118 |
116 |
{ |
{ |
119 |
117 |
rg_prof_start("keys_info"); |
rg_prof_start("keys_info"); |
|
118 |
|
rg_log_enter('rg_keys_info key=' . $key); |
120 |
119 |
|
|
121 |
120 |
$ret = array(); |
$ret = array(); |
122 |
121 |
$ret['ok'] = 0; |
$ret['ok'] = 0; |
123 |
122 |
while(1) { |
while(1) { |
|
123 |
|
if (empty($key)) { |
|
124 |
|
rg_keys_set_error('you did not uploaded the key'); |
|
125 |
|
break; |
|
126 |
|
} |
|
127 |
|
|
124 |
128 |
if (strpos($key, "PRIVATE KEY") !== FALSE) { |
if (strpos($key, "PRIVATE KEY") !== FALSE) { |
125 |
129 |
rg_keys_set_error("private instead of public key"); |
rg_keys_set_error("private instead of public key"); |
126 |
130 |
break; |
break; |
|
... |
... |
function rg_keys_info($key) |
136 |
140 |
|
|
137 |
141 |
if ((strncmp($ret['type'], 'ssh-', 4) != 0) |
if ((strncmp($ret['type'], 'ssh-', 4) != 0) |
138 |
142 |
&& (strncmp($ret['type'], 'ecdsa-', 6) != 0)) { |
&& (strncmp($ret['type'], 'ecdsa-', 6) != 0)) { |
|
143 |
|
rg_log('key: ' . $key); |
139 |
144 |
rg_keys_set_error("key does not start with ssh- or ecdsa-"); |
rg_keys_set_error("key does not start with ssh- or ecdsa-"); |
140 |
145 |
break; |
break; |
141 |
146 |
} |
} |
|
... |
... |
function rg_keys_info($key) |
252 |
257 |
break; |
break; |
253 |
258 |
} |
} |
254 |
259 |
|
|
|
260 |
|
rg_log_exit(); |
255 |
261 |
rg_prof_end("keys_info"); |
rg_prof_end("keys_info"); |
256 |
262 |
return $ret; |
return $ret; |
257 |
263 |
} |
} |
|
... |
... |
function rg_keys_remove($db, $ui, $list) |
292 |
298 |
. " (" . rg_event_error() . ")"); |
. " (" . rg_event_error() . ")"); |
293 |
299 |
break; |
break; |
294 |
300 |
} |
} |
295 |
|
rg_event_signal_daemon("", 0); |
|
|
301 |
|
rg_event_signal_daemon('', 0); |
296 |
302 |
|
|
297 |
303 |
$ret = TRUE; |
$ret = TRUE; |
298 |
304 |
break; |
break; |
|
... |
... |
function rg_keys_add($db, $ui, $key) |
391 |
397 |
|
|
392 |
398 |
$event = array("category" => 1000, "prio" => 50, |
$event = array("category" => 1000, "prio" => 50, |
393 |
399 |
'ui' => array('email' => $ui['confirmed'] > 0 ? $ui['email'] : ""), |
'ui' => array('email' => $ui['confirmed'] > 0 ? $ui['email'] : ""), |
394 |
|
"key_id" => $key_id); |
|
|
400 |
|
'ki' => $ki, |
|
401 |
|
'key_id' => $key_id); |
395 |
402 |
$r = rg_event_add($db, $event); |
$r = rg_event_add($db, $event); |
396 |
403 |
if ($r !== TRUE) { |
if ($r !== TRUE) { |
397 |
404 |
rg_keys_set_error("cannot add event" |
rg_keys_set_error("cannot add event" |
|
... |
... |
function rg_keys_add($db, $ui, $key) |
407 |
414 |
} |
} |
408 |
415 |
$do_rollback = 0; |
$do_rollback = 0; |
409 |
416 |
|
|
410 |
|
rg_event_signal_daemon("", 0); |
|
|
417 |
|
rg_event_signal_daemon('', 0); |
411 |
418 |
|
|
412 |
419 |
$ret = $key_id; |
$ret = $key_id; |
413 |
420 |
break; |
break; |
File inc/sess.inc.php changed (mode: 100644) (index 95e92a4..b7916a4) |
... |
... |
function rg_sess_add($db, $uid, $sid, $session_time, $lock_ip) |
31 |
31 |
. ", @@session_time@@, @@ip@@)"; |
. ", @@session_time@@, @@ip@@)"; |
32 |
32 |
$res = rg_sql_query_params($db, $sql, $params); |
$res = rg_sql_query_params($db, $sql, $params); |
33 |
33 |
if ($res === FALSE) { |
if ($res === FALSE) { |
34 |
|
rg_log("\tCannot insert (" . rg_sql_error() . ")!"); |
|
|
34 |
|
rg_log("Cannot insert (" . rg_sql_error() . ")!"); |
35 |
35 |
break; |
break; |
36 |
36 |
} |
} |
37 |
37 |
rg_sql_free_result($res); |
rg_sql_free_result($res); |
|
... |
... |
function rg_sess_valid($db, $sid) |
65 |
65 |
$sql = "SELECT * FROM sess WHERE sid = @@sid@@"; |
$sql = "SELECT * FROM sess WHERE sid = @@sid@@"; |
66 |
66 |
$res = rg_sql_query_params($db, $sql, $params); |
$res = rg_sql_query_params($db, $sql, $params); |
67 |
67 |
if ($res === FALSE) { |
if ($res === FALSE) { |
68 |
|
rg_log("\tCannot select (" . rg_sql_error() . ")!"); |
|
|
68 |
|
rg_log("Cannot select (" . rg_sql_error() . ")!"); |
69 |
69 |
break; |
break; |
70 |
70 |
} |
} |
71 |
71 |
$rows = rg_sql_num_rows($res); |
$rows = rg_sql_num_rows($res); |
|
... |
... |
function rg_sess_valid($db, $sid) |
78 |
78 |
} |
} |
79 |
79 |
|
|
80 |
80 |
if ($r === FALSE) { |
if ($r === FALSE) { |
81 |
|
rg_log("\tSession not found."); |
|
|
81 |
|
rg_log("Session not found."); |
82 |
82 |
break; |
break; |
83 |
83 |
} |
} |
84 |
84 |
|
|
85 |
85 |
$now = time(); |
$now = time(); |
86 |
86 |
if ($r['expire'] < $now) { |
if ($r['expire'] < $now) { |
87 |
|
rg_log("\tSession too old (" . ($now - $r['expire']) . "s)."); |
|
|
87 |
|
rg_log("Session too old (" . ($now - $r['expire']) . "s)."); |
88 |
88 |
break; |
break; |
89 |
89 |
} |
} |
90 |
90 |
|
|
91 |
91 |
$ip = @$_SERVER['REMOTE_ADDR']; |
$ip = @$_SERVER['REMOTE_ADDR']; |
92 |
92 |
if (!empty($r['ip']) && (strcmp($r['ip'], $ip) != 0)) { |
if (!empty($r['ip']) && (strcmp($r['ip'], $ip) != 0)) { |
93 |
|
rg_log("\tSession invalid because of IP" |
|
|
93 |
|
rg_log("Session invalid because of IP" |
94 |
94 |
. " login=" . $r['ip'] . " now=$ip."); |
. " login=" . $r['ip'] . " now=$ip."); |
95 |
95 |
break; |
break; |
96 |
96 |
} |
} |
97 |
97 |
|
|
98 |
98 |
$uid = $r['uid']; |
$uid = $r['uid']; |
99 |
|
rg_log("\tSession valid, uid=$uid, expire=+" |
|
|
99 |
|
rg_log("Session valid, uid=$uid, expire=+" |
100 |
100 |
. ($r['expire'] - $now) . "s"); |
. ($r['expire'] - $now) . "s"); |
101 |
101 |
$ret = $r; |
$ret = $r; |
102 |
102 |
break; |
break; |
|
... |
... |
function rg_sess_update($db, $sess) |
133 |
133 |
. " WHERE sid = @@sid@@"; |
. " WHERE sid = @@sid@@"; |
134 |
134 |
$res = rg_sql_query_params($db, $sql, $sess); |
$res = rg_sql_query_params($db, $sql, $sess); |
135 |
135 |
if ($res === FALSE) { |
if ($res === FALSE) { |
136 |
|
rg_log("\tCannot update (" . rg_sql_error() . ")!"); |
|
|
136 |
|
rg_log("Cannot update (" . rg_sql_error() . ")!"); |
137 |
137 |
// We will not exit here. At least in cache to be ok |
// We will not exit here. At least in cache to be ok |
138 |
138 |
} else { |
} else { |
139 |
139 |
$sess['last_db_write'] = $now; |
$sess['last_db_write'] = $now; |
|
... |
... |
function rg_sess_destroy($db, $sid, &$ui) |
165 |
165 |
$sql = "DELETE FROM sess WHERE sid = @@sid@@"; |
$sql = "DELETE FROM sess WHERE sid = @@sid@@"; |
166 |
166 |
$res = rg_sql_query_params($db, $sql, $params); |
$res = rg_sql_query_params($db, $sql, $params); |
167 |
167 |
if ($res === FALSE) { |
if ($res === FALSE) { |
168 |
|
rg_log("\tCannot delete (" . rg_sql_error() . ")!"); |
|
|
168 |
|
rg_log("Cannot delete (" . rg_sql_error() . ")!"); |
169 |
169 |
break; |
break; |
170 |
170 |
} |
} |
171 |
171 |
rg_sql_free_result($res); |
rg_sql_free_result($res); |
File inc/ssh.inc.php changed (mode: 100644) (index d00ebef..40e6e5d) |
... |
... |
require_once($INC . "/sql.inc.php"); |
6 |
6 |
require_once($INC . "/state.inc.php"); |
require_once($INC . "/state.inc.php"); |
7 |
7 |
require_once($INC . "/prof.inc.php"); |
require_once($INC . "/prof.inc.php"); |
8 |
8 |
require_once($INC . "/repo.inc.php"); |
require_once($INC . "/repo.inc.php"); |
|
9 |
|
require_once($INC . "/totp.inc.php"); |
9 |
10 |
|
|
10 |
11 |
function rg_ssh_status($db, $uid) |
function rg_ssh_status($db, $uid) |
11 |
12 |
{ |
{ |
|
... |
... |
function rg_ssh_status($db, $uid) |
14 |
15 |
echo "Here will be the status.\n"; |
echo "Here will be the status.\n"; |
15 |
16 |
|
|
16 |
17 |
// also details about payments: warn if disk space is low etc. |
// also details about payments: warn if disk space is low etc. |
17 |
|
|
|
18 |
|
exit(0); |
|
19 |
18 |
} |
} |
20 |
19 |
|
|
21 |
20 |
/* |
/* |
|
... |
... |
function rg_ssh_repos($db, $uid) |
45 |
44 |
echo "You have no repository.\n"; |
echo "You have no repository.\n"; |
46 |
45 |
} |
} |
47 |
46 |
rg_sql_free_result($res); |
rg_sql_free_result($res); |
48 |
|
|
|
49 |
|
exit(0); |
|
50 |
47 |
} |
} |
51 |
48 |
|
|
52 |
49 |
/* |
/* |
|
... |
... |
function rg_ssh_repo($db, $uid, $paras) |
86 |
83 |
echo "Master: " . $mri['name'] . "\n"; |
echo "Master: " . $mri['name'] . "\n"; |
87 |
84 |
} |
} |
88 |
85 |
} |
} |
|
86 |
|
} |
89 |
87 |
|
|
90 |
|
exit(0); |
|
|
88 |
|
/* |
|
89 |
|
* Deal with TOTP stuff |
|
90 |
|
*/ |
|
91 |
|
function rg_ssh_totp($db, $uid, $paras) |
|
92 |
|
{ |
|
93 |
|
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]; |
|
99 |
|
|
|
100 |
|
$cmd = array_shift($paras); |
|
101 |
|
switch ($cmd) { |
|
102 |
|
case 'enroll': |
|
103 |
|
if (empty($paras)) { |
|
104 |
|
$secret = rg_totp_base32_generate(16); |
|
105 |
|
|
|
106 |
|
$r = rg_totp_enroll($db, $uid, 'SSH', $secret, $ip, 'f'); |
|
107 |
|
if ($r !== TRUE) { |
|
108 |
|
echo "An internal error occured; please try again later.\n"; |
|
109 |
|
break; |
|
110 |
|
} |
|
111 |
|
|
|
112 |
|
echo "Scan the following code with the mobile application:\n"; |
|
113 |
|
echo rg_totp_text($secret) . "\n"; |
|
114 |
|
echo "Or manually enter the following code: " . $secret . "\n"; |
|
115 |
|
echo "To finish the enrollment you will have to confirm"; |
|
116 |
|
echo " it by running the following command:"; |
|
117 |
|
echo " ssh ... enroll <6_digits_code_generated_by_device>\n"; |
|
118 |
|
break; |
|
119 |
|
} |
|
120 |
|
|
|
121 |
|
$confirm = array_shift($paras); |
|
122 |
|
|
|
123 |
|
$lt = rg_totp_list($db, $uid); |
|
124 |
|
if ($lt['ok'] != 1) { |
|
125 |
|
echo "An internal error occured; please try again later.\n"; |
|
126 |
|
break; |
|
127 |
|
} |
|
128 |
|
|
|
129 |
|
$done = FALSE; |
|
130 |
|
$ierror = FALSE; |
|
131 |
|
foreach ($lt['list'] as $_id => $t) { |
|
132 |
|
$_r = rg_totp_verify($t['secret'], $confirm); |
|
133 |
|
if ($_r === TRUE) { |
|
134 |
|
if (strcmp($t['conf'], 't') != 0) { |
|
135 |
|
$x = rg_totp_conf($db, $uid, $t['id']); |
|
136 |
|
if ($x !== TRUE) { |
|
137 |
|
$ierror = TRUE; |
|
138 |
|
break; |
|
139 |
|
} |
|
140 |
|
} |
|
141 |
|
|
|
142 |
|
$done = TRUE; |
|
143 |
|
break; |
|
144 |
|
} |
|
145 |
|
} |
|
146 |
|
|
|
147 |
|
if ($ierror) |
|
148 |
|
break; |
|
149 |
|
|
|
150 |
|
if ($done === FALSE) |
|
151 |
|
echo "Invalid confirmation token; try to re-enroll\n"; |
|
152 |
|
else |
|
153 |
|
echo "Success!\n"; |
|
154 |
|
break; |
|
155 |
|
|
|
156 |
|
case 'val': break; |
|
157 |
|
default: |
|
158 |
|
echo "Posible TOTP commands:\n"; |
|
159 |
|
echo " enroll\n"; |
|
160 |
|
echo " val\n"; |
|
161 |
|
echo " inval - Invalidate IP address\n"; |
|
162 |
|
break; |
|
163 |
|
} |
|
164 |
|
|
|
165 |
|
rg_log_exit(); |
|
166 |
|
rg_prof_end('ssh_totp'); |
91 |
167 |
} |
} |
92 |
168 |
|
|
93 |
169 |
function rg_ssh_dispatch($db, $uid, $cmd) |
function rg_ssh_dispatch($db, $uid, $cmd) |
94 |
170 |
{ |
{ |
95 |
|
$paras = explode(" ", $cmd); |
|
|
171 |
|
$paras = explode(' ', $cmd); |
96 |
172 |
$cmd = array_shift($paras); |
$cmd = array_shift($paras); |
97 |
173 |
|
|
|
174 |
|
echo "\n== Welcome to RocketGit! ==\n"; |
|
175 |
|
|
98 |
176 |
switch ($cmd) { |
switch ($cmd) { |
99 |
177 |
case 'status': rg_ssh_status($db, $uid); break; |
case 'status': rg_ssh_status($db, $uid); break; |
100 |
178 |
case 'repos': rg_ssh_repos($db, $uid); break; |
case 'repos': rg_ssh_repos($db, $uid); break; |
101 |
179 |
case 'repo': rg_ssh_repo($db, $uid, $paras); break; |
case 'repo': rg_ssh_repo($db, $uid, $paras); break; |
|
180 |
|
case 'totp': rg_ssh_totp($db, $uid, $paras); break; |
102 |
181 |
case '': |
case '': |
103 |
|
echo "Available commmands: status, repos, repo.\n"; |
|
104 |
|
exit(0); |
|
|
182 |
|
echo "Available commmands: status, repos, repo, totp.\n"; |
|
183 |
|
break; |
105 |
184 |
} |
} |
|
185 |
|
|
|
186 |
|
exit(0); |
106 |
187 |
} |
} |
107 |
188 |
|
|
108 |
189 |
?> |
?> |
File inc/totp.inc.php added (mode: 100644) (index 0000000..c9b0574) |
|
1 |
|
<?php |
|
2 |
|
|
|
3 |
|
$rg_totp_error = ''; |
|
4 |
|
|
|
5 |
|
function rg_totp_set_error($str) |
|
6 |
|
{ |
|
7 |
|
global $rg_totp_error; |
|
8 |
|
$rg_totp_error = $str; |
|
9 |
|
rg_log('set_error: ' . $str); |
|
10 |
|
} |
|
11 |
|
|
|
12 |
|
function rg_totp_error() |
|
13 |
|
{ |
|
14 |
|
global $rg_totp_error; |
|
15 |
|
return $rg_totp_error; |
|
16 |
|
} |
|
17 |
|
|
|
18 |
|
/* |
|
19 |
|
* Event functions |
|
20 |
|
*/ |
|
21 |
|
$rg_totp_functions = array( |
|
22 |
|
//2006 => "rg_totp_link_by_name" |
|
23 |
|
); |
|
24 |
|
rg_event_register_functions($rg_totp_functions); |
|
25 |
|
|
|
26 |
|
|
|
27 |
|
$rg_totp_base32_tab = "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567"; |
|
28 |
|
|
|
29 |
|
/* |
|
30 |
|
* Generates a secret (returned as google base32 string) |
|
31 |
|
*/ |
|
32 |
|
function rg_totp_base32_generate($digits) |
|
33 |
|
{ |
|
34 |
|
global $rg_totp_base32_tab; |
|
35 |
|
|
|
36 |
|
$ret = ''; |
|
37 |
|
for ($i = 0; $i < $digits; $i++) |
|
38 |
|
$ret .= $rg_totp_base32_tab[rand(0, 31)]; |
|
39 |
|
|
|
40 |
|
return $ret; |
|
41 |
|
} |
|
42 |
|
|
|
43 |
|
/* |
|
44 |
|
* Transforms a base32 google string into a binary array |
|
45 |
|
*/ |
|
46 |
|
function rg_totp_base32_decode($s) |
|
47 |
|
{ |
|
48 |
|
global $rg_totp_base32_tab; |
|
49 |
|
|
|
50 |
|
$s = strtoupper($s); |
|
51 |
|
$s_len = strlen($s); |
|
52 |
|
|
|
53 |
|
$ret = ''; |
|
54 |
|
$buf = 0; |
|
55 |
|
$buf_bits = 0; |
|
56 |
|
$i = 0; |
|
57 |
|
while ($i < $s_len) { |
|
58 |
|
while (($i < $s_len) && ($buf_bits < 8)) { |
|
59 |
|
$n = strpos($rg_totp_base32_tab, $s[$i++]); |
|
60 |
|
$buf = ($buf << 5) | $n; |
|
61 |
|
$buf_bits += 5; |
|
62 |
|
} |
|
63 |
|
|
|
64 |
|
// less than 8 bits left, pad right with 0 |
|
65 |
|
if ($buf_bits < 8) { |
|
66 |
|
$buf <<= (8 - $buf_bits); |
|
67 |
|
$buf_bits = 8; |
|
68 |
|
} |
|
69 |
|
|
|
70 |
|
// Now, we have more than 8 bits in buffer, extract |
|
71 |
|
// next out char. |
|
72 |
|
$buf_bits -= 8; |
|
73 |
|
$c = chr($buf >> $buf_bits); |
|
74 |
|
$ret .= $c; |
|
75 |
|
$buf &= ((1 << $buf_bits) - 1); |
|
76 |
|
} |
|
77 |
|
|
|
78 |
|
return $ret; |
|
79 |
|
} |
|
80 |
|
|
|
81 |
|
function rg_totp_compute($key, $tc, $digits) |
|
82 |
|
{ |
|
83 |
|
$key_bin = rg_totp_base32_decode($key); |
|
84 |
|
|
|
85 |
|
$tc_bin = hex2bin(sprintf("%016x", $tc)); |
|
86 |
|
|
|
87 |
|
$h = hash_hmac('sha1', $tc_bin, $key_bin, TRUE /*bin output*/); |
|
88 |
|
|
|
89 |
|
$o = ord($h[strlen($h) - 1]) & 0x0F; |
|
90 |
|
$i = (ord($h[$o]) << 24) | (ord($h[$o + 1]) << 16) |
|
91 |
|
| (ord($h[$o + 2]) << 8) | ord($h[$o + 3]); |
|
92 |
|
$i &= 0x7FFFFFFF; |
|
93 |
|
return sprintf("%0" . $digits . "u", $i % pow(10, $digits)); |
|
94 |
|
} |
|
95 |
|
|
|
96 |
|
function rg_totp_verify_tc($key, $tc, $token) |
|
97 |
|
{ |
|
98 |
|
$t = rg_totp_compute($key, $tc, 6); |
|
99 |
|
if (strcmp($token, $t) == 0) |
|
100 |
|
return TRUE; |
|
101 |
|
|
|
102 |
|
return FALSE; |
|
103 |
|
} |
|
104 |
|
|
|
105 |
|
/* |
|
106 |
|
* Verifies if a login token is valid |
|
107 |
|
* We try 2 tcs before currend ts and after current ts |
|
108 |
|
*/ |
|
109 |
|
function rg_totp_verify($key, $token) |
|
110 |
|
{ |
|
111 |
|
rg_prof_start('totp_verify'); |
|
112 |
|
|
|
113 |
|
$tc = intval(time() / 30); |
|
114 |
|
while (1) { |
|
115 |
|
$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) |
|
133 |
|
break; |
|
134 |
|
|
|
135 |
|
$ret = FALSE; |
|
136 |
|
break; |
|
137 |
|
} |
|
138 |
|
|
|
139 |
|
rg_prof_end('totp_verify'); |
|
140 |
|
return $ret; |
|
141 |
|
} |
|
142 |
|
|
|
143 |
|
/* |
|
144 |
|
* Returns a PNG encoded QR code |
|
145 |
|
*/ |
|
146 |
|
function rg_totp_png($secret) |
|
147 |
|
{ |
|
148 |
|
global $rg_ssh_host; |
|
149 |
|
|
|
150 |
|
$extra = gmdate('Y-m-d H:i'); |
|
151 |
|
$issuer = $rg_ssh_host; |
|
152 |
|
$cmd = "qrencode -o - --level=H --type=PNG 'otpauth://totp/$extra?secret=$secret&issuer=$issuer'"; |
|
153 |
|
$a = rg_exec($cmd); |
|
154 |
|
if ($a['ok'] != 1) |
|
155 |
|
return FALSE; |
|
156 |
|
|
|
157 |
|
return $a['data']; |
|
158 |
|
} |
|
159 |
|
|
|
160 |
|
/* |
|
161 |
|
* Returns a UTF-8 encoded QR code |
|
162 |
|
*/ |
|
163 |
|
function rg_totp_text($secret) |
|
164 |
|
{ |
|
165 |
|
global $rg_ssh_host; |
|
166 |
|
|
|
167 |
|
$extra = gmdate('Y-m-d H:i'); |
|
168 |
|
$issuer = $rg_ssh_host; |
|
169 |
|
$cmd = "qrencode -o - --level=M --margin=2 --type=UTF8 'otpauth://totp/$extra?secret=$secret&issuer=$issuer'"; |
|
170 |
|
$a = rg_exec($cmd); |
|
171 |
|
if ($a['ok'] != 1) |
|
172 |
|
return FALSE; |
|
173 |
|
|
|
174 |
|
return $a['data']; |
|
175 |
|
} |
|
176 |
|
|
|
177 |
|
/* |
|
178 |
|
* Cosmetic fixes for a login token row |
|
179 |
|
*/ |
|
180 |
|
function rg_totp_cosmetic(&$row) |
|
181 |
|
{ |
|
182 |
|
if (isset($row['itime'])) |
|
183 |
|
$row['itime_nice'] = gmdate('Y-m-d H:i', $row['itime']); |
|
184 |
|
|
|
185 |
|
if (!isset($row['used'])) |
|
186 |
|
$row['used'] = 0; |
|
187 |
|
|
|
188 |
|
if ($row['used'] > 0) |
|
189 |
|
$row['used_nice'] = gmdate('Y-m-d H:i', $row['used']); |
|
190 |
|
else |
|
191 |
|
$row['used_nice'] = "n/a"; |
|
192 |
|
|
|
193 |
|
if (isset($row['conf'])) { |
|
194 |
|
if (strcmp($row['conf'], 't') == 0) |
|
195 |
|
$row['conf_nice'] = 'confirmed'; |
|
196 |
|
else |
|
197 |
|
$row['conf_nice'] = 'pending'; |
|
198 |
|
} |
|
199 |
|
} |
|
200 |
|
|
|
201 |
|
/* |
|
202 |
|
* Sets when a login token was last used |
|
203 |
|
*/ |
|
204 |
|
function rg_totp_set_last_use($db, $uid, $id, $ts) |
|
205 |
|
{ |
|
206 |
|
rg_prof_start('totp_set_last_use'); |
|
207 |
|
rg_log_enter('totp_set_last_use uid=' . $uid . ' id=' . $id); |
|
208 |
|
|
|
209 |
|
$ret = FALSE; |
|
210 |
|
while (1) { |
|
211 |
|
$params = array('uid' => $uid, |
|
212 |
|
'id' => $id, |
|
213 |
|
'used' => $ts); |
|
214 |
|
$sql = 'UPDATE login_tokens SET used = @@used@@' |
|
215 |
|
. ' WHERE uid = @@uid@@' |
|
216 |
|
. ' AND id = @@id@@' |
|
217 |
|
. ' AND used != @@used@@'; |
|
218 |
|
$res = rg_sql_query_params($db, $sql, $params); |
|
219 |
|
if ($res === FALSE) { |
|
220 |
|
rg_user_set_error('cannot update last used (' . rg_sql_error()); |
|
221 |
|
break; |
|
222 |
|
} |
|
223 |
|
rg_sql_free_result($res); |
|
224 |
|
|
|
225 |
|
$key = 'user' . '::' . $uid . '::' . 'login_tokens' |
|
226 |
|
. '::' . 'list' . '::' . $id . '::' . 'used'; |
|
227 |
|
$a = array('used' => $ts); |
|
228 |
|
rg_totp_cosmetic($a); |
|
229 |
|
rg_cache_merge($key, $a, RG_SOCKET_NO_WAIT); |
|
230 |
|
|
|
231 |
|
$ret = TRUE; |
|
232 |
|
break; |
|
233 |
|
} |
|
234 |
|
|
|
235 |
|
rg_log_exit(); |
|
236 |
|
rg_prof_end('totp_set_last_use'); |
|
237 |
|
return $ret; |
|
238 |
|
} |
|
239 |
|
|
|
240 |
|
/* |
|
241 |
|
* Add a new secret login token to database |
|
242 |
|
*/ |
|
243 |
|
function rg_totp_enroll($db, $uid, $name, $secret, $ip, $conf) |
|
244 |
|
{ |
|
245 |
|
rg_prof_start('totp_enroll'); |
|
246 |
|
rg_log_enter('totp_enroll name=' . $name); |
|
247 |
|
|
|
248 |
|
$ret = FALSE; |
|
249 |
|
while (1) { |
|
250 |
|
$params = array('uid' => $uid, 'name' => $name, |
|
251 |
|
'secret' => $secret, |
|
252 |
|
'itime' => time(), 'ip' => $ip, 'conf' => $conf); |
|
253 |
|
$sql = 'INSERT INTO login_tokens (uid, itime, name, secret, ip' |
|
254 |
|
. ', conf)' |
|
255 |
|
. ' VALUES (@@uid@@, @@itime@@, @@name@@' |
|
256 |
|
. ', @@secret@@, @@ip@@, @@conf@@)' |
|
257 |
|
. ' RETURNING id'; |
|
258 |
|
$res = rg_sql_query_params($db, $sql, $params); |
|
259 |
|
if ($res === FALSE) { |
|
260 |
|
rg_totp_set_error('cannot insert login token; try again later'); |
|
261 |
|
break; |
|
262 |
|
} |
|
263 |
|
$row = rg_sql_fetch_array($res); |
|
264 |
|
rg_sql_free_result($res); |
|
265 |
|
|
|
266 |
|
$params['id'] = $row['id']; |
|
267 |
|
rg_totp_cosmetic($params); |
|
268 |
|
$key = 'user' . '::' . $uid . '::' . 'login_tokens' |
|
269 |
|
. '::' . 'list' . '::' . $params['id']; |
|
270 |
|
rg_cache_set($key, $params, RG_SOCKET_NO_WAIT); |
|
271 |
|
|
|
272 |
|
$ret = TRUE; |
|
273 |
|
break; |
|
274 |
|
} |
|
275 |
|
|
|
276 |
|
rg_log_exit(); |
|
277 |
|
rg_prof_end('totp_enroll'); |
|
278 |
|
return $ret; |
|
279 |
|
} |
|
280 |
|
|
|
281 |
|
/* |
|
282 |
|
* Confirm a SSH enrollment process |
|
283 |
|
*/ |
|
284 |
|
function rg_totp_conf($db, $uid, $id) |
|
285 |
|
{ |
|
286 |
|
rg_prof_start('totp_conf'); |
|
287 |
|
rg_log_enter('totp_conf id=' . $id); |
|
288 |
|
|
|
289 |
|
$ret = FALSE; |
|
290 |
|
while (1) { |
|
291 |
|
$params = array('uid' => $uid, 'id' => $id, 'conf' => 't'); |
|
292 |
|
$sql = 'UPDATE login_tokens SET conf = @@conf@@' |
|
293 |
|
. ' WHERE uid = @@uid@@' |
|
294 |
|
. ' AND id = @@id@@' |
|
295 |
|
. ' AND conf != @@conf@@'; |
|
296 |
|
$res = rg_sql_query_params($db, $sql, $params); |
|
297 |
|
if ($res === FALSE) { |
|
298 |
|
rg_totp_set_error('cannot confirm login token; try again later'); |
|
299 |
|
break; |
|
300 |
|
} |
|
301 |
|
rg_sql_free_result($res); |
|
302 |
|
|
|
303 |
|
$key = 'user' . '::' . $uid . '::' . 'login_tokens' |
|
304 |
|
. '::' . 'list' . '::' . $params['id']; |
|
305 |
|
$a = array('conf' => 't'); |
|
306 |
|
rg_totp_cosmetic($params); |
|
307 |
|
rg_cache_merge($key, $a, RG_SOCKET_NO_WAIT); |
|
308 |
|
|
|
309 |
|
$ret = TRUE; |
|
310 |
|
break; |
|
311 |
|
} |
|
312 |
|
|
|
313 |
|
rg_log_exit(); |
|
314 |
|
rg_prof_end('totp_conf'); |
|
315 |
|
return $ret; |
|
316 |
|
} |
|
317 |
|
|
|
318 |
|
/* |
|
319 |
|
* Returns a list of login tokens from database |
|
320 |
|
*/ |
|
321 |
|
function rg_totp_list($db, $uid) |
|
322 |
|
{ |
|
323 |
|
rg_prof_start('totp_list'); |
|
324 |
|
rg_log_enter('totp_list'); |
|
325 |
|
|
|
326 |
|
while (1) { |
|
327 |
|
$key = 'user' . '::' . $uid . '::' . 'login_tokens'; |
|
328 |
|
$ret = rg_cache_get($key); |
|
329 |
|
if ($ret !== FALSE) { |
|
330 |
|
$ret['ok'] = 1; |
|
331 |
|
break; |
|
332 |
|
} |
|
333 |
|
|
|
334 |
|
$ret = array(); |
|
335 |
|
$ret['ok'] = 0; |
|
336 |
|
|
|
337 |
|
$params = array('uid' => $uid); |
|
338 |
|
$sql = 'SELECT * FROM login_tokens' |
|
339 |
|
. ' WHERE uid = @@uid@@' |
|
340 |
|
. ' ORDER BY itime'; |
|
341 |
|
$res = rg_sql_query_params($db, $sql, $params); |
|
342 |
|
if ($res === FALSE) { |
|
343 |
|
rg_totp_set_error('cannot load login tokens'); |
|
344 |
|
break; |
|
345 |
|
} |
|
346 |
|
|
|
347 |
|
$ret['list'] = array(); |
|
348 |
|
while (($row = rg_sql_fetch_array($res))) { |
|
349 |
|
$id = $row['id']; |
|
350 |
|
|
|
351 |
|
rg_totp_cosmetic($row); |
|
352 |
|
|
|
353 |
|
$ret['list'][$id] = $row; |
|
354 |
|
} |
|
355 |
|
rg_sql_free_result($res); |
|
356 |
|
|
|
357 |
|
rg_cache_set($key, $ret, RG_SOCKET_NO_WAIT); |
|
358 |
|
$ret['ok'] = 1; |
|
359 |
|
break; |
|
360 |
|
} |
|
361 |
|
|
|
362 |
|
rg_log_exit(); |
|
363 |
|
rg_prof_end('totp_list'); |
|
364 |
|
return $ret; |
|
365 |
|
} |
|
366 |
|
|
|
367 |
|
/* |
|
368 |
|
* Remove a list of login tokens |
|
369 |
|
*/ |
|
370 |
|
function rg_totp_remove($db, $uid, $list) |
|
371 |
|
{ |
|
372 |
|
rg_prof_start('totp_remove'); |
|
373 |
|
rg_log_enter('totp_remove uid=' . $uid |
|
374 |
|
. ' list=' . rg_array2string($list)); |
|
375 |
|
|
|
376 |
|
$ret = FALSE; |
|
377 |
|
while (1) { |
|
378 |
|
if (empty($list)) { |
|
379 |
|
rg_totp_set_error('you did not select anything'); |
|
380 |
|
break; |
|
381 |
|
} |
|
382 |
|
|
|
383 |
|
$my_list = array(); |
|
384 |
|
foreach ($list as $id => $junk) |
|
385 |
|
$my_list[] = sprintf("%u", $id); |
|
386 |
|
|
|
387 |
|
$params = array('uid' => $uid); |
|
388 |
|
$sql_list = implode(', ', $my_list); |
|
389 |
|
$sql = 'DELETE FROM login_tokens' |
|
390 |
|
. ' WHERE uid = @@uid@@' |
|
391 |
|
. ' AND id IN (' . $sql_list . ')'; |
|
392 |
|
$res = rg_sql_query_params($db, $sql, $params); |
|
393 |
|
if ($res === FALSE) { |
|
394 |
|
rg_totp_set_error('cannot remove login token ' . $id |
|
395 |
|
. ' (' . rg_sql_error() . ')'); |
|
396 |
|
break; |
|
397 |
|
} |
|
398 |
|
rg_sql_free_result($res); |
|
399 |
|
|
|
400 |
|
foreach ($my_list as $junk => $_id) { |
|
401 |
|
$key = 'user' . '::' . $uid . '::' . 'login_tokens' |
|
402 |
|
. '::' . 'list' . $_id; |
|
403 |
|
rg_cache_unset($key, RG_SOCKET_NO_WAIT); |
|
404 |
|
} |
|
405 |
|
|
|
406 |
|
$ret = TRUE; |
|
407 |
|
break; |
|
408 |
|
} |
|
409 |
|
|
|
410 |
|
rg_log_exit(); |
|
411 |
|
rg_prof_end('totp_remove'); |
|
412 |
|
return $ret; |
|
413 |
|
} |
|
414 |
|
|
|
415 |
|
/* |
|
416 |
|
* High-level function for listing tokens |
|
417 |
|
*/ |
|
418 |
|
function rg_totp_list_high_level($db, $rg) |
|
419 |
|
{ |
|
420 |
|
$ret = ''; |
|
421 |
|
|
|
422 |
|
$del_errmsg = array(); |
|
423 |
|
$rg['HTML:del_errmsg'] = ''; |
|
424 |
|
$rg['HTML:del_status'] = ''; |
|
425 |
|
|
|
426 |
|
$delete = rg_var_uint('delete'); |
|
427 |
|
while ($delete == 1) { |
|
428 |
|
if (!rg_valid_referer()) { |
|
429 |
|
$del_errmsg[] = 'invalid referer; try again'; |
|
430 |
|
break; |
|
431 |
|
} |
|
432 |
|
|
|
433 |
|
if (!rg_token_valid($db, $rg, 'login_tokens_list', FALSE)) { |
|
434 |
|
$del_errmsg[] = 'invalid token; try again.'; |
|
435 |
|
break; |
|
436 |
|
} |
|
437 |
|
|
|
438 |
|
$list = rg_var_str("delete_list"); |
|
439 |
|
$r = rg_totp_remove($db, $rg['login_ui']['uid'], $list); |
|
440 |
|
if ($r !== TRUE) { |
|
441 |
|
$del_errmsg[] = 'cannot delete: ' . rg_totp_error(); |
|
442 |
|
break; |
|
443 |
|
} |
|
444 |
|
|
|
445 |
|
$rg['HTML:del_status'] = rg_template('user/settings/totp/delete_ok.html', |
|
446 |
|
$rg, TRUE /*xss*/); |
|
447 |
|
break; |
|
448 |
|
} |
|
449 |
|
|
|
450 |
|
$r = rg_totp_list($db, $rg['login_ui']['uid']); |
|
451 |
|
if ($r['ok'] !== 1) { |
|
452 |
|
$rg['totp_errmsg'] = rg_totp_error(); |
|
453 |
|
$ret .= rg_template('user/settings/totp/list_err.html', |
|
454 |
|
$rg, TRUE /*xss*/); |
|
455 |
|
} else { |
|
456 |
|
$rg['rg_form_token'] = rg_token_get($db, $rg, 'login_tokens_list'); |
|
457 |
|
$rg['HTML:del_errmsg'] = rg_template_errmsg($del_errmsg); |
|
458 |
|
$ret .= rg_template_table('user/settings/totp/list', $r['list'], $rg); |
|
459 |
|
} |
|
460 |
|
|
|
461 |
|
return $ret; |
|
462 |
|
} |
|
463 |
|
|
|
464 |
|
/* |
|
465 |
|
* Main function for TOTP login token |
|
466 |
|
*/ |
|
467 |
|
function rg_totp_high_level($db, $rg) |
|
468 |
|
{ |
|
469 |
|
rg_prof_start('totp_high_level'); |
|
470 |
|
rg_log_enter('totp_high_level'); |
|
471 |
|
|
|
472 |
|
$rg['totp'] = array(); |
|
473 |
|
|
|
474 |
|
$ret = ''; |
|
475 |
|
$errmsg = array(); |
|
476 |
|
|
|
477 |
|
$rg['totp']['name'] = rg_var_str('totp::name'); |
|
478 |
|
|
|
479 |
|
$enroll = rg_var_uint('enroll'); |
|
480 |
|
while ($enroll == 1) { |
|
481 |
|
$name = rg_var_str('totp::name'); |
|
482 |
|
$ver = rg_var_str('totp::ver'); |
|
483 |
|
$secret = rg_var_str('totp::secret'); |
|
484 |
|
|
|
485 |
|
if (strlen($name) == 0) { |
|
486 |
|
$errmsg[] = "invalid name"; |
|
487 |
|
break; |
|
488 |
|
} |
|
489 |
|
|
|
490 |
|
if (strlen($ver) != 6) { |
|
491 |
|
$errmsg[] = "invalid number; you must enter a 6 digit number"; |
|
492 |
|
break; |
|
493 |
|
} |
|
494 |
|
|
|
495 |
|
if (!rg_valid_referer()) { |
|
496 |
|
$errmsg[] = "invalid referer; try again"; |
|
497 |
|
break; |
|
498 |
|
} |
|
499 |
|
|
|
500 |
|
if (!rg_token_valid($db, $rg, 'user_totp', FALSE)) { |
|
501 |
|
$errmsg[] = "invalid token; try again"; |
|
502 |
|
break; |
|
503 |
|
} |
|
504 |
|
|
|
505 |
|
$r = rg_totp_verify($secret, $ver); |
|
506 |
|
if ($r === FALSE) { |
|
507 |
|
$errmsg[] = rg_template('user/settings/totp/ver_error.html', |
|
508 |
|
$rg, TRUE /*xss*/); |
|
509 |
|
break; |
|
510 |
|
} |
|
511 |
|
|
|
512 |
|
$ip = $_SERVER['REMOTE_ADDR']; |
|
513 |
|
$r = rg_totp_enroll($db, $rg['login_ui']['uid'], |
|
514 |
|
$name, $secret, $ip, 't'); |
|
515 |
|
if ($r !== TRUE) { |
|
516 |
|
$errmsg[] = rg_totp_error(); |
|
517 |
|
break; |
|
518 |
|
} |
|
519 |
|
|
|
520 |
|
$ret .= rg_template('user/settings/totp/enroll_ok.html', |
|
521 |
|
$rg, TRUE /*xss*/); |
|
522 |
|
|
|
523 |
|
break; |
|
524 |
|
} |
|
525 |
|
|
|
526 |
|
$ret .= rg_totp_list_high_level($db, $rg); |
|
527 |
|
|
|
528 |
|
// defaults |
|
529 |
|
if ($enroll == 0) { |
|
530 |
|
$name = ''; |
|
531 |
|
$ver = ''; |
|
532 |
|
$secret = rg_totp_base32_generate(16); |
|
533 |
|
} |
|
534 |
|
|
|
535 |
|
$rg['totp']['name'] = $name; |
|
536 |
|
$rg['totp']['ver'] = $ver; |
|
537 |
|
$rg['totp']['secret'] = $secret; |
|
538 |
|
|
|
539 |
|
$png = rg_totp_png($rg['totp']['secret']); |
|
540 |
|
if ($png === FALSE) { |
|
541 |
|
$rg['totp']['img'] = 0; |
|
542 |
|
} else { |
|
543 |
|
$rg['totp']['img'] = 1; |
|
544 |
|
$rg['totp']['png'] = base64_encode($png); |
|
545 |
|
} |
|
546 |
|
|
|
547 |
|
$rg['HTML:errmsg'] = rg_template_errmsg($errmsg); |
|
548 |
|
|
|
549 |
|
$rg['rg_form_token'] = rg_token_get($db, $rg, 'user_totp'); |
|
550 |
|
$ret .= rg_template('user/settings/totp/main.html', $rg, TRUE /*xss*/); |
|
551 |
|
|
|
552 |
|
// hints |
|
553 |
|
$hints = array(); |
|
554 |
|
$hints[]['HTML:hint'] = rg_template("user/settings/totp/hints.html", $rg, FALSE /*xss*/); |
|
555 |
|
$ret .= rg_template_table("hints/list", $hints, $rg); |
|
556 |
|
|
|
557 |
|
rg_log_exit(); |
|
558 |
|
rg_prof_end('totp_high_level'); |
|
559 |
|
return $ret; |
|
560 |
|
} |
|
561 |
|
|
|
562 |
|
?> |
File inc/user.inc.php changed (mode: 100644) (index e660e53..975e84f) |
... |
... |
require_once($INC . "/rights.inc.php"); |
8 |
8 |
require_once($INC . "/events.inc.php"); |
require_once($INC . "/events.inc.php"); |
9 |
9 |
require_once($INC . "/cache.inc.php"); |
require_once($INC . "/cache.inc.php"); |
10 |
10 |
require_once($INC . "/plan.inc.php"); |
require_once($INC . "/plan.inc.php"); |
|
11 |
|
require_once($INC . "/totp.inc.php"); |
11 |
12 |
|
|
12 |
13 |
$rg_user_rights = array( |
$rg_user_rights = array( |
13 |
14 |
"C" => "Create repository", |
"C" => "Create repository", |
|
... |
... |
function rg_user_error() |
42 |
43 |
*/ |
*/ |
43 |
44 |
$rg_user_functions = array( |
$rg_user_functions = array( |
44 |
45 |
2000 => "rg_user_event_new", |
2000 => "rg_user_event_new", |
|
46 |
|
2001 => "rg_user_event_login", |
45 |
47 |
2002 => "rg_user_event_notify_user", |
2002 => "rg_user_event_notify_user", |
46 |
48 |
2005 => "rg_user_event_rename", |
2005 => "rg_user_event_rename", |
47 |
49 |
2006 => "rg_user_link_by_name" |
2006 => "rg_user_link_by_name" |
|
... |
... |
function rg_user_event_new($db, $event) |
57 |
59 |
|
|
58 |
60 |
$event['op'] = "new"; |
$event['op'] = "new"; |
59 |
61 |
// create link by name |
// create link by name |
60 |
|
$ret[] = array_merge($event, array("category" => 2006, "prio" => 200)); |
|
|
62 |
|
$ret[] = array_merge($event, array("category" => 2006, "prio" => 500)); |
61 |
63 |
// notify user |
// notify user |
62 |
|
$ret[] = array_merge($event, array("category" => 2002, "prio" => 100)); |
|
|
64 |
|
$ret[] = array_merge($event, array("category" => 2002, "prio" => 200)); |
|
65 |
|
|
|
66 |
|
return $ret; |
|
67 |
|
} |
|
68 |
|
|
|
69 |
|
/* |
|
70 |
|
* Event for login |
|
71 |
|
*/ |
|
72 |
|
function rg_user_event_login($db, $event) |
|
73 |
|
{ |
|
74 |
|
$ret = FALSE; |
|
75 |
|
while (1) { |
|
76 |
|
$r = rg_user_set_last_seen($db, $event['uid'], $event['ts'], |
|
77 |
|
$event['IP']); |
|
78 |
|
if ($r !== TRUE) |
|
79 |
|
break; |
|
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 |
|
$ret = array(); |
|
89 |
|
break; |
|
90 |
|
} |
63 |
91 |
|
|
64 |
92 |
return $ret; |
return $ret; |
65 |
93 |
} |
} |
|
... |
... |
function rg_user_info($db, $uid, $user, $email) |
656 |
684 |
/* |
/* |
657 |
685 |
* Update last_seen field |
* Update last_seen field |
658 |
686 |
*/ |
*/ |
659 |
|
function rg_user_set_last_seen($db, $uid) |
|
|
687 |
|
function rg_user_set_last_seen($db, $uid, $ts, $ip) |
660 |
688 |
{ |
{ |
661 |
|
rg_log_enter("user_set_last_seen: uid=$uid"); |
|
|
689 |
|
rg_log_enter("user_set_last_seen: uid=$uid ts=$ts"); |
662 |
690 |
|
|
663 |
691 |
$ret = FALSE; |
$ret = FALSE; |
664 |
692 |
while (1) { |
while (1) { |
665 |
|
$now = time(); |
|
666 |
|
|
|
667 |
|
$IP = $_SERVER['REMOTE_ADDR']; |
|
668 |
|
|
|
669 |
|
$params = array("last_seen" => $now, |
|
670 |
|
"last_ip" => $IP, |
|
|
693 |
|
$params = array("last_seen" => $ts, |
|
694 |
|
"last_ip" => $ip, |
671 |
695 |
"uid" => $uid); |
"uid" => $uid); |
672 |
|
$sql = "UPDATE users SET last_seen = @@last_seen@@" |
|
673 |
|
. ", last_ip = @@last_ip@@" |
|
674 |
|
. " WHERE uid = @@uid@@"; |
|
|
696 |
|
$sql = 'UPDATE users SET last_seen = @@last_seen@@' |
|
697 |
|
. ', last_ip = @@last_ip@@' |
|
698 |
|
. ' WHERE uid = @@uid@@' |
|
699 |
|
. ' AND last_seen != @@last_seen@@'; |
675 |
700 |
$res = rg_sql_query_params($db, $sql, $params); |
$res = rg_sql_query_params($db, $sql, $params); |
676 |
701 |
if ($res === FALSE) { |
if ($res === FALSE) { |
677 |
702 |
rg_user_set_error("cannot update last seen (" . rg_sql_error() . ")"); |
rg_user_set_error("cannot update last seen (" . rg_sql_error() . ")"); |
|
... |
... |
function rg_user_login_by_sid($db, &$rg) |
715 |
740 |
break; |
break; |
716 |
741 |
|
|
717 |
742 |
$sess = rg_sess_valid($db, $rg['sid']); |
$sess = rg_sess_valid($db, $rg['sid']); |
718 |
|
if ($sess == FALSE) { |
|
719 |
|
rg_log("session is not valid"); |
|
|
743 |
|
if ($sess == FALSE) |
720 |
744 |
break; |
break; |
721 |
|
} |
|
722 |
745 |
|
|
723 |
746 |
$uid = $sess['uid']; |
$uid = $sess['uid']; |
724 |
747 |
$rg['login_ui'] = rg_user_info($db, $uid, "", ""); |
$rg['login_ui'] = rg_user_info($db, $uid, "", ""); |
725 |
748 |
if ($rg['login_ui']['exists'] != 1) { |
if ($rg['login_ui']['exists'] != 1) { |
726 |
|
rg_log("\tUid $uid does not exists (" . rg_user_error() . ")!"); |
|
|
749 |
|
rg_log("Uid $uid does not exists (" . rg_user_error() . ")!"); |
727 |
750 |
rg_user_set_error("invalid uid"); |
rg_user_set_error("invalid uid"); |
728 |
751 |
break; |
break; |
729 |
752 |
} |
} |
|
... |
... |
function rg_user_login_by_sid($db, &$rg) |
744 |
767 |
*/ |
*/ |
745 |
768 |
function rg_user_pass_valid($db, $uid, $pass) |
function rg_user_pass_valid($db, $uid, $pass) |
746 |
769 |
{ |
{ |
747 |
|
rg_log("user_pass_valid: uid=$uid, pass=$pass..."); |
|
|
770 |
|
rg_log_enter("user_pass_valid: uid=$uid, pass=$pass..."); |
748 |
771 |
|
|
749 |
|
if (empty($pass)) { |
|
750 |
|
rg_user_set_error("password is empty"); |
|
751 |
|
return FALSE; |
|
752 |
|
} |
|
|
772 |
|
$ret = FALSE; |
|
773 |
|
while (1) { |
|
774 |
|
if (empty($pass)) { |
|
775 |
|
rg_user_set_error("password is empty"); |
|
776 |
|
break; |
|
777 |
|
} |
753 |
778 |
|
|
754 |
|
$ui = rg_user_info($db, $uid, "", ""); |
|
755 |
|
if ($ui['exists'] != 1) { |
|
756 |
|
rg_user_set_error("user does not exists"); |
|
757 |
|
return FALSE; |
|
758 |
|
} |
|
|
779 |
|
$ui = rg_user_info($db, $uid, "", ""); |
|
780 |
|
if ($ui['exists'] != 1) { |
|
781 |
|
rg_user_set_error("user does not exists"); |
|
782 |
|
break; |
|
783 |
|
} |
759 |
784 |
|
|
760 |
|
$pass_hash = rg_user_pass($ui['salt'], $pass); |
|
761 |
|
if (strcmp($pass_hash, $ui['pass']) != 0) { |
|
762 |
|
rg_user_set_error("password is not ok"); |
|
763 |
|
return FALSE; |
|
|
785 |
|
$pass_hash = rg_user_pass($ui['salt'], $pass); |
|
786 |
|
if (strcmp($pass_hash, $ui['pass']) != 0) { |
|
787 |
|
rg_user_set_error("password is not ok"); |
|
788 |
|
break; |
|
789 |
|
} |
|
790 |
|
|
|
791 |
|
rg_log("Pass is valid."); |
|
792 |
|
$ret = TRUE; |
|
793 |
|
break; |
764 |
794 |
} |
} |
765 |
795 |
|
|
766 |
|
rg_log("\tPass is valid."); |
|
|
796 |
|
rg_log_exit(); |
767 |
797 |
return TRUE; |
return TRUE; |
768 |
798 |
} |
} |
769 |
799 |
|
|
|
... |
... |
function rg_user_auto_login($db, $uid, $lock_ip, &$ui) |
823 |
853 |
/* |
/* |
824 |
854 |
* Test if login is OK |
* Test if login is OK |
825 |
855 |
*/ |
*/ |
826 |
|
function rg_user_login_by_user_pass($db, $user, $pass, $lock_ip, &$ui) |
|
|
856 |
|
function rg_user_login_by_user_pass($db, $user, $pass, $login_token, $lock_ip, |
|
857 |
|
&$ui) |
827 |
858 |
{ |
{ |
828 |
859 |
rg_prof_start("user_login_by_user_pass"); |
rg_prof_start("user_login_by_user_pass"); |
829 |
860 |
rg_log_enter("user_login_by_user_pass: user=$user, pass=$pass" |
rg_log_enter("user_login_by_user_pass: user=$user, pass=$pass" |
830 |
|
. " lock_ip=$lock_ip"); |
|
|
861 |
|
. " login_token=$login_token lock_ip=$lock_ip"); |
831 |
862 |
|
|
832 |
863 |
$ui = array(); |
$ui = array(); |
833 |
864 |
$ui['uid'] = 0; |
$ui['uid'] = 0; |
|
... |
... |
function rg_user_login_by_user_pass($db, $user, $pass, $lock_ip, &$ui) |
837 |
868 |
$ret = FALSE; |
$ret = FALSE; |
838 |
869 |
while (1) { |
while (1) { |
839 |
870 |
if (empty($user) || empty($pass)) { |
if (empty($user) || empty($pass)) { |
840 |
|
rg_user_set_error("invalid user or pass"); |
|
|
871 |
|
rg_user_set_error("invalid user, pass or login_token"); |
841 |
872 |
rg_log("user or pass are empty"); |
rg_log("user or pass are empty"); |
842 |
873 |
break; |
break; |
843 |
874 |
} |
} |
|
... |
... |
function rg_user_login_by_user_pass($db, $user, $pass, $lock_ip, &$ui) |
846 |
877 |
if ($ui0['ok'] != 1) |
if ($ui0['ok'] != 1) |
847 |
878 |
break; |
break; |
848 |
879 |
if ($ui0['exists'] != 1) { |
if ($ui0['exists'] != 1) { |
849 |
|
rg_user_set_error("invalid user or pass"); |
|
|
880 |
|
rg_user_set_error("invalid user, pass or login_token"); |
850 |
881 |
rg_log("user doesn't exists"); |
rg_log("user doesn't exists"); |
851 |
882 |
break; |
break; |
852 |
883 |
} |
} |
853 |
884 |
|
|
854 |
885 |
if ($ui0['suspended'] > 0) { |
if ($ui0['suspended'] > 0) { |
855 |
|
rg_user_set_error("invalid user or pass"); |
|
|
886 |
|
rg_user_set_error("invalid user, pass or login_token"); |
856 |
887 |
rg_log("account is suspended"); |
rg_log("account is suspended"); |
857 |
888 |
break; |
break; |
858 |
889 |
} |
} |
859 |
890 |
|
|
860 |
891 |
if ($ui0['confirmed'] == 0) { |
if ($ui0['confirmed'] == 0) { |
861 |
|
rg_user_set_error("invalid user or pass"); |
|
|
892 |
|
rg_user_set_error("invalid user, pass or login_token"); |
862 |
893 |
rg_log("account is not confirmed"); |
rg_log("account is not confirmed"); |
863 |
894 |
break; |
break; |
864 |
895 |
} |
} |
865 |
896 |
|
|
866 |
897 |
$pass_hash = rg_user_pass($ui0['salt'], $pass); |
$pass_hash = rg_user_pass($ui0['salt'], $pass); |
867 |
898 |
if (strcmp($pass_hash, $ui0['pass']) != 0) { |
if (strcmp($pass_hash, $ui0['pass']) != 0) { |
868 |
|
rg_user_set_error("invalid user or pass"); |
|
|
899 |
|
rg_user_set_error("invalid user, pass or login token"); |
869 |
900 |
rg_log("pass mismatch db:" . $ui0['pass'] . " computed=$pass_hash"); |
rg_log("pass mismatch db:" . $ui0['pass'] . " computed=$pass_hash"); |
870 |
901 |
break; |
break; |
871 |
902 |
} |
} |
872 |
903 |
|
|
|
904 |
|
$lt = rg_totp_list($db, $ui0['uid']); |
|
905 |
|
if ($lt['ok'] != 1) { |
|
906 |
|
rg_user_set_error('login token error: ' . rg_totp_error()); |
|
907 |
|
break; |
|
908 |
|
} |
|
909 |
|
|
|
910 |
|
$used_login_token_id = 0; |
|
911 |
|
if (!empty($lt['list'])) { |
|
912 |
|
$lt_good = FALSE; |
|
913 |
|
foreach ($lt['list'] as $_id => $t) { |
|
914 |
|
$_r = rg_totp_verify($t['secret'], $login_token); |
|
915 |
|
if ($_r === TRUE) { |
|
916 |
|
if (strcmp($t['conf'], 't') != 0) |
|
917 |
|
rg_totp_conf($db, $ui0['uid'], $t['id']); |
|
918 |
|
|
|
919 |
|
$used_login_token_id = $t['id']; |
|
920 |
|
$lt_good = TRUE; |
|
921 |
|
break; |
|
922 |
|
} |
|
923 |
|
|
|
924 |
|
rg_log("DEBUG: token " . $t['id'] . " does not verify"); |
|
925 |
|
} |
|
926 |
|
|
|
927 |
|
if ($lt_good !== TRUE) { |
|
928 |
|
rg_user_set_error('invalid user, pass or login_token'); |
|
929 |
|
rg_log('invalid token'); |
|
930 |
|
break; |
|
931 |
|
} |
|
932 |
|
} |
|
933 |
|
|
|
934 |
|
$event = array( |
|
935 |
|
'category' => 2001, |
|
936 |
|
'prio' => 100, |
|
937 |
|
'uid' => $ui0['uid'], |
|
938 |
|
'used_login_token_id' => $used_login_token_id, |
|
939 |
|
'ts' => time()); |
|
940 |
|
$r = rg_event_add($db, $event); |
|
941 |
|
if ($r !== TRUE) { |
|
942 |
|
rg_user_set_error('internal error; try again later'); |
|
943 |
|
break; |
|
944 |
|
} |
|
945 |
|
rg_event_signal_daemon('', 0); |
|
946 |
|
|
873 |
947 |
$ui = $ui0; |
$ui = $ui0; |
874 |
948 |
rg_user_auto_login($db, $ui['uid'], $lock_ip, $ui); |
rg_user_auto_login($db, $ui['uid'], $lock_ip, $ui); |
875 |
949 |
|
|
876 |
|
rg_user_set_last_seen($db, $ui['uid']); |
|
877 |
|
|
|
878 |
950 |
$ret = TRUE; |
$ret = TRUE; |
879 |
951 |
break; |
break; |
880 |
952 |
} |
} |
|
... |
... |
function rg_user_forgot_pass_mail_prepare($db, $email) |
1136 |
1208 |
while (1) { |
while (1) { |
1137 |
1209 |
$r = rg_user_info($db, 0, "", $email); |
$r = rg_user_info($db, 0, "", $email); |
1138 |
1210 |
if ($r['ok'] == 0) { |
if ($r['ok'] == 0) { |
1139 |
|
rg_log("\tInternal error."); |
|
|
1211 |
|
rg_log("Internal error."); |
1140 |
1212 |
break; |
break; |
1141 |
1213 |
} |
} |
1142 |
1214 |
if ($r['exists'] == 0) { |
if ($r['exists'] == 0) { |
1143 |
|
rg_log("\tUser does not exists."); |
|
|
1215 |
|
rg_log("User does not exists."); |
1144 |
1216 |
$ret['ok'] = 1; |
$ret['ok'] = 1; |
1145 |
1217 |
break; |
break; |
1146 |
1218 |
} |
} |
|
... |
... |
function rg_user_forgot_pass_mail($db, $email) |
1185 |
1257 |
$ret['ok'] = 0; |
$ret['ok'] = 0; |
1186 |
1258 |
$ret['exists'] = 0; |
$ret['exists'] = 0; |
1187 |
1259 |
|
|
1188 |
|
$r = rg_user_forgot_pass_mail_prepare($db, $email); |
|
1189 |
|
if ($r['exists'] != 1) { |
|
1190 |
|
rg_log("\tUser does not exists."); |
|
1191 |
|
return $r; |
|
1192 |
|
} |
|
|
1260 |
|
while (1) { |
|
1261 |
|
$r = rg_user_forgot_pass_mail_prepare($db, $email); |
|
1262 |
|
if ($r['exists'] != 1) { |
|
1263 |
|
rg_log("User does not exists."); |
|
1264 |
|
$ret = $r; |
|
1265 |
|
break; |
|
1266 |
|
} |
1193 |
1267 |
|
|
1194 |
|
$ret['exists'] = 1; |
|
|
1268 |
|
$ret['exists'] = 1; |
1195 |
1269 |
|
|
1196 |
|
$headers = "From: $rg_admin_name <$rg_admin_email>"; |
|
|
1270 |
|
$headers = "From: $rg_admin_name <$rg_admin_email>"; |
1197 |
1271 |
|
|
1198 |
|
$base_url = rg_base_url(); |
|
|
1272 |
|
$base_url = rg_base_url(); |
1199 |
1273 |
|
|
1200 |
|
if (!mail($email, |
|
1201 |
|
"Forgot password", |
|
1202 |
|
"Hello!\n\n" |
|
1203 |
|
. "If you want to reset the password, follow:\n" |
|
1204 |
|
. $base_url |
|
1205 |
|
. rg_re_url("/op/forgot_link") . "/" . $r['token'] |
|
1206 |
|
. "\n\nRocketGit team", |
|
1207 |
|
$headers, |
|
1208 |
|
"-f $rg_admin_email")) { |
|
1209 |
|
rg_user_set_error("cannot send mail ($php_errormsg)!"); |
|
1210 |
|
return $ret; |
|
1211 |
|
} |
|
|
1274 |
|
if (!mail($email, |
|
1275 |
|
"Forgot password", |
|
1276 |
|
"Hello!\n\n" |
|
1277 |
|
. "If you want to reset the password, follow:\n" |
|
1278 |
|
. $base_url |
|
1279 |
|
. rg_re_url("/op/forgot_link") . "/" . $r['token'] |
|
1280 |
|
. "\n\nRocketGit team", |
|
1281 |
|
$headers, |
|
1282 |
|
"-f $rg_admin_email")) { |
|
1283 |
|
rg_user_set_error("cannot send mail ($php_errormsg)!"); |
|
1284 |
|
break; |
|
1285 |
|
} |
1212 |
1286 |
|
|
1213 |
|
$ret['ok'] = 1; |
|
|
1287 |
|
$ret['ok'] = 1; |
|
1288 |
|
break; |
|
1289 |
|
} |
1214 |
1290 |
|
|
1215 |
1291 |
rg_log("DEBUG: user_forgot_pass_mail: ret=" . rg_array2string($ret) . "."); |
rg_log("DEBUG: user_forgot_pass_mail: ret=" . rg_array2string($ret) . "."); |
|
1292 |
|
rg_log_exit(); |
1216 |
1293 |
return $ret; |
return $ret; |
1217 |
1294 |
} |
} |
1218 |
1295 |
|
|
File inc/user/keys/keys.php changed (mode: 100644) (index b20dcf2..eb6ec36) |
1 |
1 |
<?php |
<?php |
2 |
2 |
rg_log("FILE: /inc/user/keys/keys"); |
rg_log("FILE: /inc/user/keys/keys"); |
3 |
3 |
|
|
|
4 |
|
$_keys = ''; |
|
5 |
|
|
4 |
6 |
$add_errmsg = array(); |
$add_errmsg = array(); |
5 |
7 |
$del_errmsg = array(); |
$del_errmsg = array(); |
6 |
8 |
|
|
7 |
|
$_keys = ""; |
|
8 |
|
|
|
9 |
|
$key = rg_var_str("key"); |
|
10 |
|
// TODO: should we accept UTF-8 chars (for comments)? |
|
11 |
|
$key = preg_replace("|[^/A-Za-z0-9 @/+_\.\=,-]|", "", $key); |
|
|
9 |
|
$key = trim(rg_var_str('key')); |
|
10 |
|
$key = str_replace("\n", ' ', $key); |
|
11 |
|
$key = str_replace("\r", ' ', $key); |
12 |
12 |
$key_id = rg_var_uint("key_id"); |
$key_id = rg_var_uint("key_id"); |
13 |
13 |
$key_delete_ids = rg_var_str("key_delete_ids"); |
$key_delete_ids = rg_var_str("key_delete_ids"); |
14 |
14 |
|
|
15 |
|
$rg['HTML:status'] = ""; |
|
|
15 |
|
$rg['HTML:add_status'] = ''; |
|
16 |
|
$rg['HTML:del_status'] = ''; |
|
17 |
|
|
|
18 |
|
while (rg_var_uint("add") == 1) { |
|
19 |
|
if (!rg_valid_referer()) { |
|
20 |
|
$add_errmsg[] = "invalid referer; try again"; |
|
21 |
|
break; |
|
22 |
|
} |
|
23 |
|
|
|
24 |
|
if (!rg_token_valid($db, $rg, 'keys', FALSE)) { |
|
25 |
|
$add_errmsg[] = "Invalid token. Try again."; |
|
26 |
|
break; |
|
27 |
|
} |
|
28 |
|
|
|
29 |
|
$_r = rg_keys_add($db, $rg['login_ui'], $key); |
|
30 |
|
if ($_r === FALSE) { |
|
31 |
|
$add_errmsg[] = rg_keys_error(); |
|
32 |
|
break; |
|
33 |
|
} |
16 |
34 |
|
|
17 |
|
if (rg_var_uint("add") == 1) { |
|
18 |
|
while (1) { |
|
19 |
|
if (!rg_valid_referer()) { |
|
20 |
|
$add_errmsg[] = "invalid referer; try again"; |
|
21 |
|
break; |
|
22 |
|
} |
|
|
35 |
|
$rg['HTML:add_status'] = rg_template("user/keys/add_ok.html", $rg, TRUE /* xss */); |
|
36 |
|
$key = ''; |
|
37 |
|
break; |
|
38 |
|
} |
23 |
39 |
|
|
24 |
|
if (!rg_token_valid($db, $rg, 'keys', FALSE)) { |
|
25 |
|
$add_errmsg[] = "Invalid token. Try again."; |
|
26 |
|
break; |
|
27 |
|
} |
|
|
40 |
|
while (rg_var_uint("delete") == 1) { |
|
41 |
|
if (!rg_valid_referer()) { |
|
42 |
|
$del_errmsg[] = "invalid referer; try again"; |
|
43 |
|
break; |
|
44 |
|
} |
28 |
45 |
|
|
29 |
|
$_r = rg_keys_add($db, $rg['login_ui'], $key); |
|
30 |
|
if ($_r === FALSE) { |
|
31 |
|
$add_errmsg[] = rg_keys_error(); |
|
32 |
|
break; |
|
33 |
|
} |
|
|
46 |
|
if (!rg_token_valid($db, $rg, 'keys', FALSE)) { |
|
47 |
|
$del_errmsg[] = "Invalid token. Try again."; |
|
48 |
|
break; |
|
49 |
|
} |
34 |
50 |
|
|
35 |
|
$key = ''; |
|
|
51 |
|
if (empty($key_delete_ids)) { |
|
52 |
|
$del_errmsg[] = "No key selected."; |
36 |
53 |
break; |
break; |
37 |
54 |
} |
} |
38 |
|
} else if (rg_var_uint("delete") == 1) { |
|
39 |
|
while (1) { |
|
40 |
|
if (!rg_valid_referer()) { |
|
41 |
|
$del_errmsg[] = "invalid referer; try again"; |
|
42 |
|
break; |
|
43 |
|
} |
|
44 |
|
|
|
45 |
|
if (!rg_token_valid($db, $rg, 'keys', FALSE)) { |
|
46 |
|
$del_errmsg[] = "Invalid token. Try again."; |
|
47 |
|
break; |
|
48 |
|
} |
|
49 |
|
|
|
50 |
|
if (empty($key_delete_ids)) { |
|
51 |
|
$del_errmsg[] = "No key selected."; |
|
52 |
|
break; |
|
53 |
|
} |
|
54 |
|
|
|
55 |
|
if (rg_keys_remove($db, $rg['login_ui'], $key_delete_ids) !== TRUE) { |
|
56 |
|
$del_errmsg[] = rg_keys_error(); |
|
57 |
|
break; |
|
58 |
|
} |
|
59 |
|
|
|
60 |
|
$rg['HTML:status'] = rg_template("user/keys/remove_ok.html", $rg, TRUE /* xss */); |
|
|
55 |
|
|
|
56 |
|
if (rg_keys_remove($db, $rg['login_ui'], $key_delete_ids) !== TRUE) { |
|
57 |
|
$del_errmsg[] = rg_keys_error(); |
61 |
58 |
break; |
break; |
62 |
59 |
} |
} |
|
60 |
|
|
|
61 |
|
$rg['HTML:del_status'] = rg_template("user/keys/remove_ok.html", $rg, TRUE /* xss */); |
|
62 |
|
break; |
63 |
63 |
} |
} |
64 |
64 |
|
|
65 |
65 |
$rg['HTML:add_errmsg'] = rg_template_errmsg($add_errmsg); |
$rg['HTML:add_errmsg'] = rg_template_errmsg($add_errmsg); |
|
... |
... |
$rg['HTML:del_errmsg'] = rg_template_errmsg($del_errmsg); |
67 |
67 |
|
|
68 |
68 |
$rg['key'] = $key; |
$rg['key'] = $key; |
69 |
69 |
$rg['rg_form_token'] = rg_token_get($db, $rg, 'keys'); |
$rg['rg_form_token'] = rg_token_get($db, $rg, 'keys'); |
70 |
|
$rg['HTML:add_form'] = rg_template("user/keys/add.html", $rg, TRUE /* xss */); |
|
|
70 |
|
$rg['HTML:add_form'] = rg_template("user/keys/add.html", $rg, TRUE /*xss*/); |
71 |
71 |
|
|
72 |
72 |
$keys_list = rg_keys_list($db, $rg['login_ui']); |
$keys_list = rg_keys_list($db, $rg['login_ui']); |
73 |
73 |
if ($keys_list === FALSE) |
if ($keys_list === FALSE) |
|
... |
... |
if ($rg_ssh_port != 0) |
80 |
80 |
$hints[]['HTML:hint'] = rg_template("hints/ssh/key.html", $rg, TRUE /* xss */); |
$hints[]['HTML:hint'] = rg_template("hints/ssh/key.html", $rg, TRUE /* xss */); |
81 |
81 |
$rg['HTML:hints'] = rg_template_table("hints/list", $hints, $rg); |
$rg['HTML:hints'] = rg_template_table("hints/list", $hints, $rg); |
82 |
82 |
|
|
83 |
|
$_keys = rg_template("user/keys/main.html", $rg, TRUE /* xss */); |
|
|
83 |
|
$_keys .= rg_template('user/keys/main.html', $rg, TRUE /*xss*/); |
84 |
84 |
?> |
?> |
File inc/util.inc.php changed (mode: 100644) (index cd8fde2..1671051) |
... |
... |
function rg_1024($v) |
65 |
65 |
} |
} |
66 |
66 |
|
|
67 |
67 |
/* |
/* |
68 |
|
* Unique ID generator |
|
|
68 |
|
* Returns a binary string of random bytes |
69 |
69 |
*/ |
*/ |
70 |
|
function rg_id($len) |
|
|
70 |
|
function rg_random_bytes($len) |
71 |
71 |
{ |
{ |
72 |
|
rg_prof_start("urandom"); |
|
|
72 |
|
rg_prof_start('random_bytes'); |
73 |
73 |
|
|
74 |
|
$id = ""; |
|
|
74 |
|
$ret = FALSE; |
75 |
75 |
|
|
76 |
|
$f = @fopen("/dev/urandom", "r"); |
|
|
76 |
|
$f = @fopen('/dev/urandom', 'r'); |
77 |
77 |
if ($f !== NULL) { |
if ($f !== NULL) { |
78 |
|
$buf = @fread($f, 128); |
|
79 |
|
if ($buf !== NULL) |
|
80 |
|
$id = sha512($buf); |
|
|
78 |
|
$ret = @fread($f, $len); |
81 |
79 |
fclose($f); |
fclose($f); |
82 |
80 |
} |
} |
83 |
81 |
|
|
84 |
|
if (empty($id)) |
|
85 |
|
$id = sha1(mt_rand() . serialize($_REQUEST)); |
|
|
82 |
|
if ($ret === FALSE) { |
|
83 |
|
$ret = ''; |
|
84 |
|
for ($i = 0; $i < $len; $i++) |
|
85 |
|
$ret .= rand(0, 255); |
|
86 |
|
} |
86 |
87 |
|
|
87 |
|
rg_prof_end("urandom"); |
|
88 |
|
return substr($id, 0, $len); |
|
|
88 |
|
rg_prof_end('random_bytes'); |
|
89 |
|
return $ret; |
|
90 |
|
} |
|
91 |
|
|
|
92 |
|
/* |
|
93 |
|
* Unique ID generator |
|
94 |
|
*/ |
|
95 |
|
function rg_id($len) |
|
96 |
|
{ |
|
97 |
|
rg_prof_start('id'); |
|
98 |
|
|
|
99 |
|
$id = rg_random_bytes(($len + 1) / 2); |
|
100 |
|
$id = bin2hex($id); |
|
101 |
|
$id = substr($id, 0, $len); |
|
102 |
|
|
|
103 |
|
rg_prof_end('id'); |
|
104 |
|
return $id; |
89 |
105 |
} |
} |
90 |
106 |
|
|
91 |
107 |
/* |
/* |
|
... |
... |
function rg_var_bool($name) |
321 |
337 |
return 0; |
return 0; |
322 |
338 |
} |
} |
323 |
339 |
|
|
|
340 |
|
/* |
|
341 |
|
* Allow only @re chars |
|
342 |
|
*/ |
324 |
343 |
function rg_var_re($name, $re) |
function rg_var_re($name, $re) |
325 |
344 |
{ |
{ |
326 |
345 |
$a = rg_var_str($name); |
$a = rg_var_str($name); |
327 |
|
return preg_replace($re, '', $a); |
|
|
346 |
|
return preg_replace('/[^' . $re . ']/', '', $a); |
328 |
347 |
} |
} |
329 |
348 |
|
|
330 |
349 |
/* |
/* |
|
... |
... |
function rg_theme_resolve_path($path) |
405 |
424 |
global $rg_theme, $rg_theme_dir; |
global $rg_theme, $rg_theme_dir; |
406 |
425 |
|
|
407 |
426 |
$url = "/themes/" . $rg_theme . "/" . $path; |
$url = "/themes/" . $rg_theme . "/" . $path; |
408 |
|
$xfile = $rg_theme_dir . "/" . $rg_theme . "/" . $path; |
|
409 |
|
if (!is_file($xfile)) |
|
410 |
|
$url = "/themes/default/" . $path; |
|
|
427 |
|
$xfile = $rg_theme_dir . "/" . $rg_theme . "/" . $path; |
|
428 |
|
if (!is_file($xfile)) |
|
429 |
|
$url = "/themes/default/" . $path; |
411 |
430 |
|
|
412 |
|
return $url; |
|
|
431 |
|
return $url; |
413 |
432 |
} |
} |
414 |
433 |
|
|
415 |
434 |
/* |
/* |
|
... |
... |
function rg_template_string(&$s, $off, &$data, $xss_protection) |
674 |
693 |
{ |
{ |
675 |
694 |
global $rg_template_functions; |
global $rg_template_functions; |
676 |
695 |
|
|
677 |
|
rg_prof_start("rg_template_string"); |
|
|
696 |
|
rg_prof_start('template_string'); |
678 |
697 |
//rg_log_enter("DEBUG: template_string: s+off=[" . substr($s, $off) . "]"); |
//rg_log_enter("DEBUG: template_string: s+off=[" . substr($s, $off) . "]"); |
679 |
698 |
|
|
680 |
699 |
$ret = ''; |
$ret = ''; |
|
... |
... |
function rg_template_string(&$s, $off, &$data, $xss_protection) |
750 |
769 |
|
|
751 |
770 |
//rg_log("DEBUG: ret=[$ret]"); |
//rg_log("DEBUG: ret=[$ret]"); |
752 |
771 |
//rg_log_exit(); |
//rg_log_exit(); |
753 |
|
rg_prof_end("rg_template_string"); |
|
|
772 |
|
rg_prof_end('template_string'); |
754 |
773 |
return $ret; |
return $ret; |
755 |
774 |
} |
} |
756 |
775 |
|
|
|
... |
... |
function rg_template($file, &$data, $xss_protection) |
763 |
782 |
global $rg_theme_dir; |
global $rg_theme_dir; |
764 |
783 |
global $rg_theme; |
global $rg_theme; |
765 |
784 |
|
|
766 |
|
rg_prof_start("rg_template"); |
|
767 |
|
rg_log_enter("rg_template: $file"); |
|
|
785 |
|
rg_prof_start('template'); |
|
786 |
|
rg_log_enter('template: ' . $file); |
768 |
787 |
|
|
769 |
788 |
$ret = ''; |
$ret = ''; |
770 |
789 |
while (1) { |
while (1) { |
|
... |
... |
function rg_template($file, &$data, $xss_protection) |
789 |
808 |
|
|
790 |
809 |
//rg_log("DEBUG: rg_template returns [$ret]"); |
//rg_log("DEBUG: rg_template returns [$ret]"); |
791 |
810 |
rg_log_exit(); |
rg_log_exit(); |
792 |
|
rg_prof_end("rg_template"); |
|
|
811 |
|
rg_prof_end('template'); |
793 |
812 |
return $ret; |
return $ret; |
794 |
813 |
} |
} |
795 |
814 |
|
|
|
... |
... |
function rg_template_table($dir, &$data, $more) |
802 |
821 |
global $rg_theme_dir; |
global $rg_theme_dir; |
803 |
822 |
global $rg_theme; |
global $rg_theme; |
804 |
823 |
|
|
805 |
|
rg_log("rg_template_table: $dir"); |
|
|
824 |
|
rg_prof_start('template_table'); |
|
825 |
|
rg_log('template_table: ' . $dir); |
806 |
826 |
|
|
807 |
827 |
$xdir = $rg_theme_dir . "/" . $rg_theme . "/" . $dir; |
$xdir = $rg_theme_dir . "/" . $rg_theme . "/" . $dir; |
808 |
828 |
if (!is_dir($xdir)) { |
if (!is_dir($xdir)) { |
|
... |
... |
function rg_template_table($dir, &$data, $more) |
811 |
831 |
rg_log("Using [$xdir]"); |
rg_log("Using [$xdir]"); |
812 |
832 |
} |
} |
813 |
833 |
|
|
814 |
|
if (!is_array($data) || empty($data)) |
|
815 |
|
return rg_template($xdir . "/nodata.html", $more, TRUE /* xss */); |
|
|
834 |
|
if (!is_array($data) || empty($data)) { |
|
835 |
|
$ret = rg_template($xdir . "/nodata.html", $more, TRUE /* xss */); |
|
836 |
|
rg_prof_end('template_table'); |
|
837 |
|
return $ret; |
|
838 |
|
} |
816 |
839 |
|
|
817 |
840 |
$head = rg_template($xdir . "/header.html", $more, TRUE /* xss */); |
$head = rg_template($xdir . "/header.html", $more, TRUE /* xss */); |
818 |
841 |
$foot = rg_template($xdir . "/footer.html", $more, TRUE /* xss */); |
$foot = rg_template($xdir . "/footer.html", $more, TRUE /* xss */); |
|
... |
... |
function rg_template_table($dir, &$data, $more) |
832 |
855 |
$body .= rg_template_string($line, 0, $more2, TRUE /* xss */); |
$body .= rg_template_string($line, 0, $more2, TRUE /* xss */); |
833 |
856 |
} |
} |
834 |
857 |
|
|
|
858 |
|
rg_prof_end('template_table'); |
835 |
859 |
return $head . $body . $foot; |
return $head . $body . $foot; |
836 |
860 |
} |
} |
837 |
861 |
|
|
|
... |
... |
function rg_socket_send($socket, $buf) |
1398 |
1422 |
/* |
/* |
1399 |
1423 |
* Connects to a socket, send @buf and returns the answer. |
* Connects to a socket, send @buf and returns the answer. |
1400 |
1424 |
* @timeout: NULL=forever, 0=no_wait |
* @timeout: NULL=forever, 0=no_wait |
1401 |
|
is 0, we do not wait for an answer. If is NULL, we wait forever. |
|
1402 |
1425 |
* @tries - how many time to retry if it fails |
* @tries - how many time to retry if it fails |
1403 |
1426 |
*/ |
*/ |
1404 |
1427 |
function rg_socket($path, $buf, $timeout, $tries, $flags) |
function rg_socket($path, $buf, $timeout, $tries, $flags) |
|
... |
... |
function rg_valid_referer() |
1485 |
1508 |
return FALSE; |
return FALSE; |
1486 |
1509 |
} |
} |
1487 |
1510 |
|
|
|
1511 |
|
/* |
|
1512 |
|
* Returns the age of an object |
|
1513 |
|
*/ |
|
1514 |
|
function rg_age($ts) |
|
1515 |
|
{ |
|
1516 |
|
$diff = time() - $ts; |
|
1517 |
|
|
|
1518 |
|
$ret = ($diff % 60) . 's'; |
|
1519 |
|
$rest = intval($diff / 60); |
|
1520 |
|
if ($rest == 0) |
|
1521 |
|
return $ret; |
|
1522 |
|
|
|
1523 |
|
$ret = ($diff % 60) . 'm' . $ret; |
|
1524 |
|
$rest = intval($diff / 60); |
|
1525 |
|
if ($rest == 0) |
|
1526 |
|
return $ret; |
|
1527 |
|
|
|
1528 |
|
$ret = ($diff % 24) . 'h' . $ret; |
|
1529 |
|
$rest = intval($diff / 24); |
|
1530 |
|
if ($rest == 0) |
|
1531 |
|
return $ret; |
|
1532 |
|
|
|
1533 |
|
$ret = ($diff % 12) . 'm' . $ret; |
|
1534 |
|
$rest = intval($diff / 12); |
|
1535 |
|
if ($rest == 0) |
|
1536 |
|
return $ret; |
|
1537 |
|
|
|
1538 |
|
$ret = $rest . 'y' . $ret; |
|
1539 |
|
|
|
1540 |
|
return $ret; |
|
1541 |
|
} |
|
1542 |
|
|
1488 |
1543 |
?> |
?> |
File root/themes/default/main.css changed (mode: 100644) (index 216038c..6ded205) |
... |
... |
form input[type="radio"] { |
61 |
61 |
} |
} |
62 |
62 |
form select option { padding: 1px 4px 1px 4px; } |
form select option { padding: 1px 4px 1px 4px; } |
63 |
63 |
form input[type="submit"] { |
form input[type="submit"] { |
64 |
|
color: red; |
|
|
64 |
|
color: #F00; |
65 |
65 |
display: inline-block; |
display: inline-block; |
66 |
66 |
font-weight: bold; |
font-weight: bold; |
67 |
67 |
font-size: 11pt; |
font-size: 11pt; |
|
... |
... |
legend { padding: 0px 2pt; } |
80 |
80 |
width: 100%; |
width: 100%; |
81 |
81 |
height: 100%; |
height: 100%; |
82 |
82 |
display: table; |
display: table; |
|
83 |
|
xxx-border-collapse: separate; |
|
84 |
|
xxx-border-spacing: 10px; |
83 |
85 |
} |
} |
84 |
86 |
|
|
85 |
87 |
.logo { |
.logo { |
|
... |
... |
legend { padding: 0px 2pt; } |
94 |
96 |
} |
} |
95 |
97 |
.logo a { |
.logo a { |
96 |
98 |
text-shadow: 0 0 2px yellow; |
text-shadow: 0 0 2px yellow; |
97 |
|
color: red; |
|
|
99 |
|
color: #F00; |
98 |
100 |
font-size: 20pt; |
font-size: 20pt; |
99 |
101 |
font-style: normal; |
font-style: normal; |
100 |
102 |
} |
} |
101 |
103 |
|
|
|
104 |
|
|
102 |
105 |
.main_menu { |
.main_menu { |
103 |
106 |
padding: 6pt; |
padding: 6pt; |
104 |
107 |
vertical-align: middle; |
vertical-align: middle; |
|
... |
... |
legend { padding: 0px 2pt; } |
111 |
114 |
text-shadow: 0 0 2px #000; |
text-shadow: 0 0 2px #000; |
112 |
115 |
padding: 6px; |
padding: 6px; |
113 |
116 |
} |
} |
114 |
|
.main_menu a:hover { color: #f00; } |
|
|
117 |
|
.main_menu a:hover { color: #F00; } |
115 |
118 |
|
|
116 |
119 |
.menus { |
.menus { |
|
120 |
|
display: table-row; |
117 |
121 |
} |
} |
118 |
122 |
|
|
119 |
123 |
.menu { |
.menu { |
|
... |
... |
legend { padding: 0px 2pt; } |
127 |
131 |
} |
} |
128 |
132 |
.menu ul li a { |
.menu ul li a { |
129 |
133 |
color: #FFF; |
color: #FFF; |
130 |
|
font-size: 13pt; |
|
|
134 |
|
font-size: 11pt; |
131 |
135 |
font-weight: bold; |
font-weight: bold; |
132 |
136 |
padding: 2px 15px; |
padding: 2px 15px; |
133 |
137 |
text-shadow: 0 0 2px #000; |
text-shadow: 0 0 2px #000; |
134 |
138 |
} |
} |
135 |
|
.menu ul li a:hover { color: #f00; } |
|
|
139 |
|
.menu ul li a:hover { color: #F00; } |
136 |
140 |
.menu ul li.selected a { border-bottom: 2px solid #f00; } |
.menu ul li.selected a { border-bottom: 2px solid #f00; } |
137 |
|
|
|
138 |
141 |
.menu2 { background-color: #AAA; } |
.menu2 { background-color: #AAA; } |
139 |
|
.menu2 ul li.selected a { border-bottom: 2px solid #f00; } |
|
140 |
|
.menu2 ul li.selected a:hover { color: red; } |
|
141 |
|
|
|
142 |
142 |
.menu3 { background-color: #BBB; } |
.menu3 { background-color: #BBB; } |
143 |
|
.menu3 ul li.selected a { border-bottom: 2px solid #f00; } |
|
144 |
|
.menu3 ul li.selected a:hover { color: red; } |
|
|
143 |
|
|
145 |
144 |
|
|
146 |
145 |
.main_title { |
.main_title { |
147 |
146 |
margin-bottom: 10px; |
margin-bottom: 10px; |
148 |
|
color: red; |
|
|
147 |
|
color: #F00; |
149 |
148 |
font-size: 20pt; |
font-size: 20pt; |
150 |
149 |
font-weight: bold; |
font-weight: bold; |
151 |
150 |
padding-bottom: 5px; |
padding-bottom: 5px; |
152 |
|
border-bottom: 2px solid red; |
|
|
151 |
|
border-bottom: 2px solid #F00; |
153 |
152 |
} |
} |
154 |
153 |
|
|
155 |
154 |
.junk {} |
.junk {} |
|
... |
... |
legend { padding: 0px 2pt; } |
157 |
156 |
.branches_and_tags { padding: 3px 0px; margin: 3px 0px; } |
.branches_and_tags { padding: 3px 0px; margin: 3px 0px; } |
158 |
157 |
.branches_and_tags a { |
.branches_and_tags a { |
159 |
158 |
padding: 3px 3px; |
padding: 3px 3px; |
160 |
|
color: black; |
|
|
159 |
|
color: #000; |
161 |
160 |
border: 1px solid #cccccc; |
border: 1px solid #cccccc; |
162 |
161 |
border-radius: 4px 4px 4px 4px; |
border-radius: 4px 4px 4px 4px; |
163 |
162 |
font-size: 10pt; |
font-size: 10pt; |
|
... |
... |
legend { padding: 0px 2pt; } |
172 |
171 |
} |
} |
173 |
172 |
|
|
174 |
173 |
#main { |
#main { |
|
174 |
|
margin-top: 10px; |
|
175 |
|
margin-left: auto; |
|
176 |
|
margin-right: auto; |
175 |
177 |
padding: 10px 15px; |
padding: 10px 15px; |
176 |
178 |
line-height: 120%; |
line-height: 120%; |
177 |
|
display: table-cell; |
|
178 |
|
} |
|
179 |
|
|
|
180 |
|
#main_fake_table { |
|
181 |
179 |
display: table; |
display: table; |
182 |
180 |
} |
} |
183 |
181 |
|
|
|
... |
... |
legend { padding: 0px 2pt; } |
265 |
263 |
overflow: hidden; |
overflow: hidden; |
266 |
264 |
} |
} |
267 |
265 |
|
|
268 |
|
.error { |
|
269 |
|
font-weight: bold; |
|
270 |
|
color: red; |
|
271 |
|
} |
|
272 |
|
|
|
273 |
266 |
.rg_keys_list { |
.rg_keys_list { |
274 |
267 |
margin-top: 20px; |
margin-top: 20px; |
275 |
268 |
} |
} |
276 |
269 |
|
|
277 |
270 |
.rg_plans_list {} |
.rg_plans_list {} |
278 |
271 |
|
|
|
272 |
|
.rg_totp_list { |
|
273 |
|
margin-top: 20px; |
|
274 |
|
} |
|
275 |
|
|
279 |
276 |
.blob_title { |
.blob_title { |
280 |
277 |
font-size: 11pt; |
font-size: 11pt; |
281 |
|
color: red; |
|
|
278 |
|
color: #F00; |
282 |
279 |
} |
} |
283 |
280 |
|
|
284 |
281 |
.source { |
.source { |
|
... |
... |
legend { padding: 0px 2pt; } |
322 |
319 |
background-color: #eee; |
background-color: #eee; |
323 |
320 |
} |
} |
324 |
321 |
|
|
325 |
|
.submenu { |
|
326 |
|
background-color: #999999; |
|
327 |
|
display: block; |
|
328 |
|
padding-left: 5px; |
|
329 |
|
} |
|
330 |
|
.submenu ul li {} |
|
331 |
|
|
|
332 |
322 |
.repo_container { } |
.repo_container { } |
333 |
323 |
|
|
334 |
324 |
.repo_header { } |
.repo_header { } |
|
... |
... |
legend { padding: 0px 2pt; } |
365 |
355 |
white-space: nowrap; |
white-space: nowrap; |
366 |
356 |
} |
} |
367 |
357 |
|
|
368 |
|
.form_error { margin-top: 6pt; color: red; } |
|
|
358 |
|
.form_error { margin-top: 6pt; color: #F00; } |
369 |
359 |
.form_error b { font-weight: bold; font-size: 11pt; } |
.form_error b { font-weight: bold; font-size: 11pt; } |
370 |
360 |
.form_error p { padding-left: 15pt; } |
.form_error p { padding-left: 15pt; } |
371 |
361 |
|
|
|
... |
... |
legend { padding: 0px 2pt; } |
374 |
364 |
.labels ul li { |
.labels ul li { |
375 |
365 |
display: inline; |
display: inline; |
376 |
366 |
padding: 3px 3px; |
padding: 3px 3px; |
377 |
|
color: black; |
|
|
367 |
|
color: #000; |
378 |
368 |
border: 1px solid #cccccc; |
border: 1px solid #cccccc; |
379 |
369 |
border-radius: 4px 4px 4px 4px; |
border-radius: 4px 4px 4px 4px; |
380 |
370 |
font-size: 10pt; |
font-size: 10pt; |
|
... |
... |
legend { padding: 0px 2pt; } |
386 |
376 |
.searches ul li { |
.searches ul li { |
387 |
377 |
display: inline; |
display: inline; |
388 |
378 |
padding: 3px 3px; |
padding: 3px 3px; |
389 |
|
color: black; |
|
|
379 |
|
color: #000; |
390 |
380 |
border: 1px solid #cccccc; |
border: 1px solid #cccccc; |
391 |
381 |
border-radius: 4px 4px 4px 4px; |
border-radius: 4px 4px 4px 4px; |
392 |
382 |
font-size: 9pt; |
font-size: 9pt; |
|
... |
... |
legend { padding: 0px 2pt; } |
403 |
393 |
font-size: 9pt; |
font-size: 9pt; |
404 |
394 |
box-shadow: 0px 2px 3px #666666; |
box-shadow: 0px 2px 3px #666666; |
405 |
395 |
} |
} |
406 |
|
.hints ul { list-style-type: none; } |
|
|
396 |
|
.hints ul { list-style-type: square; margin-left: 9pt; } |
407 |
397 |
.hints ul li { } |
.hints ul li { } |
408 |
398 |
.hints ul li a { } |
.hints ul li a { } |
409 |
399 |
|
|
|
... |
... |
legend { padding: 0px 2pt; } |
446 |
436 |
margin-top: 3px; |
margin-top: 3px; |
447 |
437 |
} |
} |
448 |
438 |
|
|
449 |
|
.warning { |
|
|
439 |
|
.mess { |
450 |
440 |
margin-top: 5px; |
margin-top: 5px; |
451 |
|
background-color: #CCC; |
|
452 |
441 |
padding: 5px; |
padding: 5px; |
453 |
|
border: 1px solid red; |
|
454 |
|
color: red; |
|
455 |
442 |
display: table; |
display: table; |
456 |
443 |
box-shadow: 0px 2px 3px #666666; |
box-shadow: 0px 2px 3px #666666; |
457 |
444 |
} |
} |
458 |
445 |
|
|
|
446 |
|
.error { |
|
447 |
|
background-color: #F00; |
|
448 |
|
border: 1px solid #000; |
|
449 |
|
color: #000; |
|
450 |
|
} |
|
451 |
|
|
|
452 |
|
.warning { |
|
453 |
|
background-color: #CCC; |
|
454 |
|
border: 1px solid #F00; |
|
455 |
|
color: #F00; |
|
456 |
|
} |
|
457 |
|
|
459 |
458 |
.ok { |
.ok { |
460 |
|
margin-top: 5px; |
|
461 |
|
padding: 5px; |
|
|
459 |
|
background-color: #8F8; |
462 |
460 |
border: 1px solid #FFF; |
border: 1px solid #FFF; |
463 |
461 |
color: #000; |
color: #000; |
464 |
|
display: table; |
|
465 |
|
box-shadow: 0px 2px 3px #666666; |
|
466 |
462 |
} |
} |
467 |
463 |
|
|
468 |
464 |
.page_title { |
.page_title { |
|
... |
... |
legend { padding: 0px 2pt; } |
496 |
492 |
} |
} |
497 |
493 |
|
|
498 |
494 |
.island_title { |
.island_title { |
499 |
|
color: red; |
|
|
495 |
|
color: #F00; |
500 |
496 |
font-size: 14pt; |
font-size: 14pt; |
501 |
497 |
font-weight: bold; |
font-weight: bold; |
502 |
498 |
padding-bottom: 9px; |
padding-bottom: 9px; |
503 |
499 |
} |
} |
|
500 |
|
|
|
501 |
|
.secret_token { |
|
502 |
|
font-size: 14pt; |
|
503 |
|
font-weight: bold; |
|
504 |
|
border: 1px solid #00F; |
|
505 |
|
padding: 5pt; |
|
506 |
|
margin: 5pt 0pt; |
|
507 |
|
} |
|
508 |
|
|
|
509 |
|
.page_description { |
|
510 |
|
margin: 5pt 0pt; |
|
511 |
|
} |