From eaa41433715ba1a1137ad9f346ea8569e31e4560 Mon Sep 17 00:00:00 2001 From: Ian Duffy Date: Thu, 25 Jul 2013 10:24:13 +0100 Subject: [PATCH] Merge LDAPPlugin Signed-off-by: Abhinandan Prateek --- .../configuration/ConfigurationService.java | 8 - .../apache/cloudstack/api/ApiConstants.java | 11 - .../cloudstack/api/ResponseGenerator.java | 3 - .../api/command/admin/ldap/LDAPConfigCmd.java | 206 ----------- .../api/command/admin/ldap/LDAPRemoveCmd.java | 71 ---- .../api/response/LDAPConfigResponse.java | 105 ------ client/tomcatconf/applicationContext.xml.in | 7 +- client/tomcatconf/commands.properties.in | 18 +- client/tomcatconf/componentContext.xml.in | 4 +- .../tomcatconf/nonossComponentContext.xml.in | 4 +- .../simulatorComponentContext.xml.in | 4 +- .../ratelimit/ApiRateLimitServiceImpl.java | 1 - plugins/user-authenticators/ldap/pom.xml | 119 +++++-- .../server/auth/LDAPUserAuthenticator.java | 173 ---------- .../api/command/LdapAddConfigurationCmd.java | 80 +++++ .../api/command/LdapCreateAccount.java | 141 ++++++++ .../command/LdapDeleteConfigurationCmd.java | 76 +++++ .../api/command/LdapListAllUsersCmd.java | 88 +++++ .../api/command/LdapListConfigurationCmd.java | 106 ++++++ .../api/command/LdapUserSearchCmd.java | 96 ++++++ .../response/LdapConfigurationResponse.java | 63 ++++ .../api/response/LdapUserResponse.java | 98 ++++++ .../cloudstack/ldap/LdapAuthenticator.java | 73 ++++ .../cloudstack/ldap/LdapConfiguration.java | 116 +++++++ .../cloudstack/ldap/LdapConfigurationVO.java | 66 ++++ .../cloudstack/ldap/LdapContextFactory.java | 103 ++++++ .../apache/cloudstack/ldap/LdapManager.java | 50 +++ .../cloudstack/ldap/LdapManagerImpl.java | 205 +++++++++++ .../org/apache/cloudstack/ldap/LdapUser.java | 75 ++++ .../cloudstack/ldap/LdapUserManager.java | 98 ++++++ .../org/apache/cloudstack/ldap/LdapUtils.java | 61 ++++ .../NoLdapUserMatchingQueryException.java | 32 ++ .../ldap/NoSuchLdapUserException.java | 15 +- .../ldap/dao/LdapConfigurationDao.java | 30 ++ .../ldap/dao/LdapConfigurationDaoImpl.java | 66 ++++ .../ldap/BasicNamingEnumerationImpl.groovy | 56 +++ .../ldap/LdapAddConfigurationCmdSpec.groovy | 89 +++++ .../ldap/LdapAuthenticatorSpec.groovy | 90 +++++ .../ldap/LdapConfigurationDaoImplSpec.groovy | 29 ++ .../ldap/LdapConfigurationResponseSpec.groovy | 49 +++ .../ldap/LdapConfigurationSpec.groovy | 181 ++++++++++ .../ldap/LdapConfigurationVO.groovy | 36 ++ .../ldap/LdapContextFactorySpec.groovy | 134 ++++++++ .../LdapDeleteConfigurationCmdSpec.groovy | 68 ++++ .../ldap/LdapListAllUsersCmdSpec.groovy | 72 ++++ .../ldap/LdapListConfigurationCmdSpec.groovy | 100 ++++++ .../ldap/LdapManagerImplSpec.groovy | 319 ++++++++++++++++++ .../ldap/LdapSearchUserCmdSpec.groovy | 72 ++++ .../ldap/LdapUserManagerSpec.groovy | 207 ++++++++++++ .../ldap/LdapUserResponseSpec.groovy | 67 ++++ .../cloudstack/ldap/LdapUserSpec.groovy | 79 +++++ .../cloudstack/ldap/LdapUtilsSpec.groovy | 68 ++++ ...oLdapUserMatchingQueryExceptionSpec.groovy | 30 ++ .../ldap/NoSuchLdapUserExceptionSpec.groovy | 30 ++ .../ldap/test/resources/cloudstack.org.ldif | 19 ++ .../src/com/cloud/api/ApiResponseHelper.java | 15 - .../src/com/cloud/configuration/Config.java | 12 +- .../ConfigurationManagerImpl.java | 172 ---------- .../cloud/server/ManagementServerImpl.java | 4 - .../vpc/MockConfigurationManagerImpl.java | 31 -- setup/db/db/schema-410to420.sql | 33 +- test/integration/component/test_ldap.py | 313 ++++++----------- tools/apidoc/gen_toc.py | 2 +- ui/css/cloudstack3.css | 84 ++++- ui/index.jsp | 61 +++- ui/scripts/accounts.js | 13 +- ui/scripts/accountsWizard.js | 158 +++++++++ ui/scripts/globalSettings.js | 143 +------- ui/scripts/ui-custom/accountsWizard.js | 116 +++++++ 69 files changed, 4222 insertions(+), 1202 deletions(-) delete mode 100644 api/src/org/apache/cloudstack/api/command/admin/ldap/LDAPConfigCmd.java delete mode 100644 api/src/org/apache/cloudstack/api/command/admin/ldap/LDAPRemoveCmd.java delete mode 100644 api/src/org/apache/cloudstack/api/response/LDAPConfigResponse.java delete mode 100644 plugins/user-authenticators/ldap/src/com/cloud/server/auth/LDAPUserAuthenticator.java create mode 100644 plugins/user-authenticators/ldap/src/org/apache/cloudstack/api/command/LdapAddConfigurationCmd.java create mode 100644 plugins/user-authenticators/ldap/src/org/apache/cloudstack/api/command/LdapCreateAccount.java create mode 100644 plugins/user-authenticators/ldap/src/org/apache/cloudstack/api/command/LdapDeleteConfigurationCmd.java create mode 100644 plugins/user-authenticators/ldap/src/org/apache/cloudstack/api/command/LdapListAllUsersCmd.java create mode 100644 plugins/user-authenticators/ldap/src/org/apache/cloudstack/api/command/LdapListConfigurationCmd.java create mode 100644 plugins/user-authenticators/ldap/src/org/apache/cloudstack/api/command/LdapUserSearchCmd.java create mode 100644 plugins/user-authenticators/ldap/src/org/apache/cloudstack/api/response/LdapConfigurationResponse.java create mode 100644 plugins/user-authenticators/ldap/src/org/apache/cloudstack/api/response/LdapUserResponse.java create mode 100644 plugins/user-authenticators/ldap/src/org/apache/cloudstack/ldap/LdapAuthenticator.java create mode 100644 plugins/user-authenticators/ldap/src/org/apache/cloudstack/ldap/LdapConfiguration.java create mode 100644 plugins/user-authenticators/ldap/src/org/apache/cloudstack/ldap/LdapConfigurationVO.java create mode 100644 plugins/user-authenticators/ldap/src/org/apache/cloudstack/ldap/LdapContextFactory.java create mode 100644 plugins/user-authenticators/ldap/src/org/apache/cloudstack/ldap/LdapManager.java create mode 100644 plugins/user-authenticators/ldap/src/org/apache/cloudstack/ldap/LdapManagerImpl.java create mode 100644 plugins/user-authenticators/ldap/src/org/apache/cloudstack/ldap/LdapUser.java create mode 100644 plugins/user-authenticators/ldap/src/org/apache/cloudstack/ldap/LdapUserManager.java create mode 100644 plugins/user-authenticators/ldap/src/org/apache/cloudstack/ldap/LdapUtils.java create mode 100644 plugins/user-authenticators/ldap/src/org/apache/cloudstack/ldap/NoLdapUserMatchingQueryException.java rename api/src/org/apache/cloudstack/api/response/LDAPRemoveResponse.java => plugins/user-authenticators/ldap/src/org/apache/cloudstack/ldap/NoSuchLdapUserException.java (66%) create mode 100644 plugins/user-authenticators/ldap/src/org/apache/cloudstack/ldap/dao/LdapConfigurationDao.java create mode 100644 plugins/user-authenticators/ldap/src/org/apache/cloudstack/ldap/dao/LdapConfigurationDaoImpl.java create mode 100644 plugins/user-authenticators/ldap/test/groovy/org/apache/cloudstack/ldap/BasicNamingEnumerationImpl.groovy create mode 100644 plugins/user-authenticators/ldap/test/groovy/org/apache/cloudstack/ldap/LdapAddConfigurationCmdSpec.groovy create mode 100644 plugins/user-authenticators/ldap/test/groovy/org/apache/cloudstack/ldap/LdapAuthenticatorSpec.groovy create mode 100644 plugins/user-authenticators/ldap/test/groovy/org/apache/cloudstack/ldap/LdapConfigurationDaoImplSpec.groovy create mode 100644 plugins/user-authenticators/ldap/test/groovy/org/apache/cloudstack/ldap/LdapConfigurationResponseSpec.groovy create mode 100644 plugins/user-authenticators/ldap/test/groovy/org/apache/cloudstack/ldap/LdapConfigurationSpec.groovy create mode 100644 plugins/user-authenticators/ldap/test/groovy/org/apache/cloudstack/ldap/LdapConfigurationVO.groovy create mode 100644 plugins/user-authenticators/ldap/test/groovy/org/apache/cloudstack/ldap/LdapContextFactorySpec.groovy create mode 100644 plugins/user-authenticators/ldap/test/groovy/org/apache/cloudstack/ldap/LdapDeleteConfigurationCmdSpec.groovy create mode 100644 plugins/user-authenticators/ldap/test/groovy/org/apache/cloudstack/ldap/LdapListAllUsersCmdSpec.groovy create mode 100644 plugins/user-authenticators/ldap/test/groovy/org/apache/cloudstack/ldap/LdapListConfigurationCmdSpec.groovy create mode 100644 plugins/user-authenticators/ldap/test/groovy/org/apache/cloudstack/ldap/LdapManagerImplSpec.groovy create mode 100644 plugins/user-authenticators/ldap/test/groovy/org/apache/cloudstack/ldap/LdapSearchUserCmdSpec.groovy create mode 100644 plugins/user-authenticators/ldap/test/groovy/org/apache/cloudstack/ldap/LdapUserManagerSpec.groovy create mode 100644 plugins/user-authenticators/ldap/test/groovy/org/apache/cloudstack/ldap/LdapUserResponseSpec.groovy create mode 100644 plugins/user-authenticators/ldap/test/groovy/org/apache/cloudstack/ldap/LdapUserSpec.groovy create mode 100644 plugins/user-authenticators/ldap/test/groovy/org/apache/cloudstack/ldap/LdapUtilsSpec.groovy create mode 100644 plugins/user-authenticators/ldap/test/groovy/org/apache/cloudstack/ldap/NoLdapUserMatchingQueryExceptionSpec.groovy create mode 100644 plugins/user-authenticators/ldap/test/groovy/org/apache/cloudstack/ldap/NoSuchLdapUserExceptionSpec.groovy create mode 100644 plugins/user-authenticators/ldap/test/resources/cloudstack.org.ldif create mode 100644 ui/scripts/accountsWizard.js create mode 100644 ui/scripts/ui-custom/accountsWizard.js diff --git a/api/src/com/cloud/configuration/ConfigurationService.java b/api/src/com/cloud/configuration/ConfigurationService.java index 381fcad95cc..cc6e47f0cc8 100644 --- a/api/src/com/cloud/configuration/ConfigurationService.java +++ b/api/src/com/cloud/configuration/ConfigurationService.java @@ -26,8 +26,6 @@ import com.cloud.exception.ConcurrentOperationException; import com.cloud.exception.ResourceUnavailableException; import com.cloud.exception.ResourceAllocationException; import org.apache.cloudstack.api.command.admin.config.UpdateCfgCmd; -import org.apache.cloudstack.api.command.admin.ldap.LDAPConfigCmd; -import org.apache.cloudstack.api.command.admin.ldap.LDAPRemoveCmd; import org.apache.cloudstack.api.command.admin.network.CreateNetworkOfferingCmd; import org.apache.cloudstack.api.command.admin.network.DeleteNetworkOfferingCmd; import org.apache.cloudstack.api.command.admin.network.UpdateNetworkOfferingCmd; @@ -272,12 +270,6 @@ public interface ConfigurationService { DiskOffering getDiskOffering(long diskOfferingId); - boolean updateLDAP(LDAPConfigCmd cmd) throws NamingException; - - boolean removeLDAP(LDAPRemoveCmd cmd); - - LDAPConfigCmd listLDAPConfig(LDAPConfigCmd cmd); - /** * @param offering * @return diff --git a/api/src/org/apache/cloudstack/api/ApiConstants.java b/api/src/org/apache/cloudstack/api/ApiConstants.java index 121403fa064..4fbbf52db80 100755 --- a/api/src/org/apache/cloudstack/api/ApiConstants.java +++ b/api/src/org/apache/cloudstack/api/ApiConstants.java @@ -525,15 +525,4 @@ public class ApiConstants { public enum VMDetails { all, group, nics, stats, secgrp, tmpl, servoff, iso, volume, min, affgrp; } - - public enum LDAPParams { - hostname, port, usessl, queryfilter, searchbase, dn, passwd, truststore, truststorepass; - - @Override - public String toString() { - return "ldap." + name(); - } - } - - } diff --git a/api/src/org/apache/cloudstack/api/ResponseGenerator.java b/api/src/org/apache/cloudstack/api/ResponseGenerator.java index d8d07cb56fb..372fc9d4d20 100644 --- a/api/src/org/apache/cloudstack/api/ResponseGenerator.java +++ b/api/src/org/apache/cloudstack/api/ResponseGenerator.java @@ -129,7 +129,6 @@ import org.apache.cloudstack.api.response.IpForwardingRuleResponse; import org.apache.cloudstack.api.response.IsolationMethodResponse; import org.apache.cloudstack.api.response.LBHealthCheckResponse; import org.apache.cloudstack.api.response.LBStickinessResponse; -import org.apache.cloudstack.api.response.LDAPConfigResponse; import org.apache.cloudstack.api.response.LoadBalancerResponse; import org.apache.cloudstack.api.response.NetworkACLItemResponse; import org.apache.cloudstack.api.response.NetworkACLResponse; @@ -363,8 +362,6 @@ public interface ResponseGenerator { VirtualRouterProviderResponse createVirtualRouterProviderResponse(VirtualRouterProvider result); - LDAPConfigResponse createLDAPConfigResponse(String hostname, Integer port, Boolean useSSL, String queryFilter, String baseSearch, String dn); - StorageNetworkIpRangeResponse createStorageNetworkIpRangeResponse(StorageNetworkIpRange result); RegionResponse createRegionResponse(Region region); diff --git a/api/src/org/apache/cloudstack/api/command/admin/ldap/LDAPConfigCmd.java b/api/src/org/apache/cloudstack/api/command/admin/ldap/LDAPConfigCmd.java deleted file mode 100644 index 6f8b7b1705b..00000000000 --- a/api/src/org/apache/cloudstack/api/command/admin/ldap/LDAPConfigCmd.java +++ /dev/null @@ -1,206 +0,0 @@ -// Licensed to the Apache Software Foundation (ASF) under one -// or more contributor license agreements. See the NOTICE file -// distributed with this work for additional information -// regarding copyright ownership. The ASF licenses this file -// to you under the Apache License, Version 2.0 (the -// "License"); you may not use this file except in compliance -// with the License. You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, -// software distributed under the License is distributed on an -// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -// KIND, either express or implied. See the License for the -// specific language governing permissions and limitations -// under the License. -package org.apache.cloudstack.api.command.admin.ldap; - - -import java.util.ArrayList; -import java.util.List; - -import javax.naming.NamingException; - -import org.apache.cloudstack.api.APICommand; -import org.apache.cloudstack.api.ApiConstants; -import org.apache.cloudstack.api.BaseCmd; -import org.apache.cloudstack.api.Parameter; -import org.apache.cloudstack.api.ServerApiException; -import org.apache.cloudstack.api.response.LDAPConfigResponse; -import org.apache.cloudstack.api.response.ListResponse; -import org.apache.commons.lang.StringEscapeUtils; -import org.apache.log4j.Logger; - -import com.cloud.exception.ConcurrentOperationException; -import com.cloud.exception.InsufficientCapacityException; -import com.cloud.exception.ResourceAllocationException; -import com.cloud.exception.ResourceUnavailableException; -import com.cloud.exception.InvalidParameterValueException; -import com.cloud.user.Account; - -@APICommand(name = "ldapConfig", description="Configure the LDAP context for this site.", responseObject=LDAPConfigResponse.class, since="3.0.0") -public class LDAPConfigCmd extends BaseCmd { - public static final Logger s_logger = Logger.getLogger(LDAPConfigCmd.class.getName()); - - private static final String s_name = "ldapconfigresponse"; - - ///////////////////////////////////////////////////// - //////////////// API parameters ///////////////////// - ///////////////////////////////////////////////////// - @Parameter(name=ApiConstants.LIST_ALL, type=CommandType.BOOLEAN, description="If true return current LDAP configuration") - private Boolean listAll; - - @Parameter(name=ApiConstants.HOST_NAME, type=CommandType.STRING, description="Hostname or ip address of the ldap server eg: my.ldap.com") - private String hostname; - - @Parameter(name=ApiConstants.PORT, type=CommandType.INTEGER, description="Specify the LDAP port if required, default is 389.") - private Integer port=0; - - @Parameter(name=ApiConstants.USE_SSL, type=CommandType.BOOLEAN, description="Check Use SSL if the external LDAP server is configured for LDAP over SSL.") - private Boolean useSSL; - - @Parameter(name=ApiConstants.SEARCH_BASE, type=CommandType.STRING, description="The search base defines the starting point for the search in the directory tree Example: dc=cloud,dc=com.") - private String searchBase; - - @Parameter(name=ApiConstants.QUERY_FILTER, type=CommandType.STRING, description="You specify a query filter here, which narrows down the users, who can be part of this domain.") - private String queryFilter; - - @Parameter(name=ApiConstants.BIND_DN, type=CommandType.STRING, description="Specify the distinguished name of a user with the search permission on the directory.") - private String bindDN; - - @Parameter(name=ApiConstants.BIND_PASSWORD, type=CommandType.STRING, description="Enter the password.") - private String bindPassword; - - @Parameter(name=ApiConstants.TRUST_STORE, type=CommandType.STRING, description="Enter the path to trust certificates store.") - private String trustStore; - - @Parameter(name=ApiConstants.TRUST_STORE_PASSWORD, type=CommandType.STRING, description="Enter the password for trust store.") - private String trustStorePassword; - - ///////////////////////////////////////////////////// - /////////////////// Accessors /////////////////////// - ///////////////////////////////////////////////////// - - public Boolean getListAll() { - return listAll == null ? Boolean.FALSE : listAll; - } - - public String getBindPassword() { - return bindPassword; - } - - public String getBindDN() { - return bindDN; - } - - public void setBindDN(String bdn) { - this.bindDN=bdn; - } - - public String getQueryFilter() { - return queryFilter; - } - - public void setQueryFilter(String queryFilter) { - this.queryFilter=StringEscapeUtils.unescapeHtml(queryFilter); - } - public String getSearchBase() { - return searchBase; - } - - public void setSearchBase(String searchBase) { - this.searchBase=searchBase; - } - - public Boolean getUseSSL() { - return useSSL == null ? Boolean.FALSE : useSSL; - } - - public void setUseSSL(Boolean useSSL) { - this.useSSL=useSSL; - } - - public String getHostname() { - return hostname; - } - - public void setHostname(String hostname) { - this.hostname=hostname; - } - - public Integer getPort() { - return port <= 0 ? 389 : port; - } - - public void setPort(Integer port) { - this.port=port; - } - - public String getTrustStore() { - return trustStore; - } - - public void setTrustStore(String trustStore) { - this.trustStore=trustStore; - } - - public String getTrustStorePassword() { - return trustStorePassword; - } - - - ///////////////////////////////////////////////////// - /////////////// API Implementation/////////////////// - ///////////////////////////////////////////////////// - - - @Override - public void execute() throws ResourceUnavailableException, - InsufficientCapacityException, ServerApiException, - ConcurrentOperationException, ResourceAllocationException { - try { - if (getListAll()){ - // return the existing conf - LDAPConfigCmd cmd = _configService.listLDAPConfig(this); - ListResponse response = new ListResponse(); - List responses = new ArrayList(); - - if(!cmd.getHostname().equals("")) { - responses.add(_responseGenerator.createLDAPConfigResponse(cmd.getHostname(), cmd.getPort(), cmd.getUseSSL(), cmd.getQueryFilter(), cmd.getSearchBase(), cmd.getBindDN())); - } - - response.setResponses(responses); - response.setResponseName(getCommandName()); - this.setResponseObject(response); - } - else if (getHostname()==null || getSearchBase() == null || getQueryFilter() == null) { - throw new InvalidParameterValueException("You need to provide hostname, searchbase and queryfilter to configure your LDAP server"); - } - else { - boolean result = _configService.updateLDAP(this); - if (result){ - LDAPConfigResponse lr = _responseGenerator.createLDAPConfigResponse(getHostname(), getPort(), getUseSSL(), getQueryFilter(), getSearchBase(), getBindDN()); - lr.setResponseName(getCommandName()); - this.setResponseObject(lr); - } - } - } - catch (NamingException ne){ - ne.printStackTrace(); - } - - } - - @Override - public String getCommandName() { - return s_name; - } - - @Override - public long getEntityOwnerId() { - return Account.ACCOUNT_ID_SYSTEM; - } - - -} \ No newline at end of file diff --git a/api/src/org/apache/cloudstack/api/command/admin/ldap/LDAPRemoveCmd.java b/api/src/org/apache/cloudstack/api/command/admin/ldap/LDAPRemoveCmd.java deleted file mode 100644 index 5159fbadb0b..00000000000 --- a/api/src/org/apache/cloudstack/api/command/admin/ldap/LDAPRemoveCmd.java +++ /dev/null @@ -1,71 +0,0 @@ -// Licensed to the Apache Software Foundation (ASF) under one -// or more contributor license agreements. See the NOTICE file -// distributed with this work for additional information -// regarding copyright ownership. The ASF licenses this file -// to you under the Apache License, Version 2.0 (the -// "License"); you may not use this file except in compliance -// with the License. You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, -// software distributed under the License is distributed on an -// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -// KIND, either express or implied. See the License for the -// specific language governing permissions and limitations -// under the License. -package org.apache.cloudstack.api.command.admin.ldap; - - -import org.apache.cloudstack.api.APICommand; -import org.apache.cloudstack.api.BaseCmd; -import org.apache.cloudstack.api.response.LDAPConfigResponse; -import org.apache.cloudstack.api.response.LDAPRemoveResponse; -import org.apache.log4j.Logger; - -import com.cloud.user.Account; - -@APICommand(name = "ldapRemove", description="Remove the LDAP context for this site.", responseObject=LDAPConfigResponse.class, since="3.0.1") -public class LDAPRemoveCmd extends BaseCmd { - public static final Logger s_logger = Logger.getLogger(LDAPRemoveCmd.class.getName()); - - private static final String s_name = "ldapremoveresponse"; - - ///////////////////////////////////////////////////// - //////////////// API parameters ///////////////////// - ///////////////////////////////////////////////////// - - - ///////////////////////////////////////////////////// - /////////////////// Accessors /////////////////////// - ///////////////////////////////////////////////////// - - - ///////////////////////////////////////////////////// - /////////////// API Implementation/////////////////// - ///////////////////////////////////////////////////// - - - @Override - public void execute(){ - boolean result = _configService.removeLDAP(this); - if (result){ - LDAPRemoveResponse lr = new LDAPRemoveResponse(); - lr.setObjectName("ldapremove"); - lr.setResponseName(getCommandName()); - this.setResponseObject(lr); - } - } - - @Override - public String getCommandName() { - return s_name; - } - - @Override - public long getEntityOwnerId() { - return Account.ACCOUNT_ID_SYSTEM; - } - - -} diff --git a/api/src/org/apache/cloudstack/api/response/LDAPConfigResponse.java b/api/src/org/apache/cloudstack/api/response/LDAPConfigResponse.java deleted file mode 100644 index bbeec630d81..00000000000 --- a/api/src/org/apache/cloudstack/api/response/LDAPConfigResponse.java +++ /dev/null @@ -1,105 +0,0 @@ -// Licensed to the Apache Software Foundation (ASF) under one -// or more contributor license agreements. See the NOTICE file -// distributed with this work for additional information -// regarding copyright ownership. The ASF licenses this file -// to you under the Apache License, Version 2.0 (the -// "License"); you may not use this file except in compliance -// with the License. You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, -// software distributed under the License is distributed on an -// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -// KIND, either express or implied. See the License for the -// specific language governing permissions and limitations -// under the License. -package org.apache.cloudstack.api.response; - -import org.apache.cloudstack.api.ApiConstants; -import org.apache.cloudstack.api.BaseResponse; - -import com.cloud.serializer.Param; -import com.google.gson.annotations.SerializedName; - -public class LDAPConfigResponse extends BaseResponse { - - @SerializedName(ApiConstants.HOST_NAME) @Param(description="Hostname or ip address of the ldap server eg: my.ldap.com") - private String hostname; - - @SerializedName(ApiConstants.PORT) @Param(description="Specify the LDAP port if required, default is 389") - private String port; - - @SerializedName(ApiConstants.USE_SSL) @Param(description="Check Use SSL if the external LDAP server is configured for LDAP over SSL") - private String useSSL; - - @SerializedName(ApiConstants.SEARCH_BASE) @Param(description="The search base defines the starting point for the search in the directory tree Example: dc=cloud,dc=com") - private String searchBase; - - @SerializedName(ApiConstants.QUERY_FILTER) @Param(description="You specify a query filter here, which narrows down the users, who can be part of this domain") - private String queryFilter; - - @SerializedName(ApiConstants.BIND_DN) @Param(description="Specify the distinguished name of a user with the search permission on the directory") - private String bindDN; - - @SerializedName(ApiConstants.BIND_PASSWORD) @Param(description="DN password") - private String bindPassword; - - public String getHostname() { - return hostname; - } - - public void setHostname(String hostname) { - this.hostname = hostname; - } - - public String getPort() { - return port; - } - - public void setPort(String port) { - this.port = port; - } - - public String getUseSSL() { - return useSSL; - } - - public void setUseSSL(String useSSL) { - this.useSSL = useSSL; - } - - public String getSearchBase() { - return searchBase; - } - - public void setSearchBase(String searchBase) { - this.searchBase = searchBase; - } - - public String getQueryFilter() { - return queryFilter; - } - - public void setQueryFilter(String queryFilter) { - this.queryFilter = queryFilter; - } - - public String getBindDN() { - return bindDN; - } - - public void setBindDN(String bindDN) { - this.bindDN = bindDN; - } - - public String getBindPassword() { - return bindPassword; - } - - public void setBindPassword(String bindPassword) { - this.bindPassword = bindPassword; - } - - -} diff --git a/client/tomcatconf/applicationContext.xml.in b/client/tomcatconf/applicationContext.xml.in index 343a8a1ea68..f29d72f8ffa 100644 --- a/client/tomcatconf/applicationContext.xml.in +++ b/client/tomcatconf/applicationContext.xml.in @@ -391,7 +391,7 @@ - + @@ -400,6 +400,11 @@ + + + + + - + + 4.0.0 cloud-plugin-user-authenticator-ldap Apache CloudStack Plugin - User Authenticator LDAP @@ -26,4 +18,93 @@ 4.2.0-SNAPSHOT ../../pom.xml + + + + + org.codehaus.gmaven + gmaven-plugin + 1.3 + + 1.7 + + + + + compile + testCompile + + + + + test/groovy + + **/*.groovy + + + + + + + + + org.codehaus.gmaven.runtime + gmaven-runtime-1.7 + 1.3 + + + org.codehaus.groovy + groovy-all + + + + + org.codehaus.groovy + groovy-all + 2.0.5 + + + + + org.apache.maven.plugins + maven-surefire-plugin + + + **/*Spec* + + + + + + com.btmatthews.maven.plugins + ldap-maven-plugin + 1.1.0 + + 11389 + ldap + false + dc=cloudstack,dc=org + 10389 + test/resources/cloudstack.org.ldif + + + + + + + + + + org.spockframework + spock-core + 0.7-groovy-2.0 + + + + + cglib + cglib-nodep + 2.2 + + diff --git a/plugins/user-authenticators/ldap/src/com/cloud/server/auth/LDAPUserAuthenticator.java b/plugins/user-authenticators/ldap/src/com/cloud/server/auth/LDAPUserAuthenticator.java deleted file mode 100644 index d928a5b9e17..00000000000 --- a/plugins/user-authenticators/ldap/src/com/cloud/server/auth/LDAPUserAuthenticator.java +++ /dev/null @@ -1,173 +0,0 @@ -// Licensed to the Apache Software Foundation (ASF) under one or more -// contributor license agreements. See the NOTICE file distributed with -// this work for additional information regarding copyright ownership. -// The ASF licenses this file to You under the Apache License, Version 2.0 -// (the "License"); you may not use this file except in compliance with -// the License. You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -package com.cloud.server.auth; - -import java.security.NoSuchAlgorithmException; -import java.security.SecureRandom; -import java.util.Hashtable; -import java.util.Map; - -import javax.ejb.Local; -import javax.inject.Inject; -import javax.naming.ConfigurationException; -import javax.naming.Context; -import javax.naming.NamingEnumeration; -import javax.naming.NamingException; -import javax.naming.directory.DirContext; -import javax.naming.directory.InitialDirContext; -import javax.naming.directory.SearchControls; -import javax.naming.directory.SearchResult; - -import org.apache.cloudstack.api.ApiConstants.LDAPParams; -import org.apache.log4j.Logger; -import org.bouncycastle.util.encoders.Base64; - -import com.cloud.configuration.dao.ConfigurationDao; -import com.cloud.user.UserAccount; -import com.cloud.user.dao.UserAccountDao; -import com.cloud.utils.exception.CloudRuntimeException; - -@Local(value={UserAuthenticator.class}) -public class LDAPUserAuthenticator extends DefaultUserAuthenticator { - public static final Logger s_logger = Logger.getLogger(LDAPUserAuthenticator.class); - - @Inject private ConfigurationDao _configDao; - @Inject private UserAccountDao _userAccountDao; - - @Override - public boolean authenticate(String username, String password, Long domainId, Map requestParameters ) { - if (s_logger.isDebugEnabled()) { - s_logger.debug("Retrieving user: " + username); - } - UserAccount user = _userAccountDao.getUserAccount(username, domainId); - if (user == null) { - s_logger.debug("Unable to find user with " + username + " in domain " + domainId); - return false; - } - - String url = _configDao.getValue(LDAPParams.hostname.toString()); - if (url==null){ - s_logger.debug("LDAP authenticator is not configured."); - return false; - } - String port = _configDao.getValue(LDAPParams.port.toString()); - String queryFilter = _configDao.getValue(LDAPParams.queryfilter.toString()); - String searchBase = _configDao.getValue(LDAPParams.searchbase.toString()); - Boolean useSSL = Boolean.valueOf(_configDao.getValue(LDAPParams.usessl.toString())); - String bindDN = _configDao.getValue(LDAPParams.dn.toString()); - String bindPasswd = _configDao.getValue(LDAPParams.passwd.toString()); - String trustStore = _configDao.getValue(LDAPParams.truststore.toString()); - String trustStorePassword = _configDao.getValue(LDAPParams.truststorepass.toString()); - - try { - // get all params - Hashtable env = new Hashtable(11); - env.put(Context.INITIAL_CONTEXT_FACTORY,"com.sun.jndi.ldap.LdapCtxFactory"); - String protocol = "ldap://" ; - if (useSSL){ - env.put(Context.SECURITY_PROTOCOL, "ssl"); - protocol="ldaps://" ; - System.setProperty("javax.net.ssl.trustStore", trustStore); - System.setProperty("javax.net.ssl.trustStorePassword", trustStorePassword); - } - env.put(Context.PROVIDER_URL, protocol + url + ":" + port); - - if (bindDN != null && bindPasswd != null){ - env.put(Context.SECURITY_PRINCIPAL, bindDN); - env.put(Context.SECURITY_CREDENTIALS, bindPasswd); - } - else { - // Use anonymous authentication - env.put(Context.SECURITY_AUTHENTICATION, "none"); - } - // Create the initial context - DirContext ctx = new InitialDirContext(env); - // use this context to search - - // substitute the queryFilter with this user info - queryFilter = queryFilter.replaceAll("\\%u", username); - queryFilter = queryFilter.replaceAll("\\%n", user.getFirstname() + " " + user.getLastname()); - queryFilter = queryFilter.replaceAll("\\%e", user.getEmail()); - - - SearchControls sc = new SearchControls(); - String[] searchFilter = { "dn" }; - sc.setReturningAttributes(new String[0]); //return no attributes - sc.setReturningAttributes(searchFilter); - sc.setSearchScope(SearchControls.SUBTREE_SCOPE); - sc.setCountLimit(1); - - // Search for objects with those matching attributes - NamingEnumeration answer = ctx.search(searchBase, queryFilter, sc); - SearchResult sr = answer.next(); - String cn = sr.getName(); - answer.close(); - ctx.close(); - - s_logger.info("DN from LDAP =" + cn); - - // check the password - env = new Hashtable(11); - env.put(Context.INITIAL_CONTEXT_FACTORY,"com.sun.jndi.ldap.LdapCtxFactory"); - protocol = "ldap://" ; - if (useSSL){ - env.put(Context.SECURITY_PROTOCOL, "ssl"); - protocol="ldaps://" ; - } - env.put(Context.PROVIDER_URL, protocol + url + ":" + port); - env.put(Context.SECURITY_PRINCIPAL, cn + "," + searchBase); - env.put(Context.SECURITY_CREDENTIALS, password); - // Create the initial context - ctx = new InitialDirContext(env); - ctx.close(); - - } catch (NamingException ne) { - s_logger.warn("Authentication Failed ! " + ne.getMessage() + (ne.getCause() != null ? ("; Caused by:" + ne.getCause().getMessage()) : "")); - return false; - } - catch (Exception e){ - e.printStackTrace(); - s_logger.warn("Unknown error encountered " + e.getMessage()); - return false; - } - - // authenticate - return true; - } - - @Override - public boolean configure(String name, Map params) - throws ConfigurationException { - if (name == null) { - name = "LDAP"; - } - super.configure(name, params); - return true; - } - - @Override - public String encode(String password) { - // Password is not used, so set to a random string - try { - SecureRandom randomGen = SecureRandom.getInstance("SHA1PRNG"); - byte bytes[] = new byte[20]; - randomGen.nextBytes(bytes); - return Base64.encode(bytes).toString(); - } catch (NoSuchAlgorithmException e) { - throw new CloudRuntimeException("Failed to generate random password",e); - } - } -} diff --git a/plugins/user-authenticators/ldap/src/org/apache/cloudstack/api/command/LdapAddConfigurationCmd.java b/plugins/user-authenticators/ldap/src/org/apache/cloudstack/api/command/LdapAddConfigurationCmd.java new file mode 100644 index 00000000000..b8e08e52928 --- /dev/null +++ b/plugins/user-authenticators/ldap/src/org/apache/cloudstack/api/command/LdapAddConfigurationCmd.java @@ -0,0 +1,80 @@ +package org.apache.cloudstack.api.command; + +import javax.inject.Inject; + +import org.apache.log4j.Logger; + +import org.apache.cloudstack.api.APICommand; +import org.apache.cloudstack.api.ApiErrorCode; +import org.apache.cloudstack.api.BaseCmd; +import org.apache.cloudstack.api.Parameter; +import org.apache.cloudstack.api.ServerApiException; +import org.apache.cloudstack.api.response.LdapConfigurationResponse; +import org.apache.cloudstack.ldap.LdapManager; + +import com.cloud.exception.InvalidParameterValueException; +import com.cloud.user.Account; + +@APICommand(name = "addLdapConfiguration", description = "Add a new Ldap Configuration", responseObject = LdapConfigurationResponse.class, since = "4.2.0") +public class LdapAddConfigurationCmd extends BaseCmd { + public static final Logger s_logger = Logger.getLogger(LdapAddConfigurationCmd.class.getName()); + private static final String s_name = "ldapconfigurationresponse"; + + @Inject + private LdapManager _ldapManager; + + @Parameter(name = "hostname", type = CommandType.STRING, required = true, description = "Hostname") + private String hostname; + + @Parameter(name = "port", type = CommandType.INTEGER, required = true, description = "Port") + private int port; + + public LdapAddConfigurationCmd() { + super(); + } + + public LdapAddConfigurationCmd(final LdapManager ldapManager) { + super(); + _ldapManager = ldapManager; + } + + @Override + public void execute() throws ServerApiException { + try { + final LdapConfigurationResponse response = _ldapManager.addConfiguration(hostname, port); + response.setObjectName("LdapAddConfiguration"); + response.setResponseName(getCommandName()); + setResponseObject(response); + } catch (final InvalidParameterValueException e) { + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, e.toString()); + } + + } + + @Override + public String getCommandName() { + return s_name; + } + + @Override + public long getEntityOwnerId() { + return Account.ACCOUNT_ID_SYSTEM; + } + + public String getHostname() { + return hostname; + } + + public int getPort() { + return port; + } + + public void setHostname(final String hostname) { + this.hostname = hostname; + } + + public void setPort(final int port) { + this.port = port; + } + +} diff --git a/plugins/user-authenticators/ldap/src/org/apache/cloudstack/api/command/LdapCreateAccount.java b/plugins/user-authenticators/ldap/src/org/apache/cloudstack/api/command/LdapCreateAccount.java new file mode 100644 index 00000000000..01aebce5252 --- /dev/null +++ b/plugins/user-authenticators/ldap/src/org/apache/cloudstack/api/command/LdapCreateAccount.java @@ -0,0 +1,141 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package org.apache.cloudstack.api.command; + +import java.security.NoSuchAlgorithmException; +import java.security.SecureRandom; +import java.util.Map; + +import javax.inject.Inject; +import javax.naming.NamingException; + +import org.apache.log4j.Logger; +import org.bouncycastle.util.encoders.Base64; + +import org.apache.cloudstack.api.APICommand; +import org.apache.cloudstack.api.ApiConstants; +import org.apache.cloudstack.api.ApiErrorCode; +import org.apache.cloudstack.api.BaseCmd; +import org.apache.cloudstack.api.Parameter; +import org.apache.cloudstack.api.ServerApiException; +import org.apache.cloudstack.api.response.AccountResponse; +import org.apache.cloudstack.api.response.DomainResponse; +import org.apache.cloudstack.context.CallContext; +import org.apache.cloudstack.ldap.LdapManager; +import org.apache.cloudstack.ldap.LdapUser; + +import com.cloud.user.Account; +import com.cloud.user.UserAccount; + +@APICommand(name = "ldapCreateAccount", description = "Creates an account from an LDAP user", responseObject = AccountResponse.class, since = "4.2.0") +public class LdapCreateAccount extends BaseCmd { + public static final Logger s_logger = Logger.getLogger(LdapCreateAccount.class.getName()); + private static final String s_name = "createaccountresponse"; + + @Inject + private LdapManager _ldapManager; + + @Parameter(name = ApiConstants.ACCOUNT, type = CommandType.STRING, description = "Creates the user under the specified account. If no account is specified, the username will be used as the account name.") + private String accountName; + + @Parameter(name = ApiConstants.ACCOUNT_TYPE, type = CommandType.SHORT, required = true, description = "Type of the account. Specify 0 for user, 1 for root admin, and 2 for domain admin") + private Short accountType; + + @Parameter(name = ApiConstants.DOMAIN_ID, type = CommandType.UUID, entityType = DomainResponse.class, + description = "Creates the user under the specified domain.") + private Long domainId; + + @Parameter(name = ApiConstants.TIMEZONE, type = CommandType.STRING, description = "Specifies a timezone for this command. For more information on the timezone parameter, see Time Zone Format.") + private String timeZone; + + @Parameter(name = ApiConstants.USERNAME, type = CommandType.STRING, required = true, description = "Unique username.") + private String userName; + + @Parameter(name = ApiConstants.NETWORK_DOMAIN, type = CommandType.STRING, description = "Network domain for the account's networks") + private String networkDomain; + + @Parameter(name = ApiConstants.ACCOUNT_DETAILS, type = CommandType.MAP, description = "details for account used to store specific parameters") + private Map details; + + @Parameter(name = ApiConstants.ACCOUNT_ID, type = CommandType.STRING, description = "Account UUID, required for adding account from external provisioning system") + private String accountUUID; + + @Parameter(name = ApiConstants.USER_ID, type = CommandType.STRING, description = "User UUID, required for adding account from external provisioning system") + private String userUUID; + + public LdapCreateAccount() { + super(); + } + + public LdapCreateAccount(final LdapManager ldapManager) { + super(); + _ldapManager = ldapManager; + } + + @Override + public void execute() throws ServerApiException { + CallContext.current().setEventDetails("Account Name: " + accountName + ", Domain Id:" + domainId); + try { + LdapUser user = _ldapManager.getUser(userName); + validateUser(user); + UserAccount userAccount = _accountService.createUserAccount(userName, generatePassword(), user.getFirstname(), user.getLastname(), user.getEmail(), timeZone, + accountName, accountType, domainId, networkDomain, details, accountUUID, userUUID); + if (userAccount != null) { + AccountResponse response = _responseGenerator.createUserAccountResponse(userAccount); + response.setResponseName(getCommandName()); + setResponseObject(response); + } else { + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to create a user account"); + } + } catch (NamingException e) { + throw new ServerApiException(ApiErrorCode.RESOURCE_UNAVAILABLE_ERROR, "No LDAP user exists with the username of " + userName); + } + } + + private String generatePassword() throws ServerApiException { + try { + SecureRandom randomGen = SecureRandom.getInstance("SHA1PRNG"); + byte bytes[] = new byte[20]; + randomGen.nextBytes(bytes); + return Base64.encode(bytes).toString(); + } catch (NoSuchAlgorithmException e) { + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to generate random password"); + } + } + + @Override + public String getCommandName() { + return s_name; + } + + @Override + public long getEntityOwnerId() { + return Account.ACCOUNT_ID_SYSTEM; + } + + private void validateUser(LdapUser user) throws ServerApiException { + if (user.getEmail() == null) { + throw new ServerApiException(ApiErrorCode.RESOURCE_UNAVAILABLE_ERROR, userName + " has no email address set within LDAP"); + } + if (user.getFirstname() == null) { + throw new ServerApiException(ApiErrorCode.RESOURCE_UNAVAILABLE_ERROR, userName + " has no firstname set within LDAP"); + } + if (user.getLastname() == null) { + throw new ServerApiException(ApiErrorCode.RESOURCE_UNAVAILABLE_ERROR, userName + " has no lastname set within LDAP"); + } + } +} \ No newline at end of file diff --git a/plugins/user-authenticators/ldap/src/org/apache/cloudstack/api/command/LdapDeleteConfigurationCmd.java b/plugins/user-authenticators/ldap/src/org/apache/cloudstack/api/command/LdapDeleteConfigurationCmd.java new file mode 100644 index 00000000000..b45bce59e1e --- /dev/null +++ b/plugins/user-authenticators/ldap/src/org/apache/cloudstack/api/command/LdapDeleteConfigurationCmd.java @@ -0,0 +1,76 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package org.apache.cloudstack.api.command; + +import javax.inject.Inject; + +import org.apache.log4j.Logger; + +import org.apache.cloudstack.api.APICommand; +import org.apache.cloudstack.api.ApiErrorCode; +import org.apache.cloudstack.api.BaseCmd; +import org.apache.cloudstack.api.Parameter; +import org.apache.cloudstack.api.ServerApiException; +import org.apache.cloudstack.api.response.LdapConfigurationResponse; +import org.apache.cloudstack.ldap.LdapManager; + +import com.cloud.exception.InvalidParameterValueException; +import com.cloud.user.Account; + +@APICommand(name = "deleteLdapConfiguration", description = "Remove an Ldap Configuration", responseObject = LdapConfigurationResponse.class, since = "4.2.0") +public class LdapDeleteConfigurationCmd extends BaseCmd { + public static final Logger s_logger = Logger.getLogger(LdapDeleteConfigurationCmd.class.getName()); + private static final String s_name = "ldapconfigurationresponse"; + + @Inject + private LdapManager _ldapManager; + + @Parameter(name = "hostname", type = CommandType.STRING, required = true, description = "Hostname") + private String hostname; + + public LdapDeleteConfigurationCmd() { + super(); + } + + public LdapDeleteConfigurationCmd(final LdapManager ldapManager) { + super(); + _ldapManager = ldapManager; + } + + @Override + public void execute() throws ServerApiException { + try { + final LdapConfigurationResponse response = _ldapManager.deleteConfiguration(hostname); + response.setObjectName("LdapDeleteConfiguration"); + response.setResponseName(getCommandName()); + setResponseObject(response); + } catch (final InvalidParameterValueException e) { + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, e.toString()); + } + + } + + @Override + public String getCommandName() { + return s_name; + } + + @Override + public long getEntityOwnerId() { + return Account.ACCOUNT_ID_SYSTEM; + } +} diff --git a/plugins/user-authenticators/ldap/src/org/apache/cloudstack/api/command/LdapListAllUsersCmd.java b/plugins/user-authenticators/ldap/src/org/apache/cloudstack/api/command/LdapListAllUsersCmd.java new file mode 100644 index 00000000000..98c9fcf9756 --- /dev/null +++ b/plugins/user-authenticators/ldap/src/org/apache/cloudstack/api/command/LdapListAllUsersCmd.java @@ -0,0 +1,88 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package org.apache.cloudstack.api.command; + +import java.util.ArrayList; +import java.util.List; + +import javax.inject.Inject; + +import org.apache.log4j.Logger; + +import org.apache.cloudstack.api.APICommand; +import org.apache.cloudstack.api.ApiErrorCode; +import org.apache.cloudstack.api.BaseListCmd; +import org.apache.cloudstack.api.ServerApiException; +import org.apache.cloudstack.api.response.LdapUserResponse; +import org.apache.cloudstack.api.response.ListResponse; +import org.apache.cloudstack.ldap.LdapManager; +import org.apache.cloudstack.ldap.LdapUser; +import org.apache.cloudstack.ldap.NoLdapUserMatchingQueryException; + +import com.cloud.user.Account; + +@APICommand(name = "listAllLdapUsers", responseObject = LdapUserResponse.class, description = "Lists all LDAP Users", since = "4.2.0") +public class LdapListAllUsersCmd extends BaseListCmd { + + public static final Logger s_logger = Logger.getLogger(LdapListAllUsersCmd.class.getName()); + private static final String s_name = "ldapuserresponse"; + @Inject + private LdapManager _ldapManager; + + public LdapListAllUsersCmd() { + super(); + } + + public LdapListAllUsersCmd(final LdapManager ldapManager) { + super(); + _ldapManager = ldapManager; + } + + private List createLdapUserResponse(List users) { + final List ldapResponses = new ArrayList(); + for (final LdapUser user : users) { + final LdapUserResponse ldapResponse = _ldapManager.createLdapUserResponse(user); + ldapResponse.setObjectName("LdapUser"); + ldapResponses.add(ldapResponse); + } + return ldapResponses; + } + + @Override + public void execute() throws ServerApiException { + try { + final List users = _ldapManager.getUsers(); + final ListResponse response = new ListResponse(); + final List ldapResponses = createLdapUserResponse(users); + response.setResponses(ldapResponses); + response.setResponseName(getCommandName()); + setResponseObject(response); + } catch (final NoLdapUserMatchingQueryException ex) { + throw new ServerApiException(ApiErrorCode.RESOURCE_UNAVAILABLE_ERROR, ex.getMessage()); + } + } + + @Override + public String getCommandName() { + return s_name; + } + + @Override + public long getEntityOwnerId() { + return Account.ACCOUNT_ID_SYSTEM; + } +} \ No newline at end of file diff --git a/plugins/user-authenticators/ldap/src/org/apache/cloudstack/api/command/LdapListConfigurationCmd.java b/plugins/user-authenticators/ldap/src/org/apache/cloudstack/api/command/LdapListConfigurationCmd.java new file mode 100644 index 00000000000..fc9bd35ab04 --- /dev/null +++ b/plugins/user-authenticators/ldap/src/org/apache/cloudstack/api/command/LdapListConfigurationCmd.java @@ -0,0 +1,106 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package org.apache.cloudstack.api.command; + +import java.util.ArrayList; +import java.util.List; + +import javax.inject.Inject; + +import org.apache.log4j.Logger; + +import org.apache.cloudstack.api.APICommand; +import org.apache.cloudstack.api.BaseListCmd; +import org.apache.cloudstack.api.Parameter; +import org.apache.cloudstack.api.response.LdapConfigurationResponse; +import org.apache.cloudstack.api.response.ListResponse; +import org.apache.cloudstack.ldap.LdapConfigurationVO; +import org.apache.cloudstack.ldap.LdapManager; + +import com.cloud.user.Account; +import com.cloud.utils.Pair; + +@APICommand(name = "listLdapConfigurations", responseObject = LdapConfigurationResponse.class, description = "Lists all LDAP configurations", since = "4.2.0") +public class LdapListConfigurationCmd extends BaseListCmd { + public static final Logger s_logger = Logger.getLogger(LdapListConfigurationCmd.class.getName()); + + private static final String s_name = "ldapconfigurationresponse"; + + @Inject + private LdapManager _ldapManager; + + @Parameter(name = "hostname", type = CommandType.STRING, required = false, description = "Hostname") + private String hostname; + + @Parameter(name = "port", type = CommandType.INTEGER, required = false, description = "Port") + private int port; + + public LdapListConfigurationCmd() { + super(); + } + + public LdapListConfigurationCmd(final LdapManager ldapManager) { + super(); + _ldapManager = ldapManager; + } + + private List createLdapConfigurationResponses(List configurations) { + final List responses = new ArrayList(); + for (final LdapConfigurationVO resource : configurations) { + final LdapConfigurationResponse configurationResponse = _ldapManager.createLdapConfigurationResponse(resource); + configurationResponse.setObjectName("LdapConfiguration"); + responses.add(configurationResponse); + } + return responses; + } + + @Override + public void execute() { + final Pair, Integer> result = _ldapManager.listConfigurations(this); + final List responses = createLdapConfigurationResponses(result.first()); + final ListResponse response = new ListResponse(); + response.setResponses(responses, result.second()); + response.setResponseName(getCommandName()); + setResponseObject(response); + } + + @Override + public String getCommandName() { + return s_name; + } + + @Override + public long getEntityOwnerId() { + return Account.ACCOUNT_ID_SYSTEM; + } + + public String getHostname() { + return hostname; + } + + public int getPort() { + return port; + } + + public void setHostname(final String hostname) { + this.hostname = hostname; + } + + public void setPort(final int port) { + this.port = port; + } +} diff --git a/plugins/user-authenticators/ldap/src/org/apache/cloudstack/api/command/LdapUserSearchCmd.java b/plugins/user-authenticators/ldap/src/org/apache/cloudstack/api/command/LdapUserSearchCmd.java new file mode 100644 index 00000000000..314581b4757 --- /dev/null +++ b/plugins/user-authenticators/ldap/src/org/apache/cloudstack/api/command/LdapUserSearchCmd.java @@ -0,0 +1,96 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package org.apache.cloudstack.api.command; + +import java.util.ArrayList; +import java.util.List; + +import javax.inject.Inject; + +import org.apache.log4j.Logger; + +import org.apache.cloudstack.api.APICommand; +import org.apache.cloudstack.api.BaseListCmd; +import org.apache.cloudstack.api.Parameter; +import org.apache.cloudstack.api.response.LdapUserResponse; +import org.apache.cloudstack.api.response.ListResponse; +import org.apache.cloudstack.ldap.LdapManager; +import org.apache.cloudstack.ldap.LdapUser; +import org.apache.cloudstack.ldap.NoLdapUserMatchingQueryException; + +import com.cloud.user.Account; + +@APICommand(name = "searchLdap", responseObject = LdapUserResponse.class, description = "Searches LDAP based on the username attribute", since = "4.2.0") +public class LdapUserSearchCmd extends BaseListCmd { + + public static final Logger s_logger = Logger.getLogger(LdapUserSearchCmd.class.getName()); + private static final String s_name = "ldapuserresponse"; + @Inject + private LdapManager _ldapManager; + + @Parameter(name = "query", type = CommandType.STRING, entityType = LdapUserResponse.class, required = true, description = "query to search using") + private String query; + + public LdapUserSearchCmd() { + super(); + } + + public LdapUserSearchCmd(final LdapManager ldapManager) { + super(); + _ldapManager = ldapManager; + } + + private List createLdapUserResponse(List users) { + final List ldapUserResponses = new ArrayList(); + if (users != null) { + for (final LdapUser user : users) { + final LdapUserResponse ldapUserResponse = _ldapManager.createLdapUserResponse(user); + ldapUserResponse.setObjectName("LdapUser"); + ldapUserResponses.add(ldapUserResponse); + } + } + return ldapUserResponses; + } + + @Override + public void execute() { + final ListResponse response = new ListResponse(); + List users = null; + + try { + users = _ldapManager.searchUsers(query); + } catch (final NoLdapUserMatchingQueryException e) { + s_logger.debug(e.getMessage()); + } + + final List ldapUserResponses = createLdapUserResponse(users); + + response.setResponses(ldapUserResponses); + response.setResponseName(getCommandName()); + setResponseObject(response); + } + + @Override + public String getCommandName() { + return s_name; + } + + @Override + public long getEntityOwnerId() { + return Account.ACCOUNT_ID_SYSTEM; + } +} \ No newline at end of file diff --git a/plugins/user-authenticators/ldap/src/org/apache/cloudstack/api/response/LdapConfigurationResponse.java b/plugins/user-authenticators/ldap/src/org/apache/cloudstack/api/response/LdapConfigurationResponse.java new file mode 100644 index 00000000000..a4e47828844 --- /dev/null +++ b/plugins/user-authenticators/ldap/src/org/apache/cloudstack/api/response/LdapConfigurationResponse.java @@ -0,0 +1,63 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package org.apache.cloudstack.api.response; + +import com.google.gson.annotations.SerializedName; + +import org.apache.cloudstack.api.BaseResponse; + +import com.cloud.serializer.Param; + +public class LdapConfigurationResponse extends BaseResponse { + @SerializedName("hostname") + @Param(description = "hostname") + private String hostname; + + @SerializedName("port") + @Param(description = "port") + private int port; + + public LdapConfigurationResponse() { + super(); + } + + public LdapConfigurationResponse(final String hostname) { + super(); + this.hostname = hostname; + } + + public LdapConfigurationResponse(final String hostname, final int port) { + this.hostname = hostname; + this.port = port; + } + + public String getHostname() { + return hostname; + } + + public int getPort() { + return port; + } + + public void setHostname(final String hostname) { + this.hostname = hostname; + } + + public void setPort(final int port) { + this.port = port; + } +} \ No newline at end of file diff --git a/plugins/user-authenticators/ldap/src/org/apache/cloudstack/api/response/LdapUserResponse.java b/plugins/user-authenticators/ldap/src/org/apache/cloudstack/api/response/LdapUserResponse.java new file mode 100644 index 00000000000..eb1c14c5b64 --- /dev/null +++ b/plugins/user-authenticators/ldap/src/org/apache/cloudstack/api/response/LdapUserResponse.java @@ -0,0 +1,98 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package org.apache.cloudstack.api.response; + +import com.google.gson.annotations.SerializedName; + +import org.apache.cloudstack.api.BaseResponse; + +import com.cloud.serializer.Param; + +public class LdapUserResponse extends BaseResponse { + @SerializedName("email") + @Param(description = "The user's email") + private String email; + + @SerializedName("principal") + @Param(description = "The user's principle") + private String principal; + + @SerializedName("firstname") + @Param(description = "The user's firstname") + private String firstname; + + @SerializedName("lastname") + @Param(description = "The user's lastname") + private String lastname; + + @SerializedName("username") + @Param(description = "The user's username") + private String username; + + public LdapUserResponse() { + super(); + } + + public LdapUserResponse(final String username, final String email, final String firstname, final String lastname, final String principal) { + super(); + this.username = username; + this.email = email; + this.firstname = firstname; + this.lastname = lastname; + this.principal = principal; + } + + public String getEmail() { + return email; + } + + public String getFirstname() { + return firstname; + } + + public String getLastname() { + return lastname; + } + + public String getPrincipal() { + return principal; + } + + public String getUsername() { + return username; + } + + public void setEmail(final String email) { + this.email = email; + } + + public void setFirstname(final String firstname) { + this.firstname = firstname; + } + + public void setLastname(final String lastname) { + this.lastname = lastname; + } + + public void setPrincipal(final String principal) { + this.principal = principal; + } + + public void setUsername(final String username) { + this.username = username; + } +} \ No newline at end of file diff --git a/plugins/user-authenticators/ldap/src/org/apache/cloudstack/ldap/LdapAuthenticator.java b/plugins/user-authenticators/ldap/src/org/apache/cloudstack/ldap/LdapAuthenticator.java new file mode 100644 index 00000000000..54b6a8b4ceb --- /dev/null +++ b/plugins/user-authenticators/ldap/src/org/apache/cloudstack/ldap/LdapAuthenticator.java @@ -0,0 +1,73 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package org.apache.cloudstack.ldap; + +import java.util.Map; + +import javax.inject.Inject; + +import org.apache.log4j.Logger; + +import org.apache.cloudstack.api.command.LdapListConfigurationCmd; + +import com.cloud.server.auth.DefaultUserAuthenticator; +import com.cloud.user.UserAccount; +import com.cloud.user.dao.UserAccountDao; + +public class LdapAuthenticator extends DefaultUserAuthenticator { + private static final Logger s_logger = Logger.getLogger(LdapAuthenticator.class.getName()); + + @Inject + private LdapManager _ldapManager; + @Inject + private UserAccountDao _userAccountDao; + + public LdapAuthenticator() { + super(); + } + + public LdapAuthenticator(final LdapManager ldapManager, final UserAccountDao userAccountDao) { + super(); + _ldapManager = ldapManager; + _userAccountDao = userAccountDao; + } + + @Override + public boolean authenticate(final String username, final String password, final Long domainId, final Map requestParameters) { + + final UserAccount user = _userAccountDao.getUserAccount(username, domainId); + + if (user == null) { + s_logger.debug("Unable to find user with " + username + " in domain " + domainId); + return false; + } else if (isLdapConfigurationPresent()) { + return _ldapManager.canAuthenticate(username, password); + } else { + return false; + } + } + + @Override + public String encode(final String password) { + return password; + } + + private boolean isLdapConfigurationPresent() { + return _ldapManager.listConfigurations(new LdapListConfigurationCmd(_ldapManager)).second() > 0; + } + +} diff --git a/plugins/user-authenticators/ldap/src/org/apache/cloudstack/ldap/LdapConfiguration.java b/plugins/user-authenticators/ldap/src/org/apache/cloudstack/ldap/LdapConfiguration.java new file mode 100644 index 00000000000..d0ae4d23424 --- /dev/null +++ b/plugins/user-authenticators/ldap/src/org/apache/cloudstack/ldap/LdapConfiguration.java @@ -0,0 +1,116 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package org.apache.cloudstack.ldap; + +import java.util.List; + +import javax.inject.Inject; +import javax.naming.directory.SearchControls; + +import org.apache.cloudstack.api.command.LdapListConfigurationCmd; + +import com.cloud.configuration.dao.ConfigurationDao; +import com.cloud.utils.Pair; + +public class LdapConfiguration { + private final static String factory = "com.sun.jndi.ldap.LdapCtxFactory"; + + private final static int scope = SearchControls.SUBTREE_SCOPE; + + @Inject + private ConfigurationDao _configDao; + + @Inject + private LdapManager _ldapManager; + + public LdapConfiguration() { + } + + public LdapConfiguration(final ConfigurationDao configDao, final LdapManager ldapManager) { + _configDao = configDao; + _ldapManager = ldapManager; + } + + public String getAuthentication() { + if ((getBindPrincipal() == null) && (getBindPassword() == null)) { + return "none"; + } else { + return "simple"; + } + } + + public String getBaseDn() { + return _configDao.getValue("ldap.basedn"); + } + + public String getBindPassword() { + return _configDao.getValue("ldap.bind.password"); + } + + public String getBindPrincipal() { + return _configDao.getValue("ldap.bind.principal"); + } + + public String getEmailAttribute() { + final String emailAttribute = _configDao.getValue("ldap.email.attribute"); + return emailAttribute == null ? "mail" : emailAttribute; + } + + public String getFactory() { + return factory; + } + + public String getFirstnameAttribute() { + final String firstnameAttribute = _configDao.getValue("ldap.firstname.attribute"); + return firstnameAttribute == null ? "givenname" : firstnameAttribute; + } + + public String getLastnameAttribute() { + final String lastnameAttribute = _configDao.getValue("ldap.lastname.attribute"); + return lastnameAttribute == null ? "sn" : lastnameAttribute; + } + + public String getProviderUrl() { + final Pair, Integer> result = _ldapManager.listConfigurations(new LdapListConfigurationCmd(_ldapManager)); + final StringBuilder providerUrls = new StringBuilder(); + String delim = ""; + for (final LdapConfigurationVO resource : result.first()) { + final String providerUrl = "ldap://" + resource.getHostname() + ":" + resource.getPort(); + providerUrls.append(delim).append(providerUrl); + delim = " "; + } + return providerUrls.toString(); + } + + public String[] getReturnAttributes() { + return new String[] {getUsernameAttribute(), getEmailAttribute(), getFirstnameAttribute(), getLastnameAttribute()}; + } + + public int getScope() { + return scope; + } + + public String getUsernameAttribute() { + final String usernameAttribute = _configDao.getValue("ldap.username.attribute"); + return usernameAttribute == null ? "uid" : usernameAttribute; + } + + public String getUserObject() { + final String userObject = _configDao.getValue("ldap.user.object"); + return userObject == null ? "inetOrgPerson" : userObject; + } +} \ No newline at end of file diff --git a/plugins/user-authenticators/ldap/src/org/apache/cloudstack/ldap/LdapConfigurationVO.java b/plugins/user-authenticators/ldap/src/org/apache/cloudstack/ldap/LdapConfigurationVO.java new file mode 100644 index 00000000000..5a243f2e59f --- /dev/null +++ b/plugins/user-authenticators/ldap/src/org/apache/cloudstack/ldap/LdapConfigurationVO.java @@ -0,0 +1,66 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package org.apache.cloudstack.ldap; + +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.GeneratedValue; +import javax.persistence.GenerationType; +import javax.persistence.Id; +import javax.persistence.Table; + +import org.apache.cloudstack.api.InternalIdentity; + +@Entity +@Table(name = "ldap_configuration") +public class LdapConfigurationVO implements InternalIdentity { + @Column(name = "hostname") + private String hostname; + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + @Column(name = "id") + private Long id; + + @Column(name = "port") + private int port; + + public LdapConfigurationVO() { + } + + public LdapConfigurationVO(final String hostname, final int port) { + this.hostname = hostname; + this.port = port; + } + + public String getHostname() { + return hostname; + } + + @Override + public long getId() { + return id; + } + + public int getPort() { + return port; + } + + public void setId(long id) { + this.id = id; + } +} \ No newline at end of file diff --git a/plugins/user-authenticators/ldap/src/org/apache/cloudstack/ldap/LdapContextFactory.java b/plugins/user-authenticators/ldap/src/org/apache/cloudstack/ldap/LdapContextFactory.java new file mode 100644 index 00000000000..fd33e8862e6 --- /dev/null +++ b/plugins/user-authenticators/ldap/src/org/apache/cloudstack/ldap/LdapContextFactory.java @@ -0,0 +1,103 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package org.apache.cloudstack.ldap; + +import java.util.Hashtable; + +import javax.inject.Inject; +import javax.naming.Context; +import javax.naming.NamingException; +import javax.naming.directory.DirContext; +import javax.naming.directory.InitialDirContext; + +import org.apache.log4j.Logger; + +public class LdapContextFactory { + private static final Logger s_logger = Logger.getLogger(LdapContextFactory.class.getName()); + + @Inject + private LdapConfiguration _ldapConfiguration; + + public LdapContextFactory() { + } + + public LdapContextFactory(final LdapConfiguration ldapConfiguration) { + _ldapConfiguration = ldapConfiguration; + } + + public DirContext createBindContext() throws NamingException { + return createBindContext(null); + } + + public DirContext createBindContext(final String providerUrl) throws NamingException { + final String bindPrincipal = _ldapConfiguration.getBindPrincipal(); + final String bindPassword = _ldapConfiguration.getBindPassword(); + return createInitialDirContext(bindPrincipal, bindPassword, providerUrl, true); + } + + private DirContext createInitialDirContext(final String principal, final String password, final boolean isSystemContext) throws NamingException { + return createInitialDirContext(principal, password, null, isSystemContext); + } + + private DirContext createInitialDirContext(final String principal, final String password, final String providerUrl, final boolean isSystemContext) throws NamingException { + return new InitialDirContext(getEnvironment(principal, password, providerUrl, isSystemContext)); + } + + public DirContext createUserContext(final String principal, final String password) throws NamingException { + return createInitialDirContext(principal, password, false); + } + + private Hashtable getEnvironment(final String principal, final String password, final String providerUrl, final boolean isSystemContext) { + final String factory = _ldapConfiguration.getFactory(); + final String url = providerUrl == null ? _ldapConfiguration.getProviderUrl() : providerUrl; + final String authentication = _ldapConfiguration.getAuthentication(); + + final Hashtable environment = new Hashtable(); + + environment.put(Context.INITIAL_CONTEXT_FACTORY, factory); + environment.put(Context.PROVIDER_URL, url); + environment.put("com.sun.jndi.ldap.read.timeout", "500"); + environment.put("com.sun.jndi.ldap.connect.pool", "true"); + + if ("none".equals(authentication) && !isSystemContext) { + environment.put(Context.SECURITY_AUTHENTICATION, "simple"); + } else { + environment.put(Context.SECURITY_AUTHENTICATION, authentication); + } + + if (principal != null) { + environment.put(Context.SECURITY_PRINCIPAL, principal); + } + + if (password != null) { + environment.put(Context.SECURITY_CREDENTIALS, password); + } + + return environment; + } + + public void testConnection(final String providerUrl) throws NamingException { + try { + createBindContext(providerUrl); + s_logger.info("LDAP Connection was successful"); + } catch (final NamingException e) { + s_logger.warn("LDAP Connection failed"); + s_logger.error(e.getMessage(), e); + throw e; + } + } +} \ No newline at end of file diff --git a/plugins/user-authenticators/ldap/src/org/apache/cloudstack/ldap/LdapManager.java b/plugins/user-authenticators/ldap/src/org/apache/cloudstack/ldap/LdapManager.java new file mode 100644 index 00000000000..aa0b7517c16 --- /dev/null +++ b/plugins/user-authenticators/ldap/src/org/apache/cloudstack/ldap/LdapManager.java @@ -0,0 +1,50 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package org.apache.cloudstack.ldap; + +import java.util.List; + +import javax.naming.NamingException; + +import org.apache.cloudstack.api.command.LdapListConfigurationCmd; +import org.apache.cloudstack.api.response.LdapConfigurationResponse; +import org.apache.cloudstack.api.response.LdapUserResponse; + +import com.cloud.exception.InvalidParameterValueException; +import com.cloud.utils.Pair; +import com.cloud.utils.component.PluggableService; + +public interface LdapManager extends PluggableService { + + LdapConfigurationResponse addConfiguration(String hostname, int port) throws InvalidParameterValueException; + + boolean canAuthenticate(String username, String password); + + LdapConfigurationResponse createLdapConfigurationResponse(LdapConfigurationVO configuration); + + LdapUserResponse createLdapUserResponse(LdapUser user); + + LdapConfigurationResponse deleteConfiguration(String hostname) throws InvalidParameterValueException; + + LdapUser getUser(final String username) throws NamingException; + + List getUsers() throws NoLdapUserMatchingQueryException; + + Pair, Integer> listConfigurations(LdapListConfigurationCmd cmd); + + List searchUsers(String query) throws NoLdapUserMatchingQueryException; +} \ No newline at end of file diff --git a/plugins/user-authenticators/ldap/src/org/apache/cloudstack/ldap/LdapManagerImpl.java b/plugins/user-authenticators/ldap/src/org/apache/cloudstack/ldap/LdapManagerImpl.java new file mode 100644 index 00000000000..b6fb3e8ab80 --- /dev/null +++ b/plugins/user-authenticators/ldap/src/org/apache/cloudstack/ldap/LdapManagerImpl.java @@ -0,0 +1,205 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package org.apache.cloudstack.ldap; + +import java.util.ArrayList; +import java.util.List; + +import javax.ejb.Local; +import javax.inject.Inject; +import javax.naming.NamingException; +import javax.naming.directory.DirContext; + +import org.apache.log4j.Logger; +import org.springframework.stereotype.Component; + +import org.apache.cloudstack.api.command.LdapAddConfigurationCmd; +import org.apache.cloudstack.api.command.LdapCreateAccount; +import org.apache.cloudstack.api.command.LdapDeleteConfigurationCmd; +import org.apache.cloudstack.api.command.LdapListAllUsersCmd; +import org.apache.cloudstack.api.command.LdapListConfigurationCmd; +import org.apache.cloudstack.api.command.LdapUserSearchCmd; +import org.apache.cloudstack.api.response.LdapConfigurationResponse; +import org.apache.cloudstack.api.response.LdapUserResponse; +import org.apache.cloudstack.ldap.dao.LdapConfigurationDao; + +import com.cloud.exception.InvalidParameterValueException; +import com.cloud.utils.Pair; + +@Component +@Local(value = LdapManager.class) +public class LdapManagerImpl implements LdapManager { + private static final Logger s_logger = Logger.getLogger(LdapManagerImpl.class.getName()); + + @Inject + private LdapConfigurationDao _ldapConfigurationDao; + + @Inject + private LdapContextFactory _ldapContextFactory; + + @Inject + private LdapUserManager _ldapUserManager; + + public LdapManagerImpl() { + super(); + } + + public LdapManagerImpl(final LdapConfigurationDao ldapConfigurationDao, final LdapContextFactory ldapContextFactory, final LdapUserManager ldapUserManager) { + super(); + _ldapConfigurationDao = ldapConfigurationDao; + _ldapContextFactory = ldapContextFactory; + _ldapUserManager = ldapUserManager; + } + + @Override + public LdapConfigurationResponse addConfiguration(final String hostname, final int port) throws InvalidParameterValueException { + LdapConfigurationVO configuration = _ldapConfigurationDao.findByHostname(hostname); + if (configuration == null) { + try { + final String providerUrl = "ldap://" + hostname + ":" + port; + _ldapContextFactory.createBindContext(providerUrl); + configuration = new LdapConfigurationVO(hostname, port); + _ldapConfigurationDao.persist(configuration); + s_logger.info("Added new ldap server with hostname: " + hostname); + return new LdapConfigurationResponse(hostname, port); + } catch (final NamingException e) { + throw new InvalidParameterValueException("Unable to bind to the given LDAP server"); + } + } else { + throw new InvalidParameterValueException("Duplicate configuration"); + } + } + + @Override + public boolean canAuthenticate(final String username, final String password) { + final String escapedUsername = LdapUtils.escapeLDAPSearchFilter(username); + try { + final LdapUser user = getUser(escapedUsername); + final String principal = user.getPrincipal(); + final DirContext context = _ldapContextFactory.createUserContext(principal, password); + closeContext(context); + return true; + } catch (final NamingException e) { + s_logger.info("Failed to authenticate user: " + username + ". incorrect password."); + return false; + } + } + + private void closeContext(final DirContext context) { + try { + if (context != null) { + context.close(); + } + } catch (final NamingException e) { + s_logger.warn(e.getMessage()); + } + } + + @Override + public LdapConfigurationResponse createLdapConfigurationResponse(final LdapConfigurationVO configuration) { + final LdapConfigurationResponse response = new LdapConfigurationResponse(); + response.setHostname(configuration.getHostname()); + response.setPort(configuration.getPort()); + return response; + } + + @Override + public LdapUserResponse createLdapUserResponse(final LdapUser user) { + final LdapUserResponse response = new LdapUserResponse(); + response.setUsername(user.getUsername()); + response.setFirstname(user.getFirstname()); + response.setLastname(user.getLastname()); + response.setEmail(user.getEmail()); + response.setPrincipal(user.getPrincipal()); + return response; + } + + @Override + public LdapConfigurationResponse deleteConfiguration(final String hostname) throws InvalidParameterValueException { + final LdapConfigurationVO configuration = _ldapConfigurationDao.findByHostname(hostname); + if (configuration == null) { + throw new InvalidParameterValueException("Cannot find configuration with hostname " + hostname); + } else { + _ldapConfigurationDao.remove(configuration.getId()); + s_logger.info("Removed ldap server with hostname: " + hostname); + return new LdapConfigurationResponse(configuration.getHostname(), configuration.getPort()); + } + } + + @Override + public List> getCommands() { + final List> cmdList = new ArrayList>(); + cmdList.add(LdapUserSearchCmd.class); + cmdList.add(LdapListAllUsersCmd.class); + cmdList.add(LdapAddConfigurationCmd.class); + cmdList.add(LdapDeleteConfigurationCmd.class); + cmdList.add(LdapListConfigurationCmd.class); + cmdList.add(LdapCreateAccount.class); + return cmdList; + } + + @Override + public LdapUser getUser(final String username) throws NamingException { + DirContext context = null; + try { + context = _ldapContextFactory.createBindContext(); + + final String escapedUsername = LdapUtils.escapeLDAPSearchFilter(username); + return _ldapUserManager.getUser(escapedUsername, context); + + } catch (final NamingException e) { + throw e; + } finally { + closeContext(context); + } + } + + @Override + public List getUsers() throws NoLdapUserMatchingQueryException { + DirContext context = null; + try { + context = _ldapContextFactory.createBindContext(); + return _ldapUserManager.getUsers(context); + } catch (final NamingException e) { + throw new NoLdapUserMatchingQueryException("*"); + } finally { + closeContext(context); + } + } + + @Override + public Pair, Integer> listConfigurations(final LdapListConfigurationCmd cmd) { + final String hostname = cmd.getHostname(); + final int port = cmd.getPort(); + final Pair, Integer> result = _ldapConfigurationDao.searchConfigurations(hostname, port); + return new Pair, Integer>(result.first(), result.second()); + } + + @Override + public List searchUsers(final String username) throws NoLdapUserMatchingQueryException { + DirContext context = null; + try { + context = _ldapContextFactory.createBindContext(); + final String escapedUsername = LdapUtils.escapeLDAPSearchFilter(username); + return _ldapUserManager.getUsers("*" + escapedUsername + "*", context); + } catch (final NamingException e) { + throw new NoLdapUserMatchingQueryException(username); + } finally { + closeContext(context); + } + } +} \ No newline at end of file diff --git a/plugins/user-authenticators/ldap/src/org/apache/cloudstack/ldap/LdapUser.java b/plugins/user-authenticators/ldap/src/org/apache/cloudstack/ldap/LdapUser.java new file mode 100644 index 00000000000..6bc1a78533c --- /dev/null +++ b/plugins/user-authenticators/ldap/src/org/apache/cloudstack/ldap/LdapUser.java @@ -0,0 +1,75 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package org.apache.cloudstack.ldap; + +public class LdapUser implements Comparable { + private final String email; + private final String principal; + private final String firstname; + private final String lastname; + private final String username; + + public LdapUser(final String username, final String email, final String firstname, final String lastname, final String principal) { + this.username = username; + this.email = email; + this.firstname = firstname; + this.lastname = lastname; + this.principal = principal; + } + + @Override + public int compareTo(final LdapUser other) { + return getUsername().compareTo(other.getUsername()); + } + + @Override + public boolean equals(final Object other) { + if (this == other) { + return true; + } + if (other instanceof LdapUser) { + final LdapUser otherLdapUser = (LdapUser)other; + return getUsername().equals(otherLdapUser.getUsername()); + } + return false; + } + + public String getEmail() { + return email; + } + + public String getFirstname() { + return firstname; + } + + public String getLastname() { + return lastname; + } + + public String getPrincipal() { + return principal; + } + + public String getUsername() { + return username; + } + + @Override + public int hashCode() { + return getUsername().hashCode(); + } +} \ No newline at end of file diff --git a/plugins/user-authenticators/ldap/src/org/apache/cloudstack/ldap/LdapUserManager.java b/plugins/user-authenticators/ldap/src/org/apache/cloudstack/ldap/LdapUserManager.java new file mode 100644 index 00000000000..f255752bf8e --- /dev/null +++ b/plugins/user-authenticators/ldap/src/org/apache/cloudstack/ldap/LdapUserManager.java @@ -0,0 +1,98 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package org.apache.cloudstack.ldap; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +import javax.inject.Inject; +import javax.naming.NamingEnumeration; +import javax.naming.NamingException; +import javax.naming.directory.Attributes; +import javax.naming.directory.DirContext; +import javax.naming.directory.SearchControls; +import javax.naming.directory.SearchResult; + +public class LdapUserManager { + + @Inject + private LdapConfiguration _ldapConfiguration; + + public LdapUserManager() { + } + + public LdapUserManager(final LdapConfiguration ldapConfiguration) { + _ldapConfiguration = ldapConfiguration; + } + + private LdapUser createUser(final SearchResult result) throws NamingException { + final Attributes attributes = result.getAttributes(); + + final String username = LdapUtils.getAttributeValue(attributes, _ldapConfiguration.getUsernameAttribute()); + final String email = LdapUtils.getAttributeValue(attributes, _ldapConfiguration.getEmailAttribute()); + final String firstname = LdapUtils.getAttributeValue(attributes, _ldapConfiguration.getFirstnameAttribute()); + final String lastname = LdapUtils.getAttributeValue(attributes, _ldapConfiguration.getLastnameAttribute()); + final String principal = result.getName() + "," + _ldapConfiguration.getBaseDn(); + + return new LdapUser(username, email, firstname, lastname, principal); + } + + public LdapUser getUser(final String username, final DirContext context) throws NamingException { + final NamingEnumeration result = searchUsers(username, context); + if (result.hasMoreElements()) { + return createUser(result.nextElement()); + } else { + throw new NamingException("No user found for username " + username); + } + } + + public List getUsers(final DirContext context) throws NamingException { + return getUsers(null, context); + } + + public List getUsers(final String username, final DirContext context) throws NamingException { + final NamingEnumeration results = searchUsers(username, context); + + final List users = new ArrayList(); + + while (results.hasMoreElements()) { + final SearchResult result = results.nextElement(); + users.add(createUser(result)); + } + + Collections.sort(users); + + return users; + } + + public NamingEnumeration searchUsers(final DirContext context) throws NamingException { + return searchUsers(null, context); + } + + public NamingEnumeration searchUsers(final String username, final DirContext context) throws NamingException { + final SearchControls controls = new SearchControls(); + + controls.setSearchScope(_ldapConfiguration.getScope()); + controls.setReturningAttributes(_ldapConfiguration.getReturnAttributes()); + + final String filter = "(&(objectClass=" + _ldapConfiguration.getUserObject() + ")" + "(" + + _ldapConfiguration.getUsernameAttribute() + "=" + (username == null ? "*" : username) + "))"; + + return context.search(_ldapConfiguration.getBaseDn(), filter, controls); + } +} \ No newline at end of file diff --git a/plugins/user-authenticators/ldap/src/org/apache/cloudstack/ldap/LdapUtils.java b/plugins/user-authenticators/ldap/src/org/apache/cloudstack/ldap/LdapUtils.java new file mode 100644 index 00000000000..8e7e93e9e48 --- /dev/null +++ b/plugins/user-authenticators/ldap/src/org/apache/cloudstack/ldap/LdapUtils.java @@ -0,0 +1,61 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package org.apache.cloudstack.ldap; + +import javax.naming.NamingException; +import javax.naming.directory.Attribute; +import javax.naming.directory.Attributes; + +public final class LdapUtils { + public static String escapeLDAPSearchFilter(final String filter) { + final StringBuilder sb = new StringBuilder(); + for (char character : filter.toCharArray()) { + switch (character) { + case '\\': + sb.append("\\5c"); + break; + case '*': + sb.append("\\2a"); + break; + case '(': + sb.append("\\28"); + break; + case ')': + sb.append("\\29"); + break; + case '\u0000': + sb.append("\\00"); + break; + default: + sb.append(character); + } + } + return sb.toString(); + } + + public static String getAttributeValue(final Attributes attributes, final String attributeName) throws NamingException { + final Attribute attribute = attributes.get(attributeName); + if (attribute != null) { + final Object value = attribute.get(); + return String.valueOf(value); + } + return null; + } + + private LdapUtils() { + } +} \ No newline at end of file diff --git a/plugins/user-authenticators/ldap/src/org/apache/cloudstack/ldap/NoLdapUserMatchingQueryException.java b/plugins/user-authenticators/ldap/src/org/apache/cloudstack/ldap/NoLdapUserMatchingQueryException.java new file mode 100644 index 00000000000..0f510c39a1a --- /dev/null +++ b/plugins/user-authenticators/ldap/src/org/apache/cloudstack/ldap/NoLdapUserMatchingQueryException.java @@ -0,0 +1,32 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package org.apache.cloudstack.ldap; + +public class NoLdapUserMatchingQueryException extends Exception { + private static final long serialVersionUID = 7124360347208388174L; + + private final String query; + + public NoLdapUserMatchingQueryException(final String query) { + super("No users matching: " + query); + this.query = query; + } + + public String getQuery() { + return query; + } +} \ No newline at end of file diff --git a/api/src/org/apache/cloudstack/api/response/LDAPRemoveResponse.java b/plugins/user-authenticators/ldap/src/org/apache/cloudstack/ldap/NoSuchLdapUserException.java similarity index 66% rename from api/src/org/apache/cloudstack/api/response/LDAPRemoveResponse.java rename to plugins/user-authenticators/ldap/src/org/apache/cloudstack/ldap/NoSuchLdapUserException.java index 0feec5bc5b5..d9bf13f6e49 100644 --- a/api/src/org/apache/cloudstack/api/response/LDAPRemoveResponse.java +++ b/plugins/user-authenticators/ldap/src/org/apache/cloudstack/ldap/NoSuchLdapUserException.java @@ -14,13 +14,18 @@ // KIND, either express or implied. See the License for the // specific language governing permissions and limitations // under the License. -package org.apache.cloudstack.api.response; +package org.apache.cloudstack.ldap; -import org.apache.cloudstack.api.BaseResponse; +public class NoSuchLdapUserException extends Exception { + private static final long serialVersionUID = 6782938919658010900L; + private final String username; -public class LDAPRemoveResponse extends BaseResponse { + public NoSuchLdapUserException(final String username) { + super("No such user: " + username); + this.username = username; + } - public LDAPRemoveResponse(){ - super(); + public String getUsername() { + return username; } } diff --git a/plugins/user-authenticators/ldap/src/org/apache/cloudstack/ldap/dao/LdapConfigurationDao.java b/plugins/user-authenticators/ldap/src/org/apache/cloudstack/ldap/dao/LdapConfigurationDao.java new file mode 100644 index 00000000000..a2d5e65248e --- /dev/null +++ b/plugins/user-authenticators/ldap/src/org/apache/cloudstack/ldap/dao/LdapConfigurationDao.java @@ -0,0 +1,30 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package org.apache.cloudstack.ldap.dao; + +import java.util.List; + +import org.apache.cloudstack.ldap.LdapConfigurationVO; + +import com.cloud.utils.Pair; +import com.cloud.utils.db.GenericDao; + +public interface LdapConfigurationDao extends GenericDao { + LdapConfigurationVO findByHostname(String hostname); + + Pair, Integer> searchConfigurations(String hostname, int port); +} \ No newline at end of file diff --git a/plugins/user-authenticators/ldap/src/org/apache/cloudstack/ldap/dao/LdapConfigurationDaoImpl.java b/plugins/user-authenticators/ldap/src/org/apache/cloudstack/ldap/dao/LdapConfigurationDaoImpl.java new file mode 100644 index 00000000000..0f2a0150eba --- /dev/null +++ b/plugins/user-authenticators/ldap/src/org/apache/cloudstack/ldap/dao/LdapConfigurationDaoImpl.java @@ -0,0 +1,66 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package org.apache.cloudstack.ldap.dao; + +import java.util.List; + +import javax.ejb.Local; + +import org.springframework.stereotype.Component; + +import org.apache.cloudstack.ldap.LdapConfigurationVO; + +import com.cloud.utils.Pair; +import com.cloud.utils.db.GenericDaoBase; +import com.cloud.utils.db.SearchBuilder; +import com.cloud.utils.db.SearchCriteria; +import com.cloud.utils.db.SearchCriteria.Op; + +@Component +@Local(value = {LdapConfigurationDao.class}) +public class LdapConfigurationDaoImpl extends GenericDaoBase implements LdapConfigurationDao { + private final SearchBuilder hostnameSearch; + private final SearchBuilder listAllConfigurationsSearch; + + public LdapConfigurationDaoImpl() { + super(); + hostnameSearch = createSearchBuilder(); + hostnameSearch.and("hostname", hostnameSearch.entity().getHostname(), SearchCriteria.Op.EQ); + hostnameSearch.done(); + + listAllConfigurationsSearch = createSearchBuilder(); + listAllConfigurationsSearch.and("hostname", listAllConfigurationsSearch.entity().getHostname(), Op.EQ); + listAllConfigurationsSearch.and("port", listAllConfigurationsSearch.entity().getPort(), Op.EQ); + listAllConfigurationsSearch.done(); + } + + @Override + public LdapConfigurationVO findByHostname(final String hostname) { + final SearchCriteria sc = hostnameSearch.create(); + sc.setParameters("hostname", hostname); + return findOneBy(sc); + } + + @Override + public Pair, Integer> searchConfigurations(final String hostname, final int port) { + final SearchCriteria sc = listAllConfigurationsSearch.create(); + if (hostname != null) { + sc.setParameters("hostname", hostname); + } + return searchAndCount(sc, null); + } +} \ No newline at end of file diff --git a/plugins/user-authenticators/ldap/test/groovy/org/apache/cloudstack/ldap/BasicNamingEnumerationImpl.groovy b/plugins/user-authenticators/ldap/test/groovy/org/apache/cloudstack/ldap/BasicNamingEnumerationImpl.groovy new file mode 100644 index 00000000000..ab7e22ac4ec --- /dev/null +++ b/plugins/user-authenticators/ldap/test/groovy/org/apache/cloudstack/ldap/BasicNamingEnumerationImpl.groovy @@ -0,0 +1,56 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package groovy.org.apache.cloudstack.ldap + +import javax.naming.NamingEnumeration +import javax.naming.NamingException +import javax.naming.directory.SearchResult + +class BasicNamingEnumerationImpl implements NamingEnumeration { + + private LinkedList items = new LinkedList(); + + @Override + public boolean hasMoreElements() { + return items.size != 0; + } + + @Override + public Object nextElement() { + SearchResult result = items.getFirst(); + items.removeFirst(); + return result; + } + + @Override + public void close() throws NamingException { + } + + @Override + public boolean hasMore() throws NamingException { + return hasMoreElements(); + } + + @Override + public Object next() throws NamingException { + return nextElement(); + } + + public void add(SearchResult item) { + items.add(item) + } +} diff --git a/plugins/user-authenticators/ldap/test/groovy/org/apache/cloudstack/ldap/LdapAddConfigurationCmdSpec.groovy b/plugins/user-authenticators/ldap/test/groovy/org/apache/cloudstack/ldap/LdapAddConfigurationCmdSpec.groovy new file mode 100644 index 00000000000..3dcb23f0328 --- /dev/null +++ b/plugins/user-authenticators/ldap/test/groovy/org/apache/cloudstack/ldap/LdapAddConfigurationCmdSpec.groovy @@ -0,0 +1,89 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package groovy.org.apache.cloudstack.ldap + +import com.cloud.exception.InvalidParameterValueException +import org.apache.cloudstack.api.ServerApiException +import org.apache.cloudstack.api.command.LdapAddConfigurationCmd +import org.apache.cloudstack.api.response.LdapConfigurationResponse +import org.apache.cloudstack.ldap.LdapManager + +class LdapAddConfigurationCmdSpec extends spock.lang.Specification { + + def "Test successful response from execute"() { + given: + def ldapManager = Mock(LdapManager) + ldapManager.addConfiguration(_, _) >> new LdapConfigurationResponse("localhost", 389) + def ldapAddConfigurationCmd = new LdapAddConfigurationCmd(ldapManager) + when: + ldapAddConfigurationCmd.execute() + then: + ldapAddConfigurationCmd.responseObject.hostname == "localhost" + ldapAddConfigurationCmd.responseObject.port == 389 + } + + def "Test failed response from execute"() { + given: + def ldapManager = Mock(LdapManager) + ldapManager.addConfiguration(_, _) >> { throw new InvalidParameterValueException() } + def ldapAddConfigurationCmd = new LdapAddConfigurationCmd(ldapManager) + when: + ldapAddConfigurationCmd.execute() + then: + thrown ServerApiException + } + + def "Test successful setting of hostname"() { + given: + def ldapManager = Mock(LdapManager) + def ldapAddConfigurationCmd = new LdapAddConfigurationCmd(ldapManager) + when: + ldapAddConfigurationCmd.setHostname("localhost") + then: + ldapAddConfigurationCmd.getHostname() == "localhost" + } + + def "Test successful setting of port"() { + given: + def ldapManager = Mock(LdapManager) + def ldapAddConfigurationCmd = new LdapAddConfigurationCmd(ldapManager) + when: + ldapAddConfigurationCmd.setPort(389) + then: + ldapAddConfigurationCmd.getPort() == 389 + } + + def "Test getEntityOwnerId is 0"() { + given: + def ldapManager = Mock(LdapManager) + def ldapAddConfigurationCmd = new LdapAddConfigurationCmd(ldapManager) + when: + long ownerId = ldapAddConfigurationCmd.getEntityOwnerId() + then: + ownerId == 1 + } + + def "Test successful return of getCommandName"() { + given: + def ldapManager = Mock(LdapManager) + def ldapAddConfigurationCmd = new LdapAddConfigurationCmd(ldapManager) + when: + String commandName = ldapAddConfigurationCmd.getCommandName() + then: + commandName == "ldapconfigurationresponse" + } +} diff --git a/plugins/user-authenticators/ldap/test/groovy/org/apache/cloudstack/ldap/LdapAuthenticatorSpec.groovy b/plugins/user-authenticators/ldap/test/groovy/org/apache/cloudstack/ldap/LdapAuthenticatorSpec.groovy new file mode 100644 index 00000000000..573f88cd1b4 --- /dev/null +++ b/plugins/user-authenticators/ldap/test/groovy/org/apache/cloudstack/ldap/LdapAuthenticatorSpec.groovy @@ -0,0 +1,90 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package groovy.org.apache.cloudstack.ldap + +import com.cloud.user.UserAccountVO +import com.cloud.user.dao.UserAccountDao +import com.cloud.utils.Pair +import org.apache.cloudstack.ldap.LdapAuthenticator +import org.apache.cloudstack.ldap.LdapConfigurationVO +import org.apache.cloudstack.ldap.LdapManager + +class LdapAuthenticatorSpec extends spock.lang.Specification { + + def "Test a failed authentication due to user not being found within cloudstack"() { + given: + LdapManager ldapManager = Mock(LdapManager) + UserAccountDao userAccountDao = Mock(UserAccountDao) + userAccountDao.getUserAccount(_, _) >> null + def ldapAuthenticator = new LdapAuthenticator(ldapManager, userAccountDao) + when: + def result = ldapAuthenticator.authenticate("rmurphy", "password", 0, null) + then: + result == false + } + + def "Test failed authentication due to ldap not being configured"() { + given: + def ldapManager = Mock(LdapManager) + List ldapConfigurationList = new ArrayList() + Pair, Integer> ldapConfigurations = new Pair, Integer>(); + ldapConfigurations.set(ldapConfigurationList, ldapConfigurationList.size()) + ldapManager.listConfigurations(_) >> ldapConfigurations + + UserAccountDao userAccountDao = Mock(UserAccountDao) + userAccountDao.getUserAccount(_, _) >> new UserAccountVO() + + def ldapAuthenticator = new LdapAuthenticator(ldapManager, userAccountDao) + when: + def result = ldapAuthenticator.authenticate("rmurphy", "password", 0, null) + then: + result == false + } + + def "Test failed authentication due to ldap bind being unsuccessful"() { + given: + + def ldapManager = Mock(LdapManager) + List ldapConfigurationList = new ArrayList() + ldapConfigurationList.add(new LdapConfigurationVO("localhost", 389)) + Pair, Integer> ldapConfigurations = new Pair, Integer>(); + ldapConfigurations.set(ldapConfigurationList, ldapConfigurationList.size()) + ldapManager.listConfigurations(_) >> ldapConfigurations + ldapManager.canAuthenticate(_, _) >> false + + UserAccountDao userAccountDao = Mock(UserAccountDao) + userAccountDao.getUserAccount(_, _) >> new UserAccountVO() + def ldapAuthenticator = new LdapAuthenticator(ldapManager, userAccountDao) + + when: + def result = ldapAuthenticator.authenticate("rmurphy", "password", 0, null) + + then: + result == false + } + + def "Test that encode doesn't change the input"() { + given: + LdapManager ldapManager = Mock(LdapManager) + UserAccountDao userAccountDao = Mock(UserAccountDao) + def ldapAuthenticator = new LdapAuthenticator(ldapManager, userAccountDao) + when: + def result = ldapAuthenticator.encode("password") + then: + result == "password" + } +} \ No newline at end of file diff --git a/plugins/user-authenticators/ldap/test/groovy/org/apache/cloudstack/ldap/LdapConfigurationDaoImplSpec.groovy b/plugins/user-authenticators/ldap/test/groovy/org/apache/cloudstack/ldap/LdapConfigurationDaoImplSpec.groovy new file mode 100644 index 00000000000..191b6098214 --- /dev/null +++ b/plugins/user-authenticators/ldap/test/groovy/org/apache/cloudstack/ldap/LdapConfigurationDaoImplSpec.groovy @@ -0,0 +1,29 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package groovy.org.apache.cloudstack.ldap + +import org.apache.cloudstack.ldap.dao.LdapConfigurationDaoImpl + +class LdapConfigurationDaoImplSpec extends spock.lang.Specification { + def "Test setting up of a LdapConfigurationDao"() { + given: + def ldapConfigurationDaoImpl = new LdapConfigurationDaoImpl(); + expect: + ldapConfigurationDaoImpl.hostnameSearch != null; + ldapConfigurationDaoImpl.listAllConfigurationsSearch != null + } +} diff --git a/plugins/user-authenticators/ldap/test/groovy/org/apache/cloudstack/ldap/LdapConfigurationResponseSpec.groovy b/plugins/user-authenticators/ldap/test/groovy/org/apache/cloudstack/ldap/LdapConfigurationResponseSpec.groovy new file mode 100644 index 00000000000..4e6bebb488f --- /dev/null +++ b/plugins/user-authenticators/ldap/test/groovy/org/apache/cloudstack/ldap/LdapConfigurationResponseSpec.groovy @@ -0,0 +1,49 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package groovy.org.apache.cloudstack.ldap + +import org.apache.cloudstack.api.response.LdapConfigurationResponse + +class LdapConfigurationResponseSpec extends spock.lang.Specification { + def "Testing succcessful setting of LdapConfigurationResponse hostname"() { + given: + LdapConfigurationResponse response = new LdapConfigurationResponse(); + when: + response.setHostname("localhost"); + then: + response.getHostname() == "localhost"; + } + + def "Testing successful setting of LdapConfigurationResponse port"() { + given: + LdapConfigurationResponse response = new LdapConfigurationResponse() + when: + response.setPort(389) + then: + response.getPort() == 389 + } + + def "Testing successful setting of LdapConfigurationResponse hostname and port via constructor"() { + given: + LdapConfigurationResponse response + when: + response = new LdapConfigurationResponse("localhost", 389) + then: + response.getHostname() == "localhost" + response.getPort() == 389 + } +} diff --git a/plugins/user-authenticators/ldap/test/groovy/org/apache/cloudstack/ldap/LdapConfigurationSpec.groovy b/plugins/user-authenticators/ldap/test/groovy/org/apache/cloudstack/ldap/LdapConfigurationSpec.groovy new file mode 100644 index 00000000000..a867fd6706e --- /dev/null +++ b/plugins/user-authenticators/ldap/test/groovy/org/apache/cloudstack/ldap/LdapConfigurationSpec.groovy @@ -0,0 +1,181 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package groovy.org.apache.cloudstack.ldap + +import com.cloud.configuration.dao.ConfigurationDao +import com.cloud.utils.Pair +import org.apache.cloudstack.api.ServerApiException +import org.apache.cloudstack.ldap.LdapConfiguration +import org.apache.cloudstack.ldap.LdapConfigurationVO +import org.apache.cloudstack.ldap.LdapManager + +import javax.naming.directory.SearchControls + +class LdapConfigurationSpec extends spock.lang.Specification { + def "Test that providerUrl successfully returns a URL when a configuration is available"() { + given: + def configDao = Mock(ConfigurationDao) + + def ldapManager = Mock(LdapManager) + List ldapConfigurationList = new ArrayList() + ldapConfigurationList.add(new LdapConfigurationVO("localhost", 389)) + Pair, Integer> result = new Pair, Integer>(); + result.set(ldapConfigurationList, ldapConfigurationList.size()) + ldapManager.listConfigurations(_) >> result + + LdapConfiguration ldapConfiguration = new LdapConfiguration(configDao, ldapManager) + + when: + String providerUrl = ldapConfiguration.getProviderUrl() + + then: + providerUrl == "ldap://localhost:389" + } + + def "Test that getAuthentication returns simple"() { + given: + def configDao = Mock(ConfigurationDao) + def ldapManager = Mock(LdapManager) + def ldapConfiguration = new LdapConfiguration(configDao, ldapManager) + configDao.getValue("ldap.bind.password") >> "password" + configDao.getValue("ldap.bind.principal") >> "cn=rmurphy,dc=cloudstack,dc=org" + when: + String authentication = ldapConfiguration.getAuthentication() + then: + authentication == "simple" + } + + def "Test that getAuthentication returns none"() { + given: + def configDao = Mock(ConfigurationDao) + def ldapManager = Mock(LdapManager) + def ldapConfiguration = new LdapConfiguration(configDao, ldapManager) + when: + String authentication = ldapConfiguration.getAuthentication() + then: + authentication == "none" + } + + def "Test that getEmailAttribute returns mail"() { + given: + def configDao = Mock(ConfigurationDao) + configDao.getValue("ldap.email.attribute") >> "mail" + def ldapManager = Mock(LdapManager) + def ldapConfiguration = new LdapConfiguration(configDao, ldapManager) + when: + String emailAttribute = ldapConfiguration.getEmailAttribute() + then: + emailAttribute == "mail" + } + + def "Test that getUsernameAttribute returns uid"() { + given: + def configDao = Mock(ConfigurationDao) + configDao.getValue("ldap.username.attribute") >> "uid" + def ldapManager = Mock(LdapManager) + def ldapConfiguration = new LdapConfiguration(configDao, ldapManager) + when: + String usernameAttribute = ldapConfiguration.getUsernameAttribute() + then: + usernameAttribute == "uid" + } + + def "Test that getFirstnameAttribute returns givenname"() { + given: + def configDao = Mock(ConfigurationDao) + configDao.getValue("ldap.firstname.attribute") >> "givenname" + def ldapManager = Mock(LdapManager) + def ldapConfiguration = new LdapConfiguration(configDao, ldapManager) + when: + String firstname = ldapConfiguration.getFirstnameAttribute() + then: + firstname == "givenname" + } + + def "Test that getLastnameAttribute returns givenname"() { + given: + def configDao = Mock(ConfigurationDao) + configDao.getValue("ldap.lastname.attribute") >> "sn" + def ldapManager = Mock(LdapManager) + def ldapConfiguration = new LdapConfiguration(configDao, ldapManager) + when: + String lastname = ldapConfiguration.getLastnameAttribute() + then: + lastname == "sn" + } + + def "Test that getUserObject returns inetOrgPerson"() { + given: + def configDao = Mock(ConfigurationDao) + configDao.getValue("ldap.user.object") >> "inetOrgPerson" + def ldapManager = Mock(LdapManager) + def ldapConfiguration = new LdapConfiguration(configDao, ldapManager) + when: + String userObject = ldapConfiguration.getUserObject() + then: + userObject == "inetOrgPerson" + } + + def "Test that getReturnAttributes returns the correct data"() { + given: + def configDao = Mock(ConfigurationDao) + configDao.getValue("ldap.firstname.attribute") >> "givenname" + configDao.getValue("ldap.lastname.attribute") >> "sn" + configDao.getValue("ldap.username.attribute") >> "uid" + configDao.getValue("ldap.email.attribute") >> "mail" + def ldapManager = Mock(LdapManager) + def ldapConfiguration = new LdapConfiguration(configDao, ldapManager) + when: + String[] returnAttributes = ldapConfiguration.getReturnAttributes() + then: + returnAttributes == ["uid", "mail", "givenname", "sn"] + } + + def "Test that getScope returns SearchControls.SUBTREE_SCOPE"() { + given: + def configDao = Mock(ConfigurationDao) + def ldapManager = Mock(LdapManager) + def ldapConfiguration = new LdapConfiguration(configDao, ldapManager) + when: + int scope = ldapConfiguration.getScope() + then: + scope == SearchControls.SUBTREE_SCOPE; + } + + def "Test that getBaseDn returns dc=cloudstack,dc=org"() { + given: + def configDao = Mock(ConfigurationDao) + configDao.getValue("ldap.basedn") >> "dc=cloudstack,dc=org" + def ldapManager = Mock(LdapManager) + def ldapConfiguration = new LdapConfiguration(configDao, ldapManager) + when: + String baseDn = ldapConfiguration.getBaseDn(); + then: + baseDn == "dc=cloudstack,dc=org" + } + + def "Test that getFactory returns com.sun.jndi.ldap.LdapCtxFactory"() { + given: + def configDao = Mock(ConfigurationDao) + def ldapManager = Mock(LdapManager) + def ldapConfiguration = new LdapConfiguration(configDao, ldapManager) + when: + String factory = ldapConfiguration.getFactory(); + then: + factory == "com.sun.jndi.ldap.LdapCtxFactory" + } +} diff --git a/plugins/user-authenticators/ldap/test/groovy/org/apache/cloudstack/ldap/LdapConfigurationVO.groovy b/plugins/user-authenticators/ldap/test/groovy/org/apache/cloudstack/ldap/LdapConfigurationVO.groovy new file mode 100644 index 00000000000..27f3dfc49d6 --- /dev/null +++ b/plugins/user-authenticators/ldap/test/groovy/org/apache/cloudstack/ldap/LdapConfigurationVO.groovy @@ -0,0 +1,36 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package groovy.org.apache.cloudstack.ldap + +import org.apache.cloudstack.ldap.LdapConfigurationVO + + +class LdapConfigurationVOSpec extends spock.lang.Specification { + def "Testing that the ID hostname and port is correctly set within the LDAP configuration VO"() { + given: "You have created an LDAP Configuration VO" + def configuration = new LdapConfigurationVO(hostname, port) + configuration.setId(id) + expect: "The id hostname and port is equal to the given data source" + configuration.getId() == id + configuration.getHostname() == hostname + configuration.getPort() == port + where: "The id, hostname and port is set to " + hostname << ["", null, "localhost"] + id << [0, 1000, -1000] + port << [0, 1000, -1000] + } +} diff --git a/plugins/user-authenticators/ldap/test/groovy/org/apache/cloudstack/ldap/LdapContextFactorySpec.groovy b/plugins/user-authenticators/ldap/test/groovy/org/apache/cloudstack/ldap/LdapContextFactorySpec.groovy new file mode 100644 index 00000000000..e9b3b6e9fa5 --- /dev/null +++ b/plugins/user-authenticators/ldap/test/groovy/org/apache/cloudstack/ldap/LdapContextFactorySpec.groovy @@ -0,0 +1,134 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package groovy.org.apache.cloudstack.ldap + +import org.apache.cloudstack.ldap.LdapConfiguration +import org.apache.cloudstack.ldap.LdapContextFactory +import spock.lang.Shared + +import javax.naming.NamingException +import javax.naming.directory.SearchControls +import javax.naming.ldap.LdapContext + +class LdapContextFactorySpec extends spock.lang.Specification { + @Shared + private def ldapConfiguration + + @Shared + private def username + + @Shared + private def principal + + @Shared + private def password + + def setupSpec() { + ldapConfiguration = Mock(LdapConfiguration) + + ldapConfiguration.getFactory() >> "com.sun.jndi.ldap.LdapCtxFactory" + ldapConfiguration.getProviderUrl() >> "ldap://localhost:389" + ldapConfiguration.getAuthentication() >> "none" + ldapConfiguration.getScope() >> SearchControls.SUBTREE_SCOPE + ldapConfiguration.getReturnAttributes() >> ["uid", "mail", "cn"] + ldapConfiguration.getUsernameAttribute() >> "uid" + ldapConfiguration.getEmailAttribute() >> "mail" + ldapConfiguration.getFirstnameAttribute() >> "givenname" + ldapConfiguration.getLastnameAttribute() >> "sn" + ldapConfiguration.getBaseDn() >> "dc=cloudstack,dc=org" + + username = "rmurphy" + principal = "cn=" + username + "," + ldapConfiguration.getBaseDn() + password = "password" + } + + def "Test successfully creating a system environment with anon bind"() { + given: + def ldapContextFactory = new LdapContextFactory(ldapConfiguration) + + when: + def result = ldapContextFactory.getEnvironment(principal, password, null, false) + + then: + result['java.naming.provider.url'] == ldapConfiguration.getProviderUrl() + result['java.naming.factory.initial'] == ldapConfiguration.getFactory() + result['java.naming.security.principal'] == principal + result['java.naming.security.authentication'] == "simple" + result['java.naming.security.credentials'] == password + } + + def "Test successfully creating a environment with username and password"() { + given: + def ldapContextFactory = new LdapContextFactory(ldapConfiguration) + + when: + def result = ldapContextFactory.getEnvironment(null, null, null, true) + + then: + result['java.naming.provider.url'] == ldapConfiguration.getProviderUrl() + result['java.naming.factory.initial'] == ldapConfiguration.getFactory() + result['java.naming.security.principal'] == null + result['java.naming.security.authentication'] == ldapConfiguration.getAuthentication() + result['java.naming.security.credentials'] == null + } + + def "Test successfully binding as a user"() { + given: + def ldapContextFactory = new LdapContextFactory(ldapConfiguration) + when: + ldapContextFactory.createUserContext(principal, password) + then: + thrown NamingException + } + + def "Test successully binding as system"() { + given: + def ldapContextFactory = new LdapContextFactory(ldapConfiguration) + when: + ldapContextFactory.createBindContext() + then: + thrown NamingException + } + + def "Test succcessfully creating a initial context"() { + given: + def ldapContextFactory = new LdapContextFactory(ldapConfiguration) + when: + ldapContextFactory.createInitialDirContext(null, null, true) + then: + thrown NamingException + } + + def "Test successful failed connection"() { + given: + def ldapContextFactory = Spy(LdapContextFactory, constructorArgs: [ldapConfiguration]) + when: + ldapContextFactory.testConnection(ldapConfiguration.getProviderUrl()) + then: + thrown NamingException + } + + def "Test successful connection"() { + given: + def ldapContextFactory = Spy(LdapContextFactory, constructorArgs: [ldapConfiguration]) + ldapContextFactory.createBindContext(_) >> Mock(LdapContext) + when: + ldapContextFactory.testConnection(ldapConfiguration.getProviderUrl()) + then: + notThrown NamingException + } +} diff --git a/plugins/user-authenticators/ldap/test/groovy/org/apache/cloudstack/ldap/LdapDeleteConfigurationCmdSpec.groovy b/plugins/user-authenticators/ldap/test/groovy/org/apache/cloudstack/ldap/LdapDeleteConfigurationCmdSpec.groovy new file mode 100644 index 00000000000..f4d185b9fe7 --- /dev/null +++ b/plugins/user-authenticators/ldap/test/groovy/org/apache/cloudstack/ldap/LdapDeleteConfigurationCmdSpec.groovy @@ -0,0 +1,68 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package groovy.org.apache.cloudstack.ldap + +import com.cloud.exception.InvalidParameterValueException +import org.apache.cloudstack.api.ServerApiException +import org.apache.cloudstack.api.command.LdapDeleteConfigurationCmd +import org.apache.cloudstack.api.response.LdapConfigurationResponse +import org.apache.cloudstack.ldap.LdapManager + +class LdapDeleteConfigurationCmdSpec extends spock.lang.Specification { + + def "Test successful response from execute"() { + given: + def ldapManager = Mock(LdapManager) + ldapManager.deleteConfiguration(_) >> new LdapConfigurationResponse("localhost") + def ldapDeleteConfigurationCmd = new LdapDeleteConfigurationCmd(ldapManager) + when: + ldapDeleteConfigurationCmd.execute() + then: + ldapDeleteConfigurationCmd.responseObject.hostname == "localhost" + } + + def "Test failed response from execute"() { + given: + def ldapManager = Mock(LdapManager) + ldapManager.deleteConfiguration(_) >> { throw new InvalidParameterValueException() } + def ldapDeleteConfigurationCmd = new LdapDeleteConfigurationCmd(ldapManager) + when: + ldapDeleteConfigurationCmd.execute() + then: + thrown ServerApiException + } + + def "Test getEntityOwnerId is 0"() { + given: + def ldapManager = Mock(LdapManager) + def ldapDeleteConfigurationCmd = new LdapDeleteConfigurationCmd(ldapManager) + when: + long ownerId = ldapDeleteConfigurationCmd.getEntityOwnerId() + then: + ownerId == 1 + } + + def "Test successful return of getCommandName"() { + given: + def ldapManager = Mock(LdapManager) + def ldapDeleteConfigurationCmd = new LdapDeleteConfigurationCmd(ldapManager) + when: + String commandName = ldapDeleteConfigurationCmd.getCommandName() + then: + commandName == "ldapconfigurationresponse" + } +} diff --git a/plugins/user-authenticators/ldap/test/groovy/org/apache/cloudstack/ldap/LdapListAllUsersCmdSpec.groovy b/plugins/user-authenticators/ldap/test/groovy/org/apache/cloudstack/ldap/LdapListAllUsersCmdSpec.groovy new file mode 100644 index 00000000000..2756e9277f4 --- /dev/null +++ b/plugins/user-authenticators/ldap/test/groovy/org/apache/cloudstack/ldap/LdapListAllUsersCmdSpec.groovy @@ -0,0 +1,72 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package groovy.org.apache.cloudstack.ldap + +import org.apache.cloudstack.api.ServerApiException +import org.apache.cloudstack.api.command.LdapListAllUsersCmd +import org.apache.cloudstack.api.response.LdapUserResponse +import org.apache.cloudstack.ldap.LdapManager +import org.apache.cloudstack.ldap.LdapUser +import org.apache.cloudstack.ldap.NoLdapUserMatchingQueryException + +class LdapListAllUsersCmdSpec extends spock.lang.Specification { + def "Test successful response from execute"() { + given: + def ldapManager = Mock(LdapManager) + List users = new ArrayList() + users.add(new LdapUser("rmurphy", "rmurphy@test.com", "Ryan", "Murphy", "cn=rmurphy,dc=cloudstack,dc=org")) + ldapManager.getUsers() >> users + LdapUserResponse response = new LdapUserResponse("rmurphy", "rmurphy@test.com", "Ryan", "Murphy", "cn=rmurphy,dc=cloudstack,dc=org") + ldapManager.createLdapUserResponse(_) >> response + def ldapListAllUsersCmd = new LdapListAllUsersCmd(ldapManager) + when: + ldapListAllUsersCmd.execute() + then: + ldapListAllUsersCmd.responseObject.getResponses().size() != 0 + } + + def "Test successful empty response from execute"() { + given: + def ldapManager = Mock(LdapManager) + ldapManager.getUsers() >> {throw new NoLdapUserMatchingQueryException()} + def ldapListAllUsersCmd = new LdapListAllUsersCmd(ldapManager) + when: + ldapListAllUsersCmd.execute() + then: + thrown ServerApiException + } + + def "Test getEntityOwnerId is 1"() { + given: + def ldapManager = Mock(LdapManager) + def ldapListAllUsersCmd = new LdapListAllUsersCmd(ldapManager) + when: + long ownerId = ldapListAllUsersCmd.getEntityOwnerId() + then: + ownerId == 1 + } + + def "Test successful return of getCommandName"() { + given: + def ldapManager = Mock(LdapManager) + def ldapListAllUsersCmd = new LdapListAllUsersCmd(ldapManager) + when: + String commandName = ldapListAllUsersCmd.getCommandName() + then: + commandName == "ldapuserresponse" + } +} diff --git a/plugins/user-authenticators/ldap/test/groovy/org/apache/cloudstack/ldap/LdapListConfigurationCmdSpec.groovy b/plugins/user-authenticators/ldap/test/groovy/org/apache/cloudstack/ldap/LdapListConfigurationCmdSpec.groovy new file mode 100644 index 00000000000..d83b926806f --- /dev/null +++ b/plugins/user-authenticators/ldap/test/groovy/org/apache/cloudstack/ldap/LdapListConfigurationCmdSpec.groovy @@ -0,0 +1,100 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package groovy.org.apache.cloudstack.ldap + +import org.apache.cloudstack.api.ServerApiException +import org.apache.cloudstack.api.command.LdapListConfigurationCmd +import org.apache.cloudstack.api.response.LdapConfigurationResponse +import org.apache.cloudstack.ldap.LdapConfigurationVO +import org.apache.cloudstack.ldap.LdapManager + +import com.cloud.utils.Pair + +class LdapListConfigurationCmdSpec extends spock.lang.Specification { + + def "Test successful response from execute"() { + given: + def ldapManager = Mock(LdapManager) + List ldapConfigurationList = new ArrayList() + ldapConfigurationList.add(new LdapConfigurationVO("localhost", 389)) + Pair, Integer> ldapConfigurations = new Pair, Integer>(); + ldapConfigurations.set(ldapConfigurationList, ldapConfigurationList.size()) + ldapManager.listConfigurations(_) >> ldapConfigurations + ldapManager.createLdapConfigurationResponse(_) >> new LdapConfigurationResponse("localhost", 389) + def ldapListConfigurationCmd = new LdapListConfigurationCmd(ldapManager) + when: + ldapListConfigurationCmd.execute() + then: + ldapListConfigurationCmd.getResponseObject().getResponses().size() != 0 + } + + def "Test failed response from execute"() { + given: + + def ldapManager = Mock(LdapManager) + List ldapConfigurationList = new ArrayList() + Pair, Integer> ldapConfigurations = new Pair, Integer>(); + ldapConfigurations.set(ldapConfigurationList, ldapConfigurationList.size()) + ldapManager.listConfigurations(_) >> ldapConfigurations + + def ldapListConfigurationCmd = new LdapListConfigurationCmd(ldapManager) + when: + ldapListConfigurationCmd.execute() + then: + ldapListConfigurationCmd.getResponseObject().getResponses().size() == 0 + } + + def "Test successful setting of hostname"() { + given: + def ldapManager = Mock(LdapManager) + def ldapListConfigurationCmd = new LdapListConfigurationCmd(ldapManager) + when: + ldapListConfigurationCmd.setHostname("localhost") + then: + ldapListConfigurationCmd.getHostname() == "localhost" + } + + def "Test successful setting of Port"() { + given: + def ldapManager = Mock(LdapManager) + def ldapListConfigurationCmd = new LdapListConfigurationCmd(ldapManager) + when: + ldapListConfigurationCmd.setPort(389) + then: + ldapListConfigurationCmd.getPort() == 389 + } + + def "Test getEntityOwnerId is 0"() { + given: + def ldapManager = Mock(LdapManager) + def ldapListConfigurationCmd = new LdapListConfigurationCmd(ldapManager) + when: + long ownerId = ldapListConfigurationCmd.getEntityOwnerId() + then: + ownerId == 1 + } + + def "Test successful return of getCommandName"() { + given: + def ldapManager = Mock(LdapManager) + def ldapListConfigurationCmd = new LdapListConfigurationCmd(ldapManager) + when: + String commandName = ldapListConfigurationCmd.getCommandName() + then: + commandName == "ldapconfigurationresponse" + } +} diff --git a/plugins/user-authenticators/ldap/test/groovy/org/apache/cloudstack/ldap/LdapManagerImplSpec.groovy b/plugins/user-authenticators/ldap/test/groovy/org/apache/cloudstack/ldap/LdapManagerImplSpec.groovy new file mode 100644 index 00000000000..548057cd932 --- /dev/null +++ b/plugins/user-authenticators/ldap/test/groovy/org/apache/cloudstack/ldap/LdapManagerImplSpec.groovy @@ -0,0 +1,319 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package groovy.org.apache.cloudstack.ldap + +import javax.naming.NamingException +import javax.naming.ldap.InitialLdapContext + +import org.apache.cloudstack.api.command.LdapListConfigurationCmd +import org.apache.cloudstack.ldap.* +import org.apache.cloudstack.ldap.dao.LdapConfigurationDaoImpl + +import com.cloud.exception.InvalidParameterValueException +import com.cloud.utils.Pair + +class LdapManagerImplSpec extends spock.lang.Specification { + def "Test that addConfiguration fails when a duplicate configuration exists"() { + given: + def ldapConfigurationDao = Mock(LdapConfigurationDaoImpl) + def ldapContextFactory = Mock(LdapContextFactory) + def ldapUserManager = Mock(LdapUserManager) + ldapConfigurationDao.findByHostname(_) >> new LdapConfigurationVO("localhost", 389) + def ldapManager = new LdapManagerImpl(ldapConfigurationDao, ldapContextFactory, ldapUserManager) + when: + ldapManager.addConfiguration("localhost", 389) + then: + thrown InvalidParameterValueException + } + + def "Test that addConfiguration fails when a binding fails"() { + given: + def ldapConfigurationDao = Mock(LdapConfigurationDaoImpl) + def ldapContextFactory = Mock(LdapContextFactory) + def ldapUserManager = Mock(LdapUserManager) + ldapContextFactory.createBindContext(_) >> { throw new NamingException() } + def ldapManager = new LdapManagerImpl(ldapConfigurationDao, ldapContextFactory, ldapUserManager) + when: + ldapManager.addConfiguration("localhost", 389) + then: + thrown InvalidParameterValueException + } + + def "Test successfully addConfiguration"() { + given: + def ldapConfigurationDao = Mock(LdapConfigurationDaoImpl) + def ldapContextFactory = Mock(LdapContextFactory) + def ldapUserManager = Mock(LdapUserManager) + ldapContextFactory.createBindContext(_) >> null + ldapConfigurationDao.persist(_) >> null + def ldapManager = new LdapManagerImpl(ldapConfigurationDao, ldapContextFactory, ldapUserManager) + when: + def result = ldapManager.addConfiguration("localhost", 389) + then: + result.hostname == "localhost" + result.port == 389 + } + + def "Test successful failed result from deleteConfiguration due to configuration not existing"() { + given: + def ldapConfigurationDao = Mock(LdapConfigurationDaoImpl) + def ldapContextFactory = Mock(LdapContextFactory) + def ldapUserManager = Mock(LdapUserManager) + ldapConfigurationDao.findByHostname(_) >> null + def ldapManager = new LdapManagerImpl(ldapConfigurationDao, ldapContextFactory, ldapUserManager) + when: + ldapManager.deleteConfiguration("localhost") + then: + thrown InvalidParameterValueException + } + + def "Test successful result from deleteConfiguration"() { + given: + def ldapConfigurationDao = Mock(LdapConfigurationDaoImpl) + def ldapContextFactory = Mock(LdapContextFactory) + def ldapUserManager = Mock(LdapUserManager) + ldapConfigurationDao.findByHostname(_) >> { + def configuration = new LdapConfigurationVO("localhost", 389) + configuration.setId(0); + return configuration; + } + ldapConfigurationDao.remove(_) >> null + def ldapManager = new LdapManagerImpl(ldapConfigurationDao, ldapContextFactory, ldapUserManager) + when: + def result = ldapManager.deleteConfiguration("localhost") + then: + result.hostname == "localhost" + result.port == 389 + } + + def "Test successful failed result from canAuthenticate due to user not found"() { + given: + def ldapConfigurationDao = Mock(LdapConfigurationDaoImpl) + def ldapContextFactory = Mock(LdapContextFactory) + def ldapUserManager = Mock(LdapUserManager) + def ldapManager = Spy(LdapManagerImpl, constructorArgs: [ldapConfigurationDao, ldapContextFactory, ldapUserManager]) + ldapManager.getUser(_) >> { throw new NamingException() } + when: + def result = ldapManager.canAuthenticate("rmurphy", "password") + then: + result == false + } + + def "Test successful failed result from canAuthenticate due to bad password"() { + given: + def ldapConfigurationDao = Mock(LdapConfigurationDaoImpl) + def ldapContextFactory = Mock(LdapContextFactory) + ldapContextFactory.createUserContext(_, _) >> { throw new NamingException() } + def ldapUserManager = Mock(LdapUserManager) + def ldapManager = Spy(LdapManagerImpl, constructorArgs: [ldapConfigurationDao, ldapContextFactory, ldapUserManager]) + ldapManager.getUser(_) >> { new LdapUser("rmurphy", "rmurphy@test.com", "Ryan", "Murphy", "cn=rmurphy,dc=cloudstack,dc=org") } + when: + def result = ldapManager.canAuthenticate("rmurphy", "password") + then: + result == false + } + + def "Test successful result from canAuthenticate"() { + given: + def ldapConfigurationDao = Mock(LdapConfigurationDaoImpl) + def ldapContextFactory = Mock(LdapContextFactory) + ldapContextFactory.createUserContext(_, _) >> null + def ldapUserManager = Mock(LdapUserManager) + def ldapManager = Spy(LdapManagerImpl, constructorArgs: [ldapConfigurationDao, ldapContextFactory, ldapUserManager]) + ldapManager.getUser(_) >> { new LdapUser("rmurphy", "rmurphy@test.com", "Ryan", "Murphy", "cn=rmurphy,dc=cloudstack,dc=org") } + when: + def result = ldapManager.canAuthenticate("rmurphy", "password") + then: + result == true + } + + def "Test successful closing of context"() { + given: + def ldapConfigurationDao = Mock(LdapConfigurationDaoImpl) + def ldapContextFactory = Mock(LdapContextFactory) + def ldapUserManager = Mock(LdapUserManager) + def ldapManager = new LdapManagerImpl(ldapConfigurationDao, ldapContextFactory, ldapUserManager) + when: + def context = Mock(InitialLdapContext) + ldapManager.closeContext(context) + then: + context.defaultInitCtx == null + } + + def "Test successful failing to close of context"() { + given: + def ldapConfigurationDao = Mock(LdapConfigurationDaoImpl) + def ldapContextFactory = Mock(LdapContextFactory) + def ldapUserManager = Mock(LdapUserManager) + def ldapManager = new LdapManagerImpl(ldapConfigurationDao, ldapContextFactory, ldapUserManager) + when: + def context = Mock(InitialLdapContext) + context.close() >> { throw new NamingException() } + ldapManager.closeContext(context) + then: + context.defaultInitCtx == null + } + + def "Test LdapConfigurationResponse generation"() { + given: + def ldapConfigurationDao = Mock(LdapConfigurationDaoImpl) + def ldapContextFactory = Mock(LdapContextFactory) + def ldapUserManager = Mock(LdapUserManager) + def ldapManager = new LdapManagerImpl(ldapConfigurationDao, ldapContextFactory, ldapUserManager) + when: + def result = ldapManager.createLdapConfigurationResponse(new LdapConfigurationVO("localhost", 389)) + then: + result.hostname == "localhost" + result.port == 389 + } + + def "Test LdapUserResponse generation"() { + given: + def ldapConfigurationDao = Mock(LdapConfigurationDaoImpl) + def ldapContextFactory = Mock(LdapContextFactory) + def ldapUserManager = Mock(LdapUserManager) + def ldapManager = new LdapManagerImpl(ldapConfigurationDao, ldapContextFactory, ldapUserManager) + when: + def result = ldapManager.createLdapUserResponse(new LdapUser("rmurphy", "rmurphy@test.com", "Ryan", "Murphy", "cn=rmurphy,dc=cloudstack,dc=org")) + then: + result.username == "rmurphy" + result.email == "rmurphy@test.com" + result.firstname == "Ryan" + result.lastname == "Murphy" + result.principal == "cn=rmurphy,dc=cloudstack,dc=org" + } + + def "Test that getCommands isn't empty"() { + given: + def ldapConfigurationDao = Mock(LdapConfigurationDaoImpl) + def ldapContextFactory = Mock(LdapContextFactory) + def ldapUserManager = Mock(LdapUserManager) + def ldapManager = new LdapManagerImpl(ldapConfigurationDao, ldapContextFactory, ldapUserManager) + when: + def result = ldapManager.getCommands() + then: + result.size() > 0 + } + + def "Test failing of getUser due to bind issue"() { + given: + def ldapConfigurationDao = Mock(LdapConfigurationDaoImpl) + def ldapContextFactory = Mock(LdapContextFactory) + def ldapUserManager = Mock(LdapUserManager) + ldapContextFactory.createBindContext() >> { throw new NamingException() } + def ldapManager = new LdapManagerImpl(ldapConfigurationDao, ldapContextFactory, ldapUserManager) + when: + ldapManager.getUser("rmurphy") + then: + thrown NamingException + } + + def "Test success of getUser"() { + given: + def ldapConfigurationDao = Mock(LdapConfigurationDaoImpl) + def ldapContextFactory = Mock(LdapContextFactory) + def ldapUserManager = Mock(LdapUserManager) + ldapContextFactory.createBindContext() >> null + ldapUserManager.getUser(_, _) >> new LdapUser("rmurphy", "rmurphy@test.com", "Ryan", "Murphy", "cn=rmurphy,dc=cloudstack,dc=org") + def ldapManager = new LdapManagerImpl(ldapConfigurationDao, ldapContextFactory, ldapUserManager) + when: + def result = ldapManager.getUser("rmurphy") + then: + result.username == "rmurphy" + result.email == "rmurphy@test.com" + result.firstname == "Ryan" + result.lastname == "Murphy" + result.principal == "cn=rmurphy,dc=cloudstack,dc=org" + } + + def "Test failing of getUsers due to bind issue"() { + given: + def ldapConfigurationDao = Mock(LdapConfigurationDaoImpl) + def ldapContextFactory = Mock(LdapContextFactory) + def ldapUserManager = Mock(LdapUserManager) + ldapContextFactory.createBindContext() >> { throw new NamingException() } + def ldapManager = new LdapManagerImpl(ldapConfigurationDao, ldapContextFactory, ldapUserManager) + when: + ldapManager.getUsers() + then: + thrown NoLdapUserMatchingQueryException + } + + def "Test success getUsers"() { + given: + def ldapConfigurationDao = Mock(LdapConfigurationDaoImpl) + def ldapContextFactory = Mock(LdapContextFactory) + def ldapUserManager = Mock(LdapUserManager) + ldapContextFactory.createBindContext() >> null + List users = new ArrayList<>(); + users.add(new LdapUser("rmurphy", "rmurphy@test.com", "Ryan", "Murphy", "cn=rmurphy,dc=cloudstack,dc=org")) + ldapUserManager.getUsers(_) >> users; + def ldapManager = new LdapManagerImpl(ldapConfigurationDao, ldapContextFactory, ldapUserManager) + when: + def result = ldapManager.getUsers() + then: + result.size() > 0; + } + + def "Testing of listConfigurations"() { + given: + def ldapConfigurationDao = Mock(LdapConfigurationDaoImpl) + def ldapContextFactory = Mock(LdapContextFactory) + def ldapUserManager = Mock(LdapUserManager) + List ldapConfigurationList = new ArrayList() + ldapConfigurationList.add(new LdapConfigurationVO("localhost", 389)) + Pair, Integer> configurations = new Pair, Integer>(); + configurations.set(ldapConfigurationList, ldapConfigurationList.size()) + ldapConfigurationDao.searchConfigurations(_, _) >> configurations + def ldapManager = new LdapManagerImpl(ldapConfigurationDao, ldapContextFactory, ldapUserManager) + when: + def result = ldapManager.listConfigurations(new LdapListConfigurationCmd()) + then: + result.second() > 0 + } + + def "Test failing of searchUsers due to a failure to bind"() { + given: + def ldapConfigurationDao = Mock(LdapConfigurationDaoImpl) + def ldapContextFactory = Mock(LdapContextFactory) + def ldapUserManager = Mock(LdapUserManager) + ldapContextFactory.createBindContext() >> { throw new NamingException() } + def ldapManager = new LdapManagerImpl(ldapConfigurationDao, ldapContextFactory, ldapUserManager) + when: + ldapManager.searchUsers("rmurphy") + then: + thrown NoLdapUserMatchingQueryException + } + + def "Test successful result from searchUsers"() { + given: + def ldapConfigurationDao = Mock(LdapConfigurationDaoImpl) + def ldapContextFactory = Mock(LdapContextFactory) + def ldapUserManager = Mock(LdapUserManager) + ldapContextFactory.createBindContext() >> null; + + List users = new ArrayList(); + users.add(new LdapUser("rmurphy", "rmurphy@test.com", "Ryan", "Murphy", "cn=rmurphy,dc=cloudstack,dc=org")) + ldapUserManager.getUsers(_, _) >> users; + + def ldapManager = new LdapManagerImpl(ldapConfigurationDao, ldapContextFactory, ldapUserManager) + when: + def result = ldapManager.searchUsers("rmurphy"); + then: + result.size() > 0; + } +} diff --git a/plugins/user-authenticators/ldap/test/groovy/org/apache/cloudstack/ldap/LdapSearchUserCmdSpec.groovy b/plugins/user-authenticators/ldap/test/groovy/org/apache/cloudstack/ldap/LdapSearchUserCmdSpec.groovy new file mode 100644 index 00000000000..b23d7c2d02f --- /dev/null +++ b/plugins/user-authenticators/ldap/test/groovy/org/apache/cloudstack/ldap/LdapSearchUserCmdSpec.groovy @@ -0,0 +1,72 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package groovy.org.apache.cloudstack.ldap + +import org.apache.cloudstack.api.ServerApiException +import org.apache.cloudstack.api.command.LdapUserSearchCmd +import org.apache.cloudstack.api.response.LdapUserResponse +import org.apache.cloudstack.ldap.LdapManager +import org.apache.cloudstack.ldap.LdapUser +import org.apache.cloudstack.ldap.NoLdapUserMatchingQueryException + +class LdapSearchUserCmdSpec extends spock.lang.Specification { + def "Test successful response from execute"() { + given: + def ldapManager = Mock(LdapManager) + List users = new ArrayList() + users.add(new LdapUser("rmurphy", "rmurphy@test.com", "Ryan", "Murphy", "cn=rmurphy,dc=cloudstack,dc=org")) + ldapManager.searchUsers(_) >> users + LdapUserResponse response = new LdapUserResponse("rmurphy", "rmurphy@test.com", "Ryan", "Murphy", "cn=rmurphy,dc=cloudstack,dc=org") + ldapManager.createLdapUserResponse(_) >> response + def ldapUserSearchCmd = new LdapUserSearchCmd(ldapManager) + when: + ldapUserSearchCmd.execute() + then: + ldapUserSearchCmd.responseObject.getResponses().size() != 0 + } + + def "Test successful empty response from execute"() { + given: + def ldapManager = Mock(LdapManager) + ldapManager.searchUsers(_) >> {throw new NoLdapUserMatchingQueryException()} + def ldapUserSearchCmd = new LdapUserSearchCmd(ldapManager) + when: + ldapUserSearchCmd.execute() + then: + ldapUserSearchCmd.responseObject.getResponses().size() == 0 + } + + def "Test getEntityOwnerId is 0"() { + given: + def ldapManager = Mock(LdapManager) + def ldapUserSearchCmd = new LdapUserSearchCmd(ldapManager) + when: + long ownerId = ldapUserSearchCmd.getEntityOwnerId() + then: + ownerId == 1 + } + + def "Test successful return of getCommandName"() { + given: + def ldapManager = Mock(LdapManager) + def ldapUserSearchCmd = new LdapUserSearchCmd(ldapManager) + when: + String commandName = ldapUserSearchCmd.getCommandName() + then: + commandName == "ldapuserresponse" + } +} diff --git a/plugins/user-authenticators/ldap/test/groovy/org/apache/cloudstack/ldap/LdapUserManagerSpec.groovy b/plugins/user-authenticators/ldap/test/groovy/org/apache/cloudstack/ldap/LdapUserManagerSpec.groovy new file mode 100644 index 00000000000..2d31aa6251d --- /dev/null +++ b/plugins/user-authenticators/ldap/test/groovy/org/apache/cloudstack/ldap/LdapUserManagerSpec.groovy @@ -0,0 +1,207 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package groovy.org.apache.cloudstack.ldap + +import org.apache.cloudstack.ldap.LdapConfiguration +import org.apache.cloudstack.ldap.LdapUserManager +import spock.lang.Shared + +import javax.naming.NamingException +import javax.naming.directory.Attribute +import javax.naming.directory.Attributes +import javax.naming.directory.SearchControls +import javax.naming.directory.SearchResult +import javax.naming.ldap.LdapContext + +class LdapUserManagerSpec extends spock.lang.Specification { + + @Shared + private def ldapConfiguration + + @Shared + private def username + + @Shared + private def email + + @Shared + private def firstname + + @Shared + private def lastname + + @Shared + private def principal + + def setupSpec() { + ldapConfiguration = Mock(LdapConfiguration) + + ldapConfiguration.getScope() >> SearchControls.SUBTREE_SCOPE + ldapConfiguration.getReturnAttributes() >> ["uid", "mail", "cn"] + ldapConfiguration.getUsernameAttribute() >> "uid" + ldapConfiguration.getEmailAttribute() >> "mail" + ldapConfiguration.getFirstnameAttribute() >> "givenname" + ldapConfiguration.getLastnameAttribute() >> "sn" + ldapConfiguration.getBaseDn() >> "dc=cloudstack,dc=org" + + username = "rmurphy" + email = "rmurphy@test.com" + firstname = "Ryan" + lastname = "Murphy" + principal = "cn=" + username + "," + ldapConfiguration.getBaseDn() + } + + def "Test that a newly created Ldap User Manager is not null"() { + given: "You have created a new Ldap user manager object" + def result = new LdapUserManager(); + expect: "The result is not null" + result != null + } + + def "Test successfully creating an Ldap User from Search result"() { + given: + def attributes = createUserAttributes(username, email, firstname, lastname) + def search = createSearchResult(attributes) + def userManager = new LdapUserManager(ldapConfiguration) + def result = userManager.createUser(search) + + expect: + + result.username == username + result.email == email + result.firstname == firstname + result.lastname == lastname + result.principal == principal + } + + def "Test successfully returning an Ldap user from a get user request"() { + given: + + def userManager = new LdapUserManager(ldapConfiguration) + + when: + def result = userManager.getUser(username, createContext()) + + then: + result.username == username + result.email == email + result.firstname == firstname + result.lastname == lastname + result.principal == principal + } + + def "Test successfully returning a list from get users"() { + given: + + def userManager = new LdapUserManager(ldapConfiguration) + + when: + def result = userManager.getUsers(username, createContext()) + + then: + result.size() == 1 + } + + def "Test successfully returning a list from get users when no username is given"() { + given: + + def userManager = new LdapUserManager(ldapConfiguration) + + when: + def result = userManager.getUsers(createContext()) + + then: + result.size() == 1 + } + + def "Test successfully throwing an exception when no users are found with getUser"() { + given: + + def searchUsersResults = new BasicNamingEnumerationImpl() + + def context = Mock(LdapContext) + context.search(_, _, _) >> searchUsersResults; + + def userManager = new LdapUserManager(ldapConfiguration) + + when: + def result = userManager.getUser(username, context) + + then: + thrown NamingException + } + + def "Test successfully returning a NamingEnumeration from searchUsers"() { + given: + def userManager = new LdapUserManager(ldapConfiguration) + + when: + def result = userManager.searchUsers(createContext()) + + then: + result.next().getName() + "," + ldapConfiguration.getBaseDn() == principal + } + + private def createContext() { + + Attributes attributes = createUserAttributes(username, email, firstname, lastname) + SearchResult searchResults = createSearchResult(attributes) + def searchUsersResults = new BasicNamingEnumerationImpl() + searchUsersResults.add(searchResults); + + def context = Mock(LdapContext) + context.search(_, _, _) >> searchUsersResults; + + return context + } + + private SearchResult createSearchResult(attributes) { + def search = Mock(SearchResult) + + search.getName() >> "cn=" + attributes.getAt("uid").get(); + + search.getAttributes() >> attributes + + return search + } + + private Attributes createUserAttributes(String username, String email, String firstname, String lastname) { + def attributes = Mock(Attributes) + + def nameAttribute = Mock(Attribute) + nameAttribute.getId() >> "uid" + nameAttribute.get() >> username + attributes.get("uid") >> nameAttribute + + def mailAttribute = Mock(Attribute) + mailAttribute.getId() >> "mail" + mailAttribute.get() >> email + attributes.get("mail") >> mailAttribute + + def givennameAttribute = Mock(Attribute) + givennameAttribute.getId() >> "givenname" + givennameAttribute.get() >> firstname + attributes.get("givenname") >> givennameAttribute + + def snAttribute = Mock(Attribute) + snAttribute.getId() >> "sn" + snAttribute.get() >> lastname + attributes.get("sn") >> snAttribute + + return attributes + } +} diff --git a/plugins/user-authenticators/ldap/test/groovy/org/apache/cloudstack/ldap/LdapUserResponseSpec.groovy b/plugins/user-authenticators/ldap/test/groovy/org/apache/cloudstack/ldap/LdapUserResponseSpec.groovy new file mode 100644 index 00000000000..aa7d5a3c75d --- /dev/null +++ b/plugins/user-authenticators/ldap/test/groovy/org/apache/cloudstack/ldap/LdapUserResponseSpec.groovy @@ -0,0 +1,67 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package groovy.org.apache.cloudstack.ldap + +import org.apache.cloudstack.api.response.LdapUserResponse + + +class LdapUserResponseSpec extends spock.lang.Specification { + def "Testing succcessful setting of LdapUserResponse email"() { + given: + LdapUserResponse response = new LdapUserResponse(); + when: + response.setEmail("rmurphy@test.com"); + then: + response.getEmail() == "rmurphy@test.com"; + } + + def "Testing successful setting of LdapUserResponse principal"() { + given: + LdapUserResponse response = new LdapUserResponse() + when: + response.setPrincipal("dc=cloudstack,dc=org") + then: + response.getPrincipal() == "dc=cloudstack,dc=org" + } + + def "Testing successful setting of LdapUserResponse username"() { + given: + LdapUserResponse response = new LdapUserResponse() + when: + response.setUsername("rmurphy") + then: + response.getUsername() == "rmurphy" + } + + def "Testing successful setting of LdapUserResponse firstname"() { + given: + LdapUserResponse response = new LdapUserResponse() + when: + response.setFirstname("Ryan") + then: + response.getFirstname() == "Ryan" + } + + def "Testing successful setting of LdapUserResponse lastname"() { + given: + LdapUserResponse response = new LdapUserResponse() + when: + response.setLastname("Murphy") + then: + response.getLastname() == "Murphy" + } +} diff --git a/plugins/user-authenticators/ldap/test/groovy/org/apache/cloudstack/ldap/LdapUserSpec.groovy b/plugins/user-authenticators/ldap/test/groovy/org/apache/cloudstack/ldap/LdapUserSpec.groovy new file mode 100644 index 00000000000..cf2f9ec06fb --- /dev/null +++ b/plugins/user-authenticators/ldap/test/groovy/org/apache/cloudstack/ldap/LdapUserSpec.groovy @@ -0,0 +1,79 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package groovy.org.apache.cloudstack.ldap + +import org.apache.cloudstack.ldap.LdapUser + +class LdapUserSpec extends spock.lang.Specification { + + def "Testing that the username is correctly set with the ldap object"() { + given: "You have created a LDAP user object with a username" + def user = new LdapUser(username, "", "", "","") + expect: "The username is equal to the given data source" + user.getUsername() == username + where: "The username is set to " + username << ["", null, "rmurphy"] + } + + def "Testing the email is correctly set with the ldap object"() { + given: "You have created a LDAP user object with a email" + def user = new LdapUser("", email, "", "","") + expect: "The email is equal to the given data source" + user.getEmail() == email + where: "The email is set to " + email << ["", null, "test@test.com"] + } + + def "Testing the firstname is correctly set with the ldap object"() { + given: "You have created a LDAP user object with a firstname" + def user = new LdapUser("", "", firstname, "", "") + expect: "The firstname is equal to the given data source" + user.getFirstname() == firstname + where: "The firstname is set to " + firstname << ["", null, "Ryan"] + } + + def "Testing the lastname is correctly set with the ldap object"() { + given: "You have created a LDAP user object with a lastname" + def user = new LdapUser("", "", "", lastname, "") + expect: "The lastname is equal to the given data source" + user.getLastname() == lastname + where: "The lastname is set to " + lastname << ["", null, "Murphy"] + } + + def "Testing the principal is correctly set with the ldap object"() { + given: "You have created a LDAP user object with a principal" + def user = new LdapUser("", "", "", "", principal) + expect: "The principal is equal to the given data source" + user.getPrincipal() == principal + where: "The username is set to " + principal << ["", null, "cn=rmurphy,dc=cloudstack,dc=org"] + } + + def "Testing that LdapUser successfully gives the correct result for a compare to"() { + given: "You have created two LDAP user objects" + def userA = new LdapUser(usernameA, "", "", "", "") + def userB = new LdapUser(usernameB, "", "", "", "") + expect: "That when compared the result is less than or equal to 0" + userA.compareTo(userB) <= 0 + where: "The following values are used" + usernameA | usernameB + "A" | "B" + "A" | "A" + } +} \ No newline at end of file diff --git a/plugins/user-authenticators/ldap/test/groovy/org/apache/cloudstack/ldap/LdapUtilsSpec.groovy b/plugins/user-authenticators/ldap/test/groovy/org/apache/cloudstack/ldap/LdapUtilsSpec.groovy new file mode 100644 index 00000000000..7fc05a95903 --- /dev/null +++ b/plugins/user-authenticators/ldap/test/groovy/org/apache/cloudstack/ldap/LdapUtilsSpec.groovy @@ -0,0 +1,68 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package groovy.org.apache.cloudstack.ldap + +import org.apache.cloudstack.ldap.LdapUtils + +import javax.naming.directory.Attribute +import javax.naming.directory.Attributes + +class LdapUtilsSpec extends spock.lang.Specification { + def "Testing that a Ldap Search Filter is correctly escaped"() { + given: "You have some input from a user" + + expect: "That the input is escaped" + LdapUtils.escapeLDAPSearchFilter(input) == result + + where: "The following inputs are given " + input | result + "Hi This is a test #çà" | "Hi This is a test #çà" + "Hi (This) = is * a \\ test # ç à ô \u0000" | "Hi \\28This\\29 = is \\2a a \\5c test # ç à ô \\00" + } + + def "Testing than an attribute is successfully returned"() { + given: "You have an attributes object with some attribute" + def attributes = Mock(Attributes) + def attribute = Mock(Attribute) + attribute.getId() >> name + attribute.get() >> value + attributes.get(name) >> attribute + + when: "You get the attribute" + String foundValue = LdapUtils.getAttributeValue(attributes, name) + + then: "Its value equals uid" + foundValue == value + + where: + name | value + "uid" | "rmurphy" + "email" | "rmurphy@test.com" + } + + def "Testing than an attribute is not successfully returned"() { + given: "You have an attributes object with some attribute" + def attributes = Mock(Attributes) + attributes.get("uid") >> null + + when: "You get the attribute" + String foundValue = LdapUtils.getAttributeValue(attributes, "uid") + + then: "Its value equals uid" + foundValue == null + } +} diff --git a/plugins/user-authenticators/ldap/test/groovy/org/apache/cloudstack/ldap/NoLdapUserMatchingQueryExceptionSpec.groovy b/plugins/user-authenticators/ldap/test/groovy/org/apache/cloudstack/ldap/NoLdapUserMatchingQueryExceptionSpec.groovy new file mode 100644 index 00000000000..4c0cc4b688f --- /dev/null +++ b/plugins/user-authenticators/ldap/test/groovy/org/apache/cloudstack/ldap/NoLdapUserMatchingQueryExceptionSpec.groovy @@ -0,0 +1,30 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package groovy.org.apache.cloudstack.ldap + +import org.apache.cloudstack.ldap.NoLdapUserMatchingQueryException + +class NoLdapUserMatchingQueryExceptionSpec extends spock.lang.Specification { + def "Test that the query is correctly set within the No LDAP user matching query exception object"() { + given: "You have created an No LDAP user matching query exception object with a query set" + def exception = new NoLdapUserMatchingQueryException(query) + expect: "The username is equal to the given data source" + exception.getQuery() == query + where: "The username is set to " + query << ["", null, "murp*"] + } +} \ No newline at end of file diff --git a/plugins/user-authenticators/ldap/test/groovy/org/apache/cloudstack/ldap/NoSuchLdapUserExceptionSpec.groovy b/plugins/user-authenticators/ldap/test/groovy/org/apache/cloudstack/ldap/NoSuchLdapUserExceptionSpec.groovy new file mode 100644 index 00000000000..dbdf646721d --- /dev/null +++ b/plugins/user-authenticators/ldap/test/groovy/org/apache/cloudstack/ldap/NoSuchLdapUserExceptionSpec.groovy @@ -0,0 +1,30 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package groovy.org.apache.cloudstack.ldap + +import org.apache.cloudstack.ldap.NoSuchLdapUserException; + +class NoSuchLdapUserExceptionSpec extends spock.lang.Specification { + def "Test that the username is correctly set within the No such LDAP user exception object"() { + given: "You have created an No such LDAP user exception object with the username set" + def exception = new NoSuchLdapUserException(username) + expect: "The username is equal to the given data source" + exception.getUsername() == username + where: "The username is set to " + username << ["", null, "rmurphy"] + } +} diff --git a/plugins/user-authenticators/ldap/test/resources/cloudstack.org.ldif b/plugins/user-authenticators/ldap/test/resources/cloudstack.org.ldif new file mode 100644 index 00000000000..b20de8178af --- /dev/null +++ b/plugins/user-authenticators/ldap/test/resources/cloudstack.org.ldif @@ -0,0 +1,19 @@ +version: 1 + +dn: dc=cloudstack,dc=org +objectClass: dcObject +objectClass: organization +dc: cloudstack +o: cloudstack + +dn: cn=Ryan Murphy,dc=cloudstack,dc=org +objectClass: inetOrgPerson +objectClass: organizationalPerson +objectClass: person +objectClass: top +cn: Ryan Murphy +sn: Murphy +givenName: Ryan +mail: rmurphy@cloudstack.org +uid: rmurphy +userpassword:: cGFzc3dvcmQ= diff --git a/server/src/com/cloud/api/ApiResponseHelper.java b/server/src/com/cloud/api/ApiResponseHelper.java index e49e169efc2..f98a3ef434b 100755 --- a/server/src/com/cloud/api/ApiResponseHelper.java +++ b/server/src/com/cloud/api/ApiResponseHelper.java @@ -78,7 +78,6 @@ import org.apache.cloudstack.api.response.LBHealthCheckPolicyResponse; import org.apache.cloudstack.api.response.LBHealthCheckResponse; import org.apache.cloudstack.api.response.LBStickinessPolicyResponse; import org.apache.cloudstack.api.response.LBStickinessResponse; -import org.apache.cloudstack.api.response.LDAPConfigResponse; import org.apache.cloudstack.api.response.LoadBalancerResponse; import org.apache.cloudstack.api.response.NetworkACLItemResponse; import org.apache.cloudstack.api.response.NetworkACLResponse; @@ -2807,20 +2806,6 @@ public class ApiResponseHelper implements ResponseGenerator { return hcResponse; } - @Override - public LDAPConfigResponse createLDAPConfigResponse(String hostname, Integer port, Boolean useSSL, String queryFilter, String searchBase, - String bindDN) { - LDAPConfigResponse lr = new LDAPConfigResponse(); - lr.setHostname(hostname); - lr.setPort(port.toString()); - lr.setUseSSL(useSSL.toString()); - lr.setQueryFilter(queryFilter); - lr.setBindDN(bindDN); - lr.setSearchBase(searchBase); - lr.setObjectName("ldapconfig"); - return lr; - } - @Override public StorageNetworkIpRangeResponse createStorageNetworkIpRangeResponse(StorageNetworkIpRange result) { StorageNetworkIpRangeResponse response = new StorageNetworkIpRangeResponse(); diff --git a/server/src/com/cloud/configuration/Config.java b/server/src/com/cloud/configuration/Config.java index c815c771290..1ec0576a7a9 100755 --- a/server/src/com/cloud/configuration/Config.java +++ b/server/src/com/cloud/configuration/Config.java @@ -424,6 +424,16 @@ public enum Config { // object store S3EnableRRS("Advanced", ManagementServer.class, Boolean.class, "s3.rrs.enabled", "false", "enable s3 reduced redundancy storage", null), + // Ldap + LdapBasedn("Advanced", ManagementServer.class, String.class, "ldap.basedn", null, "Sets the basedn for LDAP", null), + LdapBindPassword("Advanced", ManagementServer.class, String.class, "ldap.bind.password", null, "Sets the bind password for LDAP", null), + LdapBindPrincipal("Advanced", ManagementServer.class, String.class, "ldap.bind.principal", null, "Sets the bind principal for LDAP", null), + LdapEmailAttribute("Advanced", ManagementServer.class, String.class, "ldap.email.attribute", "mail", "Sets the email attribute used within LDAP", null), + LdapFirstnameAttribute("Advanced", ManagementServer.class, String.class, "ldap.firstname.attribute", "givenname", "Sets the firstname attribute used within LDAP", null), + LdapLastnameAttribute("Advanced", ManagementServer.class, String.class, "ldap.lastname.attribute", "sn", "Sets the lastname attribute used within LDAP", null), + LdapUsernameAttribute("Advanced", ManagementServer.class, String.class, "ldap.username.attribute", "uid", "Sets the username attribute used within LDAP", null), + LdapUserObject("Advanced", ManagementServer.class, String.class, "ldap.user.object", "inetOrgPerson", "Sets the object type of users within LDAP", null), + // VMSnapshots VMSnapshotMax("Advanced", VMSnapshotManager.class, Integer.class, "vmsnapshot.max", "10", "Maximum vm snapshots for a vm", null), VMSnapshotCreateWait("Advanced", VMSnapshotManager.class, Integer.class, "vmsnapshot.create.wait", "1800", "In second, timeout for create vm snapshot", null), @@ -616,4 +626,4 @@ public enum Config { public static List getConfigListByScope(String scope) { return _scopeLevelConfigsMap.get(scope); } -} +} \ No newline at end of file diff --git a/server/src/com/cloud/configuration/ConfigurationManagerImpl.java b/server/src/com/cloud/configuration/ConfigurationManagerImpl.java index b2dedb3681f..1243fb877bd 100755 --- a/server/src/com/cloud/configuration/ConfigurationManagerImpl.java +++ b/server/src/com/cloud/configuration/ConfigurationManagerImpl.java @@ -43,10 +43,7 @@ import org.apache.log4j.Logger; import org.springframework.stereotype.Component; import org.apache.cloudstack.acl.SecurityChecker; -import org.apache.cloudstack.api.ApiConstants.LDAPParams; import org.apache.cloudstack.api.command.admin.config.UpdateCfgCmd; -import org.apache.cloudstack.api.command.admin.ldap.LDAPConfigCmd; -import org.apache.cloudstack.api.command.admin.ldap.LDAPRemoveCmd; import org.apache.cloudstack.api.command.admin.network.CreateNetworkOfferingCmd; import org.apache.cloudstack.api.command.admin.network.DeleteNetworkOfferingCmd; import org.apache.cloudstack.api.command.admin.network.UpdateNetworkOfferingCmd; @@ -1542,175 +1539,6 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati } - @Override - @DB - public boolean removeLDAP(LDAPRemoveCmd cmd) { - _configDao.expunge(LDAPParams.hostname.toString()); - _configDao.expunge(LDAPParams.port.toString()); - _configDao.expunge(LDAPParams.queryfilter.toString()); - _configDao.expunge(LDAPParams.searchbase.toString()); - _configDao.expunge(LDAPParams.usessl.toString()); - _configDao.expunge(LDAPParams.dn.toString()); - _configDao.expunge(LDAPParams.passwd.toString()); - _configDao.expunge(LDAPParams.truststore.toString()); - _configDao.expunge(LDAPParams.truststorepass.toString()); - return true; - } - - @Override - @DB - public LDAPConfigCmd listLDAPConfig(LDAPConfigCmd cmd) { - String hostname = _configDao.getValue(LDAPParams.hostname.toString()); - cmd.setHostname(hostname == null ? "" : hostname); - String port = _configDao.getValue(LDAPParams.port.toString()); - cmd.setPort(port == null ? 0 : Integer.valueOf(port)); - String queryFilter = _configDao.getValue(LDAPParams.queryfilter.toString()); - cmd.setQueryFilter(queryFilter == null ? "" : queryFilter); - String searchBase = _configDao.getValue(LDAPParams.searchbase.toString()); - cmd.setSearchBase(searchBase == null ? "" : searchBase); - String useSSL = _configDao.getValue(LDAPParams.usessl.toString()); - cmd.setUseSSL(useSSL == null ? Boolean.FALSE : Boolean.valueOf(useSSL)); - String binddn = _configDao.getValue(LDAPParams.dn.toString()); - cmd.setBindDN(binddn == null ? "" : binddn); - String truststore = _configDao.getValue(LDAPParams.truststore.toString()); - cmd.setTrustStore(truststore == null ? "" : truststore); - return cmd; - } - - @Override - @DB - public boolean updateLDAP(LDAPConfigCmd cmd) { - try { - // set the ldap details in the zone details table with a zone id of - // -12 - String hostname = cmd.getHostname(); - Integer port = cmd.getPort(); - String queryFilter = cmd.getQueryFilter(); - String searchBase = cmd.getSearchBase(); - Boolean useSSL = cmd.getUseSSL(); - String bindDN = cmd.getBindDN(); - String bindPasswd = cmd.getBindPassword(); - String trustStore = cmd.getTrustStore(); - String trustStorePassword = cmd.getTrustStorePassword(); - - if (bindDN != null && bindPasswd == null) { - throw new InvalidParameterValueException( - "If you specify a bind name then you need to provide bind password too."); - } - - // check query filter if it contains valid substitution - if (!queryFilter.contains("%u") && !queryFilter.contains("%n") && !queryFilter.contains("%e")) { - throw new InvalidParameterValueException( - "QueryFilter should contain at least one of the substitutions: %u, %n or %e: " + queryFilter); - } - - // check if the info is correct - Hashtable env = new Hashtable(11); - env.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory"); - String protocol = "ldap://"; - if (useSSL) { - env.put(Context.SECURITY_PROTOCOL, "ssl"); - protocol = "ldaps://"; - if (trustStore == null || trustStorePassword == null) { - throw new InvalidParameterValueException( - "If you plan to use SSL then you need to configure the trust store."); - } - System.setProperty("javax.net.ssl.trustStore", trustStore); - System.setProperty("javax.net.ssl.trustStorePassword", trustStorePassword); - } - env.put(Context.PROVIDER_URL, protocol + hostname + ":" + port); - if (bindDN != null && bindPasswd != null) { - env.put(Context.SECURITY_AUTHENTICATION, "simple"); - env.put(Context.SECURITY_PRINCIPAL, bindDN); - env.put(Context.SECURITY_CREDENTIALS, bindPasswd); - } - // Create the initial context - DirContext ctx = new InitialDirContext(env); - ctx.close(); - - // store the result in DB Configuration - ConfigurationVO cvo = _configDao.findByName(LDAPParams.hostname.toString()); - if (cvo == null) { - cvo = new ConfigurationVO("Hidden", "DEFAULT", "management-server", LDAPParams.hostname.toString(), - null, "Hostname or ip address of the ldap server eg: my.ldap.com"); - } - cvo.setValue(DBEncryptionUtil.encrypt(hostname)); - _configDao.persist(cvo); - - cvo = _configDao.findByName(LDAPParams.port.toString()); - if (cvo == null) { - cvo = new ConfigurationVO("Hidden", "DEFAULT", "management-server", LDAPParams.port.toString(), null, - "Specify the LDAP port if required, default is 389"); - } - cvo.setValue(DBEncryptionUtil.encrypt(port.toString())); - _configDao.persist(cvo); - - cvo = _configDao.findByName(LDAPParams.queryfilter.toString()); - if (cvo == null) { - cvo = new ConfigurationVO("Hidden", "DEFAULT", "management-server", LDAPParams.queryfilter.toString(), - null, - "You specify a query filter here, which narrows down the users, who can be part of this domain"); - } - cvo.setValue(DBEncryptionUtil.encrypt(queryFilter)); - _configDao.persist(cvo); - - cvo = _configDao.findByName(LDAPParams.searchbase.toString()); - if (cvo == null) { - cvo = new ConfigurationVO("Hidden", "DEFAULT", "management-server", LDAPParams.searchbase.toString(), - null, - "The search base defines the starting point for the search in the directory tree Example: dc=cloud,dc=com."); - } - cvo.setValue(DBEncryptionUtil.encrypt(searchBase)); - _configDao.persist(cvo); - - cvo = _configDao.findByName(LDAPParams.usessl.toString()); - if (cvo == null) { - cvo = new ConfigurationVO("Hidden", "DEFAULT", "management-server", LDAPParams.usessl.toString(), null, - "Check Use SSL if the external LDAP server is configured for LDAP over SSL."); - } - cvo.setValue(DBEncryptionUtil.encrypt(useSSL.toString())); - _configDao.persist(cvo); - - cvo = _configDao.findByName(LDAPParams.dn.toString()); - if (cvo == null) { - cvo = new ConfigurationVO("Hidden", "DEFAULT", "management-server", LDAPParams.dn.toString(), null, - "Specify the distinguished name of a user with the search permission on the directory"); - } - cvo.setValue(DBEncryptionUtil.encrypt(bindDN)); - _configDao.persist(cvo); - - cvo = _configDao.findByName(LDAPParams.passwd.toString()); - if (cvo == null) { - cvo = new ConfigurationVO("Hidden", "DEFAULT", "management-server", LDAPParams.passwd.toString(), null, - "Enter the password"); - } - cvo.setValue(DBEncryptionUtil.encrypt(bindPasswd)); - _configDao.persist(cvo); - - cvo = _configDao.findByName(LDAPParams.truststore.toString()); - if (cvo == null) { - cvo = new ConfigurationVO("Hidden", "DEFAULT", "management-server", LDAPParams.truststore.toString(), - null, "Enter the path to trusted keystore"); - } - cvo.setValue(DBEncryptionUtil.encrypt(trustStore)); - _configDao.persist(cvo); - - cvo = _configDao.findByName(LDAPParams.truststorepass.toString()); - if (cvo == null) { - cvo = new ConfigurationVO("Hidden", "DEFAULT", "management-server", - LDAPParams.truststorepass.toString(), null, "Enter the password for trusted keystore"); - } - cvo.setValue(DBEncryptionUtil.encrypt(trustStorePassword)); - _configDao.persist(cvo); - - s_logger.debug("The ldap server is configured: " + hostname); - } catch (NamingException ne) { - throw new InvalidParameterValueException("Naming Exception, check you ldap data ! " + ne.getMessage() - + (ne.getCause() != null ? ("; Caused by:" + ne.getCause().getMessage()) : "")); - } - return true; - } - @Override @DB @ActionEvent(eventType = EventTypes.EVENT_ZONE_EDIT, eventDescription = "editing zone", async = false) diff --git a/server/src/com/cloud/server/ManagementServerImpl.java b/server/src/com/cloud/server/ManagementServerImpl.java index 7ac12547090..2a203b443e7 100755 --- a/server/src/com/cloud/server/ManagementServerImpl.java +++ b/server/src/com/cloud/server/ManagementServerImpl.java @@ -87,8 +87,6 @@ import org.apache.cloudstack.api.command.admin.internallb.ListInternalLBVMsCmd; import org.apache.cloudstack.api.command.admin.internallb.ListInternalLoadBalancerElementsCmd; import org.apache.cloudstack.api.command.admin.internallb.StartInternalLBVMCmd; import org.apache.cloudstack.api.command.admin.internallb.StopInternalLBVMCmd; -import org.apache.cloudstack.api.command.admin.ldap.LDAPConfigCmd; -import org.apache.cloudstack.api.command.admin.ldap.LDAPRemoveCmd; import org.apache.cloudstack.api.command.admin.network.AddNetworkDeviceCmd; import org.apache.cloudstack.api.command.admin.network.AddNetworkServiceProviderCmd; import org.apache.cloudstack.api.command.admin.network.CreateNetworkOfferingCmd; @@ -2504,8 +2502,6 @@ public class ManagementServerImpl extends ManagerBase implements ManagementServe cmdList.add(ReconnectHostCmd.class); cmdList.add(UpdateHostCmd.class); cmdList.add(UpdateHostPasswordCmd.class); - cmdList.add(LDAPConfigCmd.class); - cmdList.add(LDAPRemoveCmd.class); cmdList.add(AddNetworkDeviceCmd.class); cmdList.add(AddNetworkServiceProviderCmd.class); cmdList.add(CreateNetworkOfferingCmd.class); diff --git a/server/test/com/cloud/vpc/MockConfigurationManagerImpl.java b/server/test/com/cloud/vpc/MockConfigurationManagerImpl.java index 80b9e231561..29b899c4f26 100755 --- a/server/test/com/cloud/vpc/MockConfigurationManagerImpl.java +++ b/server/test/com/cloud/vpc/MockConfigurationManagerImpl.java @@ -26,8 +26,6 @@ import javax.naming.ConfigurationException; import javax.naming.NamingException; import org.apache.cloudstack.api.command.admin.config.UpdateCfgCmd; -import org.apache.cloudstack.api.command.admin.ldap.LDAPConfigCmd; -import org.apache.cloudstack.api.command.admin.ldap.LDAPRemoveCmd; import org.apache.cloudstack.api.command.admin.network.CreateNetworkOfferingCmd; import org.apache.cloudstack.api.command.admin.network.DeleteNetworkOfferingCmd; import org.apache.cloudstack.api.command.admin.network.UpdateNetworkOfferingCmd; @@ -87,8 +85,6 @@ import com.cloud.user.Account; import com.cloud.utils.component.ManagerBase; import com.cloud.vm.VirtualMachine.Type; import org.apache.cloudstack.api.command.admin.config.UpdateCfgCmd; -import org.apache.cloudstack.api.command.admin.ldap.LDAPConfigCmd; -import org.apache.cloudstack.api.command.admin.ldap.LDAPRemoveCmd; import org.apache.cloudstack.api.command.admin.network.CreateNetworkOfferingCmd; import org.apache.cloudstack.api.command.admin.network.DeleteNetworkOfferingCmd; import org.apache.cloudstack.api.command.admin.network.UpdateNetworkOfferingCmd; @@ -383,33 +379,6 @@ public class MockConfigurationManagerImpl extends ManagerBase implements Configu return null; } - /* (non-Javadoc) - * @see com.cloud.configuration.ConfigurationService#updateLDAP(org.apache.cloudstack.api.commands.LDAPConfigCmd) - */ - @Override - public boolean updateLDAP(LDAPConfigCmd cmd) throws NamingException { - // TODO Auto-generated method stub - return false; - } - - /* (non-Javadoc) - * @see com.cloud.configuration.ConfigurationService#removeLDAP(org.apache.cloudstack.api.commands.LDAPRemoveCmd) - */ - @Override - public boolean removeLDAP(LDAPRemoveCmd cmd) { - // TODO Auto-generated method stub - return false; - } - - /* (non-Javadoc) - * @see com.cloud.configuration.ConfigurationService#listLDAPConfig(org.apache.cloudstack.api.commands.LDAPConfigCmd) - */ - @Override - public LDAPConfigCmd listLDAPConfig(LDAPConfigCmd cmd) { - // TODO Auto-generated method stub - return null; - } - /* (non-Javadoc) * @see com.cloud.configuration.ConfigurationService#isOfferingForVpc(com.cloud.offering.NetworkOffering) */ diff --git a/setup/db/db/schema-410to420.sql b/setup/db/db/schema-410to420.sql index 82ca403ad42..f4cd1b1a9e0 100644 --- a/setup/db/db/schema-410to420.sql +++ b/setup/db/db/schema-410to420.sql @@ -1462,7 +1462,7 @@ CREATE VIEW `cloud`.`disk_offering_view` AS disk_offering.iops_write_rate, disk_offering.sort_key, disk_offering.type, - disk_offering.display_offering, + disk_offering.display_offering, domain.id domain_id, domain.uuid domain_uuid, domain.name domain_name, @@ -1516,7 +1516,7 @@ CREATE VIEW `cloud`.`user_vm_view` AS data_center.uuid data_center_uuid, data_center.name data_center_name, data_center.is_security_group_enabled security_group_enabled, - data_center.networktype data_center_type, + data_center.networktype data_center_type, host.id host_id, host.uuid host_uuid, host.name host_name, @@ -1672,7 +1672,7 @@ CREATE VIEW `cloud`.`volume_view` AS volumes.attached, volumes.removed, volumes.pod_id, - volumes.display_volume, + volumes.display_volume, volumes.format, account.id account_id, account.uuid account_uuid, @@ -1688,7 +1688,7 @@ CREATE VIEW `cloud`.`volume_view` AS data_center.id data_center_id, data_center.uuid data_center_uuid, data_center.name data_center_name, - data_center.networktype data_center_type, + data_center.networktype data_center_type, vm_instance.id vm_id, vm_instance.uuid vm_uuid, vm_instance.name vm_name, @@ -1820,7 +1820,7 @@ CREATE VIEW `cloud`.`template_view` AS data_center.name data_center_name, launch_permission.account_id lp_account_id, template_store_ref.store_id, - image_store.scope as store_scope, + image_store.scope as store_scope, template_store_ref.state, template_store_ref.download_state, template_store_ref.download_pct, @@ -1840,7 +1840,7 @@ CREATE VIEW `cloud`.`template_view` AS resource_tags.resource_uuid tag_resource_uuid, resource_tags.resource_type tag_resource_type, resource_tags.customer tag_customer, - CONCAT(vm_template.id, '_', IFNULL(data_center.id, 0)) as temp_zone_pair + CONCAT(vm_template.id, '_', IFNULL(data_center.id, 0)) as temp_zone_pair from `cloud`.`vm_template` inner join @@ -1859,7 +1859,7 @@ CREATE VIEW `cloud`.`template_view` AS `cloud`.`template_store_ref` ON template_store_ref.template_id = vm_template.id and template_store_ref.store_role = 'Image' left join `cloud`.`image_store` ON image_store.removed is NULL AND template_store_ref.store_id is not NULL AND image_store.id = template_store_ref.store_id - left join + left join `cloud`.`template_zone_ref` ON template_zone_ref.template_id = vm_template.id AND template_store_ref.store_id is NULL AND template_zone_ref.removed is null left join `cloud`.`data_center` ON (image_store.data_center_id = data_center.id OR template_zone_ref.zone_id = data_center.id) @@ -2048,7 +2048,7 @@ INSERT IGNORE INTO `cloud`.`configuration` VALUES ('Advanced', 'DEFAULT', 'manag INSERT IGNORE INTO `cloud`.`configuration` VALUES ('Advanced', 'DEFAULT', 'management-server', 'vm.disk.throttling.bytes_write_rate', 0, 'Default disk I/O write rate in bytes per second allowed in User vm\'s disk. '); -- Re-enable foreign key checking, at the end of the upgrade path -SET foreign_key_checks = 1; +SET foreign_key_checks = 1; UPDATE `cloud`.`snapshot_policy` set uuid=id WHERE uuid is NULL; #update shared sg enabled network with not null name in Advance Security Group enabled network @@ -2142,10 +2142,25 @@ CREATE VIEW `cloud`.`project_view` AS left join `cloud`.`project_account` pacct ON projects.id = pacct.project_id; +INSERT IGNORE INTO `cloud`.`configuration` VALUES ('Advanced', 'DEFAULT', 'management-server', 'ldap.bind.principal', NULL, 'Specifies the bind principal to use for bind to LDAP'); +INSERT IGNORE INTO `cloud`.`configuration` VALUES ('Advanced', 'DEFAULT', 'management-server', 'ldap.bind.password', NULL, 'Specifies the password to use for binding to LDAP'); +INSERT IGNORE INTO `cloud`.`configuration` VALUES ('Advanced', 'DEFAULT', 'management-server', 'ldap.username.attribute', 'uid', 'Sets the username attribute used within LDAP'); +INSERT IGNORE INTO `cloud`.`configuration` VALUES ('Advanced', 'DEFAULT', 'management-server', 'ldap.email.attribute', 'mail', 'Sets the email attribute used within LDAP'); +INSERT IGNORE INTO `cloud`.`configuration` VALUES ('Advanced', 'DEFAULT', 'management-server', 'ldap.firstname.attribute', 'givenname', 'Sets the firstname attribute used within LDAP'); +INSERT IGNORE INTO `cloud`.`configuration` VALUES ('Advanced', 'DEFAULT', 'management-server', 'ldap.lastname.attribute', 'sn', 'Sets the lastname attribute used within LDAP'); +INSERT IGNORE INTO `cloud`.`configuration` VALUES ('Advanced', 'DEFAULT', 'management-server', 'ldap.user.object', 'inetOrgPerson', 'Sets the object type of users within LDAP'); +INSERT IGNORE INTO `cloud`.`configuration` VALUES ('Advanced', 'DEFAULT', 'management-server', 'ldap.basedn', NULL, 'Sets the basedn for LDAP'); + +CREATE TABLE `cloud`.`ldap_configuration` ( + `id` bigint unsigned NOT NULL auto_increment COMMENT 'id', + `hostname` varchar(255) NOT NULL COMMENT 'the hostname of the ldap server', + `port` int(10) COMMENT 'port that the ldap server is listening on', + PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; + INSERT IGNORE INTO `cloud`.`configuration` VALUES ('Network', 'DEFAULT', 'management-server', 'network.loadbalancer.haproxy.max.conn', '4096', 'Load Balancer(haproxy) maximum number of concurrent connections(global max)'); ALTER TABLE `cloud`.`network_offerings` ADD COLUMN `concurrent_connections` int(10) unsigned COMMENT 'Load Balancer(haproxy) maximum number of concurrent connections(global max)'; - ALTER TABLE `cloud`.`sync_queue` MODIFY `queue_size` smallint(6) NOT NULL DEFAULT '0' COMMENT 'number of items being processed by the queue'; ALTER TABLE `cloud`.`sync_queue` MODIFY `queue_size_limit` smallint(6) NOT NULL DEFAULT '1' COMMENT 'max number of items the queue can process concurrently'; diff --git a/test/integration/component/test_ldap.py b/test/integration/component/test_ldap.py index fc3bd486266..83f970be27d 100644 --- a/test/integration/component/test_ldap.py +++ b/test/integration/component/test_ldap.py @@ -44,78 +44,30 @@ class Services: def __init__(self): self.services = { "account": { - "email": "test@test.com", - "firstname": "test", - "lastname": "t", - "username": "test", - "password": "password", + "email": "rmurphy@cloudstack.org", + "firstname": "Ryan", + "lastname": "Murphy", + "username": "rmurphy", + "password": "internalcloudstackpassword", }, - "ldapCon_1":#valid values&Query filter as email. + "ldapConfiguration_1": { - "ldapHostname": "10.147.38.163", - "port": "389", - "binddn": "CN=test,CN=Users,DC=hyd-qa,DC=com", - "bindpass": "aaaa_1111", - "queryfilter": "(&(mail=%e))", - "searchbase": "CN=Users,DC=hyd-qa,DC=com", - "ldapusername": "test", - "ldappasswd": "aaaa_1111" - }, - "ldapCon_2": ##valid values&Query filter as displayName. - { - "ldapHostname": "10.147.38.163", - "port": "389", - "binddn": "CN=test,CN=Users,DC=hyd-qa,DC=com", - "bindpass": "aaaa_1111", - "queryfilter": "(&(displayName=%u))", - "searchbase": "CN=Users,DC=hyd-qa,DC=com", - "ldapusername": "test", - "ldappasswd": "aaaa_1111" - }, - "ldapCon_3": #Configuration with missing parameters value(queryfilter) - { - "ldapHostname": "10.147.38.163", - "port": "389", - "binddn": "CN=test,CN=Users,DC=hyd-qa,DC=com", - "bindpass": "aaaa_1111", - "queryfilter": "", - "searchbase": "CN=Users,DC=hyd-qa,DC=com", - "ldapusername": "test", - "ldappasswd": "aaaa_1111" - }, - - "ldapCon_4": #invalid configuration-wrong query filter - { - "ldapHostname": "10.147.38.163", - "port": "389", - "binddn": "CN=test,CN=Users,DC=hyd-qa,DC=com", - "bindpass": "aaaa_1111", - "queryfilter": "(&(displayName=%p))", - "searchbase":"CN=Users,DC=hyd-qa,DC=com", - "ldapusername": "test", - "ldappasswd": "aaaa_1111" - }, - "ldapCon_5": #Configuration with invalid ldap credentials - { - "ldapHostname": "10.147.38.163", - "port": "389", - "binddn": "CN=test,CN=Users,DC=hyd-qa,DC=com", - "bindpass": "aaaa_1111", - "queryfilter": "(&(displayName=%u))", - "searchbase": "CN=Users,DC=hyd-qa,DC=com", - "ldapusername": "test", - "ldappasswd": "aaaa" + "basedn": "dc=cloudstack,dc=org", + "emailAttribute": "mail", + "realnameAttribute": "cn", + "userObject": "inetOrgPerson", + "usernameAttribute": "uid", + "hostname": "localhost", + "port": "10389", + "ldapUsername": "rmurphy", + "ldapPassword": "password" } - - - } class TestLdap(cloudstackTestCase): """ - This test perform registering ldap configuration details in CS and create a user[ldap user] in CS - and validate user credentials against LDAP server:AD + This tests attempts to register a LDAP server and authenticate as an LDAP user. """ @classmethod @@ -134,8 +86,6 @@ class TestLdap(cloudstackTestCase): @classmethod def tearDownClass(cls): try: - #Cleanup resources used - #print "tear down class" cleanup_resources(cls.api_client, cls._cleanup) except Exception as tde: @@ -144,10 +94,10 @@ class TestLdap(cloudstackTestCase): def setUp(self): - self.apiclient = self.testClient.getApiClient() + self.apiClient = self.testClient.getApiClient() self.acct = createAccount.createAccountCmd() - self.acct.accounttype = 0 #We need a regular user. admins have accounttype=1 + self.acct.accounttype = 0 self.acct.firstname = self.services["account"]["firstname"] self.acct.lastname = self.services["account"]["lastname"] self.acct.password = self.services["account"]["password"] @@ -155,208 +105,153 @@ class TestLdap(cloudstackTestCase): self.acct.email = self.services["account"]["email"] self.acct.account = self.services["account"]["username"] self.acct.domainid = 1 - # mapping ldap user by creating same user in cloudstack - - self.acctRes = self.apiclient.createAccount(self.acct) + self.acctRes = self.apiClient.createAccount(self.acct) return def tearDown(self): try: - #Clean up, terminate the created accounts, domains etc - deleteAcct = deleteAccount.deleteAccountCmd() deleteAcct.id = self.acctRes.id acct_name=self.acctRes.name - self.apiclient.deleteAccount(deleteAcct) + self.apiClient.deleteAccount(deleteAcct) self.debug("Deleted the the following account name %s:" %acct_name) - #delete only if ldapconfig registered in CS - if(self.ldapconfRes): - deleteldapconfg=ldapRemove.ldapRemoveCmd() - res=self.apiclient.ldapRemove(deleteldapconfg) + if(self.ldapconfRes==1): + self._deleteLdapConfiguration(self.services["ldapConfiguration_1"]) except Exception as e: raise Exception("Warning: Exception during cleanup : %s" % e) return @attr(tags=["advanced", "basic"]) - def test_01_configLDAP(self): - ''' - This test is to verify ldapConfig API with valid values.(i.e query fileter as email) - ''' - # 1. This test covers ldapConfig & login API with valid ldap credentials.. - # require ldap configuration:ldapCon_1 + def test_01_addLdapConfiguration(self): + """ + This test configures LDAP and attempts to authenticate as a user. + """ + self.debug("start test") - self.ldapconfRes=self._testldapConfig(self.services["ldapCon_1"]) + self.ldapconfRes=self._addLdapConfiguration(self.services["ldapConfiguration_1"]) if(self.ldapconfRes==1): + self.debug("Ldap Configuration was succcessful") - self.debug("configure ldap successful") - - #validating the user credentials with ldap Server - loginRes = self.chkLogin(self.services["ldapCon_1"]["ldapusername"], self.services["ldapCon_1"]["ldappasswd"]) - self.assertEquals(loginRes,1,"ldap Authentication failed") + loginRes = self._checkLogin(self.services["ldapConfiguration_1"]["ldapUsername"],self.services["ldapConfiguration_1"]["ldapPassword"]) + self.debug(loginRes) + self.assertEquals(loginRes,1,"Ldap Authentication") else: self.debug("LDAP Configuration failed with exception") - self.assertEquals(self.ldapconfRes,1,"ldapConfig API failed") + self.assertEquals(self.ldapconfRes,1,"addLdapConfiguration failed") self.debug("end test") - @attr(tags=["advanced", "basic"]) - def test_02_configLDAP(self): - ''' - This test is to verify ldapConfig API with valid values.(i.e query fileter as displayName) - ''' - - # 1. This test covers ldapConfig & login API with valid ldap credentials. - # 2. require ldap configuration:ldapCon_2 - - self.debug("start test") - self.ldapconfRes=self._testldapConfig(self.services["ldapCon_2"]) - self.assertEquals(self.ldapconfRes,1,"ldapConfig API failed") - if(self.ldapconfRes==1): - self.debug("configure ldap successful") - #validating the user credentials with ldap Server - loginRes = self.chkLogin(self.services["ldapCon_2"]["ldapusername"], self.services["ldapCon_2"]["ldappasswd"]) - self.assertEquals(loginRes,1,"ldap Authentication failed") - else: - self.debug("LDAP Configuration failed with exception") - self.debug("end test") - - @attr(tags=["advanced", "basic"]) - def test_03_configLDAP(self): - - ''' - This test is to verify ldapConfig API with missing config parameters value(i.queryfilter) - ''' - - # 1. Issue ldapConfig API with no ldap config parameter value and check behavior - # 2. require ldap configuration:ldapCon_3 - - self.debug("start test...") - self.ldapconfRes=self._testldapConfig(self.services["ldapCon_3"]) - self.assertEquals(self.ldapconfRes,0,"LDAP configuration successful with invalid value.API failed") - self.debug("end test") - @attr(tags=["advanced", "basic"]) - def test_04_configLDAP(self): - ''' - This test is to verify ldapConfig API with invalid configuration values(by passing wrong query filter) - ''' - # 1. calling ldapConfig API with invalid query filter value and check behavior - # 2. require ldap configuration:ldapCon_4 - - self.debug("start test...") - self.ldapconfRes=self._testldapConfig(self.services["ldapCon_4"]) - self.assertEquals(self.ldapconfRes,0,"API failed") - - - @attr(tags=["advanced", "basic"]) - def test_05_configLDAP(self): - - ''' - This test is to verify login API functionality by passing wrong ldap credentials - ''' - # 1.This script first configure the ldap and validates the user credentials using login API - # 2. require ldap configuration:ldapCon_5 - - - self.debug("start test") - self.ldapconfRes=self._testldapConfig(self.services["ldapCon_5"]) - self.assertEquals(self.ldapconfRes,1,"API failed") - #validating the cloudstack user credentials with ldap Server - loginRes = self.chkLogin(self.services["ldapCon_5"]["ldapusername"], self.services["ldapCon_5"]["ldappasswd"]) - self.assertNotEqual(loginRes,1,"login API failed") - self.debug("end test") - - @attr(tags=["advanced", "basic"]) - def test_06_removeLDAP(self): - ''' - This test is to verify ldapRemove API functionality - ''' - # 1. This script fist configures ldap and removes the configured ldap values - # 2. require ldap configuration:ldapCon_1 - - - self.debug("start test") - self.ldapconfRes=self._testldapConfig(self.services["ldapCon_1"]) - if(self.ldapconfRes==1): - self.debug("ldap configured successfully") - deleteldapconfg=ldapRemove.ldapRemoveCmd() - res=self.apiclient.ldapRemove(deleteldapconfg) - self.debug("ldap removed successfully") - self.ldapconfRes=0 - else: - - self.debug("LDAP Configuration failed with exception") - self.assertEquals(self.ldapconfRes,0,"ldapconfig API failed") - self.debug("end test") - - def _testldapConfig(self,ldapSrvD): + def _addLdapConfiguration(self,ldapConfiguration): """ - :param ldapSrvD - + :param ldapConfiguration """ - #This Method takes dictionary as parameter, - # reads the ldap configuration values from the passed dictionary and - # register the ldapconfig detail in cloudstack - # & return true or false based on ldapconfig API response - self.debug("start ldapconfig test") - #creating the ldapconfig cmd object - lpconfig = ldapConfig.ldapConfigCmd() - #Config the ldap server by assigning the ldapconfig dict variable values to ldapConfig object - lpconfig.hostname = ldapSrvD["ldapHostname"] - lpconfig.port = ldapSrvD["port"] - lpconfig.binddn = ldapSrvD["binddn"] - lpconfig.bindpass = ldapSrvD["bindpass"] - lpconfig.searchbase = ldapSrvD["searchbase"] - lpconfig.queryfilter = ldapSrvD["queryfilter"] + # Setup Global settings - #end of assigning the variables + updateConfigurationCmd = updateConfiguration.updateConfigurationCmd() + updateConfigurationCmd.name = "ldap.basedn" + updateConfigurationCmd.value = ldapConfiguration['basedn'] + updateConfigurationResponse = self.apiClient.updateConfiguration(updateConfigurationCmd) + self.debug("updated the parameter %s with value %s"%(updateConfigurationResponse.name, updateConfigurationResponse.value)) - #calling the ldapconfig Api - self.debug("calling ldapconfig API") + updateConfigurationCmd = updateConfiguration.updateConfigurationCmd() + updateConfigurationCmd.name = "ldap.email.attribute" + updateConfigurationCmd.value = ldapConfiguration['emailAttribute'] + updateConfigurationResponse = self.apiClient.updateConfiguration(updateConfigurationCmd) + self.debug("updated the parameter %s with value %s"%(updateConfigurationResponse.name, updateConfigurationResponse.value)) + + + updateConfigurationCmd = updateConfiguration.updateConfigurationCmd() + updateConfigurationCmd.name = "ldap.realname.attribute" + updateConfigurationCmd.value = ldapConfiguration['realnameAttribute'] + updateConfigurationResponse = self.apiClient.updateConfiguration(updateConfigurationCmd) + self.debug("updated the parameter %s with value %s"%(updateConfigurationResponse.name, updateConfigurationResponse.value)) + + + updateConfigurationCmd = updateConfiguration.updateConfigurationCmd() + updateConfigurationCmd.name = "ldap.user.object" + updateConfigurationCmd.value = ldapConfiguration['userObject'] + updateConfigurationResponse = self.apiClient.updateConfiguration(updateConfigurationCmd) + self.debug("updated the parameter %s with value %s"%(updateConfigurationResponse.name, updateConfigurationResponse.value)) + + + updateConfigurationCmd = updateConfiguration.updateConfigurationCmd() + updateConfigurationCmd.name = "ldap.username.attribute" + updateConfigurationCmd.value = ldapConfiguration['usernameAttribute'] + updateConfigurationResponse = self.apiClient.updateConfiguration(updateConfigurationCmd) + self.debug("updated the parameter %s with value %s"%(updateConfigurationResponse.name, updateConfigurationResponse.value)) + + self.debug("start addLdapConfiguration test") + + ldapServer = addLdapConfiguration.addLdapConfigurationCmd() + ldapServer.hostname = ldapConfiguration['hostname'] + ldapServer.port = ldapConfiguration['port'] + + self.debug("calling addLdapConfiguration API command") try: - lpconfig1 = self.apiclient.ldapConfig(lpconfig) - self.debug("ldapconfig API succesfful") + self.apiClient.addLdapConfiguration(ldapServer) + self.debug("addLdapConfiguration was successful") return 1 except Exception, e: - self.debug("ldapconfig API failed %s" %e) + self.debug("addLdapConfiguration failed %s" %e) return 0 - def chkLogin(self, username, password): + def _deleteLdapConfiguration(self,ldapConfiguration): + + """ + + :param ldapConfiguration + + """ + + ldapServer = deleteLdapConfiguration.deleteLdapConfigurationCmd() + ldapServer.hostname = ldapConfiguration["hostname"] + + try: + self.apiClient.deleteLdapConfiguration(ldapServer) + self.debug("deleteLdapConfiguration was successful") + return 1 + except Exception, e: + self.debug("deleteLdapConfiguration failed %s" %e) + return 0 + + def _checkLogin(self, username, password): """ :param username: :param password: """ - self.debug("login test") + self.debug("Attempting to login.") try: - login1 = login.loginCmd() - login1.username = username - login1.password = password - loginRes = self.apiclient.login(login1) + loginParams = login.loginCmd() + loginParams.username = username + loginParams.password = password + loginRes = self.apiClient.login(loginParams) self.debug("login response %s" % loginRes) if loginRes is None: self.debug("login not successful") + return 0 else: self.debug("login successful") return 1 diff --git a/tools/apidoc/gen_toc.py b/tools/apidoc/gen_toc.py index 33a7e75c7b1..f582340912c 100644 --- a/tools/apidoc/gen_toc.py +++ b/tools/apidoc/gen_toc.py @@ -118,7 +118,7 @@ known_categories = { 'TrafficType': 'Usage', 'Product': 'Product', 'LB': 'Load Balancer', - 'ldap': 'LDAP', + 'Ldap': 'LDAP', 'Swift': 'Swift', 'S3' : 'S3', 'SecondaryStorage': 'Host', diff --git a/ui/css/cloudstack3.css b/ui/css/cloudstack3.css index 50cfbd483ce..93658dbad7e 100644 --- a/ui/css/cloudstack3.css +++ b/ui/css/cloudstack3.css @@ -5834,7 +5834,7 @@ label.error { .multi-wizard .buttons { width: 100%; position: absolute; - top: 519px; + bottom: 10px; left: 0px; } @@ -12267,3 +12267,85 @@ div.ui-dialog div.autoscaler div.field-group div.form-container form div.form-it color: #0000FF !important; } +.accounts-wizard table { + margin: 0; + width: 100%; + table-layout: fixed; +} + +.accounts-wizard td:last-child { + border: none; +} + +.accounts-wizard tbody tr:nth-child(even) { + background: #DFE1E3; +} + +.accounts-wizard tbody tr:nth-child(odd) { + background: #F2F0F0; +} + +.accounts-wizard .content { + display: inline-block; +} + +.accounts-wizard .content:last-child { + margin-left: 14px; +} + +.accounts-wizard .select-container { + overflow: auto; +} + +.accounts-wizard .input-area{ + width: 320px; + font-size: 15px; + color: #485867; + text-shadow: 0px 2px 1px #FFFFFF; +} + +.ldap-account-choice { + border: none !important; + border-radius: 0 0 0 0 !important; +} + +.manual-account-details .name { + margin-top: 2px; + width: 100px; + float: left; + padding-bottom:10px; +} + +.manual-account-details .value { + float: left; +} + +.manual-account-details .form-item:after { + content: "."; + display: block; + clear: both; + visibility: hidden; + line-height: 0; + height: 0; +} + +.manual-account-details .form-item { + padding: 10px; + width: 278px; +} + +.manual-account-details select, .manual-account-details input { + width: 150px; +} + +.manual-account-details > *:nth-child(even) { + background: #DFE1E3; +} + +.manual-account-details > *:nth-child(odd) { + background: #F2F0F0; +} + +.manual-account-details .value { + display: inline-block; +} \ No newline at end of file diff --git a/ui/index.jsp b/ui/index.jsp index af5fa37c4d0..813dbcb4d0a 100644 --- a/ui/index.jsp +++ b/ui/index.jsp @@ -238,10 +238,10 @@ under the License.
-
+

-

+

@@ -432,7 +432,7 @@ under the License.
- +
@@ -469,6 +469,37 @@ under the License.
+ +
+
+
+
+ +
+
+ +
+
+
+
+
+ +
+
@@ -505,7 +536,7 @@ under the License.
- +
@@ -533,7 +564,7 @@ under the License.
- +
@@ -752,7 +783,7 @@ under the License.
  • - +
    @@ -976,7 +1007,7 @@ under the License.
    -   +  
    @@ -1050,7 +1081,7 @@ under the License. " view-all-target="virtualRouters"> - +
    @@ -1225,7 +1256,7 @@ under the License.
    - +
    @@ -1618,7 +1649,7 @@ under the License. - + @@ -1642,7 +1673,7 @@ under the License. - + @@ -1650,7 +1681,7 @@ under the License. - + @@ -1671,8 +1702,8 @@ under the License. - - + + @@ -1687,6 +1718,8 @@ under the License. + + diff --git a/ui/scripts/accounts.js b/ui/scripts/accounts.js index bf0eaf4e432..e3e28e47a30 100644 --- a/ui/scripts/accounts.js +++ b/ui/scripts/accounts.js @@ -76,7 +76,7 @@ return 'label.add.account'; } }, - + /* createForm: { title: 'label.add.account', desc: 'label.add.account', @@ -284,14 +284,21 @@ } }); }, - + */ notification: { poll: function(args) { args.complete({ actionFilter: accountActionfilter }); } - } + }, + + action: { + custom: cloudStack.uiCustom.accountsWizard( + cloudStack.accountsWizard + ) + }, + } }, diff --git a/ui/scripts/accountsWizard.js b/ui/scripts/accountsWizard.js new file mode 100644 index 00000000000..a350085c1df --- /dev/null +++ b/ui/scripts/accountsWizard.js @@ -0,0 +1,158 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +(function(cloudStack, $) { + cloudStack.accountsWizard = { + + manuallyInputtedAccountInformation: { + domainid: { + label: 'label.domain', + docID: 'helpAccountDomain', + validation: { + required: true + }, + select: function(args) { + var data = {}; + + if (args.context.users) { // In accounts section + data.listAll = true; + } else if (args.context.domains) { // In domain section (use specific domain) + data.id = args.context.domains[0].id; + } + + $.ajax({ + url: createURL("listDomains"), + data: data, + dataType: "json", + async: false, + success: function(json) { + var items = []; + domainObjs = json.listdomainsresponse.domain; + $(domainObjs).each(function() { + items.push({ + id: this.id, + description: this.path + }); + + if (this.level == 0) + rootDomainId = this.id; + }); + args.response.success({ + data: items + }); + } + }); + } + }, + account: { + label: 'label.account', + docID: 'helpAccountAccount', + validation: { + required: true + }, + }, + accounttype: { + label: 'label.type', + docID: 'helpAccountType', + validation: { + required: true + }, + select: function(args) { + var items = []; + items.push({ + id: 0, + description: "User" + }); //regular-user + items.push({ + id: 1, + description: "Admin" + }); //root-admin + args.response.success({ + data: items + }); + } + }, + timezone: { + label: 'label.timezone', + docID: 'helpAccountTimezone', + select: function(args) { + var items = []; + items.push({ + id: "", + description: "" + }); + for (var p in timezoneMap) + items.push({ + id: p, + description: timezoneMap[p] + }); + args.response.success({ + data: items + }); + } + }, + networkdomain: { + label: 'label.network.domain', + docID: 'helpAccountNetworkDomain', + validation: { + required: false + } + } + }, + + action: function(args) { + var array1 = []; + + array1.push("&username=" + args.data.username); + array1.push("&domainid=" + args.data.domainid); + + if (args.data.account != null && args.data.account.length != 0) { + array1.push("&account=" + args.data.account); + } + + if (args.data.accounttype == "1" && args.data.domainid != rootDomainId) { + args.data.accounttype = "2"; + } + array1.push("&accountType=" + args.data.accounttype); + + if (args.data.timezone != null && args.data.timezone.length != 0) { + array1.push("&timezone=" + args.data.timezone); + } + + if (args.data.networkdomain != null && args.data.networkdomain != 0) { + array1.push("&networkDomain=" + args.data.networkdomain); + } + + console.log(array1.join("")); + console.log(args.data); + + $.ajax({ + url: createURL("ldapCreateAccount" + array1.join("")), + dataType: "json", + success: function(json) { + var item = json.createaccountresponse.account; + args.response.success({ + data: item + }); + }, + error: function(XMLHttpResponse) { + args.response.error(parseXMLHttpResponse(XMLHttpResponse)); + } + }); + } + } +}(cloudStack, jQuery)); diff --git a/ui/scripts/globalSettings.js b/ui/scripts/globalSettings.js index 947a3b27966..48b71d4e6de 100644 --- a/ui/scripts/globalSettings.js +++ b/ui/scripts/globalSettings.js @@ -99,7 +99,6 @@ } } }, - ldapConfiguration: { type: 'select', title: 'LDAP Configuration', @@ -110,29 +109,18 @@ hostname: { label: 'Hostname' }, - queryfilter: { - label: 'Query Filter' - }, - searchbase: { - label: 'Search Base' - }, port: { label: 'LDAP Port' }, - ssl: { - label: 'SSL' - - } - }, dataProvider: function(args) { var data = {}; listViewDataProvider(args, data); $.ajax({ - url: createURL('ldapConfig&listall=true'), //Need a list LDAP configuration API call which needs to be implemented + url: createURL('listLdapConfigurations'), data: data, success: function(json) { - var items = json.ldapconfigresponse.ldapconfig; + var items = json.ldapconfigurationresponse.LdapConfiguration; args.response.success({ data: items }); @@ -142,12 +130,9 @@ } }); }, - detailView: { name: 'label.details', actions: { - - // Remove LDAP remove: { label: 'Remove LDAP', messages: { @@ -159,192 +144,96 @@ } }, action: function(args) { - $.ajax({ - url: createURL("ldapRemove"), + url: createURL("deleteLdapConfiguration&hostname=" + args.context.ldapConfiguration[0].hostname), success: function(json) { - args.response.success(); - } - }); $(window).trigger('cloudStack.fullRefresh'); - } } }, - tabs: { - details: { title: 'LDAP Configuration Details', fields: [{ hostname: { label: 'Hostname' }, - description: { - label: 'label.description' - }, - ssl: { - label: 'SSL' + port: { + label: 'Port' } }], dataProvider: function(args) { + var items = []; + console.log(args); $.ajax({ - url: createURL("ldapConfig&listAll=true"), + url: createURL("listLdapConfigurations&hostname=" + args.context.ldapConfiguration[0].hostname), dataType: "json", async: true, success: function(json) { - var item = json.ldapconfigresponse.ldapconfig; + var item = json.ldapconfigurationresponse.LdapConfiguration; args.response.success({ - data: item + data: item[0] }); } }); } - } - } }, - actions: { add: { - label: 'Configure LDAP', - messages: { confirm: function(args) { return 'Do you really want to configure LDAP ? '; }, notification: function(args) { - return 'LDAP configured'; + console.log(args); + return 'Successfully added a new LDAP server'; } }, - createForm: { - title: 'Configure LDAP', fields: { - name: { - label: 'Bind DN', - validation: { - required: true - } - }, - password: { - label: 'Bind Password', - validation: { - required: true - }, - isPassword: true - }, hostname: { label: 'Hostname', validation: { required: true } }, - queryfilter: { - label: 'Query Filter', - validation: { - required: true - }, - docID: 'helpLdapQueryFilter' - }, - searchbase: { - label: 'SearchBase', - validation: { - required: true - } - }, - ssl: { - label: 'SSL', - isBoolean: true, - isChecked: false - - }, port: { label: 'Port', - defaultValue: '389' - }, - truststore: { - label: 'Trust Store', - isHidden: true, - dependsOn: 'ssl', - validation: { - required: true - } - }, - truststorepassword: { - label: 'Trust Store Password', - isHidden: true, - dependsOn: 'ssl', validation: { required: true } } - } - - }, - - action: function(args) { var array = []; - array.push("&binddn=" + todb(args.data.name)); - array.push("&bindpass=" + todb(args.data.password)); array.push("&hostname=" + todb(args.data.hostname)); - array.push("&searchbase=" + todb(args.data.searchbase)); - array.push("&queryfilter=" + todb(args.data.queryfilter).replace('&', '%26')); - array.push("&port=" + todb(args.data.port)); - - if (args.$form.find('.form-item[rel=ssl]').find('input[type=checkbox]').is(':Checked') == true) { - - array.push("&ssl=true"); - if (args.data.truststore != "") - array.push("&truststore=" + todb(args.data.truststore)); - - if (args.data.truststorepassword != "") - array.push("&truststorepass=" + todb(args.data.truststorepassword)); - - } else - array.push("&ssl=false"); - + array.push("&port=" + todb(args.data.port));; $.ajax({ - url: createURL("ldapConfig" + array.join("")), + url: createURL("addLdapConfiguration" + array.join("")), dataType: "json", - type: "POST", + async: true, success: function(json) { - var items = json.ldapconfigresponse.ldapconfig; + var items = json.ldapconfigurationresponse.LdapAddConfiguration; args.response.success({ data: items }); - }, - error: function(json) { args.response.error(parseXMLHttpResponse(json)); - } - - }); - - } } - } - - - } - - - }, hypervisorCapabilities: { type: 'select', diff --git a/ui/scripts/ui-custom/accountsWizard.js b/ui/scripts/ui-custom/accountsWizard.js new file mode 100644 index 00000000000..4037788103f --- /dev/null +++ b/ui/scripts/ui-custom/accountsWizard.js @@ -0,0 +1,116 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +(function($, cloudStack) { + cloudStack.uiCustom.accountsWizard = function(args) { + return function(listViewArgs) { + var context = listViewArgs.context; + + var accountsWizard = function(data) { + var $wizard = $('#template').find('div.accounts-wizard').clone(); + var $form = $wizard.find('form'); + + var close = function() { + $wizard.dialog('destroy'); + $('div.overlay').fadeOut(function() { + $('div.overlay').remove(); + }); + }; + + var completeAction = function() { + var data = cloudStack.serializeForm($form); + args.action({ + context: context, + data: data, + response: { + success: function(args) { + $('.list-view').listView('refresh'); + close(); + }, + error: function(message) { + close(); + if(message) { + cloudStack.dialog.notice({ + message: message + }); + } + } + } + }); + } + + $wizard.click(function(event) { + var $target = $(event.target); + if ($target.closest('div.button.next').size()) { + $form.validate(); + if ($form.valid()) { + completeAction(); + return true; + } else { + return false; + } + } + + if ($target.closest('div.button.cancel').size()) { + close(); + return false; + } + }); + + var form = cloudStack.dialog.createForm({ + context: context, + noDialog: true, + form: { + title: '', + fields: args.manuallyInputtedAccountInformation + } + }); + + var $manualDetails = form.$formContainer.find('form .form-item'); + $wizard.find('.manual-account-details').append($manualDetails); + + var $table = $wizard.find('.ldap-account-choice tbody'); + + $.ajax({ + url: createURL("listAllLdapUsers"), + dataType: "json", + async: false, + success: function(json) { + $(json.ldapuserresponse.LdapUser).each(function() { + var result = $(""); + result.append(""); + result.append("" + this.firstname + " " + this.lastname + ""); + result.append("" + this.username + ""); + result.append("" + this.email + ""); + $table.append(result); + }) + } + }); + + return $wizard.dialog({ + title: _l('label.add.account'), + width: 800, + height: 500, + closeOnEscape: false, + zIndex: 5000 + }).closest('.ui-dialog').overlay(); + } + + accountsWizard(args); + }; + }; +})(jQuery, cloudStack);