-
Notifications
You must be signed in to change notification settings - Fork 28
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
GT-56: Add base class for OIDC AuthTokens, add EOSCAAI AuthToken #430
base: dev
Are you sure you want to change the base?
Changes from all commits
25fbf08
b6d8bd9
59d65ca
c771f70
e1c9d77
c35e4b5
7677f3a
0db9af6
4d6f481
8e8160b
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
<?php | ||
|
||
namespace org\gocdb\security\authentication; | ||
|
||
/** | ||
* AuthToken for use with the EOSC AAI | ||
* | ||
* Requires installation/config of mod_auth_openidc before use. | ||
* | ||
* The token is stateless because it relies on the mod_auth_openidc | ||
* session and simply reads the attributes stored in the session. | ||
*/ | ||
class EOSCAAIAuthToken extends OIDCAuthToken | ||
{ | ||
public function __construct() | ||
{ | ||
$this->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 ' . | ||
'<a href=https://eosc-helpdesk.eosc-portal.eu/>https://eosc-helpdesk.eosc-portal.eu/</a>'; | ||
|
||
if (isset($_SERVER['OIDC_access_token'])) { | ||
$this->setTokenFromSession(); | ||
} | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,211 @@ | ||
<?php | ||
|
||
namespace org\gocdb\security\authentication; | ||
|
||
/** | ||
* An abstract class for the logic of integrating with IdPs via OIDC. | ||
* | ||
* It is expected that concrete subclasses are created for each | ||
* new IdP GOCDB integrates with via OIDC, providing specific information | ||
* for that IdP. | ||
* | ||
* Any subclass will require installation/config of mod_auth_openidc | ||
* before use. | ||
* | ||
* Any subclass token is expected to be stateless because it relies on the | ||
* mod_auth_openidc session and simply reads the attributes stored in the | ||
* session. | ||
*/ | ||
abstract class OIDCAuthToken implements IAuthentication | ||
tofu-rocketry marked this conversation as resolved.
Show resolved
Hide resolved
|
||
{ | ||
private $userDetails = null; | ||
private $authorities = array(); | ||
private $principal; | ||
protected $acceptedIssuers; | ||
protected $authRealm; | ||
protected $groupHeader; | ||
protected $groupSplitChar; | ||
protected $bannedGroups; | ||
protected $requiredGroups; | ||
protected $helpString; | ||
|
||
/** | ||
* {@see IAuthentication::eraseCredentials()} | ||
*/ | ||
public function eraseCredentials() | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why is this function and validate() function below left blank? |
||
{ | ||
} | ||
|
||
/** | ||
* {@see IAuthentication::getAuthorities()} | ||
*/ | ||
public function getAuthorities() | ||
{ | ||
return $this->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 = []; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. should this block of code start with a comment, like in the similar function checkRequiredGroups() above? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. saying something like "// Build up a list of banned groups" |
||
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 = '<br />- '; | ||
$groupString = implode($prependString, $groupArray); | ||
throw new BadCredentialsException( | ||
null, | ||
'You do not belong to the correct group(s) ' . | ||
'to gain access to this site.<br /><br />' . $errorContext . | ||
$prependString . $groupString . '<br /><br />' . $this->helpString | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. is $prependString needed here, when $groupString starts with $prependString? |
||
); | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
helpMessage
is probably more readable thanhelpString