Closed csarrazi closed 2 years ago
I would love to see a RoleProvider implementation for this. The part that provides the ldap roles could be created in a generic fashion, but it could also simply be a database role provider:
security:
firewalls:
# default ldap provider
secured_area:
role_provider: ldap.role_provider
# default database provider
secured_area:
role_provider: dbal.role_provider
# or maybe chain them
secured_area:
role_provider: app.my_chained_role_provider
Just like my PR with user-checkers, this is one of the limitations I hit when writing my application's security.
The idea is not bad, but the main issue is that the roles should actually be fetched using the UserProvider (or a class used by the UserProvider). As roles are tied to users, I don't think it makes sense to actually have the role_provider
in the firewalls
section. And having different roles depending on the section of the site you're in seems awkward for me. Whenever that's the case, you would actually also go for a different user provider.
So instead of the firewalls
section, I actually put the config for this in the providers
section.
However, this is not the main topic here. First and foremost, we need to identify which standard RBAC strategies exist within LDAP implementations, which will be a start.
I am not sure there are many RBAC strategies in LDAP. However, there are group membership schemas:
This requires to match groups to roles afterwards. There also exist custom group/role mappings, since LDAP schemas are customisable, so some sort of generic implementation is needed. As @iltar stated, a RoleProvider could be of some help here, be it tied to firewall or user providers.
Actually, there are quite a few papers about this.
See
Regardless, as strategies are different for different implementations, it means that we need at least 4 (or 5, depending on whether vendors actually support INCITS 359) different implementations for fetching roles from an LDAP server:
I don't really think this would be helpful for other user providers. I mean, what is the actual chance that you need to fetch roles from a third-party storage if you're already using a database (entity, propel) for managing your users? Maybe in the case where you are using web services to authenticate, but these would actually return the correct information in the user endpoint (/me
or /users/123abc
, for example).
@csarrazi These documents seem very theorical. Do they provide any LDAP schema nor implementation ?
There are actually quite a few. It is actually the case for some Oracle products (Management Services, for example), Apache Fortress, and a few others.
I think in the vast majority of cases you really only need some way of mapping LDAP group membership to roles, as mentioned by @Adirelle. The way I have handled this in an LDAP bundle I have is to map role names to specific group names in the config such as:
ldap
security:
roles:
SUPER_ADMIN: [ 'Domain Admins' ]
ROLE_APP_USER: [ 'Network Team', 'HelpDesk' ]
Then the user provider retrieves all of the user's groups recursively (if possible -- easier to do in AD), and checks what roles they map to.
Then there would also need to be a config option to map the group membership attribute for the query in the user provider. Currently there is already a config option for mapping both the UID and password attributes.
Anyway, just my two cents on this.
You'd better use group DNs than just their names.
Good point, a name can be duplicated in the domain. Though that is highly confusing to begin with. A name itself is easier/more flexible, but I see the security perspective you're getting at.
Before it stopped working with Symfony 2.8, I was using https://github.com/BorisMorel/LdapBundle
in security.yml
security:
providers:
ldap:
id: imag_ldap.security.user.provider
imag_ldap:
client:
host: your.host.foo
port: 389
# version: 3 # Optional
# username: foo # Optional
# password: bar # Optional
# network_timeout: 10 # Optional
# referrals_enabled: true # Optional
# bind_username_before: true # Optional
# skip_roles: false # Optional
user:
base_dn: ou=people,dc=host,dc=foo
# filter: (&(foo=bar)(ObjectClass=Person)) #Optional
name_attribute: uid
role:
base_dn: ou=group, dc=host, dc=foo
# filter: (ou=group) #Optional
name_attribute: cn
user_attribute: member
user_id: [ dn or username ]
# user_class: IMAG\LdapBundle\User\LdapUser # Optiona
This allowed me to import my Active Directory groups and use them directly in my firewall, without even needing to map them to existing manually defined roles. They just got a ROLE_
prefix.
Here was my config security.yml
for example:
security:
providers:
ldap:
id: imag_ldap.security.user.provider
access_control:
- { path: ^/login, roles: IS_AUTHENTICATED_ANONYMOUSLY }
- { path: ^/, roles: ROLE_MY_AD_GROUP }
imag_ldap:
client:
host: ldaps://myldapserver.mydomain.com
port: 636
version: 3
username: cn=myreadonlyuser,cn=Users,dc=mydomain,dc=com
password: mypassword
referrals_enabled: 0
user:
base_dn: dc=mydomain,dc=com
name_attribute: sAMAccountName
role:
base_dn: dc=mydomain,dc=com
user_attribute: member
user_id: dn
Worked great until 2.8 broke the bundle, which is no longer maintained. With the new LDAP component in Symfony 3.1 I am just not authenticated against groups at all right now.
Especially in shops where Active Directory is used, groups are heavily in use to restrict access and define organisational units. I really don't want to replicate this in the db.
Would love to be able to do this again.Is there any plans to get this in for 3.2?
Here is the LDAP schema for apache fortress: https://github.com/apache/directory-fortress-core/blob/master/ldap/schema/fortress.schema
It is not using a standard schema although we have discussed frequently the idea of standardizing it and maybe one day we will. As it stands it supports RBAC (ANSI INCITS 359) and ARBAC policies which is probably more than you need here. This includes hierarchical roles, static and dynamic separation of duties, organizational controls like admin role and permissions.
You could probably start out with support for RBAC0 the core which would require about 4 or 5 object classes. It's actually pretty easy to represent basic RBAC in LDAP. Only when you get into the higher functions that require hierarchies does it get a bit complicated.
I'm in for what @iltar said. I want my users to come from LDAP but all the role logic is not in LDAP, a role provider would come in very handy!
@csarrazi Regarding this statement:
The idea is not bad, but the main issue is that the roles should actually be fetched using the UserProvider (or a class used by the UserProvider).
I think one of the easier ways to to enable this is to open up LdapUserProvider
and expose most private fields so people can extend the class on their own.
Actually, thing is, the authentication logic should be separate from the authorization logic. Having both at the same place is what is problematic in Symfony in the first place, which means that we should not care about having the user permissions or roles in the user object. This means that you cannot rely on having separate authentication and authorization systems right now, without implementing a user provider which use the different authentication and authorization sources.
Having a RoleProvider which would be used by the authorization system would help fix this, by using either:
RoleAware
or RoleAwareUserInterface
) could be fetched directly by a RoleAwareUserRoleProvider
RoleProvider
's objective would be to populate the authentication token with the correct roles/permissions, from various sources (The User object, should the roles / permissions be stored directly in the previous step, or another source like a database, should they be stored elsewhere).However, this requires a revamp of the Security component.
@fabpot What do you think about this? I think this would be the best approach, and I definitely think that it would lead to dramatic code simplifications. This would also improve separation of concerns, as authentication and authorization are two very different things, though often tied together.
Finally moving up from symfony2 to symfony3. I have been using kpnuguard as in version 2 with my own class which fills in the roles in the user object, and even the photo stored in LDAP of the user.
Would like to see a code sample somewhere showing a none hacky way of filling these details in
`
function FetchUserMembershipGroups($username) {
$this->errormessage = null;
// Calling user must already be logged in $this->ldapconn
$GroupsBase = "ou=Groups,".$this->basedn;
$result = ldap_search($this->ldapconn, $GroupsBase, "memberUid=$username");
if(! $result){
$this->errormessage = ldap_error($this->ldapconn);
return FALSE;
}
$groupdata = ldap_get_entries($this->ldapconn, $result);
return $groupdata;
}
/*****************************************************************
* getRoles()
*****************************************************************/
function getRoles() {
$groupdata = $this->FetchUserMembershipGroups($this->username);
$groups = array();
foreach($groupdata as $group){
if ($group['cn'][0]){
$groups[] = str_replace(" ", "_", strtoupper ("ROLE_".$group['cn'][0]) ) ;
}
}
return $groups;
}
`
@fabpot do we want to imlement this in Symfony ? Seems like a good feature for people using LDAP.
Thank you for this suggestion. There has not been a lot of activity here for a while. Would you still like to see this feature?
Could I get an answer? If I do not hear anything I will assume this issue is resolved or abandoned. Please get back to me <3
@carsonbot I think this is still relevant.
Yes, this is still relevant. The use case still stands despite the passage from time since 2016. People are just doing workarounds http://www.jsimmons.co.uk/2015/08/28/how-to-connect-symfony2-with-ipbrick-ldap-using-knpuguard/
@alehaa @deakus is maybe one of you open to work on this?
No matter how relevant or needed such a feature is, if there is nobody to work on it, it won't make it in Symfony. I've never used LDAP (or managed to get it working locally), so I am not motivated (nor able) to work on this. Yet, I'm open to help others with the process of contributing something to Symfony.
To me, making it more easy to work around (e.g. by making the ldap user provider open for extension for roles) is also an option if there is no standard for this LDAP.
I would love to contribute this feature, but it will take some time before I can submit it, as I'm busy with other projects right now.
@carsonbot asked if we would like to see this feature, as no activity on this issue has been seen for some time. I say yes in answer to that specific question that was asked.
@wouterj I do use LDAP, and have got it working since symfony2 with dynamic role assignment from LDAP. The way I have written it, it has little chance of making it into symfony as it stands now. However it is currently out in the world making money.
I will need to rafactor it to be more academic CS design pattern focused rather getting the job done focused. 😜 Please refer to the comment I posted in feb 2018.
The whole point of using a framework such as symfony is to stop reinventing the wheel ,and to avoid manual factoring. This is the reason why I would like to see it within symfony.
Could I do this? ... possibly.
However this issue has been open since May 2016, and in Jan 2021 has less than 30 comments and is still open. I dont think many people care for the feature. Therefore would this be a good use of time if only myself and @alehaa have the LDAP itch which needs to be scratched.
Maybe the issue should be closed. Shame really as when @csarrazi opened the issue it was quite exciting as it showed a valid insight into a LDAP use case.
https://symfony.com/doc/current/security/ldap.html
Still seems stange reading that you have to fill in your own array of default_roles after authenticating a user from LDAP, and leaving all that user info behind.
Its like going into a shoe shop and asking to buy just one shoe.
There is no unique standard to store roles in LDAP, yet a lot of vendors provides their own schemas that could be used to extract these information (and much more), see https://ldapwiki.com/wiki/Schema%20Extensions. So an extensible LDAP user provider using an interface and a few implementations seems a good idea.
Thanks for the responses!
@deakus in case you did: it wasn't my intention to actively hurt anyone participating in this issue. Complex issues like this one often have the tendency to stay open for eternity, so I decided to try my luck and ping the two interested people to see if we can get this issue forward somewhere in Symfony 5.x (thanks @alehaa!). I'm not sure, but I think LDAP is used by quite some people (only a very small group of developers actively participates in GitHub issues). In other words, I think there definitely is interested in this feature, we just needed someone that could find some time to work on it :)
Thank you for this suggestion. There has not been a lot of activity here for a while. Would you still like to see this feature?
I would really like to have this feature.
Is there anyone wanting to work on this one? I'm happy to help people with their contribution, but I have no personal need for this feature.
If nobody has a personal need for this feature (which often is the conclusion if nobody is contributing the actual feature), I'm in favor of closing this issue.
This is a required feature, I use this every day. I have a implementation that works in a way i see fit to make it work. At some point I will hopefully have time to polish it up and offer it up to others who have the same requirement. Untill then I agree close the issue.
Thank you for this suggestion. There has not been a lot of activity here for a while. Would you still like to see this feature?
Currently, the Ldap user provider is only able to fetch basic user information.
In the medium/long term (Symfony 3.2+), we want to be able to support user roles (Role-Based Access Control – RBAC) stored in an LDAP server.
We definitely want to do this using a standard way, in order to be able to integrate with popular LDAP-based identity providers.
This issue will be used in order to track the discussion about the future implementation, and the improvement which need to be done to the
LdapUserProvider
. The topmost post (this one), will recap the content of the discussion.