diff --git a/htdocs/web_portal/index.php b/htdocs/web_portal/index.php index 2e21d49f6..3a2a012f3 100644 --- a/htdocs/web_portal/index.php +++ b/htdocs/web_portal/index.php @@ -26,6 +26,8 @@ // Require GocContextPath which is used in most of the views scripts require_once __DIR__.'/GocContextPath.php'; +use org\gocdb\security\authentication\BadCredentialsException; + // Set the timezone date_default_timezone_set("UTC"); @@ -84,9 +86,21 @@ function rejectIfNotAuthenticated($message = null){ try { Draw_Page($Page_Type); +} catch (BadCredentialsException $error) { + /** + * `show_view('error.php', ..., $rawOutput)` is not suitable here. + * - setting rawOutput to FALSE triggers another exception because it + * tries to render a pretty error in a GOCDB window, which fails because + * the user isn't authroised. + * - setting rawOutput to TRUE also isn't ideal as it displays html tags + * in the otherwise nicely formatted output. + * die-ing like this atleast gives the user a somewhat nicely formatted + * error. + */ + die($error->getMessage()); } catch (ErrorException $e) { /* ErrorExceptions may be thrown by an invalid configuration so it is - not safe to try to give a pretty output. Set 'raw' to true. */ + not safe to try to give a pretty output. Set 'rawOutput' to true. */ show_view('error.php', $e->getMessage(), NULL, TRUE); die(); } catch(Exception $e) { diff --git a/lib/Authentication/AuthTokens/EOSCAAIAuthToken.php b/lib/Authentication/AuthTokens/EOSCAAIAuthToken.php new file mode 100644 index 000000000..4b6e6080b --- /dev/null +++ b/lib/Authentication/AuthTokens/EOSCAAIAuthToken.php @@ -0,0 +1,31 @@ +acceptedIssuers = array("https://aai-demo.eosc-portal.eu/auth/realms/core"); + $this->authRealm = "EOSC Proxy IdP"; + $this->groupHeader = "OIDC_CLAIM_eduperson_entitlement"; + $this->groupSplitChar = ','; + $this->bannedGroups = array(); + $this->requiredGroups = array("urn:geant:eosc-portal.eu:res:gocdb.eosc-portal.eu"); + $this->helpString = 'Please seek assistance by opening a ticket against the ' . + '"EOSC AAI: Core Infrastructure Proxy" group in ' . + 'https://eosc-helpdesk.eosc-portal.eu/'; + + if (isset($_SERVER['OIDC_access_token'])) { + $this->setTokenFromSession(); + } + } +} diff --git a/lib/Authentication/AuthTokens/OIDCAuthToken.php b/lib/Authentication/AuthTokens/OIDCAuthToken.php new file mode 100644 index 000000000..d14f7e5bb --- /dev/null +++ b/lib/Authentication/AuthTokens/OIDCAuthToken.php @@ -0,0 +1,211 @@ +authorities; + } + + /** + * {@see IAuthentication::getCredentials()} + * @return string An empty string as passwords are not used by this token. + */ + public function getCredentials() + { + return ""; // none used in this token, handled by IdP + } + + /** + * A custom object used to store additional user details. + * Allows non-security related user information (such as email addresses, + * telephone numbers etc) to be stored in a convenient location. + * {@see IAuthentication::getDetails()} + * + * @return Object or null if not used + */ + public function getDetails() + { + return $this->userDetails; + } + + /** + * {@see IAuthentication::getPrinciple()} + * @return string unique principle string of user + */ + public function getPrinciple() + { + return $this->principal; + } + + /** + * {@see IAuthentication::setAuthorities($authorities)} + */ + public function setAuthorities($authorities) + { + $this->authorities = $authorities; + } + + /** + * {@see IAuthentication::setDetails($userDetails)} + * @param Object $userDetails + */ + public function setDetails($userDetails) + { + $this->userDetails = $userDetails; + } + + /** + * {@see IAuthentication::validate()} + */ + public function validate() + { + } + + /** + * {@see IAuthentication::isPreAuthenticating()} + */ + public static function isPreAuthenticating() + { + return true; + } + + /** + * Returns true, this token reads the session attributes and so + * does not need to be stateful itself. + * {@see IAuthentication::isStateless()} + */ + public static function isStateless() + { + return true; + } + + /** + * Set principal/User details from the session and check group membership. + */ + protected function setTokenFromSession() + { + if (in_array($_SERVER['OIDC_CLAIM_iss'], $this->acceptedIssuers, true)) { + $this->principal = $_SERVER['REMOTE_USER']; + $this->userDetails = array( + 'AuthenticationRealm' => array($this->authRealm) + ); + + // Check group membership is acceptable. + $this->checkBannedGroups(); + $this->checkRequiredGroups(); + } + } + + /** + * Check the token lists all the required groups. + */ + protected function checkRequiredGroups() + { + $groupArray = explode( + $this->groupSplitChar, + $_SERVER[$this->groupHeader] + ); + + // Build up a list of missing groups. + $missingGoodGroups = []; + foreach ($this->requiredGroups as $group) { + if (!in_array($group, $groupArray)) { + $missingGoodGroups[] = $group; + } + } + + // If the list of missing groups is not empty, reject the user. + if (!empty($missingGoodGroups)) { + $this->rejectUser( + 'You are missing the following group(s):', + $missingGoodGroups + ); + } + } + + /** + * Check the token lists none of the banned groups. + */ + protected function checkBannedGroups() + { + $groupArray = explode( + $this->groupSplitChar, + $_SERVER[$this->groupHeader] + ); + + $presentBadGroups = []; + foreach ($this->bannedGroups as $group) { + if (in_array($group, $groupArray)) { + $presentBadGroups[] = $group; + } + } + + // If the list of present bad groups is not empty, reject the user. + if (!empty($presentBadGroups)) { + $this->rejectUser( + 'We do not grant access to GOCDB to members of the following group(s):', + $presentBadGroups + ); + } + } + + /** + * Craft a BadCredentialsException exception. + * + * Uses the given error message to provide the end user more context. + * + * @param string $errorContext Context for the error. + * @param string[] $groupArray An array of group memberships + */ + protected function rejectUser($errorContext, $groupArray) + { + // For readability, when listing groups to the user, + // start each one on a new line with a '-' character. + $prependString = '
- '; + $groupString = implode($prependString, $groupArray); + throw new BadCredentialsException( + null, + 'You do not belong to the correct group(s) ' . + 'to gain access to this site.

' . $errorContext . + $prependString . $groupString . '

' . $this->helpString + ); + } +}