Closed INSEAD-asim closed 8 years ago
Hi, Can you provide config.yml and security.yml
Thanks for so fast. Please find them as below: :)
// /app/config/security.yml
security:
encoders:
Acme\ADFS\Security\User\MyCustomUserProvider: plaintext
role_hierarchy:
# ROLE_ADMIN: ROLE_USER
# ROLE_SUPER_ADMIN: [ROLE_USER, ROLE_ADMIN, ROLE_ALLOWED_TO_SWITCH]
providers:
saml_user_provider:
id: my.custom.service # implements UserManagerInterface
firewalls:
saml:
pattern: ^/
anonymous: false
aerial_ship_saml_sp:
local_logout_path: /logout
provider: saml_user_provider
create_user_if_not_exists: false
services:
somename:
idp:
file: "@AcmeADFSBundle/Resources/MetaData.xml"
sp:
config:
entity_id: https://secured.example.edu/
logout:
path: /logout
target: /
invalidate_session: true
dev:
pattern: ^/(_(profiler|wdt)|css|images|js)/
security: false
access_control:
- { path: ^/saml/sp, roles: IS_AUTHENTICATED_ANONYMOUSLY }
# - { path: ^/saml/sp/login, roles: IS_AUTHENTICATED_ANONYMOUSLY }
# - { path: ^/saml/sp/acs, roles: IS_AUTHENTICATED_ANONYMOUSLY }
- { path: ^/demo/secured/hello, roles: ROLE_ADMIN }
- { path: ^/, roles: ROLE_USER }
# - { path: ^/login, roles: IS_AUTHENTICATED_ANONYMOUSLY, requires_channel: https }
// /app/config/config.yml
imports:
- { resource: parameters.yml }
- { resource: security.yml }
framework:
#esi: ~
#translator: { fallback: %locale% }
secret: %secret%
router:
resource: "%kernel.root_dir%/config/routing.yml"
strict_requirements: ~
form: ~
csrf_protection: ~
validation: { enable_annotations: true }
templating:
engines: ['twig']
#assets_version: SomeVersionScheme
default_locale: "%locale%"
trusted_proxies: ~
session: ~
fragments: ~
# Twig Configuration
twig:
debug: %kernel.debug%
strict_variables: %kernel.debug%
# Assetic Configuration
assetic:
debug: %kernel.debug%
use_controller: false
bundles: [ ]
#java: /usr/bin/java
filters:
cssrewrite: ~
#closure:
# jar: %kernel.root_dir%/Resources/java/compiler.jar
#yui_css:
# jar: %kernel.root_dir%/Resources/java/yuicompressor-2.4.7.jar
# Doctrine Configuration
doctrine:
dbal:
driver: %database_driver%
host: %database_host%
port: %database_port%
dbname: %database_name%
user: %database_user%
password: %database_password%
charset: UTF8
# if using pdo_sqlite as your database driver, add the path in parameters.yml
# e.g. database_path: %kernel.root_dir%/data/data.db3
# path: %database_path%
orm:
auto_generate_proxy_classes: %kernel.debug%
auto_mapping: true
# Swiftmailer Configuration
swiftmailer:
transport: %mailer_transport%
host: %mailer_host%
username: %mailer_user%
password: %mailer_password%
spool: { type: memory }
aerial_ship_saml_sp:
driver: orm
sso_state_entity_class: Acme\ADFSBundle\Entity\SSOState
Also, let me know if I need to send claims from ADFS server in any specific SHA hash. Our server support SHA-1 and SHA-256 only.
Judging by the method and Configuration Reference
Your configuration is missing base_url
# if different then url being used in request
# used for construction of assertion consumer and logout urls in SP entity descriptor
base_url: https://100.200.100.200/
/**
* @param string $path
* @return string
* @throws \RuntimeException
*/
protected function buildPath($path)
{
if (isset($this->config['base_url']) && $this->config['base_url']) {
return $this->config['base_url'] . $path;
} else {
if (!$this->request) {
throw new \RuntimeException('Request not set');
}
return $this->httpUtils->generateUri($this->request, $path);
}
}
Since method should be compatible with request also ill look into the creation to see why it is failing to set the Request
Thanks @i3or1s :) It is not showing error any more after base_url but it is also not invalidating session. Whenever I navigate to /logout
, it redirects to IdP and then comes back to SP but I am still login with valid rule. Am I still missing something?
I have one more issue... application stuck in loop when UsernameNotFoundException
thrown. Below is the code of user provider and associated class
// Acme/ADFSBundle/Security/User/MyCustomUserProvider
<?php
namespace Acme\ADFSBundle\Security\User;
use Symfony\Component\Security\Core\User\UserInterface;
use Symfony\Component\Security\Core\Exception\UsernameNotFoundException;
use Symfony\Component\Security\Core\Exception\UnsupportedUserException;
use AerialShip\SamlSPBundle\Security\Core\User\UserManagerInterface;
use AerialShip\SamlSPBundle\Bridge\SamlSpInfo;
class MyCustomUserProvider implements UserManagerInterface
{
public function loadUserByUsername($username)
{
if ($username == '0846037') {
$roles = array('ROLE_USER');
return new MyCustomUser($username, $roles);
}
throw new UsernameNotFoundException(
sprintf('Username "%s" does not exist.', $username)
);
}
public function loadUserBySamlInfo(SamlSpInfo $samlInfo)
{
$username = $samlInfo->getNameID()->getValue();
return $this->loadUserByUsername($username);
}
public function createUserFromSamlInfo(SamlSpInfo $samlInfo)
{
throw new UsernameNotFoundException(
sprintf('Username "%s" does not exist.', $samlInfo)
);
}
public function refreshUser(UserInterface $user)
{
if (!$user instanceof MyCustomUser) {
throw new UnsupportedUserException(
sprintf('Instances of "%s" are not supported.', get_class($user))
);
}
return $this->loadUserByUsername($user->getUsername());
}
public function supportsClass($class)
{
return $class === 'Acme\ADFSBundle\Security\User\MyCustomUser';
}
}
// Acme/ADFSBundle/Security/User/MyCustomUser
<?php
namespace Acme\ADFSBundle\Security\User;
use Symfony\Component\Security\Core\User\UserInterface;
use Symfony\Component\Security\Core\User\EquatableInterface;
class MyCustomUser implements UserInterface, EquatableInterface
{
private $username;
private $roles;
public function __construct($username, array $roles)
{
$this->username = $username;
$this->roles = $roles;
}
public function getRoles()
{
return $this->roles;
}
public function getPassword()
{
return '';
}
public function getSalt()
{
return '';
}
public function getUsername()
{
return $this->username;
}
public function eraseCredentials()
{
}
public function isEqualTo(UserInterface $user)
{
if (!$user instanceof MyCustomUser) {
return false;
}
if ($this->username !== $user->getUsername()) {
return false;
}
if ($this->roles !== $user->getRoles()) {
return false;
}
return true;
}
}
Hi,
When loging out you should first visit saml logout route then code will redirect you to regular /logout
As for the loop i would need to check rest of the code, but from what i see i think your problem might be in the refreshUser(UserInterface $user)
since this method is calling loadUserByUsername($user->getUsername())
and loadUserByUsername
is throwing an exception that you did not handle.
I would not call loadUserByUsername
at all since you already have a user.
You can check out http://symfony.com/doc/current/cookbook/security/entity_provider.html might give you some insight.
Is saml logout route is /saml/sp/logout
or one provided by my IdP? If I call IdP logout url, it comes back and show me XML file I sent you earlier :(
I will try to fix the things you mentioned. But when I didn't implemented loadUserByUsername
, it is not loading user in role in loadUserBySamlInfo
i.e. if I just throw error it is again in infinite loop. Can you tell me what would be the both functions in this case? These are the only issue I am stuck. Rest I found your bundle very useful.
Hi @i3or1s, I am just back from my vacations and started looking into this again. I follow your guide as per directions to see if I miss anything. I am only facing two issues i.e. not logging out and site stuck in loop for failed authorization. I call the saml logout (IdP logout) page and it redirects me back to SP but it shows XML page. If I try to load site again, it shows error at /saml/sp/failure
and next reloads auto-login me at site. If I don't implement refreshUser()
or loadUserByUsername()
function, my site is not working as Symfony try to reload user information at each new request. Can you help if I am still missing something? I can also try to look into the code and fix if think there is some bug. Thanks
What did you register in IDP as logout route?
It seems I figure the fix for site stuck in loop for failed authorization. I am returning $user
as it is in refreshUser()
, authorizing user with empty roles in loadUserByUsername()
and authorizing user with valid roles in loadUserBySamlInfo()
. It shows 403 Access Denied in case of failed authorization and no more loop. I will perform some more test to see if it would be valid.
Regarding logout route, my IdP has my SP logout route i.e. /saml/sp/logout
. I am calling my IdP logout i.e. https://adfs.example.org/adfs/ls/?wa=wsignout1.0
Ok that is good :smile: :+1:
Thanks. But still I am facing issue with logout :(
Can you achieve logout on your application (without trying to do it through IDP)?
I think in your configuration setup is missing logout_path: /saml/sp/logout
The route in IdP is auto defined by metadata provided by your bundle /saml/sp/FederationMetadata.xml
. I am unable to logout without IdP as it also redirects to IdP and it again come to SP and shows same XML page. I try adding logout_path
in security.yml but still same. Do I need to setup something special in IdP for NameID or any other special parameter?
Logout saml/sp/logout
should invalidate SSOState and send to logout
which is handled by symfony. After everything is cleaned out it will redirect you to route which is behind firewall and will redirect you to login again on IDP.
I am not sure how you end up getting xml view (which is valid).
Looks to me that issue is in configuration. I am not sure what exactly is happening since i cant see the app it self running and debug.
I read again through entire config and from what i see all of the routes are behind ROLE_USER
access_control:
- { path: ^/, roles: ROLE_USER }
I suggest debugging entire process and pinpointing moment when issue occurs. It would be helpful to describe entire debug process and what happens up until the error.
One issue I found that in config, I set anonymous: false
. I change it to anonymous: true
. Now, it is showing the same XML at logout but if I call my SP url again, it shows "SSO session has expired" along with trace and SamlSpToken. I think the issue is on IdP as well as it is not invalidating my cookie and calling my SP again re-logins me without asking login information. I need to check this with our IdP expert may be they have insight.
It would be good if I can sort out this XML showing issue. I think it should either go to target
or success_handler
configured in security.yml. But it is not going anywhere and showing XML instead. Do you think that we need to use invalidate_session: true
?
Hi @i3or1s, I try to connect strings. Here are some findings.
In factory \vendor\aerialship\saml-sp-bundle\src\AerialShip\SamlSPBundle\DependencyInjection\Security\Factory\SamlSpFactory.php
, we have a function as:
protected function createRelyingPartyLogout(ContainerBuilder $container, $id)
{
$this->createRelyingPartyLogoutSendRequest($container, $id);
$this->createRelyingPartyLogoutReceiveResponse($container, $id);
$this->createRelyingPartyLogoutReceiveRequest($container, $id);
$service = new DefinitionDecorator('aerial_ship_saml_sp.relying_party.logout');
$container->setDefinition('aerial_ship_saml_sp.relying_party.logout.'.$id, $service);
$service->addMethodCall('append', array(new Reference('aerial_ship_saml_sp.relying_party.logout.receive_response.'.$id)));
$service->addMethodCall('append', array(new Reference('aerial_ship_saml_sp.relying_party.logout.receive_request.'.$id)));
// must come after receive response
$service->addMethodCall('append', array(new Reference('aerial_ship_saml_sp.relying_party.logout.send_request.'.$id)));
$service->addMethodCall('append', array(new Reference('aerial_ship_saml_sp.relying_party.logout.fallback')));
}
I think these functions are called in listener \vendor\aerialship\saml-sp-bundle\src\AerialShip\SamlSPBundle\Security\Http\Firewall\SamlSpAuthenticationListener.php
in function attemptAuthentication()
. Since the LogoutReceiveRequest->manage()
is returning response as XML in \vendor\aerialship\saml-sp-bundle\src\AerialShip\SamlSPBundle\Bridge\LogoutReceiveRequest.php
, it is shown in the browser and rest of the functions i.e. send_request and fallback not executed. Can you please help if I am missing something in configuration? To me, I look everything in configuration but it seems correct.
I also tried to remove logout URLs /saml/sp/logout
from IdP. It then throws an error while logout but interestingly when I visit my SP url, it asks for authentication at IdP.
Sorry, I am bothering you much but in-fact I really find this bundle useful and I like to use it in our application. Thanks for all your work.
@i3or1s, Can you share the documentation of IdP you used as reference to build this bundle? I am using this as a reference to understand the flow. http://support.ca.com/cadocs/0/CA%20SiteMinder%20r12%20SP2-ENU/Bookshelf_Files/HTML/252747.html
@i3or1s, I try to study IdP and SP behavior with help of documentations. My understanding it that when IdP send request with query string of parameters SAMLRequest
, the SP should output redirect request back to IdP. In our case, it is showing XML. Can you shed light on this?
Hi @INSEAD-asim i was on vacation so i could not respond sooner. I suggest we continue this over the email if you are all ok with it?
Hi @i3or1s @INSEAD-asim, I have the same problems than you: It shows the xml (LogoutResponse) when i try to logout from another sp
Did you guys find a solution ?
Thanks!
Great work by the way.
Hi @mvanmeerbeck, I able to find another fork by @rodrigodirknsj. There was signing issue which I resolved. You can check my fork at https://github.com/INSEAD-asim/SamlSPBundle.git
BTW, I also implemented to download and cache IdP MetaData.xml. :)
Issue is that logout request it not processed when initiated through browser.
You can find this line in the 'Bridge/LogoutReceiveRequest.php' method 'manage'.
As you see it is returning Resonse
should be redirect to idp.
This will be processed in version 2.
Checkout also this link https://wiki.shibboleth.net/confluence/display/SHIB2/SLOIssues it has the reason why this is yet not processed.
New version of LightSAML core is available in https://github.com/lightSAML/lightSAML and it's released, with SLO implemented in https://github.com/lightSAML/lightSAML-Logout which is in alpha version.
Great @tmilos ! It means that we will be moving to the new library soon. I will surely test it.
First of all, Big Thanks for such a great effort.
I am facing an issue while logging out. It shows following stack trace
I try to set request at
aerialship\saml-sp-bundle\src\AerialShip\SamlSPBundle\Bridge\LogoutReceiveRequest.php
at line 74 as:but it shows xml response as under and not forward to logout (I replaced the urls with dummy one).
can you help if I am missing something? Thanks