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 8a5b003845f54d2771beaec7b14b96b2029ee9db

Allow login token to be appended to the password
Author: Catalin(ux) M. BOIE
Author date (UTC): 2018-08-29 21:35
Committer name: Catalin(ux) M. BOIE
Committer date (UTC): 2018-08-29 21:35
Parent(s): e8ed0db5fd9b41c5a63e1e171adb615a33817da8
Signing key:
Tree: 82e4836b67dd5df2a439d99ca3761bdb9d4f64fe
File Lines added Lines deleted
inc/user.inc.php 105 46
tests/by_http.php 85 23
File inc/user.inc.php changed (mode: 100644) (index 4be234c..7ed8a5e)
... ... function rg_user_auto_login($db, $uid, $lock_ip, $domain, &$ui)
992 992 /* /*
993 993 * Helper for rg_user_login_by_user_pass for db * Helper for rg_user_login_by_user_pass for db
994 994 */ */
995 function rg_user_login_by_user_pass_db($db, $user, $pass, $login_token,
996 $lock_ip, $domain, &$ui)
995 function rg_user_login_by_user_pass_db($db, $user, $pass, $lock_ip, $domain,
996 &$ui)
997 997 { {
998 998 global $rg_account_email_confirm; global $rg_account_email_confirm;
999 999
1000 1000 rg_prof_start('user_login_by_user_pass_db'); rg_prof_start('user_login_by_user_pass_db');
1001 1001 rg_log_enter('user_login_by_user_pass_db: user=' . $user rg_log_enter('user_login_by_user_pass_db: user=' . $user
1002 . ' login_token=' . $login_token . ' lock_ip=' . $lock_ip
1003 . ' domain=' . $domain);
1002 . ' lock_ip=' . $lock_ip . ' domain=' . $domain);
1004 1003
1005 1004 $ui = rg_user_empty(); $ui = rg_user_empty();
1006 1005
 
... ... function rg_user_login_by_user_pass_db($db, $user, $pass, $login_token,
1041 1040 /* /*
1042 1041 * Authorize a user * Authorize a user
1043 1042 */ */
1044 function rg_user_login_by_user_pass($db, $user, $pass, $login_token,
1043 function rg_user_login_by_user_pass_helper($db, $user, $pass, $login_token,
1045 1044 $lock_ip, $domain, &$ui) $lock_ip, $domain, &$ui)
1046 1045 { {
1047 1046 global $rg_login_functions; global $rg_login_functions;
 
... ... function rg_user_login_by_user_pass($db, $user, $pass, $login_token,
1049 1048 // TODO: what about $ui - for ldap will not have some elements. // TODO: what about $ui - for ldap will not have some elements.
1050 1049 // Should we fake them? // Should we fake them?
1051 1050
1052 rg_prof_start('user_login_by_user_pass');
1053 rg_log_enter('user_login_by_user_pass: user=' . $user
1051 rg_prof_start('user_login_by_user_pass_helper');
1052 rg_log_enter('user_login_by_user_pass_helper: user=' . $user
1054 1053 . ' login_token=' . $login_token . ' lock_ip=' . $lock_ip . ' login_token=' . $login_token . ' lock_ip=' . $lock_ip
1055 1054 . ' domain=' . $domain); . ' domain=' . $domain);
1056 1055
 
... ... function rg_user_login_by_user_pass($db, $user, $pass, $login_token,
1067 1066
1068 1067 while (1) { while (1) {
1069 1068 $r = rg_user_login_by_user_pass_db($db, $user, $pass, $r = rg_user_login_by_user_pass_db($db, $user, $pass,
1070 $login_token, $lock_ip, $domain, $ui);
1069 $lock_ip, $domain, $ui);
1071 1070 if ($r === TRUE) if ($r === TRUE)
1072 1071 break; break;
1073 1072
1074 1073 $ret['errmsg'] = rg_user_error(); $ret['errmsg'] = rg_user_error();
1075 1074
1076 // Try external authentication
1075 // Try external authentication (LDAP etc.)
1077 1076 foreach ($rg_login_functions as $funcs) { foreach ($rg_login_functions as $funcs) {
1078 1077 $r = $funcs['login']($db, $user, $pass, $ui); $r = $funcs['login']($db, $user, $pass, $ui);
1079 1078 if ($r['ok'] === 1) { if ($r['ok'] === 1) {
 
... ... function rg_user_login_by_user_pass($db, $user, $pass, $login_token,
1218 1217 } }
1219 1218 //rg_log_ml('DEBUG: vi: ' . print_r($vi, TRUE)); //rg_log_ml('DEBUG: vi: ' . print_r($vi, TRUE));
1220 1219 if (($vi['enrolled'] == 1) && ($vi['token_valid'] != 1)) { if (($vi['enrolled'] == 1) && ($vi['token_valid'] != 1)) {
1221 $ret['errmsg'] = 'invalid user, pass or login token';
1222 rg_log('invalid token');
1223 break;
1220 // It may be possible that the login_token to be appended
1221 // to the pass.
1222 $_list = array();
1223 $_found = FALSE;
1224 foreach ($_list as $_lt) {
1225 $vi = rg_totp_verify_any($db, $ui['uid'], $_lt);
1226 if ($vi['ok'] == 1) {
1227 $_found = TRUE;
1228 break;
1229 }
1230 }
1231 if (!$_found) {
1232 $ret['errmsg'] = 'invalid user, pass or login token';
1233 rg_log('invalid token');
1234 break;
1235 }
1224 1236 } }
1225 1237
1226 1238 $event = array( $event = array(
 
... ... function rg_user_login_by_user_pass($db, $user, $pass, $login_token,
1241 1253 break; break;
1242 1254 } }
1243 1255
1256 rg_log_exit();
1257 rg_prof_end('user_login_by_user_pass_helper');
1258 return $ret;
1259 }
1260
1261 function rg_user_login_by_user_pass($db, $user, $pass, $login_token,
1262 $lock_ip, $domain, &$ui)
1263 {
1264 rg_prof_start('user_login_by_user_pass');
1265 rg_log_enter('rg_user_login_by_user_pass');
1266
1267 while (1) {
1268 $ret = rg_user_login_by_user_pass_helper($db, $user, $pass,
1269 $login_token, $lock_ip, $domain, $ui);
1270 if ($ret['ok'] == 1)
1271 break;
1272
1273 if (!empty($login_token))
1274 break;
1275
1276 // Password may contain a login_token
1277 $lt = substr($pass, -6);
1278 $pass2 = substr($pass, 0, -6);
1279 $ret = rg_user_login_by_user_pass_helper($db, $user, $pass2,
1280 $lt, $lock_ip, $domain, $ui);
1281 if ($ret['ok'] == 1)
1282 break;
1283
1284 // Password may contain a scratch code
1285 $lt = substr($pass, -8);
1286 $pass2 = substr($pass, 0, -8);
1287 $ret = rg_user_login_by_user_pass_helper($db, $user, $pass2,
1288 $lt, $lock_ip, $domain, $ui);
1289 break;
1290 }
1291
1244 1292 rg_log_exit(); rg_log_exit();
1245 1293 rg_prof_end('user_login_by_user_pass'); rg_prof_end('user_login_by_user_pass');
1246 1294 return $ret; return $ret;
 
... ... function rg_user_http_git($db, $rg, $paras)
2313 2361 } }
2314 2362 $repo_path = $r['repo_path']; $repo_path = $r['repo_path'];
2315 2363
2316 if ($r['allow'] !== 1) {
2317 rg_log('DEBUG: allow != 1 => 401');
2318 // Connecting user has no rights to push, not even anon.
2319 // The user may be authed at this point, but may try another
2320 // user/pass combination.
2364 // push_allowed is only about the rights, not about auth/login_tokens
2365 rg_log('DEBUG: push=' . $r['push']
2366 . ' push_allowed=' . $r['push_allowed']
2367 . ' no_user_provided=' . ($no_user_provided ? 'yes' : 'no')
2368 . ' authd=' . ($authd['ok'] === 1 ? 'yes' : 'no')
2369 . ' exists=' . $auth_ui['exists']);
2370 $send_401 = FALSE;
2371 while (1) {
2372 if ($r['allow'] !== 1) {
2373 rg_log('DEBUG: allow != 1 => 401');
2374 // Connecting user has no rights to push, not even anon.
2375 // The user may be authed at this point, but may try another
2376 // user/pass combination.
2377 $send_401 = TRUE;
2378 break;
2379 }
2380
2381 if ($r['push'] !== 1) {
2382 rg_log('DEBUG: it is a fetch');
2383 break;
2384 }
2385
2386 if ($no_user_provided) {
2387 rg_log('DEBUG: no user provided');
2388 $send_401 = TRUE;
2389 break;
2390 }
2391
2392 if (($authd['ok'] !== 1) && ($auth_ui['exists'] == 1)) {
2393 rg_log('DEBUG: user exists but not authenticated');
2394 $send_401 = TRUE;
2395 break;
2396 }
2397
2398 break;
2399 }
2400 if ($send_401) {
2401 rg_log('DEBUG: sending 401');
2321 2402 header($protocol . ' 401 Unauthorized status'); header($protocol . ' 401 Unauthorized status');
2322 2403 header('WWW-Authenticate: Basic' header('WWW-Authenticate: Basic'
2323 . ' realm="Use \'guest\' user if you have no account"');
2404 . ' realm="Use user \'guest\' if you have no account"');
2324 2405 echo 'RocketGit: Info: == Welcome to RocketGit! ==' . "\n"; echo 'RocketGit: Info: == Welcome to RocketGit! ==' . "\n";
2325 2406 echo 'RocketGit: Info: you are connecting from IP ' . $rg['ip'] . '.' . "\n"; echo 'RocketGit: Info: you are connecting from IP ' . $rg['ip'] . '.' . "\n";
2326 echo 'RocketGit: Error: ' . $r['errmsg'] . '!';
2407 echo 'RocketGit: Info: debug id ' . $rg_log_sid . '.' . "\n";
2408 echo 'RocketGit: Info: Use user \'guest\' with any'
2409 . ' password if you want to push anonymously.' . "\n";
2410 echo 'RocketGit: Info: Append the login token or the'
2411 . ' scratch code to the password if you are'
2412 . ' enrolled into 2fa.' . "\n";
2327 2413 break; break;
2328 2414 } }
2329 2415
2330 rg_log('DEBUG: push_allowed=' . $r['push_allowed']
2331 . ' no_user_provided=' . ($no_user_provided ? 'yes' : 'no')
2332 . ' authd=' . ($authd['ok'] === 1 ? 'yes' : 'no')
2333 . ' exists=' . $auth_ui['exists']
2334 . ' push=' . $r['push']);
2335 if (($r['push'] === 1) && ($r['push_allowed'] !== 1)) {
2336 // We have only anon push rights at this point.
2337 // If user is correct, but password is not, we will ask
2338 // the user to try again. If user is not correct,
2339 // we will go on with anon push access.
2340 if ($no_user_provided
2341 || (($authd['ok'] !== 1) && ($auth_ui['exists'] == 1))) {
2342 rg_log('DEBUG: send 401');
2343 header($protocol . ' 401 Unauthorized status');
2344 header('WWW-Authenticate: Basic'
2345 . ' realm="Use \'guest\' user if you have no account"');
2346 echo 'RocketGit: Info: == Welcome to RocketGit! ==' . "\n";
2347 echo 'RocketGit: Info: you are connecting from IP ' . $rg['ip'] . '.' . "\n";
2348 echo 'RocketGit: Info: Use user \'guest\' with any password if you want to push anonymously.' . "\n";
2349 break;
2350 }
2351 } else if ($r['push'] === 1) {
2352 rg_log('DEBUG: user has full push rights');
2353 } else {
2354 rg_log('DEBUG: it is a fetch');
2355 }
2356
2357 2416 $content_length = isset($_SERVER['CONTENT_LENGTH']) ? $content_length = isset($_SERVER['CONTENT_LENGTH']) ?
2358 2417 intval($_SERVER['CONTENT_LENGTH']) : 0; intval($_SERVER['CONTENT_LENGTH']) : 0;
2359 2418 $content_encoding = isset($_SERVER['HTTP_CONTENT_ENCODING']) ? $content_encoding = isset($_SERVER['HTTP_CONTENT_ENCODING']) ?
File tests/by_http.php changed (mode: 100644) (index 07a90c9..7352745)
2 2 error_reporting(E_ALL | E_STRICT); error_reporting(E_ALL | E_STRICT);
3 3 ini_set("track_errors", "On"); ini_set("track_errors", "On");
4 4
5 $rg_cache_debug = FALSE;
6 $rg_util_debug = FALSE;
7 $rg_sql_debug = 100;
8
5 9 $INC = dirname(__FILE__) . "/../inc"; $INC = dirname(__FILE__) . "/../inc";
6 10 require_once(dirname(__FILE__) . "/config.php"); require_once(dirname(__FILE__) . "/config.php");
7 11 require_once($INC . "/init.inc.php"); require_once($INC . "/init.inc.php");
 
... ... $a = rg_exec('rm -rf .by_http'
43 47 . ' && mkdir .by_http' . ' && mkdir .by_http'
44 48 . ' && cd .by_http' . ' && cd .by_http'
45 49 . ' && git init' . ' && git init'
46 . ' && git remote add origin ' . escapeshellarg($repo['clone_url_http'])
47 . ' && git remote add origin2 ' . escapeshellarg($repo2['clone_url_http'])
48 . ' && git remote add origin2_git ' . escapeshellarg($repo2['clone_url_git'])
50 . ' && git remote add public_repo ' . escapeshellarg($repo['clone_url_http'])
51 . ' && git remote add private_repo ' . escapeshellarg($repo2['clone_url_http'])
52 . ' && git remote add private_repo_git ' . escapeshellarg($repo2['clone_url_git'])
49 53 . ' && echo "a signature" > a' . ' && echo "a signature" > a'
50 54 . ' && git add a' . ' && git add a'
51 55 . ' && git commit -a -m "' . $commit_body . '"', '', FALSE, FALSE); . ' && git commit -a -m "' . $commit_body . '"', '', FALSE, FALSE);
 
... ... if ($a['ok'] != 1) {
56 60
57 61
58 62 rg_log(''); rg_log('');
59 rg_log_enter('Trying to push master (without user/pass)...');
63 rg_log_enter('Trying to push to public (without user/pass)...');
60 64 putenv('GIT_ASKPASS=' . dirname(__FILE__) . '/ask_pass_guest'); putenv('GIT_ASKPASS=' . dirname(__FILE__) . '/ask_pass_guest');
61 $r = rg_exec('cd .by_http && git push origin master', '', FALSE, FALSE);
65 $r = rg_exec('cd .by_http && git push public_repo master', '', FALSE, FALSE);
62 66 if ($r['ok'] != 1) { if ($r['ok'] != 1) {
63 67 rg_log_ml('out: ' . print_r($r, TRUE)); rg_log_ml('out: ' . print_r($r, TRUE));
64 rg_log('Seems I cannot push master without authentication (anonymous push)!');
68 rg_log('Seems I cannot push without authentication (anonymous push)!');
65 69 exit(1); exit(1);
66 70 } }
67 71 if (!strstr($r['stderr'], 'transformed into a pull request')) { if (!strstr($r['stderr'], 'transformed into a pull request')) {
 
... ... rg_log_exit();
73 77
74 78
75 79 rg_log(''); rg_log('');
76 rg_log_enter('Trying to push master (with user/pass)...');
80 rg_log_enter('Trying to push to public (with user/pass)...');
77 81 putenv('GIT_ASKPASS=' . dirname(__FILE__) . '/ask_pass'); putenv('GIT_ASKPASS=' . dirname(__FILE__) . '/ask_pass');
78 $r = rg_exec('cd .by_http && strace -s2000 -f -tt -o x.strace git push --verbose origin master', '', FALSE, FALSE);
82 $r = rg_exec('cd .by_http && strace -s2000 -f -tt -o by_http.strace git push --verbose public_repo master', '', FALSE, FALSE);
79 83 if ($r['ok'] != 1) { if ($r['ok'] != 1) {
80 84 rg_log_ml('out: ' . print_r($r, TRUE)); rg_log_ml('out: ' . print_r($r, TRUE));
81 rg_log('Seems I cannot push master with authentication!');
85 rg_log('Seems I cannot push with authentication!');
82 86 exit(1); exit(1);
83 87 } }
84 88 rg_log_exit(); rg_log_exit();
85 89
86 90
87 91 rg_log(''); rg_log('');
88 rg_log_enter('Trying to fetch master (with user/pass)...');
92 rg_log_enter('Trying to fetch from public (with user/pass)...');
89 93 putenv('GIT_ASKPASS=' . dirname(__FILE__) . '/ask_pass'); putenv('GIT_ASKPASS=' . dirname(__FILE__) . '/ask_pass');
90 $r = rg_exec('cd .by_http && strace -s2000 -f -tt -o x.strace git fetch --verbose origin master', '', FALSE, FALSE);
94 $r = rg_exec('cd .by_http && strace -s2000 -f -tt -o by_http.strace git fetch --verbose public_repo master', '', FALSE, FALSE);
91 95 if ($r['ok'] != 1) { if ($r['ok'] != 1) {
92 96 rg_log_ml('out: ' . print_r($r, TRUE)); rg_log_ml('out: ' . print_r($r, TRUE));
93 rg_log('Seems I cannot fetch master with authentication!');
97 rg_log('Seems I cannot fetch with authentication!');
94 98 exit(1); exit(1);
95 99 } }
96 100 rg_log_exit(); rg_log_exit();
97 101
98 102
99 103 rg_log(''); rg_log('');
100 rg_log_enter('Trying to fetch master (private repo; by git://)...');
104 rg_log_enter('Trying to fetch from private (private repo; by git://)...');
101 105 $r = rg_exec('cd .by_http && strace -s2000 -f -tt -o fetch_by_git.strace' $r = rg_exec('cd .by_http && strace -s2000 -f -tt -o fetch_by_git.strace'
102 . ' git fetch --verbose origin2_git master', '', FALSE, FALSE);
106 . ' git fetch --verbose private_repo_git master', '', FALSE, FALSE);
103 107 if ($r['ok'] == 1) { if ($r['ok'] == 1) {
104 108 rg_log_ml('out: ' . print_r($r, TRUE)); rg_log_ml('out: ' . print_r($r, TRUE));
105 109 rg_log('Seems I can fetch by git:// from a private repo!'); rg_log('Seems I can fetch by git:// from a private repo!');
 
... ... rg_log_exit();
109 113
110 114
111 115 rg_log(''); rg_log('');
112 rg_log_enter('Trying to push master (without user/pass; private repo)...');
116 rg_log_enter('Trying to push to private (without user/pass; private repo)...');
113 117 putenv('GIT_ASKPASS=' . dirname(__FILE__) . '/ask_pass_guest'); putenv('GIT_ASKPASS=' . dirname(__FILE__) . '/ask_pass_guest');
114 118 $r = rg_exec('cd .by_http && strace -s2000 -f -tt -o push_no_user.strace' $r = rg_exec('cd .by_http && strace -s2000 -f -tt -o push_no_user.strace'
115 . ' git push --verbose origin2 master', '', FALSE, FALSE);
119 . ' git push --verbose private_repo master', '', FALSE, FALSE);
116 120 if ($r['ok'] == 1) { if ($r['ok'] == 1) {
117 121 rg_log_ml('out: ' . print_r($r, TRUE)); rg_log_ml('out: ' . print_r($r, TRUE));
118 rg_log('Seems I can push master without authentication (anonymous push)!');
122 rg_log('Seems I can push without authentication (anonymous push)!');
119 123 exit(1); exit(1);
120 124 } }
121 125 if (!strstr($r['stderr'], 'Authentication failed')) { if (!strstr($r['stderr'], 'Authentication failed')) {
 
... ... rg_log_exit();
127 131
128 132
129 133 rg_log(''); rg_log('');
130 rg_log_enter('Trying to push master (with user/pass; private repo)...');
134 rg_log_enter('Trying to push to private (with user/pass; private repo)...');
131 135 putenv('GIT_ASKPASS=' . dirname(__FILE__) . '/ask_pass'); putenv('GIT_ASKPASS=' . dirname(__FILE__) . '/ask_pass');
132 136 $r = rg_exec('cd .by_http && strace -s2000 -f -tt -o push_with_user.strace' $r = rg_exec('cd .by_http && strace -s2000 -f -tt -o push_with_user.strace'
133 . ' git push --verbose origin2 master', '', FALSE, FALSE);
137 . ' git push --verbose private_repo master', '', FALSE, FALSE);
138 if ($r['ok'] != 1) {
139 rg_log_ml('out: ' . print_r($r, TRUE));
140 rg_log('Seems I cannot push with authentication!');
141 exit(1);
142 }
143 rg_log_exit();
144
145
146 rg_log('');
147 rg_log_enter('Trying to fetch from private (with user/pass; private repo)...');
148 putenv('GIT_ASKPASS=' . dirname(__FILE__) . '/ask_pass');
149 $r = rg_exec('cd .by_http && strace -s2000 -f -tt -o x2.strace git fetch --verbose private_repo', '', FALSE, FALSE);
150 if ($r['ok'] != 1) {
151 rg_log_ml('out: ' . print_r($r, TRUE));
152 rg_log('Seems I cannot fetch with authentication!');
153 exit(1);
154 }
155 rg_log_exit();
156
157
158 $r = totp_enroll($db);
159 if ($r['ok'] !== 1)
160 exit(1);
161 $key = $r['key'];
162 rg_log('key: ' . $key);
163
164
165 rg_log('');
166 rg_log_enter('Trying to push to public (with user/pass/login_token_missing)...');
167 putenv('GIT_ASKPASS=' . dirname(__FILE__) . '/ask_pass');
168 $r = rg_exec('cd .by_http && strace -s2000 -f -tt -o by_http.strace git push --verbose public_repo master', '', FALSE, FALSE);
169 if ($r['ok'] !== 0) {
170 rg_log_ml('out: ' . print_r($r, TRUE));
171 rg_log('Seems I can push without passing login_token!');
172 exit(1);
173 }
174 rg_log_exit();
175
176
177 rg_log('');
178 rg_log_enter('Trying to fetch from public (with user/pass/login_token_missing)...');
179 putenv('GIT_ASKPASS=' . dirname(__FILE__) . '/ask_pass');
180 $r = rg_exec('cd .by_http && strace -s2000 -f -tt -o by_http.strace git fetch --verbose public_repo master', '', FALSE, FALSE);
181 if ($r['ok'] !== 1) {
182 rg_log_ml('out: ' . print_r($r, TRUE));
183 rg_log('Seems I cannot fetch without passing login_token!');
184 exit(1);
185 }
186 rg_log_exit();
187
188
189 rg_log('');
190 rg_log_enter('Trying to push to public (with user/pass/login_token_ok)...');
191 putenv('RG_LOGIN_TOKEN=' . rg_totp_compute($key, (time() - 30) / 30, 6));
192 putenv('GIT_ASKPASS=' . dirname(__FILE__) . '/ask_pass');
193 $r = rg_exec('cd .by_http && strace -s2000 -f -tt -o by_http.strace git push --verbose public_repo master', '', FALSE, FALSE);
134 194 if ($r['ok'] != 1) { if ($r['ok'] != 1) {
135 195 rg_log_ml('out: ' . print_r($r, TRUE)); rg_log_ml('out: ' . print_r($r, TRUE));
136 rg_log('Seems I cannot push master with authentication!');
196 rg_log('Seems I cannot push with authentication!');
137 197 exit(1); exit(1);
138 198 } }
139 199 rg_log_exit(); rg_log_exit();
140 200
141 201
142 202 rg_log(''); rg_log('');
143 rg_log_enter('Trying to fetch master (with user/pass; private repo)...');
203 rg_log_enter('Trying to fetch from public (with user/pass/login_token_ok)...');
204 putenv('RG_LOGIN_TOKEN=' . rg_totp_compute($key, (time() - 0) / 30, 6));
144 205 putenv('GIT_ASKPASS=' . dirname(__FILE__) . '/ask_pass'); putenv('GIT_ASKPASS=' . dirname(__FILE__) . '/ask_pass');
145 $r = rg_exec('cd .by_http && strace -s2000 -f -tt -o x2.strace git fetch --verbose origin2', '', FALSE, FALSE);
206 $r = rg_exec('cd .by_http && strace -s2000 -f -tt -o by_http.strace git fetch --verbose public_repo master', '', FALSE, FALSE);
146 207 if ($r['ok'] != 1) { if ($r['ok'] != 1) {
147 208 rg_log_ml('out: ' . print_r($r, TRUE)); rg_log_ml('out: ' . print_r($r, TRUE));
148 rg_log('Seems I cannot fetch master with authentication!');
209 rg_log('Seems I cannot fetch with authentication!');
149 210 exit(1); exit(1);
150 211 } }
212 putenv('RG_LOGIN_TOKEN=');
151 213 rg_log_exit(); rg_log_exit();
152 214
153 215
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