Skip to content
Closed
26 changes: 25 additions & 1 deletion include/functions.inc
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,6 @@ function head($title="", $config = []) {
["href" => "/manage/event.php", "text" => "Events"],
["href" => "/manage/users.php", "text" => "Users"],
["href" => "/manage/user-notes.php", "text" => "Notes"],
["href" => "/manage/github.php", "text" => "Github"],
];
$CSS = ["/styles/master.css"];
$SEARCH = [];
Expand Down Expand Up @@ -258,6 +257,11 @@ function find_group_address_from_notes_for($id) {
define("MT_USER_APPROVE_MAIL", "[email protected]");
define("MT_USER_REMOVE_MAIL", "[email protected]");
function user_approve($id) {
if (!is_admin($_SESSION["username"])) {
warn("you're not allowed to take actions on users.");
exit;
}

$res = db_query_safe("UPDATE users SET cvsaccess=1, enable=1 WHERE userid=?", [$id]);
if ($res && mysql_affected_rows()) {
$cc = find_group_address_from_notes_for($id);
Expand All @@ -284,6 +288,10 @@ function user_approve($id) {
}

function user_remove($id) {
if (!is_admin($_SESSION["username"])) {
warn("you're not allowed to take actions on users.");
exit;
}
$userinfo = fetch_user($id);
$res = db_query_safe("DELETE FROM users WHERE userid=?", [$id]);
if ($res && mysql_affected_rows()) {
Expand Down Expand Up @@ -313,6 +321,21 @@ function user_remove($id) {
}
}

function user_unlink_github($id) {
$db = DB::connect();

if(!can_modify($_SESSION['username'], $id)) {
warn("you're not allowed to take actions on users.");
exit;
}

$query = $db->prepare('UPDATE users SET github = ? WHERE userid = ?');
$query->execute([null, (int)$id]);

warn("record $id updated");
exit;
}

function is_admin($user) {
$admins = [
"jimw",
Expand Down Expand Up @@ -428,6 +451,7 @@ function validateAction($k) {
switch($k) {
case "approve":
case "remove":
case "github_unlink":
return $k;
default:
warn("that action ('" . hsc($k) . "') is not understood.");
Expand Down
199 changes: 44 additions & 155 deletions public/manage/github.php
Original file line number Diff line number Diff line change
@@ -1,176 +1,65 @@
<?php // vim: et ts=2 sw=2
<?php

// This script evolved from a quick'n'dirty shell script. If you are reading
// this feel free to clean it!
use App\GitHub\Client;
use App\GitHub\OAuthClient;

require __DIR__ . '/../../vendor/autoload.php';
require __DIR__ . '/../../include/login.inc';
require __DIR__ . '/../../github-config.php';

@include __DIR__ . '/../../github-config.php';
if (!defined('GITHUB_CLIENT_ID') || !defined('GITHUB_CLIENT_SECRET')) {
die('GITHUB_CLIENT_ID or GITHUB_CLIENT_SECRET not defined. Please verify ./github-config.php');
}

define('GITHUB_PHP_OWNER_TEAM_ID', 65141);
define('GITHUB_REPO_TEAM_ID', 138591);
if (!defined('GITHUB_USER_AGENT')) {
define('GITHUB_USER_AGENT', 'php.net repository management (main.php.net, [email protected], [email protected])');
head("github administration");
warn('GITHUB_CLIENT_ID or GITHUB_CLIENT_SECRET not defined. Please verify ./github-config.php');
foot();
exit;
}

function github_api($endpoint, $method = 'GET', $options = [])
{
$options['method'] = $method;
$options['user_agent'] = GITHUB_USER_AGENT;

$ctxt = stream_context_create(['http' => $options]);

$url = 'https://api.github.com'.$endpoint;
$s = @file_get_contents($url, false, $ctxt);
if ($s === false) {
die('Request to GitHub failed. Endpoint: '.$endpoint);
}

return json_decode($s);
$oauth = new OAuthClient(GITHUB_CLIENT_ID, GITHUB_CLIENT_SECRET);
if (!isset($_GET['code'])) {
header('Location: ' . $oauth->getRequestCodeUrl());
exit;
}

function github_current_user($access_token = false)
{
if (!$access_token) {
$access_token = $_SESSION['github']['access_token'];
}
head("github administration");

if (empty($_SESSION['github']['current_user'])) {
$user = github_api('/user?access_token='.urlencode($access_token));
if (!$user->login) {
die('Failed to get current user');
}
try {
if (isset($_GET['code'])) {
$response = $oauth->requestAccessToken($_GET['code']);
if (!isset($response['access_token'])) {
throw new RuntimeException('Can not receive the access token');
}

$_SESSION['github']['current_user'] = $user;
}
$client = new Client($response['access_token']);
$user = $client->me();

return $_SESSION['github']['current_user'];
}
if (!isset($user['login'])) {
throw new RuntimeException('Can not get the user GitHub login');
}

function github_require_valid_user()
{
if (isset($_SESSION['github']['access_token'])) {
return true;
}
$username = $_SESSION['credentials'][0];

if (isset($_GET['code'])) {
$data = [
'client_id' => GITHUB_CLIENT_ID,
'client_secret' => GITHUB_CLIENT_SECRET,
'code' => $_GET['code']
];
$data_encoded = http_build_query($data);
$opts = [
'method' => 'POST',
'user_agent' => GITHUB_USER_AGENT,
'header' => 'Content-type: application/x-www-form-urlencoded',
'content' => $data_encoded,
];
$ctxt = stream_context_create(['http' => $opts]);
$s = @file_get_contents('https://github.com/login/oauth/access_token', false, $ctxt);
if (!$s) {
die('Failed while checking with GitHub,either you are trying to hack us or our configuration is wrong (GITHUB_CLIENT_SECRET outdated?)');
}
$gh = [];
parse_str($s, $gh);
if (empty($gh['access_token'])) {
die("GitHub responded but didn't send an access_token");
}
$db = \App\DB::connect();
$query = $db->prepare('SELECT userid FROM users WHERE username = ?');
$query->execute([$username]);
if (!$query->rowCount()) {
throw new RuntimeException('was not able to find user matching ' . $username);
}

$user = github_current_user($gh['access_token']);
$account = $query->fetch();
$query = $db->prepare('SELECT userid FROM users WHERE github = ? AND userid != ?');
$query->execute([$user['login'], $account['userid']]);
if ($query->rowCount() > 0) {
throw new RuntimeException('GitHub account ' . $user['login'] . ' is already linked');
}

$endpoint = '/teams/'.urlencode((string)GITHUB_PHP_OWNER_TEAM_ID).'/members/'.urlencode($user->login);
$opts = ['user_agent' => GITHUB_USER_AGENT];
$ctxt = stream_context_create(['http' => $opts]);
$is_member = file_get_contents('https://api.github.com'.$endpoint.'?access_token='.urlencode($gh['access_token']), false, $ctxt);
$query = $db->prepare('UPDATE users SET github = ? WHERE userid = ?');
$query->execute([$user['login'], $account['userid']]);

if ($is_member === false) {
head("github administration");
echo '<h1>You (Authenticated GitHub user: '.htmlentities($user->login). ') are no member of the php organization on github.</h1>'.
'<p>Please contact an existing member if you see need.</p>';
foot();
exit;
echo '<h1>We linked your GitHub account with your profile.</h1>' .
'<p><a href="/manage/users.php?username=' . $username . '">Back to profile</a></p>';
}
// SUCCESS
$_SESSION['github']['access_token'] = $gh['access_token'];
header('Location: github.php');
exit;
}

// Start oauth
header('Location: https://github.com/login/oauth/authorize?scope=repo&client_id='.urlencode(GITHUB_CLIENT_ID));
exit;
}

if (isset($_POST['description']) && isset($_SESSION['github']['access_token'])) {
action_create_repo();
} elseif (isset($_GET['login']) || isset($_GET['code']) || isset($_SESSION['github']['access_token'])) {
action_form();
} else {
action_default();
}

function action_default()
{
head("github administration");
echo '<p>This tool is for administrating PHP repos on GitHub. Currently it is used for adding repos only.</p>';
echo '<p><b>NOTE:</b> Only members of the PHP organisation on GitHub can use this tool. We try to keep the number of members limited.</p>';
echo '<p>In case you are a member you can <a href="github.php?login=1">login using GitHub</a>.</p>';
foot();
}

function action_form()
{
github_require_valid_user();
$user = $_SESSION['github']['current_user'];
head("github administration");
?>
<p><b>GitHub user: </b> <?php echo htmlentities($user->login); ?></p>
<p>Creating a GitHub repo using this form ensures the proper configuration. This
includes disabling the GitHub wiki and issue tracker as well as enabling the
php-pulls user to push changes made on git.php.net.</p>
<p>The name, description and homepage should follow other existing repositories.</p>
<form method="post" action="github.php">
Github repo name: https://github.com/php/<input name="name"> (i.e. pecl-category-foobar)<br>
Description: <input name="description"> (i.e. PECL foobar extension)<br>
Homepage: <input name="homepage"> (i.e. http://pecl.php.net/package/foobar)<br>
<input type="submit" value="Create Repository on GitHub">
<input type="hidden" name="action" value="create">
<?php
foot();
} catch (\Exception $e) {
warn($e->getMessage());
}

function action_create_repo()
{
github_require_valid_user();

$data = [
'name' => $_POST['name'],
'description' => $_POST['description'],

'homepage' => $_POST['homepage'],
'private' => false,
'has_issues' => false,
'has_wiki' => false,
'has_downloads' => false,
'team_id' => GITHUB_REPO_TEAM_ID,
];
$data_j = json_encode($data);
$opts = [
'content' => $data_j,
];
$res = github_api('/orgs/php/repos?access_token='.urlencode($_SESSION['github']['access_token']), 'POST', $opts);

head("github administration");
if (isset($res->html_url)) {
echo '<p>Repo created!</p><p><a href="'.htmlentities($res->html_url, ENT_QUOTES).'">Check on GitHub</a>.</p>';
} else {
echo "Error while creating repo.";
}
foot();
}
?>
foot();
37 changes: 28 additions & 9 deletions public/manage/users.php
Original file line number Diff line number Diff line change
Expand Up @@ -84,11 +84,6 @@ function csrf_validate(&$mydata, $name) {
$action = filter_input(INPUT_POST, "action", FILTER_CALLBACK, ["options" => "validateAction"]);
if ($id && $action) {
csrf_validate($_SESSION, $action);
if (!is_admin($_SESSION["username"])) {
warn("you're not allowed to take actions on users.");
exit;
}

switch ($action) {
case 'approve':
user_approve((int)$id);
Expand All @@ -98,6 +93,10 @@ function csrf_validate(&$mydata, $name) {
user_remove((int)$id);
break;

case 'github_unlink':
user_unlink_github((int)$id);
break;

default:
warn("that action ('$action') is not understood.");
}
Expand Down Expand Up @@ -199,6 +198,24 @@ function csrf_validate(&$mydata, $name) {
<td><?php echo hsc($userdata['username']);?></td>
<?php endif ?>
</tr>
<tr>
<th>GitHub account:</th>
<?php if ($github = $userdata['github']): ?>
<td><?php echo hsc($github);?>
<?php if(can_modify($_SESSION['username'], $id)) {?>
<form method="post" action="users.php?id=<?php echo $id?>">
<input type="hidden" name="csrf" value="<?php echo csrf_generate($_SESSION, 'github_unlink') ?>" />
<input type="hidden" name="action" value="github_unlink" />
<input type="submit" value="Unlink" />
</form>
<?php } ?>
</td>
<?php elseif(can_modify($_SESSION["username"],$id)): ?>
<td><a href="/manage/github.php">Link GitHub account</a></td>
<?php else: ?>
<td>&mdash;</td>
<?php endif ?>
</tr>
<tr>
<td colspan="2">Leave password fields blank to leave password unchanged.</td>
</tr>
Expand Down Expand Up @@ -321,7 +338,7 @@ function csrf_validate(&$mydata, $name) {
$search = filter_input(INPUT_GET, "search", FILTER_UNSAFE_RAW) ?: "";
$order = filter_input(INPUT_GET, "order", FILTER_UNSAFE_RAW) ?: "";

$query = new Query("SELECT DISTINCT SQL_CALC_FOUND_ROWS users.userid,cvsaccess,username,name,email,GROUP_CONCAT(note) note FROM users ");
$query = new Query("SELECT DISTINCT SQL_CALC_FOUND_ROWS users.userid,cvsaccess,username,name,email,github,GROUP_CONCAT(note) note FROM users ");
$query->add(" LEFT JOIN users_note ON users_note.userid = users.userid ");

if ($search) {
Expand All @@ -338,7 +355,7 @@ function csrf_validate(&$mydata, $name) {
$query->add(" GROUP BY users.userid ");

if ($order) {
if (!in_array($order, ["username", "name", "email", "note"], true)) {
if (!in_array($order, ["username", "name", "email", "note", "github"], true)) {
die("Invalid order!");
}
if ($forward) {
Expand Down Expand Up @@ -380,7 +397,8 @@ function csrf_validate(&$mydata, $name) {
<th><a href="?<?php echo array_to_url($extra,["order"=>"username"]);?>">username</a></th>
<th><a href="?<?php echo array_to_url($extra,["order"=>"name"]);?>">name</a></th>
<?php if (!$unapproved) { ?>
<th colspan="2"><a href="?<?php echo array_to_url($extra,["order"=>"email"]);?>">email</a></th>
<th><a href="?<?php echo array_to_url($extra,["order"=>"email"]);?>">email</a></th>
<th><a href="?<?php echo array_to_url($extra,["order"=>"github"]);?>">github</a></th>
<?php } else { ?>
<th><a href="?<?php echo array_to_url($extra,["order"=>"email"]);?>">email</a></th>
<th><a href="?<?php echo array_to_url($extra,["order"=>"note"]);?>">note</a></th>
Expand All @@ -395,7 +413,8 @@ function csrf_validate(&$mydata, $name) {
<td><a href="https://people.php.net/?username=<?php echo hsc($userdata['username']) ?>"><?php echo hsc($userdata['username']) ?></a></td>
<td><?php echo hsc($userdata['name']);?></td>
<?php if (!$unapproved) { ?>
<td colspan="2"><?php echo hsc($userdata['email']);?></td>
<td><?php echo hsc($userdata['email']);?></td>
<td><a href="https://github.com/<?php echo hsc($userdata['github']) ?>"><?php echo hsc($userdata['github']);?></a></td>
<?php } else { ?>
<td><?php echo hsc($userdata['email']);?></td>
<td><?php echo hsc($userdata['note']) ?></td>
Expand Down
1 change: 1 addition & 0 deletions schema.sql
Original file line number Diff line number Diff line change
Expand Up @@ -197,6 +197,7 @@ CREATE TABLE `users` (
`name` varchar(255) NOT NULL DEFAULT '',
`email` varchar(255) NOT NULL DEFAULT '',
`username` varchar(16) DEFAULT NULL,
`github` varchar(39) DEFAULT NULL,
`cvsaccess` int(1) NOT NULL DEFAULT 0,
`spamprotect` int(1) NOT NULL DEFAULT 1,
`forgot` varchar(32) DEFAULT NULL,
Expand Down
Loading