diff --git a/.github/workflows/dokuwiki.yml b/.github/workflows/dokuwiki.yml new file mode 100644 index 0000000..bb23bc1 --- /dev/null +++ b/.github/workflows/dokuwiki.yml @@ -0,0 +1,11 @@ +name: DokuWiki Default Tasks +on: + push: + pull_request: + schedule: + - cron: '14 0 5 * *' + + +jobs: + all: + uses: dokuwiki/github-action/.github/workflows/all.yml@main diff --git a/.github/workflows/phpTestLinux.yml b/.github/workflows/phpTestLinux.yml deleted file mode 100644 index 968c447..0000000 --- a/.github/workflows/phpTestLinux.yml +++ /dev/null @@ -1,52 +0,0 @@ -name: PHP Tests on Linux - -on: [push, pull_request] - -jobs: - testLinux: - name: PHP ${{ matrix.php-versions }} DokuWiki ${{ matrix.dokuwiki-branch }} - runs-on: ubuntu-latest - if: github.event_name == 'push' || github.event.pull_request.head.repo.full_name != github.repository - - strategy: - matrix: - php-versions: ['7.2', '7.3', '7.4', '8.0'] - dokuwiki-branch: [ 'master', 'stable'] - exclude: - - dokuwiki-branch: 'stable' - php-versions: '8.0' - fail-fast: false - - steps: - - name: Checkout - uses: actions/checkout@v2 - - - name: Setup PHP - uses: shivammathur/setup-php@v2 - with: - php-version: ${{ matrix.php-versions }} - extensions: mbstring, intl, PDO, pdo_sqlite, bz2 - - - name: Setup problem matchers - run: | - echo ::add-matcher::${{ runner.tool_cache }}/php.json - echo ::add-matcher::${{ runner.tool_cache }}/phpunit.json - - - name: Download DokuWiki Test-setup - run: wget https://raw.github.com/splitbrain/dokuwiki-travis/master/travis.sh - - - name: Run DokuWiki Test-setup - env: - CI_SERVER: 1 - DOKUWIKI: ${{ matrix.dokuwiki-branch }} - run: sh travis.sh - - - name: Setup PHPUnit - run: | - php _test/fetchphpunit.php - cd _test - - - name: Run PHPUnit - run: | - cd _test - php phpunit.phar --verbose --stderr --group plugin_oauth diff --git a/Adapter.php b/Adapter.php index 814862a..de3969c 100644 --- a/Adapter.php +++ b/Adapter.php @@ -2,6 +2,8 @@ namespace dokuwiki\plugin\oauth; +use dokuwiki\Extension\EventHandler; +use dokuwiki\Extension\Event; use dokuwiki\Extension\ActionPlugin; use OAuth\Common\Consumer\Credentials; use OAuth\Common\Http\Exception\TokenResponseException; @@ -32,7 +34,7 @@ abstract class Adapter extends ActionPlugin * * @inheritDoc */ - public function register(\Doku_Event_Handler $controller) + public function register(EventHandler $controller) { $controller->register_hook('PLUGIN_OAUTH_BACKEND_REGISTER', 'AFTER', $this, 'handleRegister'); } @@ -40,7 +42,7 @@ public function register(\Doku_Event_Handler $controller) /** * Auto register this plugin with the oAuth authentication plugin */ - public function handleRegister(\Doku_Event $event, $param) + public function handleRegister(Event $event, $param) { $event->data[$this->getServiceID()] = $this; } @@ -64,6 +66,7 @@ public function initOAuthService($storageId = '') $serviceFactory = new ServiceFactory(); $serviceFactory->setHttpClient(new HTTPClient()); + $servicename = $this->getServiceID(); $serviceclass = $this->registerServiceClass(); if ($serviceclass) { @@ -143,8 +146,10 @@ public function refreshOutdatedToken() } $token = $oauth->getStorage()->retrieveAccessToken($oauth->service()); - if ($token->getEndOfLife() < 0 || - $token->getEndOfLife() - time() > 3600) { + if ( + $token->getEndOfLife() < 0 || + $token->getEndOfLife() - time() > 3600 + ) { // token is still good return; } @@ -168,7 +173,7 @@ public function refreshOutdatedToken() * but might need to be overwritten for specific services * * @throws TokenResponseException - * @throws Exception + * @throws \Exception */ public function login() { @@ -182,7 +187,7 @@ public function login() $parameters['state'] = urlencode(base64_encode(json_encode( [ 'animal' => $animal, - 'state' => md5(rand()), + 'state' => md5(random_int(0, mt_getrandmax())), ] ))); $oauth->getStorage()->storeAuthorizationState($oauth->service(), $parameters['state']); @@ -221,12 +226,10 @@ public function checkToken() $oauth = $this->getOAuthService(); if (is_a($oauth, Abstract2Service::class)) { - /** @var Abstract2Service $oauth */ if (!$INPUT->get->has('code')) return false; $state = $INPUT->get->str('state', null); $accessToken = $oauth->requestAccessToken($INPUT->get->str('code'), $state); } else { - /** @var Abstract1Service $oauth */ if (!$INPUT->get->has('oauth_token')) return false; /** @var TokenInterface $token */ $token = $oauth->getStorage()->retrieveAccessToken($this->getServiceID()); @@ -239,7 +242,8 @@ public function checkToken() if ( $accessToken->getEndOfLife() !== $accessToken::EOL_NEVER_EXPIRES && - !$accessToken->getRefreshToken()) { + !$accessToken->getRefreshToken() + ) { msg('Service did not provide a Refresh Token. You will be logged out when the session expires.'); } @@ -256,7 +260,7 @@ public function loginButton() global $ID; $attr = buildAttributes([ - 'href' => wl($ID, array('oauthlogin' => $this->getServiceID()), false, '&'), + 'href' => wl($ID, ['oauthlogin' => $this->getServiceID()], false, '&'), 'class' => 'plugin_oauth_' . $this->getServiceID(), 'style' => 'background-color: ' . $this->getColor(), ]); diff --git a/Exception.php b/Exception.php index a77842e..efdba5b 100644 --- a/Exception.php +++ b/Exception.php @@ -43,5 +43,4 @@ public function setContext(array $context) { $this->context = $context; } - } diff --git a/HTTPClient.php b/HTTPClient.php index 3dff1a2..8f18ea1 100644 --- a/HTTPClient.php +++ b/HTTPClient.php @@ -12,15 +12,14 @@ */ class HTTPClient implements ClientInterface { - /** @inheritDoc */ public function retrieveResponse( UriInterface $endpoint, $requestBody, - array $extraHeaders = array(), + array $extraHeaders = [], $method = 'POST' ) { - $http = new DokuHTTPClient; + $http = new DokuHTTPClient(); $http->keep_alive = false; $http->headers = array_merge($http->headers, $extraHeaders); diff --git a/OAuthManager.php b/OAuthManager.php index b00414f..b7f0f94 100644 --- a/OAuthManager.php +++ b/OAuthManager.php @@ -22,6 +22,7 @@ public function startFlow($servicename) $session = Session::getInstance(); $session->setLoginData($servicename, $ID); + $service = $this->loadService($servicename); $service->initOAuthService(); $service->login(); // redirects @@ -36,9 +37,7 @@ public function startFlow($servicename) */ public function continueFlow() { - return $this->loginByService() or - $this->loginBySession() or - $this->loginByCookie(); + return $this->loginByService() || $this->loginBySession() || $this->loginByCookie(); } /** @@ -62,6 +61,7 @@ protected function loginByService() if (!$logindata) return false; $service = $this->loadService($logindata['servicename']); $service->initOAuthService(); + $session->clearLoginData(); // oAuth login @@ -196,14 +196,14 @@ protected function validateUserData($userdata, $servicename) $hlp = plugin_load('helper', 'oauth'); if (!$hlp->checkMail($userdata['mail'])) { - throw new Exception('rejectedEMail', [join(', ', $hlp->getValidDomains())]); + throw new Exception('rejectedEMail', [implode(', ', $hlp->getValidDomains())]); } // make username from mail if empty if (!isset($userdata['user'])) $userdata['user'] = ''; $userdata['user'] = $auth->cleanUser((string)$userdata['user']); if ($userdata['user'] === '') { - list($userdata['user']) = explode('@', $userdata['mail']); + [$userdata['user']] = explode('@', $userdata['mail']); } // make full name from username if empty @@ -238,7 +238,7 @@ protected function processUserData($userdata, $servicename) if ($localUser) { $localUserInfo = $auth->getUserData($localUser); $localUserInfo['user'] = $localUser; - if(isset($localUserInfo['pass'])) unset($localUserInfo['pass']); + if (isset($localUserInfo['pass'])) unset($localUserInfo['pass']); // check if the user allowed access via this service if (!in_array($auth->cleanGroup($servicename), $localUserInfo['grps'])) { @@ -312,5 +312,4 @@ protected function loadService($servicename) if ($srv === null) throw new Exception("No such service $servicename"); return $srv; } - } diff --git a/RedirectSetting.php b/RedirectSetting.php index 7694207..545d3f4 100644 --- a/RedirectSetting.php +++ b/RedirectSetting.php @@ -7,10 +7,11 @@ /** * Custom Setting to display the default redirect URL */ -class RedirectSetting extends Setting { - +class RedirectSetting extends Setting +{ /** @inheritdoc */ - function update($input) { + public function update($input) + { return true; } @@ -22,11 +23,10 @@ public function html(\admin_plugin_config $plugin, $echo = false) $hlp = plugin_load('helper', 'oauth'); $key = htmlspecialchars($this->key); - $value = ''.$hlp->redirectURI().''; + $value = '' . $hlp->redirectURI() . ''; - $label = ''; - $input = '
'.$value.'
'; - return array($label, $input); + $label = ''; + $input = '
' . $value . '
'; + return [$label, $input]; } - } diff --git a/Service/AbstractOAuth2Base.php b/Service/AbstractOAuth2Base.php index 0d72fb0..5927984 100644 --- a/Service/AbstractOAuth2Base.php +++ b/Service/AbstractOAuth2Base.php @@ -12,7 +12,6 @@ */ abstract class AbstractOAuth2Base extends AbstractService { - /** @inheritdoc */ protected function parseAccessTokenResponse($responseBody) { @@ -55,5 +54,4 @@ public function isValidScope($scope) { return true; } - } diff --git a/Session.php b/Session.php index 530cfff..3b63438 100644 --- a/Session.php +++ b/Session.php @@ -8,7 +8,7 @@ class Session { /** @var Session */ - protected static $instance = null; + protected static $instance; /** * hidden constructor @@ -52,10 +52,7 @@ public function setLoginData($servicename, $id) */ public function getLoginData() { - if (isset($_SESSION[DOKU_COOKIE]['auth']['oauth'])) { - return $_SESSION[DOKU_COOKIE]['auth']['oauth']; - } - return false; + return $_SESSION[DOKU_COOKIE]['auth']['oauth'] ?? false; } /** @@ -83,10 +80,10 @@ public function setUser($userdata, $resettime = true) global $USERINFO; if ( - !isset($userdata['user']) or - !isset($userdata['name']) or - !isset($userdata['mail']) or - !isset($userdata['grps']) or + !isset($userdata['user']) || + !isset($userdata['name']) || + !isset($userdata['mail']) || + !isset($userdata['grps']) || !is_array($userdata['grps']) ) { throw new Exception('Missing user data, cannot save to session'); @@ -111,10 +108,7 @@ public function setUser($userdata, $resettime = true) */ public function getUser() { - if (isset($_SESSION[DOKU_COOKIE]['auth']['info'])) { - return $_SESSION[DOKU_COOKIE]['auth']['info']; - } - return false; + return $_SESSION[DOKU_COOKIE]['auth']['info'] ?? false; } /** @@ -133,7 +127,17 @@ public function setCookie($servicename, $storageId) $cookie = "$servicename|oauth|$storageId"; $cookieDir = empty($conf['cookiedir']) ? DOKU_REL : $conf['cookiedir']; $time = time() + $validityPeriodInSeconds; - setcookie(DOKU_COOKIE, $cookie, $time, $cookieDir, '', ($conf['securecookie'] && is_ssl()), true); + setcookie( + DOKU_COOKIE, + $cookie, + [ + 'expires' => $time, + 'path' => $cookieDir, + 'domain' => '', + 'secure' => $conf['securecookie'] && is_ssl(), + 'httponly' => true + ] + ); } /** @@ -144,7 +148,7 @@ public function setCookie($servicename, $storageId) public function getCookie() { if (!isset($_COOKIE[DOKU_COOKIE])) return false; - list($servicename, $oauth, $storageId) = explode('|', $_COOKIE[DOKU_COOKIE]); + [$servicename, $oauth, $storageId] = explode('|', $_COOKIE[DOKU_COOKIE]); if ($oauth !== 'oauth') return false; return ['servicename' => $servicename, 'storageId' => $storageId]; } @@ -173,6 +177,5 @@ public function clear() { //FIXME clear cookie? $this->clearLoginData(); - } } diff --git a/Storage.php b/Storage.php index cf2cb3a..f2bf76b 100644 --- a/Storage.php +++ b/Storage.php @@ -45,7 +45,7 @@ protected function loadServiceFile($service) if (file_exists($file)) { return unserialize(io_readFile($file, false)); } else { - return array(); + return []; } } diff --git a/action/login.php b/action/login.php index dad62a8..8bb16f8 100644 --- a/action/login.php +++ b/action/login.php @@ -1,5 +1,8 @@ */ -class action_plugin_oauth_login extends DokuWiki_Action_Plugin +class action_plugin_oauth_login extends ActionPlugin { /** @var helper_plugin_oauth */ protected $hlp; @@ -31,10 +34,10 @@ public function __construct() /** * Registers a callback function for a given event * - * @param Doku_Event_Handler $controller DokuWiki's event controller object + * @param EventHandler $controller DokuWiki's event controller object * @return void */ - public function register(Doku_Event_Handler $controller) + public function register(EventHandler $controller) { global $conf; if ($conf['authtype'] != 'oauth') return; @@ -51,10 +54,10 @@ public function register(Doku_Event_Handler $controller) /** * Start an oAuth login or restore environment after successful login * - * @param Doku_Event $event + * @param Event $event * @return void */ - public function handleStart(Doku_Event $event) + public function handleStart(Event $event) { global $INPUT; @@ -65,7 +68,7 @@ public function handleStart(Doku_Event $event) try { $om = new OAuthManager(); $om->startFlow($servicename); - } catch (TokenResponseException|Exception $e) { + } catch (TokenResponseException | Exception $e) { $this->hlp->showException($e, 'login failed'); } } @@ -73,11 +76,11 @@ public function handleStart(Doku_Event $event) /** * Add the oAuth login links to login form * - * @param Doku_Event $event event object by reference + * @param Event $event event object by reference * @return void * @deprecated can be removed in the future */ - public function handleOldLoginForm(Doku_Event $event) + public function handleOldLoginForm(Event $event) { /** @var Doku_Form $form */ $form = $event->data; @@ -103,11 +106,11 @@ public function handleOldLoginForm(Doku_Event $event) /** * Add the oAuth login links to login form * - * @param Doku_Event $event event object by reference + * @param Event $event event object by reference * @return void * @deprecated can be removed in the future */ - public function handleLoginForm(Doku_Event $event) + public function handleLoginForm(Event $event) { /** @var Form $form */ $form = $event->data; @@ -140,9 +143,9 @@ protected function prepareLoginButtons() if (count($validDomains) > 0) { $html .= '

