Skip to content

Commit

Permalink
Merge pull request #11 from glennacheson/allow-nameid-override
Browse files Browse the repository at this point in the history
Tested, works flawlessly
  • Loading branch information
steve-ks authored Oct 19, 2018
2 parents 6140436 + 453f601 commit 4b656b8
Show file tree
Hide file tree
Showing 2 changed files with 80 additions and 53 deletions.
6 changes: 6 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -57,13 +57,19 @@ Within the saml.php config file the SAML Service Provider array needs to be fill
// The destination is the consuming SAML URL. This might be a SamlAuthController receiving the SAML response.
'destination' => 'https://localhost/samlsp/module.php/saml/sp/saml2-acs.php/default-sp',
// Issuer could be anything, mostly it makes sense to pass the metadata URL
// This is the IDP Entity ID that will be sent to the SP.
'issuer' => 'https://localhost',
// OPTIONAL: Use a specific audience restriction value when creating the SAMLRequest object.
// Default value is the assertion consumer service URL (the base64 encoded SP url).
// This is a bugfix for Nextcloud as SP and can be removed for normal SPs.
'audience_restriction' => 'http://localhost',
// OPTIONAL: Use to override the default email attribute to set a custom NameID value for a SP
// The NameID attribute must be a property on the User model.
'nameID' => 'username',
],
],
Expand Down
127 changes: 74 additions & 53 deletions src/Http/Traits/SamlAuth.php
Original file line number Diff line number Diff line change
Expand Up @@ -21,62 +21,68 @@ trait SamlAuth

/**
* Get either the url or the content of a given file.
*/
protected function getSamlFile($configPath, $url) {
if ($url)
*/
protected function getSamlFile($configPath, $url)
{
if ($url) {
return Storage::disk('saml')->url($configPath);
}
return Storage::disk('saml')->get($configPath);
}
}

