diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000..1e4f5fc --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,41 @@ +name: Release + +on: + push: + tags: ['*'] + workflow_dispatch: + +jobs: + build: + runs-on: ubuntu-latest + permissions: + contents: write + + steps: + - uses: actions/checkout@v3 + + - uses: actions/setup-node@v3 + with: + node-version: 16 + + - name: Create package + run: | + rm -rf *.tar.gz + npx --yes wspackager + + - name: Check file existence + id: check_files + uses: andstor/file-existence-action@v1 + with: + files: "${{ github.event.repository.name }}_*.tar.gz" + + - name: On Build Failure + if: steps.check_files.outputs.files_exists == 'false' + run: | + echo "Packaging FAILED" && exit 1 + + - name: Release + uses: softprops/action-gh-release@v1 + if: startsWith(github.ref, 'refs/tags/') && steps.check_files.outputs.files_exists == 'true' + with: + files: "${{ github.event.repository.name }}_*.tar.gz" diff --git a/.gitignore b/.gitignore deleted file mode 100644 index 9df9469..0000000 --- a/.gitignore +++ /dev/null @@ -1,6 +0,0 @@ -.phpcs-cache -.php_cs.cache -phpcs.xml -vendor -.idea -/de.softcreatr.wsc.*.tar.gz diff --git a/.php-cs-fixer.dist.php b/.php-cs-fixer.dist.php new file mode 100644 index 0000000..2ff7aa9 --- /dev/null +++ b/.php-cs-fixer.dist.php @@ -0,0 +1,132 @@ +exclude('*/vendor/*') + ->in(__DIR__.'/files/') + ->notPath('lib/system/api'); + +return (new PhpCsFixer\Config()) + ->setRiskyAllowed(true) + ->setRules([ + '@PSR1' => true, + '@PSR2' => true, + + 'array_push' => true, + 'backtick_to_shell_exec' => true, + 'no_alias_language_construct_call' => true, + 'no_mixed_echo_print' => true, + 'pow_to_exponentiation' => true, + 'random_api_migration' => true, + + 'array_syntax' => ['syntax' => 'short'], + 'no_multiline_whitespace_around_double_arrow' => true, + 'no_trailing_comma_in_singleline_array' => true, + 'no_whitespace_before_comma_in_array' => true, + 'normalize_index_brace' => true, + 'whitespace_after_comma_in_array' => true, + + 'non_printable_character' => ['use_escape_sequences_in_strings' => true], + + 'lowercase_static_reference' => true, + 'magic_constant_casing' => true, + 'magic_method_casing' => true, + 'native_function_casing' => true, + 'native_function_type_declaration_casing' => true, + + 'cast_spaces' => ['space' => 'none'], + 'lowercase_cast' => true, + 'no_unset_cast' => true, + 'short_scalar_cast' => true, + + 'class_attributes_separation' => true, + 'no_blank_lines_after_class_opening' => true, + 'no_null_property_initialization' => true, + 'self_accessor' => true, + 'single_class_element_per_statement' => true, + 'single_trait_insert_per_statement' => true, + + 'no_empty_comment' => true, + 'single_line_comment_style' => ['comment_types' => ['hash']], + + 'native_constant_invocation' => ['strict' => false], + + 'no_alternative_syntax' => true, + 'no_trailing_comma_in_list_call' => true, + 'no_unneeded_control_parentheses' => ['statements' => ['break', 'clone', 'continue', 'echo_print', 'return', 'switch_case', 'yield', 'yield_from']], + 'no_unneeded_curly_braces' => ['namespaces' => true], + 'switch_continue_to_break' => true, + 'trailing_comma_in_multiline' => ['elements' => ['arrays']], + + 'function_typehint_space' => true, + 'lambda_not_used_import' => true, + 'native_function_invocation' => ['include' => ['@all']], + 'no_unreachable_default_argument_value' => true, + 'nullable_type_declaration_for_default_null_value' => true, + 'return_type_declaration' => true, + 'static_lambda' => true, + + 'fully_qualified_strict_types' => true, + 'no_leading_import_slash' => true, + 'no_unused_imports' => true, + //'ordered_imports' => true, + + 'declare_equal_normalize' => true, + 'dir_constant' => true, + 'explicit_indirect_variable' => true, + 'function_to_constant' => true, + 'is_null' => true, + 'no_unset_on_property' => true, + + 'list_syntax' => ['syntax' => 'short'], + + 'clean_namespace' => true, + 'no_leading_namespace_whitespace' => true, + 'single_blank_line_before_namespace' => true, + + 'no_homoglyph_names' => true, + + 'binary_operator_spaces' => true, + 'concat_space' => ['spacing' => 'one'], + 'increment_style' => ['style' => 'post'], + 'logical_operators' => true, + 'object_operator_without_whitespace' => true, + 'operator_linebreak' => true, + 'standardize_increment' => true, + 'standardize_not_equals' => true, + 'ternary_operator_spaces' => true, + 'ternary_to_elvis_operator' => true, + 'ternary_to_null_coalescing' => true, + 'unary_operator_spaces' => true, + + 'no_useless_return' => true, + 'return_assignment' => true, + + 'multiline_whitespace_before_semicolons' => true, + 'no_empty_statement' => true, + 'no_singleline_whitespace_before_semicolons' => true, + 'space_after_semicolon' => ['remove_in_empty_for_expressions' => true], + + 'escape_implicit_backslashes' => true, + 'explicit_string_variable' => true, + 'heredoc_to_nowdoc' => true, + 'no_binary_string' => true, + 'simple_to_complex_string_variable' => true, + + 'array_indentation' => true, + 'blank_line_before_statement' => ['statements' => ['return', 'exit']], + 'compact_nullable_typehint' => true, + 'method_chaining_indentation' => true, + 'no_extra_blank_lines' => ['tokens' => ['case', 'continue', 'curly_brace_block', 'default', 'extra', 'parenthesis_brace_block', 'square_brace_block', 'switch', 'throw', 'use']], + 'no_spaces_around_offset' => true, + + // SoftCreatR style + 'blank_line_between_import_groups' => true, + 'global_namespace_import' => [ + 'import_classes' => true, + 'import_constants' => true, + 'import_functions' => false, + ], + 'ordered_imports' => [ + 'imports_order' => ['class', 'function', 'const'], + ], + ]) + ->setFinder($finder); diff --git a/README.md b/README.md index 4ca8f28..e8dab03 100644 --- a/README.md +++ b/README.md @@ -1,11 +1,11 @@ -# OpenAuth.dev Provider for WoltLab Suite 5 +# OpenAuth.dev Provider for WoltLab Suite
![openauth-icon](https://user-images.githubusercontent.com/81188/87538212-25d2ef00-c69c-11ea-87a7-b967826cb669.png) -### OpenAuth.dev Provider for WoltLab Suite 5 +### OpenAuth.dev Provider for WoltLab Suite
@@ -29,8 +29,8 @@ WIP You need: -- A WoltLab Suite installation (5.2.0 or newer) -- PHP (7.0.22 or newer) +- A WoltLab Suite installation (5.4.0 or newer) +- PHP (7.2.24 or newer) - Activated `fsockopen` (including support for SSL connections) on your webspace/server - A free user account on [OpenAuth.dev](https://www.openauth.dev), which has been authorized as a developer diff --git a/constants.php b/constants.php index 68c6837..797d0c5 100644 --- a/constants.php +++ b/constants.php @@ -1,12 +1,5 @@ - + diff --git a/files/acp/database/install_dev.openauth.wsc.login.php b/files/acp/database/install_dev.openauth.wsc.login.php new file mode 100644 index 0000000..e2873a8 --- /dev/null +++ b/files/acp/database/install_dev.openauth.wsc.login.php @@ -0,0 +1,41 @@ +columns([ + IntDatabaseTableColumn::create('openAuthID'), + NotNullVarchar255DatabaseTableColumn::create('openAuthAvatar'), + TinyintDatabaseTableColumn::create('enableOpenAuthAvatar') + ->length(1) + ->notNull() + ->defaultValue(0), + ]), +]; diff --git a/files/lib/action/OpenAuthAction.class.php b/files/lib/action/OpenAuthAction.class.php index b15c67d..e0b0135 100644 --- a/files/lib/action/OpenAuthAction.class.php +++ b/files/lib/action/OpenAuthAction.class.php @@ -26,200 +26,249 @@ namespace wcf\action; use Exception; +use Psr\Http\Client\ClientExceptionInterface; use wcf\data\user\User; use wcf\data\user\UserEditor; -use wcf\system\exception\IllegalLinkException; +use wcf\form\RegisterForm; use wcf\system\exception\NamedUserException; use wcf\system\exception\PermissionDeniedException; -use wcf\system\exception\SystemException; use wcf\system\openauth\OpenAuthAPI; use wcf\system\request\LinkHandler; -use wcf\system\user\authentication\UserAuthenticationFactory; +use wcf\system\user\authentication\oauth\exception\StateValidationException; use wcf\system\WCF; use wcf\util\HeaderUtil; -use function hash_equals; +use function wcf\functions\exception\logThrowable; class OpenAuthAction extends AbstractAction { /** * @inheritDoc */ - public $neededModules = [ - 'OPENAUTH_CLIENT_ID', - 'OPENAUTH_CLIENT_SECRET' - ]; + public $neededModules = ['OPENAUTH_CLIENT_ID', 'OPENAUTH_CLIENT_SECRET']; + + /** + * @var string + */ + private const STATE = self::class . "\0state_parameter"; /** * @inheritDoc * - * @throws IllegalLinkException + * @throws \wcf\system\exception\PermissionDeniedException */ - public function readParameters() { + public function readParameters(): void + { parent::readParameters(); if (WCF::getSession()->spiderID) { - throw new IllegalLinkException(); + throw new PermissionDeniedException(); } } /** * @inheritDoc * - * @throws IllegalLinkException - * @throws NamedUserException - * @throws PermissionDeniedException - * @throws SystemException - * @throws Exception + * @throws \GuzzleHttp\Exception\GuzzleException + * @throws \wcf\system\exception\NamedUserException + * @throws \wcf\system\exception\PermissionDeniedException */ - public function execute() + public function execute(): void { parent::execute(); - if (isset($_REQUEST['code'])) { - // check state - if ( - empty($_REQUEST['state']) || - !WCF::getSession()->getVar('__openauthInit') || - !hash_equals($_REQUEST['state'], WCF::getSession()->getVar('__openauthInit')) - ) { - throw new IllegalLinkException(); - } - - WCF::getSession()->unregister('__openauthInit'); - - // check OAuth code - $tokenInfo = OpenAuthAPI::getInstance()->checkOAuthCode($_REQUEST['code']); - - if ($tokenInfo === null) { - throw new NamedUserException(WCF::getLanguage()->getDynamicVariable( - 'wcf.user.3rdparty.openauth.connect.error.configuration') - ); - } - - if (empty($tokenInfo['access_token'])) { - throw new NamedUserException(WCF::getLanguage()->get( - 'wcf.user.3rdparty.openauth.connect.error.invalid') - ); - } - - // get user data - $userData = OpenAuthAPI::getInstance()->getUserInfo($tokenInfo['access_token']); - - if ($userData === null) { - throw new NamedUserException(WCF::getLanguage()->getDynamicVariable( - 'wcf.user.3rdparty.openauth.connect.error.configuration') - ); - } - - if (empty($userData['sub'])) { - throw new NamedUserException(WCF::getLanguage()->get( - 'wcf.user.3rdparty.openauth.connect.error.invalid') - ); - } - - $openAuthUserID = $userData['sub']; - $openAuthUsername = (!empty($userData['nickname'])) ? $userData['nickname'] : ''; - $openAuthMail = (!empty($userData['email'])) ? $userData['email'] : ''; - $openAuthAvatar = (!empty($userData['picture'])) ? $userData['picture'] : null; + try { + if (isset($_GET['code'])) { + $tokenInfo = OpenAuthAPI::getInstance()->checkOAuthCode($_REQUEST['code']); - $user = User::getUserByAuthData('openauth:' . $openAuthUserID); + if ($tokenInfo === null) { + throw new NamedUserException( + WCF::getLanguage()->getDynamicVariable('wcf.user.3rdparty.openauth.connect.error.configuration') + ); + } - if (WCF::getUser()->userID) { - // user is signed in and would connect - if ($user->userID) { - // another account is connected to this openauth account - throw new NamedUserException(WCF::getLanguage()->getDynamicVariable( - 'wcf.user.3rdparty.openauth.connect.error.inuse') + if (empty($tokenInfo['access_token'])) { + throw new NamedUserException( + WCF::getLanguage()->get('wcf.user.3rdparty.openauth.connect.error.invalid') ); } - WCF::getSession()->register('__openAuthUsername', $openAuthUsername); - WCF::getSession()->register('__openAuthData', $userData); + $this->processUser($this->getUser($tokenInfo['access_token'])); + } elseif (isset($_GET['error'])) { + $this->handleError($_GET['error']); + } else { + $this->initiate(); + } + } catch (NamedUserException $e) { + throw $e; + } catch (StateValidationException $e) { + throw new NamedUserException( + WCF::getLanguage()->getDynamicVariable( + 'wcf.user.3rdparty.login.error.stateValidation' + ) + ); + } catch (Exception $e) { + $exceptionID = logThrowable($e); + + $type = 'genericException'; + if ($e instanceof ClientExceptionInterface) { + $type = 'httpError'; + } - HeaderUtil::redirect(LinkHandler::getInstance()->getLink('AccountManagement') . '#3rdParty'); + throw new NamedUserException( + WCF::getLanguage()->getDynamicVariable( + 'wcf.user.3rdparty.login.error.' . $type, + [ + 'exceptionID' => $exceptionID, + ] + ) + ); + } + } - $this->executed(); + /** + * Initiates the OAuth flow. + * + * @throws \GuzzleHttp\Exception\GuzzleException + * @throws \wcf\system\exception\NamedUserException + * @throws \wcf\system\exception\SystemException + */ + protected function initiate(): void + { + $token = OpenAuthAPI::getInstance()->generateRandom(); + $scopes = OpenAuthAPI::getInstance()->getScopes(); + $authorizationUri = OpenAuthAPI::getInstance()->getAuthorizationURI($token, $scopes); - exit; - } + WCF::getSession()->register(self::STATE, $token); - if ($user->userID) { - $userAuthentication = UserAuthenticationFactory::getInstance()->getUserAuthentication(); + if ($scopes === null) { + throw new NamedUserException( + WCF::getLanguage()->getDynamicVariable('wcf.user.3rdparty.openauth.connect.error.configuration') + ); + } - if (null === $userAuthentication) { - throw new SystemException('No valid authentication instance found.'); - } + if ($authorizationUri === null) { + throw new NamedUserException( + WCF::getLanguage()->getDynamicVariable('wcf.user.3rdparty.openauth.connect.error.configuration') + ); + } - // login - if ($userAuthentication->supportsPersistentLogins()) { - $password = OpenAuthAPI::getInstance()->generateRandom(); - $updateData = [ - 'openAuthAvatar' => $openAuthAvatar, - 'password' => $password - ]; + HeaderUtil::redirect($authorizationUri); - $userEditor = new UserEditor($user); - $userEditor->update($updateData); + exit; + } - // reload user to retrieve salt - $user = new User($user->userID); + /** + * @throws \wcf\system\exception\NamedUserException + * @throws \GuzzleHttp\Exception\GuzzleException + * @throws \wcf\system\exception\SystemException + */ + protected function getUser(string $accessToken): array + { + $userData = OpenAuthAPI::getInstance()->getUserInfo($accessToken); - $userAuthentication->storeAccessData($user, $user->username, $password); - } + if ($userData === null) { + throw new NamedUserException( + WCF::getLanguage()->getDynamicVariable('wcf.user.3rdparty.openauth.connect.error.configuration') + ); + } - WCF::getSession()->changeUser($user); - WCF::getSession()->update(); + if (empty($userData['sub'])) { + throw new NamedUserException( + WCF::getLanguage()->get('wcf.user.3rdparty.openauth.connect.error.invalid') + ); + } - HeaderUtil::redirect(LinkHandler::getInstance()->getLink()); + return $userData; + } - $this->executed(); + /** + * @throws \wcf\system\exception\NamedUserException + * @throws \wcf\system\exception\SystemException + */ + protected function processUser(array $userData): void + { + $user = User::getUserByAuthData('openauth:' . $userData['sub']); - exit; + if ($user->userID) { + if (WCF::getUser()->userID) { + // This account belongs to an existing user, but we are already logged in. + // This can't be handled. + throw new NamedUserException( + WCF::getLanguage()->getDynamicVariable('wcf.user.3rdparty.facebook.connect.error.inuse') + ); } - // register - WCF::getSession()->register('__3rdPartyProvider', 'openauth'); - WCF::getSession()->register('__openAuthData', $userData); - WCF::getSession()->register('__username', $openAuthUsername); - WCF::getSession()->register('__email', $openAuthMail); - - if (REGISTER_USE_CAPTCHA) { - WCF::getSession()->register('noRegistrationCaptcha', true); + // This account belongs to an existing user, we are not logged in. + // Perform the login, and update avatar, if there's one. + if (!empty($userData['picture'])) { + (new UserEditor($user))->update([ + 'openAuthAvatar' => $userData['picture'], + ]); } + WCF::getSession()->changeUser($user); WCF::getSession()->update(); - - HeaderUtil::redirect(LinkHandler::getInstance()->getLink('Register')); - - $this->executed(); + HeaderUtil::redirect(LinkHandler::getInstance()->getLink()); exit; } - // user declined or another error occured - if (isset($_GET['error'])) { - throw new NamedUserException(WCF::getLanguage()->getDynamicVariable('wcf.user.3rdparty.openauth.connect.error.' . $_GET['error'])); - } + WCF::getSession()->register('__3rdPartyProvider', 'facebook'); - $state = OpenAuthAPI::getInstance()->generateRandom(); - $scopes = OpenAuthAPI::getInstance()->getScopes(); - $authorizationUri = OpenAuthAPI::getInstance()->getAuthorizationURI($state, $scopes); + if (WCF::getUser()->userID) { + // This account does not belong to anyone, and we are already logged in. + // Thus, we want to connect this account. + WCF::getSession()->register('__oauthUser', $userData); - WCF::getSession()->register('__openauthInit', $state); + HeaderUtil::redirect(LinkHandler::getInstance()->getLink('AccountManagement') . '#3rdParty'); - if ($scopes === null) { - throw new NamedUserException(WCF::getLanguage()->getDynamicVariable('wcf.user.3rdparty.openauth.connect.error.configuration')); + exit; } - if ($authorizationUri === null) { - throw new NamedUserException(WCF::getLanguage()->getDynamicVariable('wcf.user.3rdparty.openauth.connect.error.configuration')); - } + // This account does not belong to anyone, and we are not logged in. + // Thus, we want to connect this account to a newly registered user. + WCF::getSession()->register('__oauthUser', $userData); + WCF::getSession()->register('__username', (!empty($userData['nickname'])) ? $userData['nickname'] : ''); + WCF::getSession()->register('__email', (!empty($userData['email'])) ? $userData['email'] : ''); - HeaderUtil::redirect($authorizationUri); + // We assume that bots won't register an external account first, so + // we skip the captcha. + WCF::getSession()->register('noRegistrationCaptcha', true); + WCF::getSession()->update(); - $this->executed(); + HeaderUtil::redirect(LinkHandler::getInstance()->getControllerLink(RegisterForm::class)); exit; } + + /** + * @throws \wcf\system\exception\NamedUserException + */ + protected function handleError(string $error): void + { + throw new NamedUserException(WCF::getLanguage()->getDynamicVariable('wcf.user.3rdparty.login.error.' . $error)); + } + + /** + * Validates the state parameter. + */ + protected function validateState(): void + { + try { + if (!isset($_GET['state'])) { + throw new StateValidationException('Missing state parameter'); + } + + if (!($sessionState = WCF::getSession()->getVar(self::STATE))) { + throw new StateValidationException('Missing state in session'); + } + + if (!\hash_equals($sessionState, (string)$_GET['state'])) { + throw new StateValidationException('Mismatching state'); + } + } finally { + WCF::getSession()->unregister(self::STATE); + } + } } diff --git a/files/lib/action/OpenAuthAvatarDownloadAction.class.php b/files/lib/action/OpenAuthAvatarDownloadAction.class.php index 2f2045c..ef160de 100644 --- a/files/lib/action/OpenAuthAvatarDownloadAction.class.php +++ b/files/lib/action/OpenAuthAvatarDownloadAction.class.php @@ -25,29 +25,33 @@ namespace wcf\action; +use DomainException; +use GuzzleHttp\Exception\TransferException; +use GuzzleHttp\Psr7\Request; +use GuzzleHttp\RequestOptions; +use RuntimeException; use wcf\data\user\avatar\OpenAuthAvatar; use wcf\data\user\avatar\UserAvatar; use wcf\data\user\User; use wcf\data\user\UserEditor; use wcf\system\exception\IllegalLinkException; -use wcf\system\exception\PermissionDeniedException; -use wcf\system\exception\SystemException; +use wcf\system\io\File; +use wcf\system\io\http\RedirectGuard; +use wcf\system\io\HttpFactory; use wcf\util\FileUtil; -use wcf\util\HTTPRequest; - -use function file_exists; -use function file_put_contents; -use function header; -use function in_array; -use function mb_strtolower; -use function md5; -use function parse_url; -use function pathinfo; -use function readfile; -use function sprintf; +use wcf\util\Url; + +use const IMAGETYPE_GIF; +use const IMAGETYPE_JPEG; +use const IMAGETYPE_PNG; class OpenAuthAvatarDownloadAction extends AbstractAction { + /** + * @inheritDoc + */ + public $neededModules = ['OPENAUTH_CLIENT_ID', 'OPENAUTH_CLIENT_SECRET']; + /** * @var int */ @@ -68,7 +72,7 @@ class OpenAuthAvatarDownloadAction extends AbstractAction * * @throws IllegalLinkException */ - public function readParameters() + public function readParameters(): void { parent::readParameters(); @@ -86,88 +90,139 @@ public function readParameters() /** * @inheritDoc * - * @throws PermissionDeniedException - * @throws SystemException + * @throws \GuzzleHttp\Exception\GuzzleException + * @throws \wcf\system\exception\PermissionDeniedException + * @throws \wcf\system\exception\SystemException */ - public function execute() + public function execute(): void { parent::execute(); if (!empty($this->user->openAuthAvatar) && OPENAUTH_CLIENT_ID && OPENAUTH_CLIENT_SECRET) { - $urlParsed = parse_url($this->user->openAuthAvatar); - $pathInfo = pathinfo($urlParsed['path']); - - if (in_array($pathInfo['extension'], ['jpg', 'png', 'gif'])) { - $contentType = 'image/png'; - $extension = 'png'; + $pathInfo = Url::parse($this->user->openAuthAvatar)['path']; + if (\in_array($pathInfo['extension'], ['jpg', 'png', 'gif', 'webp'])) { if ($pathInfo['extension'] === 'jpg') { $contentType = 'image/jpeg'; $extension = 'jpg'; } elseif ($pathInfo['extension'] === 'gif') { $contentType = 'image/gif'; $extension = 'gif'; + } elseif ($pathInfo['extension'] === 'webp') { + $contentType = 'image/webp'; + $extension = 'webp'; + } else { + $contentType = 'image/png'; + $extension = 'png'; } - $cachedFilename = sprintf( - OPENAuthAvatar::OPENAUTH_CACHE_LOCATION, - md5(mb_strtolower($this->user->openAuthAvatar)), + $fileLocation = WCF_DIR . \sprintf( + OpenAuthAvatar::OPENAUTH_CACHE_LOCATION, + \md5(\mb_strtolower($this->user->openAuthAvatar)), $extension ); - if (file_exists(WCF_DIR . $cachedFilename)) { - @header('content-type: ' . $contentType); - @readfile(WCF_DIR . $cachedFilename); + if (\file_exists($fileLocation)) { + $this->executed(); + + @\header('content-type: ' . $contentType); + @\readfile($fileLocation); + exit; } try { - $request = new HTTPRequest($this->user->openAuthAvatar); - $request->execute(); - $reply = $request->getReply(); - - $fileExtension = 'png'; - $mimeType = 'image/png'; - - if (isset($reply['httpHeaders']['content-type'][0])) { - switch ($reply['httpHeaders']['content-type'][0]) { - case 'image/jpeg': - $mimeType = 'image/jpeg'; - $fileExtension = 'jpg'; - break; - case 'image/gif': - $mimeType = 'image/gif'; - $fileExtension = 'gif'; - break; + // download image + $file = null; + $response = null; + $tmp = FileUtil::getTemporaryFilename('openauth_avatar_'); + + try { + $file = new File($tmp); + $client = HttpFactory::makeClient([ + RequestOptions::TIMEOUT => 10, + RequestOptions::STREAM => true, + RequestOptions::ALLOW_REDIRECTS => [ + 'on_redirect' => new RedirectGuard(), + ], + ]); + + $request = new Request('GET', $this->user->openAuthAvatar, ['accept' => 'image/*']); + $response = $client->send($request); + + while (!$response->getBody()->eof()) { + try { + $file->write($response->getBody()->read(8192)); + } catch (RuntimeException $e) { + throw new DomainException( + 'Failed to read response body.', + 0, + $e + ); + } } + + $file->flush(); + } catch (TransferException $e) { + throw new DomainException('Failed to request', 0, $e); + } finally { + if ($response && $response->getBody()) { + $response->getBody()->close(); + } + + if ($file) { + $file->close(); + } + } + + // check file type + $imageData = @\getimagesize($tmp); + + if (!$imageData) { + throw new DomainException(); + } + + switch ($imageData[2]) { + case IMAGETYPE_PNG: + $extension = 'png'; + break; + case IMAGETYPE_GIF: + $extension = 'gif'; + break; + case IMAGETYPE_JPEG: + $extension = 'jpg'; + break; + default: + throw new DomainException(); } - $cachedFilename = sprintf( + $fileLocation = WCF_DIR . \sprintf( OpenAuthAvatar::OPENAUTH_CACHE_LOCATION, - md5(mb_strtolower($this->user->openAuthAvatar)), - $fileExtension + \md5(\mb_strtolower($this->user->openAuthAvatar)), + $extension ); - file_put_contents(WCF_DIR . $cachedFilename, $reply['body']); - FileUtil::makeWritable(WCF_DIR . $cachedFilename); + \rename($tmp, $fileLocation); - @header('content-type: ' . $mimeType); - @readfile(WCF_DIR . $cachedFilename); + // update mtime for correct expiration calculation + @\touch($fileLocation); - exit; - } catch (SystemException $e) { - $editor = new UserEditor($this->user); - $editor->update([ - 'enableOpenAuthAvatar' => 0 - ]); + $this->executed(); + + @\header('content-type: ' . $imageData['mime']); + @\readfile($fileLocation); - throw $e; + exit; + } catch (DomainException $e) { + (new UserEditor($this->user))->update(['enableOpenAuthAvatar' => 0]); } } } - @header('content-type: image/svg+xml'); - @readfile(WCF_DIR . 'images/avatars/avatar-default.svg'); + $this->executed(); + + @\header('content-type: image/svg+xml'); + @\readfile(WCF_DIR . 'images/avatars/avatar-default.svg'); exit; } diff --git a/files/lib/data/user/avatar/OpenAuthAvatar.class.php b/files/lib/data/user/avatar/OpenAuthAvatar.class.php index e0bf87c..04d3a02 100644 --- a/files/lib/data/user/avatar/OpenAuthAvatar.class.php +++ b/files/lib/data/user/avatar/OpenAuthAvatar.class.php @@ -2,7 +2,6 @@ /* * Copyright by The OpenAuth.dev Team. - * This file is part of dev.openauth.wsc.login. * * License: GNU Lesser General Public License v2.1 * @@ -27,30 +26,20 @@ namespace wcf\data\user\avatar; use wcf\system\cache\runtime\UserRuntimeCache; -use wcf\system\exception\SystemException; use wcf\system\request\LinkHandler; use wcf\system\WCF; -use function file_exists; -use function filemtime; -use function in_array; -use function mb_strtolower; -use function md5; -use function parse_url; -use function pathinfo; -use function sprintf; - class OpenAuthAvatar extends DefaultAvatar { /** * @var string */ - const OPENAUTH_CACHE_LOCATION = 'images/avatars/openauth/%s.%s'; + public const OPENAUTH_CACHE_LOCATION = 'images/avatars/openauth/%s.%s'; /** * @var int */ - const OPENAUTH_CACHE_EXPIRE = 7; + public const OPENAUTH_CACHE_EXPIRE = 7; /** * @var string @@ -77,38 +66,41 @@ public function __construct($userID) /** * @inheritDoc * - * @throws SystemException + * @throws \wcf\system\exception\SystemException */ public function getURL($size = null): string { if (empty($this->url)) { $user = UserRuntimeCache::getInstance()->getObject($this->userID); + if (null === $user || !$user->userID) { + return ''; + } + if (empty($user->openAuthAvatar)) { return ''; } - $urlParsed = parse_url($user->openAuthAvatar); - $pathInfo = pathinfo($urlParsed['path']); + $urlParsed = \parse_url($user->openAuthAvatar); + $pathInfo = \pathinfo($urlParsed['path']); - if (!in_array($pathInfo['extension'], ['jpg', 'png', 'gif'])) { + if (!\in_array($pathInfo['extension'], ['jpg', 'png', 'gif'])) { return ''; } - $cachedFilename = sprintf( + $cachedFilename = \sprintf( self::OPENAUTH_CACHE_LOCATION, - md5(mb_strtolower($user->openAuthAvatar)), + \md5(\mb_strtolower($user->openAuthAvatar)), $pathInfo['extension'] ); - if ( - file_exists(WCF_DIR . $cachedFilename) && - filemtime(WCF_DIR . $cachedFilename) > (TIME_NOW - (self::OPENAUTH_CACHE_EXPIRE * 86400)) + if (\file_exists(WCF_DIR . $cachedFilename) + && \filemtime(WCF_DIR . $cachedFilename) > (TIME_NOW - (self::OPENAUTH_CACHE_EXPIRE * 86400)) ) { $this->url = WCF::getPath() . $cachedFilename; } else { $this->url = LinkHandler::getInstance()->getLink('OpenAuthAvatarDownload', [ - 'forceFrontend' => true + 'forceFrontend' => true, ], 'userID=' . $this->userID); } } diff --git a/files/lib/system/event/listener/OpenAuthAccountManagementListener.class.php b/files/lib/system/event/listener/OpenAuthAccountManagementListener.class.php index 4b34c8c..6bd9f27 100644 --- a/files/lib/system/event/listener/OpenAuthAccountManagementListener.class.php +++ b/files/lib/system/event/listener/OpenAuthAccountManagementListener.class.php @@ -28,7 +28,6 @@ use wcf\form\AbstractForm; use wcf\form\AccountManagementForm; use wcf\page\AbstractPage; -use wcf\system\exception\SystemException; use wcf\system\request\LinkHandler; use wcf\system\WCF; use wcf\util\HeaderUtil; @@ -53,20 +52,22 @@ class OpenAuthAccountManagementListener implements IParameterizedEventListener /** * @inheritDoc + * + * @return void */ - public function execute($eventObj, $className, $eventName, array &$parameters) + public function execute($eventObj, $className, $eventName, array &$parameters): void { if (OPENAUTH_CLIENT_ID === "" && OPENAUTH_CLIENT_SECRET === "") { return; } - $this->$eventName($eventObj); + $this->{$eventName}($eventObj); } /** * @see AbstractForm::readFormParameters() */ - protected function readFormParameters() + protected function readFormParameters(): void { if (isset($_POST['openauthDisconnect'])) { $this->openAuthDisconnect = (int)$_POST['openauthDisconnect']; @@ -80,43 +81,43 @@ protected function readFormParameters() /** * @see AbstractPage::assignVariables() */ - protected function assignVariables() + protected function assignVariables(): void { WCF::getTPL()->assign([ 'openAuthConnect' => $this->openAuthConnect, - 'openAuthDisconnect' => $this->openAuthDisconnect + 'openAuthDisconnect' => $this->openAuthDisconnect, ]); } /** * @see AbstractForm::save() */ - protected function save(AccountManagementForm $eventObj) + protected function save(AccountManagementForm $eventObj): void { - if ($this->openAuthConnect && WCF::getSession()->getVar('__openAuthData')) { - $userData = WCF::getSession()->getVar('__openAuthData'); + if ($this->openAuthConnect && WCF::getSession()->getVar('__oauthUser')) { + $userData = WCF::getSession()->getVar('__oauthUser'); $eventObj->additionalFields['authData'] = 'openauth:' . $userData['sub']; $eventObj->additionalFields['openAuthID'] = $userData['sub']; $this->success = 'wcf.user.3rdparty.openauth.connect.success'; - WCF::getSession()->unregister('__openAuthData'); - WCF::getSession()->unregister('__openAuthUsername'); + WCF::getSession()->unregister('__oauthUser'); } elseif ($this->openAuthDisconnect && StringUtil::startsWith(WCF::getUser()->authData, 'openauth:')) { $eventObj->additionalFields['authData'] = ''; $eventObj->additionalFields['openAuthID'] = null; $eventObj->additionalFields['openAuthAvatar'] = null; $eventObj->additionalFields['enableOpenAuthAvatar'] = 0; + $this->success = 'wcf.user.3rdparty.openauth.disconnect.success'; } } /** - * @throws SystemException + * @throws \wcf\system\exception\SystemException * * @see AbstractForm::saved() */ - protected function saved() + protected function saved(): void { if (!empty($this->success)) { HeaderUtil::delayedRedirect( diff --git a/files/lib/system/event/listener/OpenAuthAvatarEditListener.class.php b/files/lib/system/event/listener/OpenAuthAvatarEditListener.class.php index e6a317a..667725a 100644 --- a/files/lib/system/event/listener/OpenAuthAvatarEditListener.class.php +++ b/files/lib/system/event/listener/OpenAuthAvatarEditListener.class.php @@ -31,38 +31,28 @@ use wcf\data\user\UserAction; use wcf\form\AbstractForm; use wcf\form\AvatarEditForm; -use wcf\system\exception\SystemException; use wcf\system\WCF; -use function file_exists; -use function in_array; -use function mb_strtolower; -use function md5; -use function parse_url; -use function pathinfo; -use function sprintf; -use function unlink; - class OpenAuthAvatarEditListener implements IParameterizedEventListener { /** * @inheritDoc + * + * @return void */ - public function execute($eventObj, $className, $eventName, array &$parameters) + public function execute($eventObj, $className, $eventName, array &$parameters): void { if (OPENAUTH_CLIENT_ID === "" && OPENAUTH_CLIENT_SECRET === "") { return; } - $this->$eventName($eventObj); + $this->{$eventName}($eventObj); } /** - * @param AvatarEditForm|UserEditForm $eventObj - * * @see AbstractPage::readData() */ - protected function readData($eventObj) + protected function readData($eventObj): void { if (empty($_POST)) { if ($eventObj instanceof UserEditForm) { @@ -76,22 +66,19 @@ protected function readData($eventObj) } /** - * @param AvatarEditForm|UserEditForm $eventObj - * * @see AbstractPage::assignVariables() */ - protected function assignVariables($eventObj) + protected function assignVariables(AvatarEditForm $eventObj): void { $this->readData($eventObj); } /** - * @param AvatarEditForm|UserEditForm $eventObj - * @throws SystemException + * @throws \wcf\system\exception\SystemException * * @see AbstractForm::save() */ - protected function save($eventObj) + protected function save($eventObj): void { if (isset($_POST['avatarType'])) { $data = []; @@ -119,11 +106,9 @@ protected function save($eventObj) } /** - * @param AvatarEditForm|UserEditForm $eventObj - * * @see AbstractForm::saved() */ - protected function saved($eventObj) + protected function saved(AvatarEditForm $eventObj): void { if (isset($_POST['avatarType']) && $_POST['avatarType'] === 'OpenAuth') { $eventObj->avatarType = 'OpenAuth'; @@ -133,27 +118,27 @@ protected function saved($eventObj) /** * Resets openauth avatar after disabling. */ - private function resetOpenAuthAvatarCache(User $user) + private function resetOpenAuthAvatarCache(User $user): void { if (!$user->enableOpenAuthAvatar) { return; } - $urlParsed = parse_url($user->openAuthAvatar); - $pathInfo = pathinfo($urlParsed['path']); + $urlParsed = \parse_url($user->openAuthAvatar); + $pathInfo = \pathinfo($urlParsed['path']); - if (!in_array($pathInfo['extension'], ['jpg', 'png', 'gif'])) { + if (!\in_array($pathInfo['extension'], ['jpg', 'png', 'gif'])) { return; } - $cachedFilename = sprintf( + $cachedFilename = \sprintf( OpenAuthAvatar::OPENAUTH_CACHE_LOCATION, - md5(mb_strtolower($user->openAuthAvatar)), + \md5(\mb_strtolower($user->openAuthAvatar)), $pathInfo['extension'] ); - if (file_exists(WCF_DIR . $cachedFilename)) { - @unlink(WCF_DIR . $cachedFilename); + if (\file_exists(WCF_DIR . $cachedFilename)) { + @\unlink(WCF_DIR . $cachedFilename); } } } diff --git a/files/lib/system/event/listener/OpenAuthRegisterListener.class.php b/files/lib/system/event/listener/OpenAuthRegisterListener.class.php index 42cc2f1..6a24466 100644 --- a/files/lib/system/event/listener/OpenAuthRegisterListener.class.php +++ b/files/lib/system/event/listener/OpenAuthRegisterListener.class.php @@ -28,7 +28,6 @@ use wcf\data\user\option\UserOption; use wcf\form\RegisterForm; use wcf\page\AbstractPage; -use wcf\system\exception\SystemException; use wcf\system\openauth\OpenAuthAPI; use wcf\system\WCF; @@ -47,46 +46,47 @@ class OpenAuthRegisterListener implements IParameterizedEventListener /** * @inheritDoc */ - public function execute($eventObj, $className, $eventName, array &$parameters) + public function execute($eventObj, $className, $eventName, array &$parameters): void { if (OPENAUTH_CLIENT_ID === "" && OPENAUTH_CLIENT_SECRET === "") { return; } - $this->$eventName($eventObj, $parameters); + $this->{$eventName}($eventObj, $parameters); } /** - * @throws SystemException + * @throws \wcf\system\exception\SystemException * * @see AbstractForm::submit() */ - protected function submit(RegisterForm $eventObj) + protected function submit(RegisterForm $eventObj): void { $this->readData($eventObj); } /** - * @throws SystemException + * @throws \wcf\system\exception\SystemException * * @see AbstractPage::readData */ - protected function readData(RegisterForm $eventObj) + protected function readData(RegisterForm $eventObj): void { if ($this->isInitialized) { return; } + $this->isInitialized = true; if (WCF::getSession()->getVar('__3rdPartyProvider') !== 'openauth') { return; } - if (empty(WCF::getSession()->getVar('__openAuthData'))) { + if (empty(WCF::getSession()->getVar('__oauthUser'))) { return; } - $this->userData = WCF::getSession()->getVar('__openAuthData'); + $this->userData = WCF::getSession()->getVar('__oauthUser'); foreach (OpenAuthAPI::getInstance()->getUserOptionNames() as $optionName => $dataName) { if (empty($this->userData[$dataName])) { @@ -115,8 +115,9 @@ protected function readData(RegisterForm $eventObj) /** * @see AbstractForm::save() + * */ - protected function save(RegisterForm $eventObj) + protected function save(RegisterForm $eventObj): void { if (empty($this->userData)) { return; @@ -131,10 +132,7 @@ protected function save(RegisterForm $eventObj) } } - /** - * @return void - */ - protected function registerVia3rdParty(RegisterForm $eventObj, array &$parameters) + protected function registerVia3rdParty($eventObj, array &$parameters): void { if (empty($this->userData['email_verified'])) { return; @@ -154,8 +152,8 @@ protected function registerVia3rdParty(RegisterForm $eventObj, array &$parameter /** * @see RegisterForm::saved */ - protected function saved() + protected function saved(): void { - WCF::getSession()->unregister('__openAuthData'); + WCF::getSession()->unregister('__oauthUser'); } } diff --git a/files/lib/system/event/listener/OpenAuthUserProfileListener.class.php b/files/lib/system/event/listener/OpenAuthUserProfileListener.class.php index 19e6d74..0d72c3b 100644 --- a/files/lib/system/event/listener/OpenAuthUserProfileListener.class.php +++ b/files/lib/system/event/listener/OpenAuthUserProfileListener.class.php @@ -32,7 +32,7 @@ class OpenAuthUserProfileListener implements IParameterizedEventListener /** * @inheritDoc */ - public function execute($eventObj, $className, $eventName, array &$parameters) + public function execute($eventObj, $className, $eventName, array &$parameters): void { if ($eventObj->enableOpenAuthAvatar) { $parameters['avatar'] = new OpenAuthAvatar($eventObj->userID); diff --git a/files/lib/system/openauth/OpenAuthAPI.class.php b/files/lib/system/openauth/OpenAuthAPI.class.php index a289acb..ed930b3 100644 --- a/files/lib/system/openauth/OpenAuthAPI.class.php +++ b/files/lib/system/openauth/OpenAuthAPI.class.php @@ -23,107 +23,65 @@ * copies or substantial portions of the Software. */ -namespace wcf\system\openauth; +namespace wcf\system\openAuth; use Exception; +use GuzzleHttp\ClientInterface; +use GuzzleHttp\Exception\GuzzleException; +use GuzzleHttp\Psr7\Request; use wcf\system\exception\SystemException; +use wcf\system\io\HttpFactory; use wcf\system\option\user\UserOptionHandler; use wcf\system\request\LinkHandler; use wcf\system\SingletonFactory; -use wcf\util\HTTPRequest; use wcf\util\JSON; -use wcf\util\StringUtil; - -use function array_intersect; -use function array_keys; -use function array_search; -use function array_values; -use function http_build_query; -use function implode; -use function is_callable; -use function mb_strlen; -use function md5; -use function mt_rand; -use function openssl_random_pseudo_bytes; -use function random_bytes; -use function random_int; -use function sprintf; -use function substr; -use function uniqid; + +use function wcf\functions\exception\logThrowable; class OpenAuthAPI extends SingletonFactory { + protected const OPENAUTH_API_URL = 'https://openauth.dev/%s?clientID=%s'; + /** - * @var string + * @var ClientInterface */ - const OPENAUTH_API_URL = 'https://openauth.dev/%s?clientID=%s'; + protected $client; /** * @var array */ protected $configuration; + /** + * @inheritDoc + */ + public function init(): void + { + $this->client = HttpFactory::makeClient(['timeout' => 10]); + } + /** * Generates a random string, using the best available method. * * @throws SystemException * @throws Exception + * */ - public static function generateRandom(int $length = 32) + public static function generateRandom(?int $length = 32): ?string { - if (class_exists('\ParagonIE\ConstantTime\Hex')) { - $hexFn = '\ParagonIE\ConstantTime\Hex::encode'; - } else { - $hexFn = '\bin2hex'; - } - - if (!null === $length || $length <= 8) { + if (!null === $length || (int)$length <= 8) { $length = 32; } - if (is_callable('random_bytes')) { - return substr($hexFn(random_bytes($length)), -$length, $length); - } - - if (is_callable('openssl_random_pseudo_bytes')) { - /** @noinspection CryptographicallySecureRandomnessInspection */ - $random = openssl_random_pseudo_bytes($length, $isSourceStrong); - - if (false === $isSourceStrong || false === $random) { - throw new SystemException('IV generation failed'); - } - - return substr($hexFn($random), -$length, $length); - } - - if (is_callable('random_int')) { - $keySpace = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'; - $str = ''; - $max = mb_strlen($keySpace, '8bit') - 1; - - for ($i = 0; $i < $length; ++$i) { - $str .= $keySpace[random_int(0, $max)]; - } - - return $str; - } - - if (is_callable('mt_rand')) { - return substr(md5(uniqid(mt_rand(), true)), -$length, $length); - } - - if (is_callable('mcrypt_create_iv')) { - /** @noinspection CryptographicallySecureRandomnessInspection */ - $randomM = mcrypt_create_iv($length, MCRYPT_DEV_RANDOM); + $keySpace = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'; + $str = ''; + $max = \mb_strlen($keySpace, '8bit') - 1; - if (false === $randomM) { - throw new SystemException('IV generation failed'); - } - - return substr($hexFn($randomM), -$length, $length); + for ($i = 0; $i < $length; $i++) { + $str .= $keySpace[\random_int(0, $max)]; } - return StringUtil::getRandomID(); + return $str; } /** @@ -133,13 +91,10 @@ public static function generateRandom(int $length = 32) */ public function getRedirectURI(): string { - return LinkHandler::getInstance()->getLink( - 'OpenAuth', - [ - 'forceFrontend' => true, - 'application' => 'wcf' - ] - ); + return LinkHandler::getInstance()->getLink('OpenAuth', [ + 'forceFrontend' => true, + 'application' => 'wcf', + ]); } /** @@ -166,37 +121,35 @@ public function getUserOptionNames(): array * Loads the remote oAuth configuration. * * @throws Exception + * @throws \GuzzleHttp\Exception\GuzzleException */ - public function getOauthConfiguration(string $configurationName): string + public function getOauthConfiguration(string $configurationName) { if (null === $this->configuration) { $this->configuration = $this->call( - sprintf( - self::OPENAUTH_API_URL, - '.well-known/openid-configuration', - OPENAUTH_CLIENT_ID - ) + \sprintf(self::OPENAUTH_API_URL, 'open-id-configuration', OPENAUTH_CLIENT_ID) ); } if (!empty($this->configuration[$configurationName])) { - return (string)$this->configuration[$configurationName]; + return $this->configuration[$configurationName]; } - return ''; + return null; } /** * Returns a list of available scopes. * * @throws Exception + * @throws \GuzzleHttp\Exception\GuzzleException */ - public function getScopes(): array + public function getScopes(): ?array { $scopes = $this->getOauthConfiguration('scopes_supported'); if (null === $scopes) { - return []; + return null; } $scopes[] = 'openid'; @@ -206,55 +159,57 @@ public function getScopes(): array $optionHandler->setInRegistration(); $optionHandler->init(); - $scopesToRemove = array_intersect(array_keys($profileScopes), array_keys($optionHandler->options)); + $scopesToRemove = \array_intersect(\array_keys($profileScopes), \array_keys($optionHandler->options)); foreach ($scopesToRemove as $scopeToRemove) { $scope = $profileScopes[$scopeToRemove]; - $key = array_search($scope, $scopes, false); + $key = \array_search($scope, $scopes, true); if ($key !== false) { unset($scopes[$key]); } } - return array_values($scopes); + return \array_values($scopes); } /** * Returns the authorization Uri. * * @throws Exception + * @throws \GuzzleHttp\Exception\GuzzleException */ - public function getAuthorizationURI(string $state, array $scopes = []): string + public function getAuthorizationURI(string $state, array $scopes = []): ?string { $authorizationEndpoint = $this->getOauthConfiguration('authorization_endpoint'); if (null === $authorizationEndpoint) { - return ''; + return null; } $requestParameters = [ 'response_type' => 'code', 'client_id' => OPENAUTH_CLIENT_ID, 'state' => $state, - 'scope' => implode(' ', $scopes), - 'redirect_uri' => $this->getRedirectURI() + 'scope' => \implode(' ', $scopes), + 'redirect_uri' => $this->getRedirectURI(), ]; - return $authorizationEndpoint . '?' . http_build_query($requestParameters, null); + return $authorizationEndpoint . '?' . \http_build_query($requestParameters, null, '&'); } /** * Checks if the given oAuth code is valid. * * @throws Exception + * @throws \GuzzleHttp\Exception\GuzzleException */ - public function checkOAuthCode(string $code): array + public function checkOAuthCode($code): ?array { $tokenEndpoint = $this->getOauthConfiguration('token_endpoint'); if (null === $tokenEndpoint) { - return []; + return null; } $params = [ @@ -262,7 +217,7 @@ public function checkOAuthCode(string $code): array 'client_secret' => OPENAUTH_CLIENT_SECRET, 'code' => $code, 'grant_type' => 'authorization_code', - 'redirect_uri' => $this->getRedirectURI() + 'redirect_uri' => $this->getRedirectURI(), ]; return $this->call($tokenEndpoint, $params); @@ -272,13 +227,14 @@ public function checkOAuthCode(string $code): array * Returns user information for the given access token from the oAuth server. * * @throws Exception + * @throws \GuzzleHttp\Exception\GuzzleException */ - public function getUserInfo(string $accessToken): array + public function getUserInfo(string $accessToken): ?array { $userinfoEndpoint = $this->getOauthConfiguration('userinfo_endpoint'); if (null === $userinfoEndpoint) { - return []; + return null; } return $this->call($userinfoEndpoint, [], $accessToken); @@ -288,25 +244,33 @@ public function getUserInfo(string $accessToken): array * Executes http requests. * * @throws Exception + * @throws \GuzzleHttp\Exception\GuzzleException */ - protected function call(string $url, array $data = [], string $accessToken = null): array + protected function call(string $url, array $data = [], ?string $accessToken = null): ?array { try { - $request = new HTTPRequest($url, [], $data); + $headers = [ + 'accept-encoding' => 'gzip', + 'accept' => 'application/json', + ]; if (null !== $accessToken) { - $request->addHeader('Authorization', 'Bearer ' . $accessToken); + $headers['authorization'] = 'Bearer ' . $accessToken; } - $request->execute(); + $response = $this->client->send(new Request('POST', $url, $headers, $data)); - return JSON::decode($request->getReply()['body']); - } catch (Exception $e) { + if ($response->getStatusCode() === 200) { + return JSON::decode((string)$response->getBody()); + } + } catch (GuzzleException $e) { if (ENABLE_DEBUG_MODE) { throw $e; } - return []; + logThrowable($e); } + + return []; } } diff --git a/files/style/ui/userLoginOpenAuth.scss b/files/style/ui/userLoginOpenAuth.scss index 5ef0d1a..05f0f6c 100644 --- a/files/style/ui/userLoginOpenAuth.scss +++ b/files/style/ui/userLoginOpenAuth.scss @@ -2,9 +2,15 @@ font-family: 'woltauth'; font-weight: normal; font-style: normal; - src: url('../font/getFont.php?font=woltauth&type=woff2') format('woff2'), - url('../font/getFont.php?font=woltauth&type=ttf') format('truetype'), - url('../font/getFont.php?font=woltauth&type=woff') format('woff'); + --woltlab-suite-preload: #{preload( + getFont("woltauth.woff2", "", "1.0.0"), + $as: "font", + $crossorigin: true, + $type: "font/woff2" + )}; + src: url(getFont("woltauth.woff2", "", "1.0.0")) format("woff2"), + url(getFont("woltauth.woff", "", "1.0.0")) format("woff") + url(getFont("woltauth.ttf", "", "1.0.0")) format("truetype"); } .fa-woltauth:before { diff --git a/install.sql b/install.sql deleted file mode 100644 index 037063b..0000000 --- a/install.sql +++ /dev/null @@ -1,3 +0,0 @@ -ALTER TABLE wcf1_user ADD openAuthID INT(10); -ALTER TABLE wcf1_user ADD openAuthAvatar VARCHAR(200); -ALTER TABLE wcf1_user ADD enableOpenAuthAvatar TINYINT(1) NOT NULL DEFAULT 0; diff --git a/language/de.xml b/language/de.xml index 30641a9..77e84f2 100644 --- a/language/de.xml +++ b/language/de.xml @@ -10,7 +10,7 @@ - + session->getVar('__openAuthUsername')}(„{$__wcf->session->getVar('__openAuthUsername')}“){/if} verknüpfen]]> @@ -28,10 +28,9 @@ - - + diff --git a/language/en.xml b/language/en.xml index a7a770b..1b73f95 100644 --- a/language/en.xml +++ b/language/en.xml @@ -10,7 +10,7 @@ - + session->getVar('__openAuthUsername')} “{$__wcf->session->getVar('__openAuthUsername')}”{/if}.]]> @@ -19,7 +19,7 @@  OpenAuth.dev, therefore your username, your email address and your profile informations have been automatically filled.]]> - + @@ -28,10 +28,9 @@ - - + diff --git a/make.sh b/make.sh deleted file mode 100644 index 0619b21..0000000 --- a/make.sh +++ /dev/null @@ -1,13 +0,0 @@ -#!/bin/bash -PACKAGE_NAME="dev.openauth.wsc.login" -rm -f acptemplates.tar -rm -f files.tar -rm -f templates.tar -rm -f ${PACKAGE_NAME}.tar -7z a -ttar -mx=9 acptemplates.tar ./acptemplates/* -7z a -ttar -mx=9 files.tar ./files/* -7z a -ttar -mx=9 templates.tar ./templates/* -7z a -ttar -mx=9 ${PACKAGE_NAME}.tar ./* -x!acptemplates -x!files -x!templates -x!${PACKAGE_NAME}.tar -x!.git -x!.gitignore -x!make.bat -x!make.sh -x!.vscode -x!.idea -x!constants.php -rm -f acptemplates.tar -rm -f files.tar -rm -f templates.tar diff --git a/option.xml b/option.xml index c81a993..0ea5555 100644 --- a/option.xml +++ b/option.xml @@ -1,5 +1,5 @@ - +