Merge LDAPPlugin

Signed-off-by: Abhinandan Prateek <aprateek@apache.org>
This commit is contained in:
Ian Duffy 2013-07-25 10:24:13 +01:00 committed by Abhinandan Prateek
parent 1f64354ec8
commit eaa4143371
69 changed files with 4222 additions and 1202 deletions

View File

@ -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

View File

@ -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();
}
}
}

View File

@ -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);

View File

@ -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<LDAPConfigResponse> response = new ListResponse<LDAPConfigResponse>();
List<LDAPConfigResponse> responses = new ArrayList<LDAPConfigResponse>();
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;
}
}

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -391,7 +391,7 @@
<bean id="MD5UserAuthenticator" class="com.cloud.server.auth.MD5UserAuthenticator">
<property name="name" value="MD5"/>
</bean>
<bean id="LDAPUserAuthenticator" class="com.cloud.server.auth.LDAPUserAuthenticator">
<bean id="LdapAuthenticator" class="org.apache.cloudstack.ldap.LdapAuthenticator">
<property name="name" value="LDAP"/>
</bean>
<bean id="SHA256SaltedUserAuthenticator" class="com.cloud.server.auth.SHA256SaltedUserAuthenticator">
@ -400,6 +400,11 @@
<bean id="PlainTextUserAuthenticator" class="com.cloud.server.auth.PlainTextUserAuthenticator">
<property name="name" value="PLAINTEXT"/>
</bean>
<bean id="LdapManager" class="org.apache.cloudstack.ldap.LdapManagerImpl" />
<bean id="LdapUserManager" class="org.apache.cloudstack.ldap.LdapUserManager" />
<bean id="LdapContextFactory" class="org.apache.cloudstack.ldap.LdapContextFactory" />
<bean id="LdapConfigurationDao" class="org.apache.cloudstack.ldap.dao.LdapConfigurationDaoImpl" />
<bean id="LdapConfiguration" class="org.apache.cloudstack.ldap.LdapConfiguration" />
<!--
Network Elements

View File

@ -210,8 +210,6 @@ scaleSystemVm=1
#### configuration commands
updateConfiguration=1
listConfigurations=1
ldapConfig=1
ldapRemove=1
listCapabilities=15
listDeploymentPlanners=1
cleanVMReservations=1
@ -666,3 +664,11 @@ listDedicatedZones=1
listDedicatedPods=1
listDedicatedClusters=1
listDedicatedHosts=1
### LDAP
searchLdap=3
listLdapConfigurations=3
addLdapConfiguration=3
deleteLdapConfiguration=3
listAllLdapUsers=3
ldapCreateAccount=3

View File

@ -127,7 +127,7 @@
<list>
<ref bean="SHA256SaltedUserAuthenticator"/>
<ref bean="MD5UserAuthenticator"/>
<ref bean="LDAPUserAuthenticator"/>
<ref bean="LdapAuthenticator"/>
<ref bean="PlainTextUserAuthenticator"/>
</list>
</property>
@ -137,7 +137,7 @@
<list>
<ref bean="SHA256SaltedUserAuthenticator"/>
<ref bean="MD5UserAuthenticator"/>
<ref bean="LDAPUserAuthenticator"/>
<ref bean="LdapAuthenticator"/>
<ref bean="PlainTextUserAuthenticator"/>
</list>
</property>

View File

@ -224,7 +224,7 @@
<list>
<ref bean="SHA256SaltedUserAuthenticator"/>
<ref bean="MD5UserAuthenticator"/>
<ref bean="LDAPUserAuthenticator"/>
<ref bean="LdapAuthenticator"/>
<ref bean="PlainTextUserAuthenticator"/>
</list>
</property>
@ -234,7 +234,7 @@
<list>
<ref bean="SHA256SaltedUserAuthenticator"/>
<ref bean="MD5UserAuthenticator"/>
<ref bean="LDAPUserAuthenticator"/>
<ref bean="LdapAuthenticator"/>
<ref bean="PlainTextUserAuthenticator"/>
</list>
</property>

View File

@ -73,7 +73,7 @@
<list>
<ref bean="SHA256SaltedUserAuthenticator"/>
<ref bean="MD5UserAuthenticator"/>
<ref bean="LDAPUserAuthenticator"/>
<ref bean="LdapAuthenticator"/>
<ref bean="PlainTextUserAuthenticator"/>
</list>
</property>
@ -83,7 +83,7 @@
<list>
<ref bean="SHA256SaltedUserAuthenticator"/>
<ref bean="MD5UserAuthenticator"/>
<ref bean="LDAPUserAuthenticator"/>
<ref bean="LdapAuthenticator"/>
<ref bean="PlainTextUserAuthenticator"/>
</list>
</property>

