Project Aims:
The aim of this project is to provide an more effective mechanism to provision users from LDAP into cloudstack. Currently Cloudstack enables LDAP authentication. In this authentication users must be first setup in Cloudstack. Once the user is setup in Cloudstack they can authenticate using their LDAP username and password. This project will improve Cloudstack LDAP integration by enabling users be setup automatically using their LDAP credentials.
...
During the code stage I began to investigate the current LDAP implementation. This includes:
- The user authenticator (plugins/user-authentication/ldap) This enables LDAP users to login to Cloudstack once the user exists within the internal Cloudstack database.
- LDAPConfig (api/src/org/apache/cloudstack/api/command/admin/ldap/LDAPConfigCmd.java) This allows for adding LDAP configuration. This is detailed over here: https://cloudstack.apache.org/docs/api/apidocs-4.1/root_admin/ldapConfig.html This did not allow multiple configurations.
- LDAPRemove (api/src/org/apache/cloudstack/api/command/admin/LDAP/LDAPRemoveCmd.java) This allows for removing the LDAP configuration
- UI features. Global settings -> LDAP configuration allowed for the addition of a single LDAP server using the LDAPConfig command and the removal of an LDAP server using the LDAPRemove command.
After reviewing this code and implementation for some time I realised that it wasn't the most maintainable code. I realised I could extend it if required. But it would involve creating more unmaintainable code and it would be messy. This goes against my own principles of developing quality. This made me make the steep but justified decision to completely redo the LDAP implementation within Cloudstack. By doing this I did expanded the scope of the project.
I began to research the most appropriate way of structuring this. I started of by redoing the implementation. This meant creating the following classes(Excluding DAOs):
- LdapManager: Manages all LDAP connections.
- LdapConfiguration: Supplies all configuration from within the Cloudstack database or defaults where required.
- LdapUserManager: Handles any interaction with LDAP user information.
- LdapUtils: Supplies static helpers, e.g. escape search queries, get attributes from search queries.
- LdapContextFactory: Manages the creation of contexts.
- LdapAuthenticator: Supplies an authenticator to Cloudstack using the LdapManager.
From this I had a solid foundation for creating API commands to allow the user to interact with an LDAP server. I went on to create the following commands:
- LdapAddConfiguration - This allows for adding multiple LDAP configurations. Each configuration is just seen as a hostname and port.
- LdapDeleteConfiguration - This allows for the deletion of an LDAP configuration based on its hostname.
- LdapListConfiguration - This lists all of the LDAP configurations that exist within the database.
- LdapListAllUsers - This lists all the users within LDAP.
Along with this global configuration options were added this includes:
- LDAP basedn: This allows the user to set the basedn for their LDAP configuration
- LDAP bind password: This allows the user to set the password to use for binding to LDAP for creating the system context. If this is left blank along with bind principal then anonymous binding is used.
- LDAP bind principal: This allows the user to set the principle to use for binding with LDAP for creating the system context. If this is left blank along with the bind password then anonymous binding is used.
- LDAP email attribute: This sets out the attribute to use for getting the users email address. Within both OpenLDAP and ActiveDirectory this is mail. For this reason this is set to mail by default.
- LDAP realname attribute: This sets out the attribute to use for getting the users realname. Within both OpenLDAP and ActiveDiretory this is cn. For this reason this is set to cn by default.
- LDAP username attribute: This sets out the attribute to use for getting the users username. Within OpenLDAP this is uid and within ActiveDirectory this is samAccountName. In order to comply with posix standards this is set as uid by default.
- LDAP user object: This sets out the object type of user accounts within LDAP. Within OpenLDAP this is inetOrgPerson and within ActiveDirectory this is user. Again, in order to comply with posix standards this is set as inetOrgperson by default.
With this implementation I believe it allows for a much more extendable and flexible approach. The whole implementation is abstracted from the Cloudstack codebase using the "plugin" model. This allows all of the LDAP features to be contained within one place. Along with this the implementation supplies a good foundation. A side affect of redoing the implementation allowed me to add support for multiple LDAP servers. This means failover is support, so for example, if you have a standard ActiveDirectory with primary and secondary domain controller. Both can be added to Cloudstack which will allow it to fall over to either one assume one is down.
...
I do realise I could implement checks within the updateUser API command to disable the updating of passwords within the Cloudstack database when LDAP is enabled however I feel this is imposing on the codebase too much.
Testing
For testing I have included an embedded LDAP server. This can be launched by running:
Code Block |
---|
language | bash |
---|
theme | Midnight |
---|
|
mvn -pl :cloud-plugin-user-authenticator-ldap ldap:run
|
Once this is up integration tests can be launched with:
Code Block |
---|
language | bash |
---|
theme | Midnight |
---|
|
nosetests --with-marvin --marvin-config=setup/dev/local. |
...
cfg test/integration/component/test_ldap.py --load |
If you wish to use this LDAP server for development purposes you can. Just add a host on the LDAP Configuration page with hostname: localhost port: 10389
UI Additions
Progress on UI features is slow. At the moment I have a list of LDAP users coming up when you click add account. You can pick an user and then fill in the optional information.
Image Added
Plans for the future.
Look at UI features.