/**
* Get either the url or the content of the saml metadata file.
*
* @param boolean url Set to true to get the metadata url, otherwise the
* file content will be returned. Defaults to false.
* file content will be returned. Defaults to false.
* @return String with either the url or the content
*/
protected function metadata($url = false) {
protected function metadata($url = false)
{
return $this->getSamlFile(config('saml.idp.metadata'), $url);
}

/**
* Get either the url or the content of the certificate file.
*
* @param boolean url Set to true to get the certificate url, otherwise the
* file content will be returned. Defaults to false.
* file content will be returned. Defaults to false.
* @return String with either the url or the content
*/
protected function certfile($url = false) {
protected function certfile($url = false)
{
return $this->getSamlFile(config('saml.idp.cert'), $url);
}

/**
* Get either the url or the content of the certificate keyfile.
*
* @param boolean url Set to true to get the certificate key url, otherwise
* the file content will be returned. Defaults to false.
* the file content will be returned. Defaults to false.
* @return String with either the url or the content
*/
protected function keyfile($url = false) {
protected function keyfile($url = false)
{
return $this->getSamlFile(config('saml.idp.key'), $url);
}

/*
|--------------------------------------------------------------------------
| Saml authentication
|--------------------------------------------------------------------------
*/
*/

/**
* Handle an http request as saml authentication request. Note that the method
* should only be called in case a saml request is also included.
* should only be called in case a saml request is also included.
*
* @param \Illuminate\Http\Request $request
* @return void
*/
public function handleSamlLoginRequest($request) {
*/
public function handleSamlLoginRequest($request)
{
// Store RelayState to session if provided
if(!empty($request->input('RelayState'))){
if (!empty($request->input('RelayState'))) {
session()->put('RelayState', $request->input('RelayState'));
}
// Handle SamlRequest if provided, otherwise just exit
Expand Down Expand Up @@ -112,7 +118,7 @@ protected function buildSamlResponse($authnRequest, $request)

// Load in both certificate and keyfile
// The files are stored within a private storage path, this prevents from
// making them accessible from outside
// making them accessible from outside
$x509 = new X509Certificate();
$certificate = $x509->loadPem($this->certfile());
// Load in keyfile content (last parameter determines of the first one is a file or its content)
Expand Down Expand Up @@ -144,16 +150,18 @@ protected function buildSamlResponse($authnRequest, $request)
// We are responding with both the email and the username as attributes
// TODO: Add here other attributes, e.g. groups / roles / permissions
$roles = array();
if(\Auth::check()){
$user = \Auth::user();
$email = $user->email;
$name = $user->name;
if (config('saml.forward_roles'))
if (\Auth::check()) {
$user = \Auth::user();
$nameID = $this->getNameId($user, $authnRequest);
$email = $user->email;
$name = $user->name;
if (config('saml.forward_roles')) {
$roles = $user->roles->pluck('name')->all();
}else {
}
} else {
$email = $request['email'];
$name = 'Place Holder';
}
}

// Generate the SAML assertion for the response xml object
$assertion
Expand All @@ -162,60 +170,61 @@ protected function buildSamlResponse($authnRequest, $request)
->setIssuer(new \LightSaml\Model\Assertion\Issuer($issuer))

->setSubject(
(new \LightSaml\Model\Assertion\Subject())
(new \LightSaml\Model\Assertion\Subject())
->setNameID(new \LightSaml\Model\Assertion\NameID(
$email,
$nameID,
\LightSaml\SamlConstants::NAME_ID_FORMAT_EMAIL
))
->addSubjectConfirmation(
(new \LightSaml\Model\Assertion\SubjectConfirmation())
(new \LightSaml\Model\Assertion\SubjectConfirmation())
->setMethod(\LightSaml\SamlConstants::CONFIRMATION_METHOD_BEARER)
->setSubjectConfirmationData(
(new \LightSaml\Model\Assertion\SubjectConfirmationData())
(new \LightSaml\Model\Assertion\SubjectConfirmationData())
->setInResponseTo($authnRequest->getId())
->setNotOnOrAfter(new \DateTime('+1 MINUTE'))
->setRecipient($authnRequest->getAssertionConsumerServiceURL())
)
)
)
)
)
)
->setConditions(
(new \LightSaml\Model\Assertion\Conditions())
->setNotBefore(new \DateTime())
->setNotOnOrAfter(new \DateTime('+1 MINUTE'))
->addItem(
new \LightSaml\Model\Assertion\AudienceRestriction([
config('saml.sp.'.base64_encode($authnRequest->getAssertionConsumerServiceURL()).'.audience_restriction',
$authnRequest->getAssertionConsumerServiceURL())])
config(
'saml.sp.'.base64_encode($authnRequest->getAssertionConsumerServiceURL()).'.audience_restriction',
$authnRequest->getAssertionConsumerServiceURL()
)])
)
)
->addItem(
(new \LightSaml\Model\Assertion\AttributeStatement())
(new \LightSaml\Model\Assertion\AttributeStatement())
->addAttribute(new \LightSaml\Model\Assertion\Attribute(
\LightSaml\ClaimTypes::EMAIL_ADDRESS,
$email
))
\LightSaml\ClaimTypes::EMAIL_ADDRESS,
$email
))
->addAttribute(new \LightSaml\Model\Assertion\Attribute(
\LightSaml\ClaimTypes::COMMON_NAME,
$name
))
\LightSaml\ClaimTypes::COMMON_NAME,
$name
))
->addAttribute(new \LightSaml\Model\Assertion\Attribute(
\LightSaml\ClaimTypes::ROLE,
$roles
))
)
\LightSaml\ClaimTypes::ROLE,
$roles
))
)
->addItem(
(new \LightSaml\Model\Assertion\AuthnStatement())
->setAuthnInstant(new \DateTime('-10 MINUTE'))
->setSessionIndex('_some_session_index')
->setAuthnContext(
(new \LightSaml\Model\Assertion\AuthnContext())
->setAuthnContextClassRef(\LightSaml\SamlConstants::AUTHN_CONTEXT_PASSWORD_PROTECTED_TRANSPORT)
)
(new \LightSaml\Model\Assertion\AuthnStatement())
->setAuthnInstant(new \DateTime('-10 MINUTE'))
->setSessionIndex('_some_session_index')
->setAuthnContext(
(new \LightSaml\Model\Assertion\AuthnContext())
->setAuthnContextClassRef(\LightSaml\SamlConstants::AUTHN_CONTEXT_PASSWORD_PROTECTED_TRANSPORT)
)
;
);

// Send out the saml response
$this->sendSamlResponse($response);
// Send out the saml response
$this->sendSamlResponse($response);
}

/**
Expand Down Expand Up @@ -245,4 +254,16 @@ protected function addRelayStateToResponse($response)
session()->remove('RelayState');
}
}

/**
* Get the NameID attribute to be used.
* @param User $user
* @param \Illuminate\Http\Request $request
* @return string
*/
protected function getNameId($user, $authnRequest)
{
$nameIdAttribute = config('saml.sp.'.base64_encode($authnRequest->getAssertionConsumerServiceURL()).'.nameID', 'email');
return $user->{$nameIdAttribute};
}
}

0 comments on commit 4b656b8

Please sign in to comment.