View File

@ -29,7 +29,6 @@ import net.sf.ehcache.CacheManager;
import org.apache.log4j.Logger;
import org.apache.cloudstack.acl.APIChecker;
import org.apache.cloudstack.api.ApiConstants.LDAPParams;
import org.apache.cloudstack.api.command.admin.ratelimit.ResetApiLimitCmd;
import org.apache.cloudstack.api.command.user.ratelimit.GetApiLimitCmd;
import org.apache.cloudstack.api.response.ApiLimitResponse;

View File

@ -1,22 +1,14 @@
<!--
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.
-->
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<!-- 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. -->
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<artifactId>cloud-plugin-user-authenticator-ldap</artifactId>
<name>Apache CloudStack Plugin - User Authenticator LDAP</name>
@ -26,4 +18,93 @@
<version>4.2.0-SNAPSHOT</version>
<relativePath>../../pom.xml</relativePath>
</parent>
<build>
<plugins>
<plugin>
<groupId>org.codehaus.gmaven</groupId>
<artifactId>gmaven-plugin</artifactId>
<version>1.3</version>
<configuration>
<providerSelection>1.7</providerSelection>
</configuration>
<executions>
<execution>
<goals>
<goal>compile</goal>
<goal>testCompile</goal>
</goals>
<configuration>
<sources>
<fileset>
<directory>test/groovy</directory>
<includes>
<include>**/*.groovy</include>
</includes>
</fileset>
</sources>
</configuration>
</execution>
</executions>
<dependencies>
<dependency>
<groupId>org.codehaus.gmaven.runtime</groupId>
<artifactId>gmaven-runtime-1.7</artifactId>
<version>1.3</version>
<exclusions>
<exclusion>
<groupId>org.codehaus.groovy</groupId>
<artifactId>groovy-all</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.codehaus.groovy</groupId>
<artifactId>groovy-all</artifactId>
<version>2.0.5</version>
</dependency>
</dependencies>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<configuration>
<includes>
<include>**/*Spec*</include>
</includes>
</configuration>
</plugin>
<plugin>
<groupId>com.btmatthews.maven.plugins</groupId>
<artifactId>ldap-maven-plugin</artifactId>
<version>1.1.0</version>
<configuration>
<monitorPort>11389</monitorPort>
<monitorKey>ldap</monitorKey>
<daemon>false</daemon>
<rootDn>dc=cloudstack,dc=org</rootDn>
<ldapPort>10389</ldapPort>
<ldifFile>test/resources/cloudstack.org.ldif</ldifFile>
</configuration>
</plugin>
</plugins>
</build>
<dependencies>
<!-- Mandatory dependencies for using Spock -->
<dependency>
<groupId>org.spockframework</groupId>
<artifactId>spock-core</artifactId>
<version>0.7-groovy-2.0</version>
</dependency>
<!-- Optional dependencies for using Spock -->
<dependency> <!-- enables mocking of classes (in addition to interfaces) -->
<groupId>cglib</groupId>
<artifactId>cglib-nodep</artifactId>
<version>2.2</version>
</dependency>
</dependencies>
</project>

View File

@ -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<String, Object[]> 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<String, String> env = new Hashtable<String, String>(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<SearchResult> 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<String, String>(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<String, Object> 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);
}
}
}

View File

@ -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;
}
}

View File

@ -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<String, String> 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");
}
}
}

View File

@ -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;
}
}

View File

@ -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<LdapUserResponse> createLdapUserResponse(List<LdapUser> users) {
final List<LdapUserResponse> ldapResponses = new ArrayList<LdapUserResponse>();
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<LdapUser> users = _ldapManager.getUsers();
final ListResponse<LdapUserResponse> response = new ListResponse<LdapUserResponse>();
final List<LdapUserResponse> 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;
}
}

View File

@ -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<LdapConfigurationResponse> createLdapConfigurationResponses(List<? extends LdapConfigurationVO> configurations) {
final List<LdapConfigurationResponse> responses = new ArrayList<LdapConfigurationResponse>();
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<List<? extends LdapConfigurationVO>, Integer> result = _ldapManager.listConfigurations(this);
final List<LdapConfigurationResponse> responses = createLdapConfigurationResponses(result.first());
final ListResponse<LdapConfigurationResponse> response = new ListResponse<LdapConfigurationResponse>();
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;
}
}