' . sprintf( - $this->getLang('eMailRestricted'), - '' . join(', ', $validDomains) . '' - ) . '

'; + $this->getLang('eMailRestricted'), + '' . implode(', ', $validDomains) . '' + ) . '

'; } $html .= '
'; @@ -157,10 +160,10 @@ protected function prepareLoginButtons() /** * When singleservice is wanted, do not show login, but execute login right away * - * @param Doku_Event $event + * @param Event $event * @return bool */ - public function handleDoLogin(Doku_Event $event) + public function handleDoLogin(Event $event) { global $ID; global $INPUT; @@ -193,10 +196,10 @@ public function handleDoLogin(Doku_Event $event) * * This can happen when the user is already logged in, but still doesn't have enough permissions * - * @param Doku_Event $event + * @param Event $event * @return void */ - public function handleDeniedForm(Doku_Event $event) + public function handleDeniedForm(Event $event) { if ($this->getConf('singleService')) { $event->preventDefault(); diff --git a/action/user.php b/action/user.php index 2c07c69..639abfe 100644 --- a/action/user.php +++ b/action/user.php @@ -1,5 +1,8 @@ */ -class action_plugin_oauth_user extends DokuWiki_Action_Plugin +class action_plugin_oauth_user extends ActionPlugin { /** @var helper_plugin_oauth */ protected $hlp; @@ -29,18 +32,22 @@ public function __construct() /** * Registers a callback function for a given event * - * @param Doku_Event_Handler $controller DokuWiki's event controller object + * @param EventHandler $controller DokuWiki's event controller object * @return void */ - public function register(Doku_Event_Handler $controller) + public function register(EventHandler $controller) { global $conf; if ($conf['authtype'] != 'oauth') return; $conf['profileconfirm'] = false; // password confirmation doesn't work with oauth only users - $controller->register_hook('HTML_UPDATEPROFILEFORM_OUTPUT', 'BEFORE', $this, - 'handleOldProfileform'); // deprecated + $controller->register_hook( + 'HTML_UPDATEPROFILEFORM_OUTPUT', + 'BEFORE', + $this, + 'handleOldProfileform' + ); // deprecated $controller->register_hook('FORM_UPDATEPROFILE_OUTPUT', 'BEFORE', $this, 'handleProfileform'); $controller->register_hook('AUTH_USER_CHANGE', 'BEFORE', $this, 'handleUsermod'); } @@ -48,10 +55,10 @@ public function register(Doku_Event_Handler $controller) /** * Save groups for all the services a user has enabled * - * @param Doku_Event $event event object by reference + * @param Event $event event object by reference * @return void */ - public function handleUsermod(Doku_Event $event) + public function handleUsermod(Event $event) { global $ACT; global $USERINFO; @@ -91,11 +98,11 @@ public function handleUsermod(Doku_Event $event) /** * Add service selection to user profile * - * @param Doku_Event $event event object by reference + * @param Event $event event object by reference * @return void * @deprecated */ - public function handleOldProfileform(Doku_Event $event) + public function handleOldProfileform(Event $event) { global $USERINFO; /** @var auth_plugin_authplain $auth */ @@ -117,7 +124,10 @@ public function handleOldProfileform(Doku_Event $event) $group = $auth->cleanGroup($service->getServiceID()); $elem = form_makeCheckboxField( 'oauth_group[' . $group . ']', - 1, $service->getLabel(), '', 'simple', + 1, + $service->getLabel(), + '', + 'simple', [ 'checked' => (in_array($group, $USERINFO['grps'])) ? 'checked' : '', ] @@ -132,10 +142,10 @@ public function handleOldProfileform(Doku_Event $event) /** * Add service selection to user profile * - * @param Doku_Event $event event object by reference + * @param Event $event event object by reference * @return void */ - public function handleProfileform(Doku_Event $event) + public function handleProfileform(Event $event) { global $USERINFO; /** @var auth_plugin_authplain $auth */ diff --git a/auth.php b/auth.php index f6d94a9..96a4d85 100644 --- a/auth.php +++ b/auth.php @@ -43,7 +43,7 @@ public function trustExternal($user, $pass, $sticky = false) // either oauth or "normal" plain auth login via form $this->om = new OAuthManager(); if ($this->om->continueFlow()) return true; - if($this->getConf('singleService')) { + if ($this->getConf('singleService')) { return false; // no normal login in singleService mode } return null; // triggers the normal auth_login() @@ -132,7 +132,7 @@ public function registerOAuthUser(&$userinfo, $servicename) $count = 1; } } - $user = $user . $count; + $user .= $count; $userinfo['user'] = $user; $groups_on_creation = []; $groups_on_creation[] = $conf['defaultgroup']; @@ -175,7 +175,7 @@ public function getUserByEmail($mail) $mail = strtolower($mail); foreach ($this->users as $user => $userinfo) { - if (strtolower($userinfo['mail']) == $mail) return $user; + if (strtolower($userinfo['mail']) === $mail) return $user; } return false; diff --git a/conf/default.php b/conf/default.php index e7365e1..044c41f 100644 --- a/conf/default.php +++ b/conf/default.php @@ -1,4 +1,5 @@ */ - $meta['info'] = array(\dokuwiki\plugin\oauth\RedirectSetting::class); $meta['custom-redirectURI'] = array('string','_caution' => 'warning'); -$meta['mailRestriction'] = array('string','_pattern' => '!^(@[^,@]+(\.[^,@]+)+(,|$))*$!'); // https://regex101.com/r/mG4aL5/3 +// https://regex101.com/r/mG4aL5/3 +$meta['mailRestriction'] = array('string','_pattern' => '!^(@[^,@]+(\.[^,@]+)+(,|$))*$!'); $meta['singleService'] = array('onoff'); $meta['register-on-auth'] = array('onoff','_caution' => 'security'); $meta['overwrite-groups'] = array('onoff','_caution' => 'danger'); diff --git a/helper.php b/helper.php index 887baf8..8cbd185 100644 --- a/helper.php +++ b/helper.php @@ -1,4 +1,7 @@ */ +use dokuwiki\Extension\Plugin; use dokuwiki\Extension\Event; use dokuwiki\plugin\oauth\Adapter; -require_once(__DIR__ . '/vendor/autoload.php'); +require_once(__DIR__ . '/vendor/autoload.php'); // @todo can be removed with next dw release /** * Basic helper methods for the oauth flow */ -class helper_plugin_oauth extends DokuWiki_Plugin +class helper_plugin_oauth extends Plugin { - /** * Load the needed libraries and initialize the named oAuth service * @@ -62,10 +65,9 @@ public function listServices($enabledonly = true) // filter out unconfigured services if ($enabledonly) { - $services = array_filter($services, function ($service) { + $services = array_filter($services, static fn($service) => /** @var Adapter $service */ - return (bool)$service->getKey(); - }); + (bool)$service->getKey()); } return $services; @@ -77,11 +79,10 @@ public function listServices($enabledonly = true) public function getValidDomains() { if ($this->getConf('mailRestriction') === '') { - return array(); + return []; } $validDomains = explode(',', trim($this->getConf('mailRestriction'), ',')); - $validDomains = array_map('trim', $validDomains); - return $validDomains; + return array_map('trim', $validDomains); } /** @@ -95,7 +96,7 @@ public function checkMail($mail) if (empty($validDomains)) return true; foreach ($validDomains as $validDomain) { - if (substr($mail, -strlen($validDomain)) === $validDomain) { + if (str_ends_with($mail, $validDomain)) { return true; } } @@ -108,7 +109,7 @@ public function checkMail($mail) * @param Exception $e * @param string $friendly - user friendly explanation if available */ - public function showException(\Exception $e, $friendly = '') + public function showException(Exception $e, $friendly = '') { global $conf; @@ -117,7 +118,7 @@ public function showException(\Exception $e, $friendly = '') // translate the message if possible, using context if available $trans = $this->getLang($msg); if ($trans) { - if (is_a($e, \dokuwiki\plugin\oauth\Exception::class)) { + if ($e instanceof \dokuwiki\plugin\oauth\Exception) { $context = $e->getContext(); $trans = sprintf($trans, ...$context); }