nixos/hosts/vm/mediakiwi/WSONoteKfetAuth/src/NoteKfetAuth.php

150 lines
4.1 KiB
PHP

<?php
/**
* Copyright 2020 Marijn van Wezel
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated
* documentation files (the "Software"), to deal in the Software without restriction, including without limitation the
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all copies or substantial portions of the
* Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT
* LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
* CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
namespace WSOAuth\AuthenticationProvider;
use MediaWiki\User\UserIdentity;
class NoteKfetAuth extends AuthProvider {
/**
* @var string
*/
private $clientId;
/**
* @var string
*/
private $clientSecret;
/**
* @inheritDoc
*/
public function __construct(
string $clientId,
string $clientSecret,
?string $authUri,
?string $redirectUri,
array $extensionData = []
) {
$this->clientId = $clientId;
$this->clientSecret = $clientSecret;
}
/**
* @inheritDoc
*/
public function login( ?string &$key, ?string &$secret, ?string &$authUrl ): bool {
// This state is used to prevent CSRF, i.e., ensuring that authentification request
// were initiated on our website.
$state = random_int(PHP_INT_MIN, PHP_INT_MAX);
$secret = "$state";
$authUrl = $GLOBALS['wgNoteKfetUrl'] . "o/authorize/?" . http_build_query([
'client_id' => $this->clientId,
'response_type' => 'code',
'scope' => '1_1',
'state' => $secret,
]);
return true;
}
/**
* @inheritDoc
*/
public function logout( UserIdentity &$user ): void {
}
/**
* @inheritDoc
*/
public function getUser( string $key, string $secret, &$errorMessage ) {
if ( !isset( $_GET['code'] ) ) {
return false;
}
if ( !isset( $_GET['state'] ) || empty( $_GET['state'] ) || ( $_GET['state'] !== $secret ) ) {
return false;
}
try {
$token = $this->getAccessTokens( $_GET['code'] );
$userInfos = $this->getUserInfos( $token );
return [
'name' => $this->sanitizeName( "$userInfos->normalized_name (note)" ),
'realname' => $userInfos->username,
'email' => $userInfos->email,
'remoteUserId' => $userInfos->id,
];
} catch ( \Exception $e ) {
return false;
}
}
private function sanitizeName( string $name ) {
// We replace forbidden chars.
$res = preg_replace('/[#\/:<>=@\|]/', '-', $name);
$res = preg_replace(['/[\[{]/', '/[\]}]/'], ['(', ')'], $res);
$res = str_replace('_', ' ', $res);
// We remove the last controls chars possibly remaining.
return preg_replace('/[^a-zA-Z0-9 !\"$%&\'()*+,\-.;?\\\^`~]/', '', $res);
}
private function getAccessTokens( string $code ) {
$data = [
'grant_type' => 'authorization_code',
'client_id' => $this->clientId,
'client_secret' => $this->clientSecret,
'code' => $code,
];
$options = [
'http' => [
'method' => 'POST',
'header' => 'Content-type: application/x-www-form-urlencoded',
'content' => http_build_query($data),
],
];
$context = stream_context_create($options);
$response = file_get_contents($GLOBALS['wgNoteKfetUrl'] . 'o/token/', false, $context);
$tokens = json_decode($response);
return $tokens->access_token;
}
private function getUserInfos( string $token ) {
$options = [
'http' => [
'method' => 'GET',
'header' => "Authorization: Bearer $token",
],
];
$context = stream_context_create($options);
$response = file_get_contents($GLOBALS['wgNoteKfetUrl'] . 'api/me/', false, $context);
return json_decode($response);
}
/**
* @inheritDoc
*/
public function saveExtraAttributes( int $id ): void {
}
}