View File

@ -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<LdapUserResponse> createLdapUserResponse(List<LdapUser> users) {
final List<LdapUserResponse> ldapUserResponses = new ArrayList<LdapUserResponse>();
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<LdapUserResponse> response = new ListResponse<LdapUserResponse>();
List<LdapUser> users = null;
try {
users = _ldapManager.searchUsers(query);
} catch (final NoLdapUserMatchingQueryException e) {
s_logger.debug(e.getMessage());
}
final List<LdapUserResponse> 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;
}
}

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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<String, Object[]> 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;
}
}

View File

@ -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<List<? extends LdapConfigurationVO>, 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;
}
}

View File

@ -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;
}
}

View File

@ -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<String, String> 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<String, String> environment = new Hashtable<String, String>();
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;
}
}
}

View File

@ -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<LdapUser> getUsers() throws NoLdapUserMatchingQueryException;
Pair<List<? extends LdapConfigurationVO>, Integer> listConfigurations(LdapListConfigurationCmd cmd);
List<LdapUser> searchUsers(String query) throws NoLdapUserMatchingQueryException;
}

View File

@ -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<Class<?>> getCommands() {
final List<Class<?>> cmdList = new ArrayList<Class<?>>();
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<LdapUser> 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<List<? extends LdapConfigurationVO>, Integer> listConfigurations(final LdapListConfigurationCmd cmd) {
final String hostname = cmd.getHostname();
final int port = cmd.getPort();
final Pair<List<LdapConfigurationVO>, Integer> result = _ldapConfigurationDao.searchConfigurations(hostname, port);
return new Pair<List<? extends LdapConfigurationVO>, Integer>(result.first(), result.second());
}
@Override
public List<LdapUser> 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);
}
}
}

View File

@ -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<LdapUser> {
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();
}
}

View File

@ -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<SearchResult> result = searchUsers(username, context);
if (result.hasMoreElements()) {
return createUser(result.nextElement());
} else {
throw new NamingException("No user found for username " + username);
}
}
public List<LdapUser> getUsers(final DirContext context) throws NamingException {
return getUsers(null, context);
}
public List<LdapUser> getUsers(final String username, final DirContext context) throws NamingException {
final NamingEnumeration<SearchResult> results = searchUsers(username, context);
final List<LdapUser> users = new ArrayList<LdapUser>();
while (results.hasMoreElements()) {
final SearchResult result = results.nextElement();
users.add(createUser(result));
}
Collections.sort(users);
return users;
}
public NamingEnumeration<SearchResult> searchUsers(final DirContext context) throws NamingException {
return searchUsers(null, context);
}
public NamingEnumeration<SearchResult> 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);
}
}

View File

@ -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() {
}
}

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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, Long> {
LdapConfigurationVO findByHostname(String hostname);
Pair<List<LdapConfigurationVO>, Integer> searchConfigurations(String hostname, int port);
}

View File

@ -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<LdapConfigurationVO, Long> implements LdapConfigurationDao {
private final SearchBuilder<LdapConfigurationVO> hostnameSearch;
private final SearchBuilder<LdapConfigurationVO> 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<LdapConfigurationVO> sc = hostnameSearch.create();
sc.setParameters("hostname", hostname);
return findOneBy(sc);
}
@Override
public Pair<List<LdapConfigurationVO>, Integer> searchConfigurations(final String hostname, final int port) {
final SearchCriteria<LdapConfigurationVO> sc = listAllConfigurationsSearch.create();
if (hostname != null) {
sc.setParameters("hostname", hostname);
}
return searchAndCount(sc, null);
}
}

View File

@ -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<String> items = new LinkedList<SearchResult>();
@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)
}
}

View File

@ -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"
}
}

View File

@ -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<LdapConfigurationVO> ldapConfigurationList = new ArrayList()
Pair<List<LdapConfigurationVO>, Integer> ldapConfigurations = new Pair<List<LdapConfigurationVO>, 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<LdapConfigurationVO> ldapConfigurationList = new ArrayList()
ldapConfigurationList.add(new LdapConfigurationVO("localhost", 389))
Pair<List<LdapConfigurationVO>, Integer> ldapConfigurations = new Pair<List<LdapConfigurationVO>, 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"
}
}

View File

@ -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
}
}

View File

@ -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
}
}

View File

