From 8f15f79d894698808dfa0b2e80f7d233becb884b Mon Sep 17 00:00:00 2001
From: "Ryan C. Gordon" <[EMAIL REDACTED]>
Date: Thu, 6 Mar 2025 03:23:41 +0000
Subject: [PATCH] Added nda-inviter
---
nda-inviter/.htaccess | 8 +
nda-inviter/index.php | 333 ++++++++++++++++++++++++++++++++++++++++++
2 files changed, 341 insertions(+)
create mode 100644 nda-inviter/.htaccess
create mode 100755 nda-inviter/index.php
diff --git a/nda-inviter/.htaccess b/nda-inviter/.htaccess
new file mode 100644
index 0000000..4ad7f28
--- /dev/null
+++ b/nda-inviter/.htaccess
@@ -0,0 +1,8 @@
+<Files .htaccess>
+ deny from all
+</Files>
+
+<Files config.php>
+ deny from all
+</Files>
+
diff --git a/nda-inviter/index.php b/nda-inviter/index.php
new file mode 100755
index 0000000..9ebcc9f
--- /dev/null
+++ b/nda-inviter/index.php
@@ -0,0 +1,333 @@
+<?php
+
+// this is the script that invites people's GitHub accounts to NDA'd forks.
+include('config.php');
+
+//config.php should have this:
+//$title = 'SDL NDA Inviter';
+//$user_agent = 'sdl-nda-inviter.php/0.1';
+//$base_url = 'https://libsdl.org/nda-inviter/';
+//$github_oauth_clientid = 'sdfsdfkjsdfksdjf';
+//$github_oauth_secret = 'sdfksdjfksdjfksdjfk';
+//$github_invite_token = 'ghp_ksdjfksdjfksdjf';
+//$logins = array('user1' => 'password1', 'user2' => 'password2', etc);
+//$repos = array('user1' => array('libsdl-org/SDL-platform', 'libsdl-org/SDL3-platform', etc));
+
+// most of this support code is from ghwikipp.
+function fail($response, $msg, $url=NULL)
+{
+ global $title;
+ if ($response != NULL) {
+ header("HTTP/1.0 $response");
+ header("Content-Type: text/html; charset=utf-8");
+ if ($url != NULL) { header("Location: $url"); }
+ }
+
+ print("\n<html><head><title>$title</title></head><body>\n<p><h1>$response</h1></p>\n<p>$msg</p>\n</body></html>\n\n");
+ exit(0);
+}
+
+function fail400($msg) { fail('400 Bad Request', $msg); }
+function fail404($msg) { fail('404 Not Found', $msg); }
+function fail503($msg) { fail('503 Service Unavailable', $msg); }
+
+function redirect($url, $msg=NULL)
+{
+ if ($msg == NULL) {
+ $msg = "We need your browser to go here now: <a href='$url'>$url</a>";
+ }
+ fail('302 Found', $msg, $url);
+}
+
+function require_session()
+{
+ if (!session_id()) {
+ if (!session_start()) {
+ fail503('Session failed to start, please try again later.');
+ }
+ }
+}
+
+function handle_oauth_error()
+{
+ if (isset($_REQUEST['error'])) { // this is probably a failure from GitHub's OAuth page.
+ require_session();
+ if (isset($_SESSION['github_oauth_state'])) { unset($_SESSION['github_oauth_state']); }
+ $reason = isset($_REQUEST['error_description']) ? $_REQUEST['error_description'] : 'unknown error';
+ fail400("GitHub auth error, try again later?<ul><li>error: " . $_REQUEST['error'] . "</li>\n<li>reason: $reason</li></ul>\n");
+ }
+}
+
+function call_github_api($url, $args=NULL, $token=NULL, $method='GET', $failonerror=true)
+{
+//print("CALL_GITHUB_API url='$url'\n");
+
+ global $user_agent;
+ $isget = ($method == 'GET');
+
+ if ($isget && ($args != NULL) && (!empty($args)) ) {
+ $url .= '?' . http_build_query($args);
+ }
+
+ $reqheaders = array(
+ "User-Agent: $user_agent",
+ 'Accept: application/json'
+ );
+
+ if ($token != NULL) {
+ $reqheaders[] = 'Authorization: token ' . $token;
+ }
+
+ if (!$isget) {
+ $reqheaders[] = 'Content-Type: application/json; charset=utf-8';
+ }
+
+ $curl = curl_init($url);
+ $curlopts = array(
+ CURLOPT_AUTOREFERER => TRUE,
+ CURLOPT_FOLLOWLOCATION => TRUE,
+ CURLOPT_RETURNTRANSFER => TRUE,
+ CURLOPT_HTTPHEADER => $reqheaders
+ );
+
+ if ($method == 'POST') {
+ $curlopts[CURLOPT_POST] = TRUE;
+ $curlopts[CURLOPT_POSTFIELDS] = json_encode(($args == NULL) ? array() : $args);
+ } else if ($method == 'PUT') {
+ $curlopts[CURLOPT_CUSTOMREQUEST] = 'PUT';
+ $curlopts[CURLOPT_POSTFIELDS] = json_encode(($args == NULL) ? array() : $args);
+ }
+
+ //print("CURLOPTS:\n"); print_r($curlopts);
+ //print("REQHEADERS:\n"); print_r($reqheaders);
+
+ curl_setopt_array($curl, $curlopts);
+ $responsejson = curl_exec($curl);
+ $curlfailed = ($responsejson === false);
+ $curlerr = $curlfailed ? curl_error($curl) : NULL;
+ $httprc = $curlfailed ? 0 : curl_getinfo($curl, CURLINFO_RESPONSE_CODE);
+ curl_close($curl);
+ unset($curl);
+
+ //print("RESPONSE:\n"); print_r(json_decode($responsejson, TRUE));
+
+ if ($curlfailed) {
+ fail503("Couldn't talk to GitHub API, try again later ($curlerr)");
+ } else if ($httprc == 204) {
+ return array( 'httprc' => 204 );
+ } else if (($httprc != 200) && ($httprc != 201)) {
+ if (!$failonerror) {
+ return NULL;
+ }
+ fail503("GitHub API reported error $httprc, try again later:<br/>\n<br/>\n" . print_r($responsejson, true) . "\n\n");
+ }
+
+ $retval = json_decode($responsejson, TRUE);
+ if ($retval == NULL) {
+ fail503('GitHub API sent us bogus JSON, try again later.');
+ }
+
+ $retval['httprc'] = $httprc;
+ return $retval;
+}
+
+
+function authorize_with_github($force=false)
+{
+ global $github_oauth_clientid;
+ global $github_oauth_secret;
+ global $blocked_data, $trusted_data, $admin_data, $always_admin, $base_url;
+
+ require_session();
+
+ handle_oauth_error(); // die now if we think GitHub refused to auth user.
+
+ //print_r($_SESSION);
+
+ // $force==true means we're being asked to make sure GitHub is still cool
+ // with this session, which we do before requests that make changes, like
+ // committing an edit. If the user auth'd with GitHub but then logged
+ // out over there since our last check, we might be letting the user do
+ // things they shouldn't be allowed to do anymore. For the basic workflow
+ // we don't care if they are still _actually_ logged in at every step,
+ // though.
+
+ if ( !$force &&
+ isset($_SESSION['github_user']) &&
+ isset($_SESSION['github_access_token']) &&
+ isset($_SESSION['last_auth_time']) &&
+ isset($_SESSION['expected_ipaddr']) &&
+ ( (time() - $_SESSION['last_auth_time']) < (60 * 60) ) &&
+ ($_SESSION['expected_ipaddr'] == $_SERVER['REMOTE_ADDR']) ) {
+ //print("ALREADY LOGGED IN\n");
+ return; // we're already logged in.
+
+ // if there's a "code" arg, this is probably a redirect back from GitHub's OAuth page.
+ // make sure this isn't the user just reloading the page before using it again, as that will fail; we'll reauth to get a new code in that case.
+ } else if ( isset($_REQUEST['code']) && (!isset($_SESSION['github_last_used_oauth_code']) || ($_SESSION['github_last_used_oauth_code'] != $_REQUEST['code'])) ) {
+ if ( !isset($_REQUEST['state']) || !isset($_SESSION['github_oauth_state']) ) {
+ fail400('GitHub authorization appears to be confused, please try again.');
+ } else if ($_SESSION['github_oauth_state'] != $_REQUEST['state']) {
+ fail400('This appears to be a bogus attempt to authorize with GitHub. If in error, please try again.');
+ }
+
+ $_SESSION['github_last_used_oauth_code'] = $_REQUEST['code'];
+
+ $tokenurl = 'https://github.com/login/oauth/access_token';
+ $response = call_github_api($tokenurl, [
+ 'client_id' => $github_oauth_clientid,
+ 'client_secret' => $github_oauth_secret,
+ 'state' => $_REQUEST['state'],
+ 'code' => $_REQUEST['code']
+ ]);
+
+ //print("\n<pre>\n"); print_r($response); print("\n</pre>\n\n");
+ if (!isset($response['access_token'])) {
+ fail503("GitHub OAuth didn't provide an access token! Please try again later.");
+ }
+
+ $token = $response['access_token'];
+ $_SESSION['github_access_token'] = $token;
+ }
+
+ // If we already have an access token (or just got one, above), see if
+ // it's still valid. If it isn't, force a full reauth. If it still
+ // works, GitHub still thinks we're cool.
+ if (isset($_SESSION['github_access_token']))
+ {
+ $response = call_github_api('https://api.github.com/user', NULL, $_SESSION['github_access_token'], 'GET', false);
+
+ if ($response != NULL) {
+ //print("\n<pre>\nGITHUB USER API RESPONSE:\n"); print_r($response); print("\n</pre>\n\n");
+
+ if ( !isset($response['login']) ) {
+ unset($_SESSION['github_access_token']);
+ fail503("GitHub didn't tell us everything we need to know about you to give you access. Please try again later.");
+ }
+
+ $_SESSION['expected_ipaddr'] = $_SERVER['REMOTE_ADDR'];
+ $_SESSION['last_auth_time'] = time();
+ $_SESSION['github_user'] = $response['login'];
+
+ //print("SESSION:\n"); print_r($_SESSION);
+
+ //print("AUTH TO GITHUB VALID AND READY\n");
+ // logged in, verified, and good to go!
+
+ if (isset($_REQUEST['code'])) { //Force a redirect to dump the code and state URL args.
+ redirect($base_url, 'Reloading to finish login!');
+ }
+
+ return; // logged in and ready to go as-is.
+ }
+
+ // still here? We'll try reauthing, below.
+ }
+
+
+ // we're starting authorization from scratch.
+
+ // kill any lingering state.
+ unset($_SESSION['github_user']);
+ unset($_SESSION['github_access_token']);
+ unset($_SESSION['expected_ipaddr']);
+ unset($_SESSION['last_auth_time']);
+
+ // !!! FIXME: no idea if this is a good idea.
+ $_SESSION['github_oauth_state'] = hash('sha256', microtime(TRUE) . rand() . $_SERVER['REMOTE_ADDR']);
+
+ $github_authorize_url = 'https://github.com/login/oauth/authorize';
+ redirect($github_authorize_url . '?' . http_build_query([
+ 'client_id' => $github_oauth_clientid,
+ 'redirect_uri' => $base_url,
+ 'state' => $_SESSION['github_oauth_state'],
+ //'scope' => 'user'
+ ]), 'Sending you to GitHub to log in...');
+
+ // if everything goes okay, GitHub will redirect the user back to our
+ // same URL and we can try again, this time with authorization.
+}
+
+function force_authorize_with_github()
+{
+ authorize_with_github(true);
+}
+
+
+// mainline!
+
+// always force to SSL.
+if (!isset($_SERVER['HTTPS'])) {
+ redirect('https://' . $_SERVER['SERVER_NAME'] . $_SERVER['REQUEST_URI']);
+}
+
+require_session();
+
+// Incorrect or no login? Spit out a prompt and quit.
+$user = isset($_REQUEST['form_user']) ? $_REQUEST['form_user'] : ($_SESSION['username'] ? $_SESSION['username'] : '');
+$pass = isset($_REQUEST['form_pass']) ? $_REQUEST['form_pass'] : ($_SESSION['password'] ? $_SESSION['password'] : '');
+
+if (!isset($logins[$user]) || ($pass != $logins[$user])) {
+ echo <<<EOF
+ <html><head><title>$title</title></head><body>
+ <center>
+ <p><h1>SDL NDA Inviter</h1></p>
+ <p>
+ <form name="loginform" method="post">
+ username: <input type="text" name="form_user" value="$user" /><br/>
+ password: <input type="password" name="form_pass" value="" /><br/>
+ <input type="submit" name="form_login" value="Login" />
+ </form>
+ </p>
+ <p>A successful login will redirect you to GitHub for authorization,
+ then back here, so we'll know your GitHub username. We don't access
+ any data other than your GitHub username.</p>
+ </p></center>
+ </body></html>
+EOF;
+ exit(0);
+}
+
+// valid login, save it to the session since GitHub will redirect away and we'll need the same login credentials after.
+$_SESSION['username'] = $user;
+$_SESSION['password'] = $pass;
+
+// see if we can hook up with GitHub to get a username.
+force_authorize_with_github();
+
+// still here? Add the github user to the repos.
+$invites = $repos[$user];
+foreach ($invites as $repo) {
+ //print("repo: $repo\n\n\n");
+ $response = call_github_api("https://api.github.com/repos/$repo/collaborators/" . $_SESSION['github_user'], [ 'permission' => 'pull' ], $github_invite_token, 'PUT');
+ //print("RESPONSE($repo):\n"); print_r($response); print("\n\n\n");
+}
+
+// reset these in case they want to get invites for a different platform.
+$_SESSION['username'] = '';
+$_SESSION['password'] = '';
+
+echo <<<EOF
+<html><head><title>$title</title></head><body>
+<p>Okay, you should have invitation(s) waiting for you for the following repositories:</p>
+<p><ul>
+
+EOF;
+
+foreach ($invites as $repo) {
+ print("<li>$repo</li>\n");
+}
+
+echo <<<EOF
+</ul></p>
+
+<p>Please check wherever GitHub sends you email.</p>
+<p>Once you accept the invitations, you'll have access.</p>
+<p>If you have problems, please <a href="mailto:icculus@icculus.org">ask Ryan for help</a>.</p>
+<p>Otherwise, you can close this browser tab now.</p>
+<p><a href="$base_url">Click here</a> to go back to the login prompt, if you have access to other invites.</p>
+</body></html>
+
+EOF;
+
+?>