@ -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<LdapConfigurationVO> ldapConfigurationList = new ArrayList()
ldapConfigurationList.add(new LdapConfigurationVO("localhost", 389))
Pair<List<LdapConfigurationVO>, Integer> result = new Pair<List<LdapConfigurationVO>, 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"
}
}

View File

@ -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]
}
}

View File

@ -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
}
}

View File

@ -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"
}
}

View File

@ -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<LdapUser> 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"
}
}

View File

@ -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<LdapConfigurationVO> ldapConfigurationList = new ArrayList()
ldapConfigurationList.add(new LdapConfigurationVO("localhost", 389))
Pair<List<LdapConfigurationVO>, Integer> ldapConfigurations = new Pair<List<LdapConfigurationVO>, 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<LdapConfigurationVO> ldapConfigurationList = new ArrayList()
Pair<List<LdapConfigurationVO>, Integer> ldapConfigurations = new Pair<List<LdapConfigurationVO>, 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"
}
}

View File

@ -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<LdapUser> 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<LdapConfigurationVO> ldapConfigurationList = new ArrayList()
ldapConfigurationList.add(new LdapConfigurationVO("localhost", 389))
Pair<List<LdapConfigurationVO>, Integer> configurations = new Pair<List<LdapConfigurationVO>, 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<LdapUser> users = new ArrayList<LdapUser>();
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;
}
}

View File

@ -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<LdapUser> 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"
}
}

View File

@ -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
}
}

View File

@ -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"
}
}

View File

@ -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"
}
}

View File

@ -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
}
}

View File

@ -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*"]
}
}

View File

@ -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"]
}
}

View File

@ -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=

View File

@ -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();

View File

@ -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),

View File

@ -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<String, String> env = new Hashtable<String, String>(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)

View File

@ -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);

View File

@ -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)
*/

View File

@ -2142,11 +2142,26 @@ 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';

View File

@ -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

View File

@ -118,7 +118,7 @@ known_categories = {
'TrafficType': 'Usage',
'Product': 'Product',
'LB': 'Load Balancer',
'ldap': 'LDAP',
'Ldap': 'LDAP',
'Swift': 'Swift',
'S3' : 'S3',
'SecondaryStorage': 'Host',

View File

@ -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;
}

View File

@ -469,6 +469,37 @@ under the License.
<div class="button next"><span><fmt:message key="label.next"/></span></div>
</div>
</div>
<!-- Accounts wizard -->
<div class="multi-wizard accounts-wizard">
<form>
<div class="steps">
<div class="content">
<div class="select-container ldap-account-choice">
<table>
<thead>
<tr>
<th style="width:40px">Select</th>
<th style="width:110px">Realname</th>
<th style="width:70px">Username</th>
<th>Email</th>
</tr>
</thead>
<tbody>
</tbody>
</table>
</div>
</div>
<div class="content input-area">
<div class="select-container manual-account-details">
</div>
</div>
</div>
</form>
<div class="buttons">
<div class="button cancel"><span><fmt:message key="label.cancel"/></span></div>
<div class="button next"><span><fmt:message key="label.add"/></span></div>
</div>
</div>
<!-- Zone wizard -->
<div class="multi-wizard zone-wizard">
<div class="progress">
@ -1687,6 +1718,8 @@ under the License.
<script type="text/javascript" src="scripts/ui-custom/uploadVolume.js?t=<%=now%>"></script>
<script type="text/javascript" src="scripts/storage.js?t=<%=now%>"></script>
<script type="text/javascript" src="scripts/templates.js?t=<%=now%>"></script>
<script type="text/javascript" src="scripts/accountsWizard.js?t=<%=now%>"></script>
<script type="text/javascript" src="scripts/ui-custom/accountsWizard.js?t=<%=now%>"></script>
<script type="text/javascript" src="scripts/accounts.js?t=<%=now%>"></script>
<script type="text/javascript" src="scripts/configuration.js?t=<%=now%>"></script>
<script type="text/javascript" src="scripts/globalSettings.js?t=<%=now%>"></script>

View File

@ -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
)
},
}
},

View File

@ -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));

View File

@ -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('&amp;', '%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',

View File

@ -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 = $("<tr>");
result.append("<td><input type=\"radio\" class=\"required\" name=\"username\" value=\"" + this.username + "\"></td>");
result.append("<td>" + this.firstname + " " + this.lastname + "</td>");
result.append("<td>" + this.username + "</td>");
result.append("<td>" + this.email + "</td>");
$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);