mirror of
https://github.com/apache/cloudstack.git
synced 2025-10-26 08:42:29 +01:00
Ldap fixes (#3694)
* pass domainid for list users
* passing arg in wizzard
* adding userfilter to list ldap users and usersource to response
port of list ldap users tests to java
* assertion of differnt junit ldap methods
* broken test for directory server (and others)
* embedded context loading
* add user and query test
* UI: filter options passing filter and domain and onchange trigger
* disable tests that only work in ide
prereqs for domain-linkage fixed
move trigger to the right location in code
trigger for changing domain
* logging, comments and refactor
implement search users per domain
retrieve appropriate list of users to filter
get domain specific ldap provider
* query cloudstack users with now db filter
* recreate ldap linked account should succeed
* disable auto import users that don't exist
* ui choice and text
* import filter and potential remove from list bug fixed
* fix rights for domain admins
* list only member of linked groups not of principle group
* Do not show ldap user filter if not importing from ldap
do not delete un-needed items from dialog permanently
delete from temp object not from global one
* localdomain should not filterout users not imported from ldap
* several types of authentication handling errors fixed and unit tested
* conflict in output name
* add conflict source field to generic import dialog
* replace reflextion by enum member call
* conflict is now called conflict 🎉
This commit is contained in:
parent
9b7acfde1e
commit
5ff932eb86
@ -336,7 +336,10 @@ public class ApiConstants {
|
|||||||
public static final String URL = "url";
|
public static final String URL = "url";
|
||||||
public static final String USAGE_INTERFACE = "usageinterface";
|
public static final String USAGE_INTERFACE = "usageinterface";
|
||||||
public static final String USER_DATA = "userdata";
|
public static final String USER_DATA = "userdata";
|
||||||
|
public static final String USER_FILTER = "userfilter";
|
||||||
public static final String USER_ID = "userid";
|
public static final String USER_ID = "userid";
|
||||||
|
public static final String USER_SOURCE = "usersource";
|
||||||
|
public static final String USER_CONFLICT_SOURCE = "conflictingusersource";
|
||||||
public static final String USE_SSL = "ssl";
|
public static final String USE_SSL = "ssl";
|
||||||
public static final String USERNAME = "username";
|
public static final String USERNAME = "username";
|
||||||
public static final String USER_CONFIGURABLE = "userconfigurable";
|
public static final String USER_CONFIGURABLE = "userconfigurable";
|
||||||
|
|||||||
@ -111,6 +111,8 @@ public interface QueryService {
|
|||||||
|
|
||||||
ListResponse<UserResponse> searchForUsers(ListUsersCmd cmd) throws PermissionDeniedException;
|
ListResponse<UserResponse> searchForUsers(ListUsersCmd cmd) throws PermissionDeniedException;
|
||||||
|
|
||||||
|
ListResponse<UserResponse> searchForUsers(Long domainId, boolean recursive) throws PermissionDeniedException;
|
||||||
|
|
||||||
ListResponse<EventResponse> searchForEvents(ListEventsCmd cmd);
|
ListResponse<EventResponse> searchForEvents(ListEventsCmd cmd);
|
||||||
|
|
||||||
ListResponse<ResourceTagResponse> listTags(ListTagsCmd cmd);
|
ListResponse<ResourceTagResponse> listTags(ListTagsCmd cmd);
|
||||||
|
|||||||
@ -27,12 +27,23 @@
|
|||||||
<version>4.14.0.0-SNAPSHOT</version>
|
<version>4.14.0.0-SNAPSHOT</version>
|
||||||
<relativePath>../../pom.xml</relativePath>
|
<relativePath>../../pom.xml</relativePath>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
||||||
|
<properties>
|
||||||
|
<ads.version>2.0.0.AM25</ads.version>
|
||||||
|
<gmaven.version>1.5</gmaven.version>
|
||||||
|
<ldap-maven.version>1.1.3</ldap-maven.version>
|
||||||
|
<ldapunit.version>1.1.3</ldapunit.version>
|
||||||
|
<groovy.version>1.1-groovy-2.4</groovy.version>
|
||||||
|
<zapdot.version>0.7</zapdot.version>
|
||||||
|
<unboundedid.version>4.0.4</unboundedid.version>
|
||||||
|
</properties>
|
||||||
|
|
||||||
<build>
|
<build>
|
||||||
<plugins>
|
<plugins>
|
||||||
<plugin>
|
<plugin>
|
||||||
<groupId>org.codehaus.gmaven</groupId>
|
<groupId>org.codehaus.gmaven</groupId>
|
||||||
<artifactId>gmaven-plugin</artifactId>
|
<artifactId>gmaven-plugin</artifactId>
|
||||||
<version>1.3</version>
|
<version>${gmaven.version}</version>
|
||||||
<configuration>
|
<configuration>
|
||||||
<providerSelection>1.7</providerSelection>
|
<providerSelection>1.7</providerSelection>
|
||||||
</configuration>
|
</configuration>
|
||||||
@ -58,7 +69,7 @@
|
|||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.codehaus.gmaven.runtime</groupId>
|
<groupId>org.codehaus.gmaven.runtime</groupId>
|
||||||
<artifactId>gmaven-runtime-1.7</artifactId>
|
<artifactId>gmaven-runtime-1.7</artifactId>
|
||||||
<version>1.3</version>
|
<version>${gmaven.version}</version>
|
||||||
<exclusions>
|
<exclusions>
|
||||||
<exclusion>
|
<exclusion>
|
||||||
<groupId>org.codehaus.groovy</groupId>
|
<groupId>org.codehaus.groovy</groupId>
|
||||||
@ -81,38 +92,126 @@
|
|||||||
<include>**/*Spec.groovy</include>
|
<include>**/*Spec.groovy</include>
|
||||||
<include>**/*Test.java</include>
|
<include>**/*Test.java</include>
|
||||||
</includes>
|
</includes>
|
||||||
|
<excludes>
|
||||||
|
<exclude>META-INF/*.SF</exclude>
|
||||||
|
<exclude>META-INF/*.DSA</exclude>
|
||||||
|
<exclude>META-INF/*.RSA</exclude>
|
||||||
|
</excludes>
|
||||||
</configuration>
|
</configuration>
|
||||||
</plugin>
|
</plugin>
|
||||||
<plugin>
|
<plugin>
|
||||||
<groupId>com.btmatthews.maven.plugins</groupId>
|
<groupId>com.btmatthews.maven.plugins</groupId>
|
||||||
<artifactId>ldap-maven-plugin</artifactId>
|
<artifactId>ldap-maven-plugin</artifactId>
|
||||||
<version>1.1.0</version>
|
<version>${ldap-maven.version}</version>
|
||||||
<configuration>
|
<configuration>
|
||||||
<monitorPort>11389</monitorPort>
|
<monitorPort>11389</monitorPort>
|
||||||
<monitorKey>ldap</monitorKey>
|
<monitorKey>ldap</monitorKey>
|
||||||
<daemon>false</daemon>
|
<daemon>false</daemon>
|
||||||
<rootDn>dc=cloudstack,dc=org</rootDn>
|
<rootDn>dc=cloudstack,dc=org</rootDn>
|
||||||
<ldapPort>10389</ldapPort>
|
<ldapPort>10389</ldapPort>
|
||||||
<ldifFile>test/resources/cloudstack.org.ldif</ldifFile>
|
<ldifFile>src/test/resources/cloudstack.org.ldif</ldifFile>
|
||||||
</configuration>
|
</configuration>
|
||||||
</plugin>
|
</plugin>
|
||||||
</plugins>
|
</plugins>
|
||||||
<testSourceDirectory>test</testSourceDirectory>
|
<testSourceDirectory>src/test/java</testSourceDirectory>
|
||||||
</build>
|
</build>
|
||||||
<dependencies>
|
<dependencies>
|
||||||
<!-- Mandatory dependencies for using Spock -->
|
<!-- Mandatory dependencies for using Spock -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.btmatthews.ldapunit</groupId>
|
||||||
|
<artifactId>ldapunit</artifactId>
|
||||||
|
<version>${ldapunit.version}</version>
|
||||||
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.spockframework</groupId>
|
<groupId>org.spockframework</groupId>
|
||||||
<artifactId>spock-core</artifactId>
|
<artifactId>spock-core</artifactId>
|
||||||
<version>1.1-groovy-2.4</version>
|
<version>${groovy.version}</version>
|
||||||
<scope>test</scope>
|
<scope>test</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
<!-- Optional dependencies for using Spock -->
|
<!-- Optional dependencies for using Spock -->
|
||||||
<dependency> <!-- enables mocking of classes (in addition to interfaces) -->
|
<dependency> <!-- enables mocking of classes (in addition to interfaces) -->
|
||||||
<groupId>cglib</groupId>
|
<groupId>cglib</groupId>
|
||||||
<artifactId>cglib-nodep</artifactId>
|
<artifactId>cglib-nodep</artifactId>
|
||||||
<scope>test</scope>
|
<scope>test</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.zapodot</groupId>
|
||||||
|
<artifactId>embedded-ldap-junit</artifactId>
|
||||||
|
<version>${zapdot.version}</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.unboundid</groupId>
|
||||||
|
<artifactId>unboundid-ldapsdk</artifactId>
|
||||||
|
<version>${unboundedid.version}</version>
|
||||||
|
<scope>test</scope>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.mockito</groupId>
|
||||||
|
<artifactId>mockito-all</artifactId>
|
||||||
|
<version>${cs.mockito.version}</version>
|
||||||
|
<scope>compile</scope>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>junit</groupId>
|
||||||
|
<artifactId>junit</artifactId>
|
||||||
|
<version>${cs.junit.version}</version>
|
||||||
|
<scope>compile</scope>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.apache.directory.server</groupId>
|
||||||
|
<artifactId>apacheds-server-integ</artifactId>
|
||||||
|
<version>${ads.version}</version>
|
||||||
|
<scope>test</scope>
|
||||||
|
<exclusions>
|
||||||
|
<!--
|
||||||
|
shared-ldap-schema module needs to be excluded to avoid multiple schema resources on the classpath
|
||||||
|
-->
|
||||||
|
<exclusion>
|
||||||
|
<groupId>org.apache.directory.shared</groupId>
|
||||||
|
<artifactId>shared-ldap-schema</artifactId>
|
||||||
|
</exclusion>
|
||||||
|
</exclusions>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.apache.directory.server</groupId>
|
||||||
|
<artifactId>apacheds-core-constants</artifactId>
|
||||||
|
<version>${ads.version}</version>
|
||||||
|
<scope>compile</scope>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.apache.directory.server</groupId>
|
||||||
|
<artifactId>apacheds-core-annotations</artifactId>
|
||||||
|
<version>${ads.version}</version>
|
||||||
|
<scope>compile</scope>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.apache.directory.server</groupId>
|
||||||
|
<artifactId>apacheds-core</artifactId>
|
||||||
|
<version>${ads.version}</version>
|
||||||
|
<scope>compile</scope>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.apache.directory.server</groupId>
|
||||||
|
<artifactId>apacheds-protocol-ldap</artifactId>
|
||||||
|
<version>${ads.version}</version>
|
||||||
|
<scope>compile</scope>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.apache.directory.server</groupId>
|
||||||
|
<artifactId>apacheds-jdbm-partition</artifactId>
|
||||||
|
<version>${ads.version}</version>
|
||||||
|
<scope>compile</scope>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.apache.directory.server</groupId>
|
||||||
|
<artifactId>apacheds-ldif-partition</artifactId>
|
||||||
|
<version>${ads.version}</version>
|
||||||
|
<scope>compile</scope>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>commons-io</groupId>
|
||||||
|
<artifactId>commons-io</artifactId>
|
||||||
|
<version>${cs.commons-io.version}</version>
|
||||||
|
</dependency>
|
||||||
</dependencies>
|
</dependencies>
|
||||||
</project>
|
</project>
|
||||||
|
|||||||
@ -0,0 +1,21 @@
|
|||||||
|
// 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;
|
||||||
|
|
||||||
|
public interface LdapConstants {
|
||||||
|
String PRINCIPAL = "principal";
|
||||||
|
}
|
||||||
@ -16,18 +16,26 @@
|
|||||||
// under the License.
|
// under the License.
|
||||||
package org.apache.cloudstack.api.command;
|
package org.apache.cloudstack.api.command;
|
||||||
|
|
||||||
|
import java.lang.reflect.ParameterizedType;
|
||||||
|
import java.lang.reflect.Type;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import javax.inject.Inject;
|
import javax.inject.Inject;
|
||||||
|
|
||||||
|
import com.cloud.domain.Domain;
|
||||||
|
import com.cloud.user.User;
|
||||||
|
import com.cloud.utils.exception.CloudRuntimeException;
|
||||||
|
import org.apache.cloudstack.acl.RoleType;
|
||||||
|
import org.apache.cloudstack.api.ApiConstants;
|
||||||
|
import org.apache.cloudstack.api.response.DomainResponse;
|
||||||
|
import org.apache.cloudstack.context.CallContext;
|
||||||
import org.apache.log4j.Logger;
|
import org.apache.log4j.Logger;
|
||||||
|
|
||||||
import org.apache.cloudstack.api.APICommand;
|
import org.apache.cloudstack.api.APICommand;
|
||||||
import org.apache.cloudstack.api.BaseListCmd;
|
import org.apache.cloudstack.api.BaseListCmd;
|
||||||
import org.apache.cloudstack.api.Parameter;
|
import org.apache.cloudstack.api.Parameter;
|
||||||
import org.apache.cloudstack.api.ServerApiException;
|
import org.apache.cloudstack.api.ServerApiException;
|
||||||
import org.apache.cloudstack.api.command.admin.user.ListUsersCmd;
|
|
||||||
import org.apache.cloudstack.api.response.LdapUserResponse;
|
import org.apache.cloudstack.api.response.LdapUserResponse;
|
||||||
import org.apache.cloudstack.api.response.ListResponse;
|
import org.apache.cloudstack.api.response.ListResponse;
|
||||||
import org.apache.cloudstack.api.response.UserResponse;
|
import org.apache.cloudstack.api.response.UserResponse;
|
||||||
@ -38,8 +46,37 @@ import org.apache.cloudstack.query.QueryService;
|
|||||||
|
|
||||||
import com.cloud.user.Account;
|
import com.cloud.user.Account;
|
||||||
|
|
||||||
@APICommand(name = "listLdapUsers", responseObject = LdapUserResponse.class, description = "Lists all LDAP Users", since = "4.2.0",
|
/**
|
||||||
requestHasSensitiveInfo = false, responseHasSensitiveInfo = false)
|
* a short flow, use plantuml to view (see http://plantuml.com)
|
||||||
|
* @startuml
|
||||||
|
* start
|
||||||
|
* :list ldap users request;
|
||||||
|
* :get ldap binding;
|
||||||
|
* if (domain == null) then (true)
|
||||||
|
* :get global trust domain;
|
||||||
|
* else (false)
|
||||||
|
* :get trustdomain for domain;
|
||||||
|
* endif
|
||||||
|
* :get ldap users\n using trust domain;
|
||||||
|
* if (filter == 'NoFilter') then (pass as is)
|
||||||
|
* elseif (filter == 'AnyDomain') then (anydomain)
|
||||||
|
* :filterList = all\n\t\tcloudstack\n\t\tusers;
|
||||||
|
* elseif (filter == 'LocalDomain')
|
||||||
|
* :filterList = local users\n\t\tfor domain;
|
||||||
|
* elseif (filter == 'PotentialImport') then (address account\nsynchronisation\nconfigurations)
|
||||||
|
* :query\n the account\n bindings;
|
||||||
|
* :check and markup\n ldap users\n for bound OUs\n with usersource;
|
||||||
|
* else ( unknown value for filter )
|
||||||
|
* :throw invalid parameter;
|
||||||
|
* stop
|
||||||
|
* endif
|
||||||
|
* :remove users in filterList\nfrom ldap users list;
|
||||||
|
* :return remaining;
|
||||||
|
* stop
|
||||||
|
* @enduml
|
||||||
|
*/
|
||||||
|
@APICommand(name = "listLdapUsers", responseObject = LdapUserResponse.class, description = "Lists LDAP Users according to the specifications from the user request.", since = "4.2.0",
|
||||||
|
requestHasSensitiveInfo = false, responseHasSensitiveInfo = false, authorized = {RoleType.Admin,RoleType.DomainAdmin})
|
||||||
public class LdapListUsersCmd extends BaseListCmd {
|
public class LdapListUsersCmd extends BaseListCmd {
|
||||||
|
|
||||||
public static final Logger s_logger = Logger.getLogger(LdapListUsersCmd.class.getName());
|
public static final Logger s_logger = Logger.getLogger(LdapListUsersCmd.class.getName());
|
||||||
@ -47,15 +84,29 @@ public class LdapListUsersCmd extends BaseListCmd {
|
|||||||
@Inject
|
@Inject
|
||||||
private LdapManager _ldapManager;
|
private LdapManager _ldapManager;
|
||||||
|
|
||||||
@Inject
|
|
||||||
private QueryService _queryService;
|
|
||||||
|
|
||||||
@Parameter(name = "listtype",
|
@Parameter(name = "listtype",
|
||||||
type = CommandType.STRING,
|
type = CommandType.STRING,
|
||||||
required = false,
|
required = false,
|
||||||
description = "Determines whether all ldap users are returned or just non-cloudstack users")
|
description = "Determines whether all ldap users are returned or just non-cloudstack users. This option is deprecated in favour for the more option rich 'userfilter' parameter")
|
||||||
|
@Deprecated
|
||||||
private String listType;
|
private String listType;
|
||||||
|
|
||||||
|
@Parameter(name = ApiConstants.USER_FILTER,
|
||||||
|
type = CommandType.STRING,
|
||||||
|
required = false,
|
||||||
|
since = "4.13",
|
||||||
|
description = "Determines what type of filter is applied on the list of users returned from LDAP.\n"
|
||||||
|
+ "\tvalid values are\n"
|
||||||
|
+ "\t'NoFilter'\t no filtering is done,\n"
|
||||||
|
+ "\t'LocalDomain'\tusers already in the current or requested domain will be filtered out of the result list,\n"
|
||||||
|
+ "\t'AnyDomain'\tusers that already exist anywhere in cloudstack will be filtered out, and\n"
|
||||||
|
+ "\t'PotentialImport'\tall users that would be automatically imported from the listing will be shown,"
|
||||||
|
+ " including those that are already in cloudstack, the later will be annotated with their userSource")
|
||||||
|
private String userFilter;
|
||||||
|
|
||||||
|
@Parameter(name = ApiConstants.DOMAIN_ID, type = CommandType.UUID, required = false, entityType = DomainResponse.class, description = "linked domain")
|
||||||
|
private Long domainId;
|
||||||
|
|
||||||
public LdapListUsersCmd() {
|
public LdapListUsersCmd() {
|
||||||
super();
|
super();
|
||||||
}
|
}
|
||||||
@ -66,27 +117,35 @@ public class LdapListUsersCmd extends BaseListCmd {
|
|||||||
_queryService = queryService;
|
_queryService = queryService;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* (as a check for isACloudstackUser is done) only non cloudstack users should be shown
|
||||||
|
* @param users a list of {@code LdapUser}s
|
||||||
|
* @return a (filtered?) list of user response objects
|
||||||
|
*/
|
||||||
private List<LdapUserResponse> createLdapUserResponse(final List<LdapUser> users) {
|
private List<LdapUserResponse> createLdapUserResponse(final List<LdapUser> users) {
|
||||||
final List<LdapUserResponse> ldapResponses = new ArrayList<LdapUserResponse>();
|
final List<LdapUserResponse> ldapResponses = new ArrayList<LdapUserResponse>();
|
||||||
for (final LdapUser user : users) {
|
for (final LdapUser user : users) {
|
||||||
if (getListType().equals("all") || !isACloudstackUser(user)) {
|
final LdapUserResponse ldapResponse = _ldapManager.createLdapUserResponse(user);
|
||||||
final LdapUserResponse ldapResponse = _ldapManager.createLdapUserResponse(user);
|
ldapResponse.setObjectName("LdapUser");
|
||||||
ldapResponse.setObjectName("LdapUser");
|
ldapResponses.add(ldapResponse);
|
||||||
ldapResponses.add(ldapResponse);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return ldapResponses;
|
return ldapResponses;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private List<UserResponse> cloudstackUsers = null;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void execute() throws ServerApiException {
|
public void execute() throws ServerApiException {
|
||||||
List<LdapUserResponse> ldapResponses = null;
|
cloudstackUsers = null;
|
||||||
|
List<LdapUserResponse> ldapResponses = new ArrayList<LdapUserResponse>();
|
||||||
final ListResponse<LdapUserResponse> response = new ListResponse<LdapUserResponse>();
|
final ListResponse<LdapUserResponse> response = new ListResponse<LdapUserResponse>();
|
||||||
try {
|
try {
|
||||||
final List<LdapUser> users = _ldapManager.getUsers(null);
|
final List<LdapUser> users = _ldapManager.getUsers(domainId);
|
||||||
ldapResponses = createLdapUserResponse(users);
|
ldapResponses = createLdapUserResponse(users);
|
||||||
|
// now filter and annotate
|
||||||
|
ldapResponses = applyUserFilter(ldapResponses);
|
||||||
} catch (final NoLdapUserMatchingQueryException ex) {
|
} catch (final NoLdapUserMatchingQueryException ex) {
|
||||||
ldapResponses = new ArrayList<LdapUserResponse>();
|
// ok, we'll make do with the empty list ldapResponses = new ArrayList<LdapUserResponse>();
|
||||||
} finally {
|
} finally {
|
||||||
response.setResponses(ldapResponses);
|
response.setResponses(ldapResponses);
|
||||||
response.setResponseName(getCommandName());
|
response.setResponseName(getCommandName());
|
||||||
@ -94,6 +153,43 @@ public class LdapListUsersCmd extends BaseListCmd {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* get a list of relevant cloudstack users, depending on the userFilter
|
||||||
|
*/
|
||||||
|
private List<UserResponse> getCloudstackUsers() {
|
||||||
|
if (cloudstackUsers == null) {
|
||||||
|
try {
|
||||||
|
cloudstackUsers = getUserFilter().getCloudstackUserList(this).getResponses();
|
||||||
|
} catch (IllegalArgumentException e) {
|
||||||
|
throw new CloudRuntimeException("error in program login; we are not filtering but still querying users to filter???", e);
|
||||||
|
}
|
||||||
|
traceUserList();
|
||||||
|
}
|
||||||
|
return cloudstackUsers;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void traceUserList() {
|
||||||
|
if(s_logger.isTraceEnabled()) {
|
||||||
|
StringBuilder users = new StringBuilder();
|
||||||
|
for (UserResponse user : cloudstackUsers) {
|
||||||
|
if (users.length()> 0) {
|
||||||
|
users.append(", ");
|
||||||
|
}
|
||||||
|
users.append(user.getUsername());
|
||||||
|
}
|
||||||
|
|
||||||
|
s_logger.trace(String.format("checking against %d cloudstackusers: %s.", this.cloudstackUsers.size(), users.toString()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<LdapUserResponse> applyUserFilter(List<LdapUserResponse> ldapResponses) {
|
||||||
|
if(s_logger.isTraceEnabled()) {
|
||||||
|
s_logger.trace(String.format("applying filter: %s or %s.", this.getListTypeString(), this.getUserFilter()));
|
||||||
|
}
|
||||||
|
List<LdapUserResponse> responseList = getUserFilter().filter(this,ldapResponses);
|
||||||
|
return responseList;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getCommandName() {
|
public String getCommandName() {
|
||||||
return s_name;
|
return s_name;
|
||||||
@ -104,20 +200,306 @@ public class LdapListUsersCmd extends BaseListCmd {
|
|||||||
return Account.ACCOUNT_ID_SYSTEM;
|
return Account.ACCOUNT_ID_SYSTEM;
|
||||||
}
|
}
|
||||||
|
|
||||||
private String getListType() {
|
String getListTypeString() {
|
||||||
return listType == null ? "all" : listType;
|
return listType == null ? "all" : listType;
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean isACloudstackUser(final LdapUser ldapUser) {
|
String getUserFilterString() {
|
||||||
final ListResponse<UserResponse> response = _queryService.searchForUsers(new ListUsersCmd());
|
return userFilter == null ? getListTypeString() == null ? "NoFilter" : getListTypeString().equals("all") ? "NoFilter" : "AnyDomain" : userFilter;
|
||||||
final List<UserResponse> cloudstackUsers = response.getResponses();
|
}
|
||||||
if (cloudstackUsers != null && cloudstackUsers.size() != 0) {
|
|
||||||
for (final UserResponse cloudstackUser : response.getResponses()) {
|
UserFilter getUserFilter() {
|
||||||
|
return UserFilter.fromString(getUserFilterString());
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean isACloudstackUser(final LdapUser ldapUser) {
|
||||||
|
boolean rc = false;
|
||||||
|
final List<UserResponse> cloudstackUsers = getCloudstackUsers();
|
||||||
|
if (cloudstackUsers != null) {
|
||||||
|
for (final UserResponse cloudstackUser : cloudstackUsers) {
|
||||||
if (ldapUser.getUsername().equals(cloudstackUser.getUsername())) {
|
if (ldapUser.getUsername().equals(cloudstackUser.getUsername())) {
|
||||||
|
if(s_logger.isTraceEnabled()) {
|
||||||
|
s_logger.trace(String.format("found user %s in cloudstack", ldapUser.getUsername()));
|
||||||
|
}
|
||||||
|
|
||||||
|
rc = true;
|
||||||
|
} else {
|
||||||
|
if(s_logger.isTraceEnabled()) {
|
||||||
|
s_logger.trace(String.format("ldap user %s does not match cloudstack user", ldapUser.getUsername(), cloudstackUser.getUsername()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean isACloudstackUser(final LdapUserResponse ldapUser) {
|
||||||
|
if(s_logger.isTraceEnabled()) {
|
||||||
|
s_logger.trace("checking response : " + ldapUser.toString());
|
||||||
|
}
|
||||||
|
final List<UserResponse> cloudstackUsers = getCloudstackUsers();
|
||||||
|
if (cloudstackUsers != null && cloudstackUsers.size() != 0) {
|
||||||
|
for (final UserResponse cloudstackUser : cloudstackUsers) {
|
||||||
|
if (ldapUser.getUsername().equals(cloudstackUser.getUsername())) {
|
||||||
|
if(s_logger.isTraceEnabled()) {
|
||||||
|
s_logger.trace(String.format("found user %s in cloudstack", ldapUser.getUsername()));
|
||||||
|
}
|
||||||
return true;
|
return true;
|
||||||
|
} else {
|
||||||
|
if(s_logger.isTraceEnabled()) {
|
||||||
|
s_logger.trace(String.format("ldap user %s does not match cloudstack user", ldapUser.getUsername(), cloudstackUser.getUsername()));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
/**
|
||||||
|
* typecheck for userfilter values and filter type dependend functionalities.
|
||||||
|
* This could have been in two switch statements elsewhere in the code.
|
||||||
|
* Arguably this is a cleaner solution.
|
||||||
|
*/
|
||||||
|
enum UserFilter {
|
||||||
|
NO_FILTER("NoFilter"){
|
||||||
|
@Override public List<LdapUserResponse> filter(LdapListUsersCmd cmd, List<LdapUserResponse> input) {
|
||||||
|
return cmd.filterNoFilter(input);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* in case of no filter we should find all users in the current domain for annotation.
|
||||||
|
*/
|
||||||
|
@Override public ListResponse<UserResponse> getCloudstackUserList(LdapListUsersCmd cmd) {
|
||||||
|
return cmd._queryService.searchForUsers(cmd.domainId,true);
|
||||||
|
|
||||||
|
}
|
||||||
|
},
|
||||||
|
LOCAL_DOMAIN("LocalDomain"){
|
||||||
|
@Override public List<LdapUserResponse> filter(LdapListUsersCmd cmd, List<LdapUserResponse> input) {
|
||||||
|
return cmd.filterLocalDomain(input);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* if we are filtering for local domain, only get users for the current domain
|
||||||
|
*/
|
||||||
|
@Override public ListResponse<UserResponse> getCloudstackUserList(LdapListUsersCmd cmd) {
|
||||||
|
return cmd._queryService.searchForUsers(cmd.domainId,false);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
ANY_DOMAIN("AnyDomain"){
|
||||||
|
@Override public List<LdapUserResponse> filter(LdapListUsersCmd cmd, List<LdapUserResponse> input) {
|
||||||
|
return cmd.filterAnyDomain(input);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* if we are filtering for any domain, get recursive all users for the root domain
|
||||||
|
*/
|
||||||
|
@Override public ListResponse<UserResponse> getCloudstackUserList(LdapListUsersCmd cmd) {
|
||||||
|
return cmd._queryService.searchForUsers(CallContext.current().getCallingAccount().getDomainId(), true);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
POTENTIAL_IMPORT("PotentialImport"){
|
||||||
|
@Override public List<LdapUserResponse> filter(LdapListUsersCmd cmd, List<LdapUserResponse> input) {
|
||||||
|
return cmd.filterPotentialImport(input);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* if we are filtering for potential imports,
|
||||||
|
* we are only looking for users in the linked domains/accounts,
|
||||||
|
* which is only relevant if we ask ldap users for this domain.
|
||||||
|
* So we are asking for all users in the current domain as well
|
||||||
|
*/
|
||||||
|
@Override public ListResponse<UserResponse> getCloudstackUserList(LdapListUsersCmd cmd) {
|
||||||
|
return cmd._queryService.searchForUsers(cmd.domainId,false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
private final String value;
|
||||||
|
|
||||||
|
UserFilter(String val) {
|
||||||
|
this.value = val;
|
||||||
|
}
|
||||||
|
|
||||||
|
public abstract List<LdapUserResponse> filter(LdapListUsersCmd cmd, List<LdapUserResponse> input);
|
||||||
|
|
||||||
|
public abstract ListResponse<UserResponse> getCloudstackUserList(LdapListUsersCmd cmd);
|
||||||
|
|
||||||
|
static UserFilter fromString(String val) {
|
||||||
|
if(NO_FILTER.toString().equalsIgnoreCase(val)) {
|
||||||
|
return NO_FILTER;
|
||||||
|
} else if (LOCAL_DOMAIN.toString().equalsIgnoreCase(val)) {
|
||||||
|
return LOCAL_DOMAIN;
|
||||||
|
} else if(ANY_DOMAIN.toString().equalsIgnoreCase(val)) {
|
||||||
|
return ANY_DOMAIN;
|
||||||
|
} else if(POTENTIAL_IMPORT.toString().equalsIgnoreCase(val)) {
|
||||||
|
return POTENTIAL_IMPORT;
|
||||||
|
} else {
|
||||||
|
throw new IllegalArgumentException(String.format("%s is not a legal 'UserFilter' value", val));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override public String toString() {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* no filtering but improve with annotation of source for existing ACS users
|
||||||
|
* @param input ldap response list of users
|
||||||
|
* @return unfiltered list of the input list of ldap users
|
||||||
|
*/
|
||||||
|
public List<LdapUserResponse> filterNoFilter(List<LdapUserResponse> input) {
|
||||||
|
if(s_logger.isTraceEnabled()) {
|
||||||
|
s_logger.trace("returning unfiltered list of ldap users");
|
||||||
|
}
|
||||||
|
annotateUserListWithSources(input);
|
||||||
|
return input;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* filter the list of ldap users. no users visible to the caller should be in the returned list
|
||||||
|
* @param input ldap response list of users
|
||||||
|
* @return a list of ldap users not already in ACS
|
||||||
|
*/
|
||||||
|
public List<LdapUserResponse> filterAnyDomain(List<LdapUserResponse> input) {
|
||||||
|
if(s_logger.isTraceEnabled()) {
|
||||||
|
s_logger.trace("filtering existing users");
|
||||||
|
}
|
||||||
|
final List<LdapUserResponse> ldapResponses = new ArrayList<LdapUserResponse>();
|
||||||
|
for (final LdapUserResponse user : input) {
|
||||||
|
if (isNotAlreadyImportedInTheCurrentDomain(user)) {
|
||||||
|
ldapResponses.add(user);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
annotateUserListWithSources(ldapResponses);
|
||||||
|
|
||||||
|
return ldapResponses;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return true unless the the user is imported in the specified cloudstack domain from LDAP
|
||||||
|
*/
|
||||||
|
private boolean isNotAlreadyImportedInTheCurrentDomain(LdapUserResponse user) {
|
||||||
|
UserResponse cloudstackUser = getCloudstackUser(user);
|
||||||
|
String domainId = getCurrentDomainId();
|
||||||
|
|
||||||
|
return cloudstackUser == null /*doesn't exist in cloudstack*/
|
||||||
|
|| ! (
|
||||||
|
cloudstackUser.getUserSource().equalsIgnoreCase(User.Source.LDAP.toString())
|
||||||
|
&& domainId.equals(cloudstackUser.getDomainId())); /* is from another source */
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* filter the list of ldap users. no users visible to the caller already in the domain specified should be in the returned list
|
||||||
|
* @param input ldap response list of users
|
||||||
|
* @return a list of ldap users not already in ACS
|
||||||
|
*/
|
||||||
|
public List<LdapUserResponse> filterLocalDomain(List<LdapUserResponse> input) {
|
||||||
|
if(s_logger.isTraceEnabled()) {
|
||||||
|
s_logger.trace("filtering local domain users");
|
||||||
|
}
|
||||||
|
final List<LdapUserResponse> ldapResponses = new ArrayList<LdapUserResponse>();
|
||||||
|
String domainId = getCurrentDomainId();
|
||||||
|
for (final LdapUserResponse user : input) {
|
||||||
|
UserResponse cloudstackUser = getCloudstackUser(user);
|
||||||
|
if (cloudstackUser == null /*doesn't exist in cloudstack*/
|
||||||
|
|| !domainId.equals(cloudstackUser.getDomainId()) /* doesn't exist in this domain */
|
||||||
|
|| !cloudstackUser.getUserSource().equalsIgnoreCase(User.Source.LDAP.toString()) /* is from another source */
|
||||||
|
) {
|
||||||
|
ldapResponses.add(user);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
annotateUserListWithSources(ldapResponses);
|
||||||
|
return ldapResponses;
|
||||||
|
}
|
||||||
|
|
||||||
|
private String getCurrentDomainId() {
|
||||||
|
String domainId = null;
|
||||||
|
if (this.domainId != null) {
|
||||||
|
Domain domain = _domainService.getDomain(this.domainId);
|
||||||
|
domainId = domain.getUuid();
|
||||||
|
} else {
|
||||||
|
final CallContext callContext = CallContext.current();
|
||||||
|
domainId = _domainService.getDomain(callContext.getCallingAccount().getDomainId()).getUuid();
|
||||||
|
}
|
||||||
|
return domainId;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param input a list of ldap users
|
||||||
|
* @return annotated list of the users of the input list, that will be automatically imported or synchronised
|
||||||
|
*/
|
||||||
|
public List<LdapUserResponse> filterPotentialImport(List<LdapUserResponse> input) {
|
||||||
|
if(s_logger.isTraceEnabled()) {
|
||||||
|
s_logger.trace("should be filtering potential imports!!!");
|
||||||
|
}
|
||||||
|
// functional possibility do not add only users not yet in cloudstack but include users that would be moved if they are so in ldap?
|
||||||
|
// this means if they are part of a account linked to an ldap group/ou
|
||||||
|
input.removeIf(ldapUser ->
|
||||||
|
(
|
||||||
|
(isACloudstackUser(ldapUser))
|
||||||
|
&& (getCloudstackUser(ldapUser).getUserSource().equalsIgnoreCase(User.Source.LDAP.toString()))
|
||||||
|
)
|
||||||
|
);
|
||||||
|
annotateUserListWithSources(input);
|
||||||
|
return input;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void annotateUserListWithSources(List<LdapUserResponse> input) {
|
||||||
|
for (final LdapUserResponse user : input) {
|
||||||
|
annotateCloudstackSource(user);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void annotateCloudstackSource(LdapUserResponse user) {
|
||||||
|
final UserResponse cloudstackUser = getCloudstackUser(user);
|
||||||
|
if (cloudstackUser != null) {
|
||||||
|
user.setUserSource(cloudstackUser.getUserSource());
|
||||||
|
} else {
|
||||||
|
user.setUserSource("");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private UserResponse getCloudstackUser(LdapUserResponse user) {
|
||||||
|
UserResponse returnObject = null;
|
||||||
|
final List<UserResponse> cloudstackUsers = getCloudstackUsers();
|
||||||
|
if (cloudstackUsers != null) {
|
||||||
|
for (final UserResponse cloudstackUser : cloudstackUsers) {
|
||||||
|
if (user.getUsername().equals(cloudstackUser.getUsername())) {
|
||||||
|
returnObject = cloudstackUser;
|
||||||
|
if (returnObject.getDomainId() == this.getCurrentDomainId()) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return returnObject;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void checkFilterMethodType(Type returnType) {
|
||||||
|
String msg = null;
|
||||||
|
if (returnType instanceof ParameterizedType) {
|
||||||
|
ParameterizedType type = (ParameterizedType) returnType;
|
||||||
|
if(type.getRawType().equals(List.class)) {
|
||||||
|
Type[] typeArguments = type.getActualTypeArguments();
|
||||||
|
if (typeArguments.length == 1) {
|
||||||
|
if (typeArguments[0].equals(LdapUserResponse.class)) {
|
||||||
|
// we're good'
|
||||||
|
} else {
|
||||||
|
msg = new String("list of return type contains " + typeArguments[0].getTypeName());
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
msg = String.format("type %s has to the wrong number of arguments", type.getRawType());
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
msg = String.format("type %s is not a List<>", type.getTypeName());
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
msg = new String("can't even begin to explain; review your method signature");
|
||||||
|
}
|
||||||
|
if(msg != null) {
|
||||||
|
throw new IllegalArgumentException(msg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@ -18,35 +18,41 @@ package org.apache.cloudstack.api.response;
|
|||||||
|
|
||||||
import com.google.gson.annotations.SerializedName;
|
import com.google.gson.annotations.SerializedName;
|
||||||
|
|
||||||
|
import org.apache.cloudstack.api.ApiConstants;
|
||||||
import org.apache.cloudstack.api.BaseResponse;
|
import org.apache.cloudstack.api.BaseResponse;
|
||||||
|
|
||||||
import com.cloud.serializer.Param;
|
import com.cloud.serializer.Param;
|
||||||
|
import org.apache.cloudstack.api.LdapConstants;
|
||||||
|
|
||||||
public class LdapUserResponse extends BaseResponse {
|
public class LdapUserResponse extends BaseResponse {
|
||||||
@SerializedName("email")
|
@SerializedName(ApiConstants.EMAIL)
|
||||||
@Param(description = "The user's email")
|
@Param(description = "The user's email")
|
||||||
private String email;
|
private String email;
|
||||||
|
|
||||||
@SerializedName("principal")
|
@SerializedName(LdapConstants.PRINCIPAL)
|
||||||
@Param(description = "The user's principle")
|
@Param(description = "The user's principle")
|
||||||
private String principal;
|
private String principal;
|
||||||
|
|
||||||
@SerializedName("firstname")
|
@SerializedName(ApiConstants.FIRSTNAME)
|
||||||
@Param(description = "The user's firstname")
|
@Param(description = "The user's firstname")
|
||||||
private String firstname;
|
private String firstname;
|
||||||
|
|
||||||
@SerializedName("lastname")
|
@SerializedName(ApiConstants.LASTNAME)
|
||||||
@Param(description = "The user's lastname")
|
@Param(description = "The user's lastname")
|
||||||
private String lastname;
|
private String lastname;
|
||||||
|
|
||||||
@SerializedName("username")
|
@SerializedName(ApiConstants.USERNAME)
|
||||||
@Param(description = "The user's username")
|
@Param(description = "The user's username")
|
||||||
private String username;
|
private String username;
|
||||||
|
|
||||||
@SerializedName("domain")
|
@SerializedName(ApiConstants.DOMAIN)
|
||||||
@Param(description = "The user's domain")
|
@Param(description = "The user's domain")
|
||||||
private String domain;
|
private String domain;
|
||||||
|
|
||||||
|
@SerializedName(ApiConstants.USER_CONFLICT_SOURCE)
|
||||||
|
@Param(description = "The authentication source for this user as known to the system or empty if the user is not yet in cloudstack.")
|
||||||
|
private String userSource;
|
||||||
|
|
||||||
public LdapUserResponse() {
|
public LdapUserResponse() {
|
||||||
super();
|
super();
|
||||||
}
|
}
|
||||||
@ -61,6 +67,11 @@ public class LdapUserResponse extends BaseResponse {
|
|||||||
this.domain = domain;
|
this.domain = domain;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public LdapUserResponse(final String username, final String email, final String firstname, final String lastname, final String principal, String domain, String userSource) {
|
||||||
|
this(username, email, firstname, lastname, principal, domain);
|
||||||
|
setUserSource(userSource);
|
||||||
|
}
|
||||||
|
|
||||||
public String getEmail() {
|
public String getEmail() {
|
||||||
return email;
|
return email;
|
||||||
}
|
}
|
||||||
@ -85,6 +96,10 @@ public class LdapUserResponse extends BaseResponse {
|
|||||||
return domain;
|
return domain;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public String getUserSource() {
|
||||||
|
return userSource;
|
||||||
|
}
|
||||||
|
|
||||||
public void setEmail(final String email) {
|
public void setEmail(final String email) {
|
||||||
this.email = email;
|
this.email = email;
|
||||||
}
|
}
|
||||||
@ -108,4 +123,67 @@ public class LdapUserResponse extends BaseResponse {
|
|||||||
public void setDomain(String domain) {
|
public void setDomain(String domain) {
|
||||||
this.domain = domain;
|
this.domain = domain;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void setUserSource(String userSource) {
|
||||||
|
this.userSource = userSource;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String toString() {
|
||||||
|
final String COLUMN = ": ";
|
||||||
|
final String COMMA = ", ";
|
||||||
|
StringBuilder selfRepresentation = new StringBuilder();
|
||||||
|
selfRepresentation.append(this.getClass().getName());
|
||||||
|
selfRepresentation.append('{');
|
||||||
|
boolean hascontent = false;
|
||||||
|
if (this.getUsername() != null) {
|
||||||
|
selfRepresentation.append(ApiConstants.USERNAME);
|
||||||
|
selfRepresentation.append(COLUMN);
|
||||||
|
selfRepresentation.append(this.getUsername());
|
||||||
|
hascontent = true;
|
||||||
|
}
|
||||||
|
if (this.getFirstname() != null) {
|
||||||
|
if(hascontent) selfRepresentation.append(COMMA);
|
||||||
|
selfRepresentation.append(ApiConstants.FIRSTNAME);
|
||||||
|
selfRepresentation.append(COLUMN);
|
||||||
|
selfRepresentation.append(this.getFirstname());
|
||||||
|
hascontent = true;
|
||||||
|
}
|
||||||
|
if (this.getLastname() != null) {
|
||||||
|
if(hascontent) selfRepresentation.append(COMMA);
|
||||||
|
selfRepresentation.append(ApiConstants.LASTNAME);
|
||||||
|
selfRepresentation.append(COLUMN);
|
||||||
|
selfRepresentation.append(this.getLastname());
|
||||||
|
hascontent = true;
|
||||||
|
}
|
||||||
|
if(this.getDomain() != null) {
|
||||||
|
if(hascontent) selfRepresentation.append(COMMA);
|
||||||
|
selfRepresentation.append(ApiConstants.DOMAIN);
|
||||||
|
selfRepresentation.append(COLUMN);
|
||||||
|
selfRepresentation.append(this.getDomain());
|
||||||
|
hascontent = true;
|
||||||
|
}
|
||||||
|
if (this.getEmail() != null) {
|
||||||
|
if(hascontent) selfRepresentation.append(COMMA);
|
||||||
|
selfRepresentation.append(ApiConstants.EMAIL);
|
||||||
|
selfRepresentation.append(COLUMN);
|
||||||
|
selfRepresentation.append(this.getEmail());
|
||||||
|
hascontent = true;
|
||||||
|
}
|
||||||
|
if (this.getPrincipal() != null) {
|
||||||
|
if(hascontent) selfRepresentation.append(COMMA);
|
||||||
|
selfRepresentation.append(LdapConstants.PRINCIPAL);
|
||||||
|
selfRepresentation.append(COLUMN);
|
||||||
|
selfRepresentation.append(this.getPrincipal());
|
||||||
|
hascontent = true;
|
||||||
|
}
|
||||||
|
if (this.getUserSource() != null) {
|
||||||
|
if (hascontent) selfRepresentation.append(COMMA);
|
||||||
|
selfRepresentation.append(ApiConstants.USER_CONFLICT_SOURCE);
|
||||||
|
selfRepresentation.append(COLUMN);
|
||||||
|
selfRepresentation.append(this.getUserSource());
|
||||||
|
}
|
||||||
|
selfRepresentation.append('}');
|
||||||
|
|
||||||
|
return selfRepresentation.toString();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@ -38,7 +38,7 @@ import com.cloud.utils.component.AdapterBase;
|
|||||||
import com.cloud.utils.exception.CloudRuntimeException;
|
import com.cloud.utils.exception.CloudRuntimeException;
|
||||||
|
|
||||||
public class LdapAuthenticator extends AdapterBase implements UserAuthenticator {
|
public class LdapAuthenticator extends AdapterBase implements UserAuthenticator {
|
||||||
private static final Logger s_logger = Logger.getLogger(LdapAuthenticator.class.getName());
|
private static final Logger LOGGER = Logger.getLogger(LdapAuthenticator.class.getName());
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
private LdapManager _ldapManager;
|
private LdapManager _ldapManager;
|
||||||
@ -61,32 +61,51 @@ public class LdapAuthenticator extends AdapterBase implements UserAuthenticator
|
|||||||
public Pair<Boolean, ActionOnFailedAuthentication> authenticate(final String username, final String password, final Long domainId, final Map<String, Object[]> requestParameters) {
|
public Pair<Boolean, ActionOnFailedAuthentication> authenticate(final String username, final String password, final Long domainId, final Map<String, Object[]> requestParameters) {
|
||||||
Pair<Boolean, ActionOnFailedAuthentication> rc = new Pair<Boolean, ActionOnFailedAuthentication>(false, null);
|
Pair<Boolean, ActionOnFailedAuthentication> rc = new Pair<Boolean, ActionOnFailedAuthentication>(false, null);
|
||||||
|
|
||||||
// TODO not allowing an empty password is a policy we shouldn't decide on. A private cloud may well want to allow this.
|
if (LOGGER.isDebugEnabled()) {
|
||||||
if (StringUtils.isEmpty(username) || StringUtils.isEmpty(password)) {
|
LOGGER.debug("Retrieving ldap user: " + username);
|
||||||
s_logger.debug("Username or Password cannot be empty");
|
|
||||||
return rc;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_ldapManager.isLdapEnabled()) {
|
// TODO not allowing an empty password is a policy we shouldn't decide on. A private cloud may well want to allow this.
|
||||||
final UserAccount user = _userAccountDao.getUserAccount(username, domainId);
|
if (!StringUtils.isEmpty(username) && !StringUtils.isEmpty(password)) {
|
||||||
List<LdapTrustMapVO> ldapTrustMapVOs = _ldapManager.getDomainLinkage(domainId);
|
if (_ldapManager.isLdapEnabled(domainId) || _ldapManager.isLdapEnabled()) {
|
||||||
if(ldapTrustMapVOs != null && ldapTrustMapVOs.size() > 0) {
|
if (LOGGER.isTraceEnabled()) {
|
||||||
if(ldapTrustMapVOs.size() == 1 && ldapTrustMapVOs.get(0).getAccountId() == 0) {
|
LOGGER.trace("LDAP is enabled in the ldapManager");
|
||||||
// We have a single mapping of a domain to an ldap group or ou
|
}
|
||||||
return authenticate(username, password, domainId, user, ldapTrustMapVOs.get(0));
|
final UserAccount user = _userAccountDao.getUserAccount(username, domainId);
|
||||||
} else {
|
if (user != null && ! User.Source.LDAP.equals(user.getSource())) {
|
||||||
// we are dealing with mapping of accounts in a domain to ldap groups
|
return rc;
|
||||||
return authenticate(username, password, domainId, user, ldapTrustMapVOs);
|
}
|
||||||
|
List<LdapTrustMapVO> ldapTrustMapVOs = getLdapTrustMapVOS(domainId);
|
||||||
|
if(ldapTrustMapVOs != null && ldapTrustMapVOs.size() > 0) {
|
||||||
|
if(ldapTrustMapVOs.size() == 1 && ldapTrustMapVOs.get(0).getAccountId() == 0) {
|
||||||
|
if (LOGGER.isTraceEnabled()) {
|
||||||
|
LOGGER.trace("We have a single mapping of a domain to an ldap group or ou");
|
||||||
|
}
|
||||||
|
rc = authenticate(username, password, domainId, user, ldapTrustMapVOs.get(0));
|
||||||
|
} else {
|
||||||
|
if (LOGGER.isTraceEnabled()) {
|
||||||
|
LOGGER.trace("we are dealing with mapping of accounts in a domain to ldap groups");
|
||||||
|
}
|
||||||
|
rc = authenticate(username, password, domainId, user, ldapTrustMapVOs);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (LOGGER.isTraceEnabled()) {
|
||||||
|
LOGGER.trace(String.format("'this' domain (%d) is not linked to ldap follow normal authentication", domainId));
|
||||||
|
}
|
||||||
|
rc = authenticate(username, password, domainId, user);
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
//domain is not linked to ldap follow normal authentication
|
|
||||||
return authenticate(username, password, domainId, user);
|
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
LOGGER.debug("Username or Password cannot be empty");
|
||||||
}
|
}
|
||||||
|
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private List<LdapTrustMapVO> getLdapTrustMapVOS(Long domainId) {
|
||||||
|
return _ldapManager.getDomainLinkage(domainId);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* checks if the user exists in ldap and create in cloudstack if needed.
|
* checks if the user exists in ldap and create in cloudstack if needed.
|
||||||
*
|
*
|
||||||
@ -97,13 +116,16 @@ public class LdapAuthenticator extends AdapterBase implements UserAuthenticator
|
|||||||
* @param ldapTrustMapVOs the trust mappings of accounts in the domain to ldap groups
|
* @param ldapTrustMapVOs the trust mappings of accounts in the domain to ldap groups
|
||||||
* @return false if the ldap user object does not exist, is not mapped to an account, is mapped to multiple accounts or if authenitication fails
|
* @return false if the ldap user object does not exist, is not mapped to an account, is mapped to multiple accounts or if authenitication fails
|
||||||
*/
|
*/
|
||||||
private Pair<Boolean, ActionOnFailedAuthentication> authenticate(String username, String password, Long domainId, UserAccount userAccount, List<LdapTrustMapVO> ldapTrustMapVOs) {
|
Pair<Boolean, ActionOnFailedAuthentication> authenticate(String username, String password, Long domainId, UserAccount userAccount, List<LdapTrustMapVO> ldapTrustMapVOs) {
|
||||||
Pair<Boolean, ActionOnFailedAuthentication> rc = new Pair<Boolean, ActionOnFailedAuthentication>(false, null);
|
Pair<Boolean, ActionOnFailedAuthentication> rc = new Pair<Boolean, ActionOnFailedAuthentication>(false, null);
|
||||||
try {
|
try {
|
||||||
LdapUser ldapUser = _ldapManager.getUser(username, domainId);
|
LdapUser ldapUser = _ldapManager.getUser(username, domainId);
|
||||||
List<String> memberships = ldapUser.getMemberships();
|
List<String> memberships = ldapUser.getMemberships();
|
||||||
|
tracelist("memberships for " + username, memberships);
|
||||||
List<String> mappedGroups = getMappedGroups(ldapTrustMapVOs);
|
List<String> mappedGroups = getMappedGroups(ldapTrustMapVOs);
|
||||||
|
tracelist("mappedgroups for " + username, mappedGroups);
|
||||||
mappedGroups.retainAll(memberships);
|
mappedGroups.retainAll(memberships);
|
||||||
|
tracelist("actual groups for " + username, mappedGroups);
|
||||||
// check membership, there must be only one match in this domain
|
// check membership, there must be only one match in this domain
|
||||||
if(ldapUser.isDisabled()) {
|
if(ldapUser.isDisabled()) {
|
||||||
logAndDisable(userAccount, "attempt to log on using disabled ldap user " + userAccount.getUsername(), false);
|
logAndDisable(userAccount, "attempt to log on using disabled ldap user " + userAccount.getUsername(), false);
|
||||||
@ -115,9 +137,16 @@ public class LdapAuthenticator extends AdapterBase implements UserAuthenticator
|
|||||||
// a valid ldap configured user exists
|
// a valid ldap configured user exists
|
||||||
LdapTrustMapVO mapping = _ldapManager.getLinkedLdapGroup(domainId,mappedGroups.get(0));
|
LdapTrustMapVO mapping = _ldapManager.getLinkedLdapGroup(domainId,mappedGroups.get(0));
|
||||||
// we could now assert that ldapTrustMapVOs.contains(mapping);
|
// we could now assert that ldapTrustMapVOs.contains(mapping);
|
||||||
// createUser in Account can only be done by account name not by account id
|
// createUser in Account can only be done by account name not by account id;
|
||||||
String accountName = _accountManager.getAccount(mapping.getAccountId()).getAccountName();
|
Account account = _accountManager.getAccount(mapping.getAccountId());
|
||||||
|
if(null == account) {
|
||||||
|
throw new CloudRuntimeException(String.format("account for user (%s) not found by id %d", username, mapping.getAccountId()));
|
||||||
|
}
|
||||||
|
String accountName = account.getAccountName();
|
||||||
rc.first(_ldapManager.canAuthenticate(ldapUser.getPrincipal(), password, domainId));
|
rc.first(_ldapManager.canAuthenticate(ldapUser.getPrincipal(), password, domainId));
|
||||||
|
if (! rc.first()) {
|
||||||
|
rc.second(ActionOnFailedAuthentication.INCREMENT_INCORRECT_LOGIN_ATTEMPT_COUNT);
|
||||||
|
}
|
||||||
// for security reasons we keep processing on faulty login attempt to not give a way information on userid existence
|
// for security reasons we keep processing on faulty login attempt to not give a way information on userid existence
|
||||||
if (userAccount == null) {
|
if (userAccount == null) {
|
||||||
// new user that is in ldap; authenticate and create
|
// new user that is in ldap; authenticate and create
|
||||||
@ -146,16 +175,29 @@ public class LdapAuthenticator extends AdapterBase implements UserAuthenticator
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (NoLdapUserMatchingQueryException e) {
|
} catch (NoLdapUserMatchingQueryException e) {
|
||||||
s_logger.debug(e.getMessage());
|
LOGGER.debug(e.getMessage());
|
||||||
disableUserInCloudStack(userAccount);
|
disableUserInCloudStack(userAccount);
|
||||||
}
|
}
|
||||||
|
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void tracelist(String msg, List<String> listToTrace) {
|
||||||
|
if (LOGGER.isTraceEnabled()) {
|
||||||
|
StringBuilder logMsg = new StringBuilder();
|
||||||
|
logMsg.append(msg);
|
||||||
|
logMsg.append(':');
|
||||||
|
for (String listMember : listToTrace) {
|
||||||
|
logMsg.append(' ');
|
||||||
|
logMsg.append(listMember);
|
||||||
|
}
|
||||||
|
LOGGER.trace(logMsg.toString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private void logAndDisable(UserAccount userAccount, String msg, boolean remove) {
|
private void logAndDisable(UserAccount userAccount, String msg, boolean remove) {
|
||||||
if (s_logger.isInfoEnabled()) {
|
if (LOGGER.isInfoEnabled()) {
|
||||||
s_logger.info(msg);
|
LOGGER.info(msg);
|
||||||
}
|
}
|
||||||
if(remove) {
|
if(remove) {
|
||||||
removeUserInCloudStack(userAccount);
|
removeUserInCloudStack(userAccount);
|
||||||
@ -164,7 +206,7 @@ public class LdapAuthenticator extends AdapterBase implements UserAuthenticator
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private List<String> getMappedGroups(List<LdapTrustMapVO> ldapTrustMapVOs) {
|
List<String> getMappedGroups(List<LdapTrustMapVO> ldapTrustMapVOs) {
|
||||||
List<String> groups = new ArrayList<>();
|
List<String> groups = new ArrayList<>();
|
||||||
for (LdapTrustMapVO vo : ldapTrustMapVOs) {
|
for (LdapTrustMapVO vo : ldapTrustMapVOs) {
|
||||||
groups.add(vo.getName());
|
groups.add(vo.getName());
|
||||||
@ -188,7 +230,9 @@ public class LdapAuthenticator extends AdapterBase implements UserAuthenticator
|
|||||||
final short accountType = ldapTrustMapVO.getAccountType();
|
final short accountType = ldapTrustMapVO.getAccountType();
|
||||||
processLdapUser(password, domainId, user, rc, ldapUser, accountType);
|
processLdapUser(password, domainId, user, rc, ldapUser, accountType);
|
||||||
} catch (NoLdapUserMatchingQueryException e) {
|
} catch (NoLdapUserMatchingQueryException e) {
|
||||||
s_logger.debug(e.getMessage());
|
LOGGER.debug(e.getMessage());
|
||||||
|
// no user in ldap ==>> disable user in cloudstack
|
||||||
|
disableUserInCloudStack(user);
|
||||||
}
|
}
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
@ -229,12 +273,16 @@ public class LdapAuthenticator extends AdapterBase implements UserAuthenticator
|
|||||||
if(!ldapUser.isDisabled()) {
|
if(!ldapUser.isDisabled()) {
|
||||||
result = _ldapManager.canAuthenticate(ldapUser.getPrincipal(), password, domainId);
|
result = _ldapManager.canAuthenticate(ldapUser.getPrincipal(), password, domainId);
|
||||||
} else {
|
} else {
|
||||||
s_logger.debug("user with principal "+ ldapUser.getPrincipal() + " is disabled in ldap");
|
LOGGER.debug("user with principal "+ ldapUser.getPrincipal() + " is disabled in ldap");
|
||||||
}
|
}
|
||||||
} catch (NoLdapUserMatchingQueryException e) {
|
} catch (NoLdapUserMatchingQueryException e) {
|
||||||
s_logger.debug(e.getMessage());
|
LOGGER.debug(e.getMessage());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return processResultAndAction(user, result);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Pair<Boolean, ActionOnFailedAuthentication> processResultAndAction(UserAccount user, boolean result) {
|
||||||
return (!result && user != null) ?
|
return (!result && user != null) ?
|
||||||
new Pair<Boolean, ActionOnFailedAuthentication>(result, ActionOnFailedAuthentication.INCREMENT_INCORRECT_LOGIN_ATTEMPT_COUNT):
|
new Pair<Boolean, ActionOnFailedAuthentication>(result, ActionOnFailedAuthentication.INCREMENT_INCORRECT_LOGIN_ATTEMPT_COUNT):
|
||||||
new Pair<Boolean, ActionOnFailedAuthentication>(result, null);
|
new Pair<Boolean, ActionOnFailedAuthentication>(result, null);
|
||||||
|
|||||||
@ -38,7 +38,6 @@ public interface LdapManager extends PluggableService {
|
|||||||
|
|
||||||
LdapConfigurationResponse addConfiguration(final LdapAddConfigurationCmd cmd) throws InvalidParameterValueException;
|
LdapConfigurationResponse addConfiguration(final LdapAddConfigurationCmd cmd) throws InvalidParameterValueException;
|
||||||
|
|
||||||
@Deprecated
|
|
||||||
LdapConfigurationResponse addConfiguration(String hostname, int port, Long domainId) throws InvalidParameterValueException;
|
LdapConfigurationResponse addConfiguration(String hostname, int port, Long domainId) throws InvalidParameterValueException;
|
||||||
|
|
||||||
boolean canAuthenticate(String principal, String password, final Long domainId);
|
boolean canAuthenticate(String principal, String password, final Long domainId);
|
||||||
@ -62,6 +61,8 @@ public interface LdapManager extends PluggableService {
|
|||||||
|
|
||||||
boolean isLdapEnabled();
|
boolean isLdapEnabled();
|
||||||
|
|
||||||
|
boolean isLdapEnabled(long domainId);
|
||||||
|
|
||||||
Pair<List<? extends LdapConfigurationVO>, Integer> listConfigurations(LdapListConfigurationCmd cmd);
|
Pair<List<? extends LdapConfigurationVO>, Integer> listConfigurations(LdapListConfigurationCmd cmd);
|
||||||
|
|
||||||
List<LdapUser> searchUsers(String query) throws NoLdapUserMatchingQueryException;
|
List<LdapUser> searchUsers(String query) throws NoLdapUserMatchingQueryException;
|
||||||
|
|||||||
@ -25,6 +25,7 @@ import javax.naming.NamingException;
|
|||||||
import javax.naming.ldap.LdapContext;
|
import javax.naming.ldap.LdapContext;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
|
|
||||||
|
import com.cloud.utils.exception.CloudRuntimeException;
|
||||||
import org.apache.cloudstack.api.LdapValidator;
|
import org.apache.cloudstack.api.LdapValidator;
|
||||||
import org.apache.cloudstack.api.command.LDAPConfigCmd;
|
import org.apache.cloudstack.api.command.LDAPConfigCmd;
|
||||||
import org.apache.cloudstack.api.command.LDAPRemoveCmd;
|
import org.apache.cloudstack.api.command.LDAPRemoveCmd;
|
||||||
@ -57,7 +58,7 @@ import com.cloud.utils.Pair;
|
|||||||
|
|
||||||
@Component
|
@Component
|
||||||
public class LdapManagerImpl implements LdapManager, LdapValidator {
|
public class LdapManagerImpl implements LdapManager, LdapValidator {
|
||||||
private static final Logger s_logger = Logger.getLogger(LdapManagerImpl.class.getName());
|
private static final Logger LOGGER = Logger.getLogger(LdapManagerImpl.class.getName());
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
private LdapConfigurationDao _ldapConfigurationDao;
|
private LdapConfigurationDao _ldapConfigurationDao;
|
||||||
@ -79,14 +80,13 @@ public class LdapManagerImpl implements LdapManager, LdapValidator {
|
|||||||
@Inject
|
@Inject
|
||||||
LdapTrustMapDao _ldapTrustMapDao;
|
LdapTrustMapDao _ldapTrustMapDao;
|
||||||
|
|
||||||
|
|
||||||
public LdapManagerImpl() {
|
public LdapManagerImpl() {
|
||||||
super();
|
super();
|
||||||
}
|
}
|
||||||
|
|
||||||
public LdapManagerImpl(final LdapConfigurationDao ldapConfigurationDao, final LdapContextFactory ldapContextFactory, final LdapUserManagerFactory ldapUserManagerFactory,
|
public LdapManagerImpl(final LdapConfigurationDao ldapConfigurationDao, final LdapContextFactory ldapContextFactory, final LdapUserManagerFactory ldapUserManagerFactory,
|
||||||
final LdapConfiguration ldapConfiguration) {
|
final LdapConfiguration ldapConfiguration) {
|
||||||
super();
|
this();
|
||||||
_ldapConfigurationDao = ldapConfigurationDao;
|
_ldapConfigurationDao = ldapConfigurationDao;
|
||||||
_ldapContextFactory = ldapContextFactory;
|
_ldapContextFactory = ldapContextFactory;
|
||||||
_ldapUserManagerFactory = ldapUserManagerFactory;
|
_ldapUserManagerFactory = ldapUserManagerFactory;
|
||||||
@ -118,10 +118,10 @@ public class LdapManagerImpl implements LdapManager, LdapValidator {
|
|||||||
context = _ldapContextFactory.createBindContext(providerUrl,domainId);
|
context = _ldapContextFactory.createBindContext(providerUrl,domainId);
|
||||||
configuration = new LdapConfigurationVO(hostname, port, domainId);
|
configuration = new LdapConfigurationVO(hostname, port, domainId);
|
||||||
_ldapConfigurationDao.persist(configuration);
|
_ldapConfigurationDao.persist(configuration);
|
||||||
s_logger.info("Added new ldap server with url: " + providerUrl + (domainId == null ? "": " for domain " + domainId));
|
LOGGER.info("Added new ldap server with url: " + providerUrl + (domainId == null ? "": " for domain " + domainId));
|
||||||
return createLdapConfigurationResponse(configuration);
|
return createLdapConfigurationResponse(configuration);
|
||||||
} catch (NamingException | IOException e) {
|
} catch (NamingException | IOException e) {
|
||||||
s_logger.debug("NamingException while doing an LDAP bind", e);
|
LOGGER.debug("NamingException while doing an LDAP bind", e);
|
||||||
throw new InvalidParameterValueException("Unable to bind to the given LDAP server");
|
throw new InvalidParameterValueException("Unable to bind to the given LDAP server");
|
||||||
} finally {
|
} finally {
|
||||||
closeContext(context);
|
closeContext(context);
|
||||||
@ -142,12 +142,15 @@ public class LdapManagerImpl implements LdapManager, LdapValidator {
|
|||||||
public boolean canAuthenticate(final String principal, final String password, final Long domainId) {
|
public boolean canAuthenticate(final String principal, final String password, final Long domainId) {
|
||||||
try {
|
try {
|
||||||
// TODO return the right account for this user
|
// TODO return the right account for this user
|
||||||
final LdapContext context = _ldapContextFactory.createUserContext(principal, password,domainId);
|
final LdapContext context = _ldapContextFactory.createUserContext(principal, password, domainId);
|
||||||
closeContext(context);
|
closeContext(context);
|
||||||
|
if(LOGGER.isTraceEnabled()) {
|
||||||
|
LOGGER.trace(String.format("User(%s) authenticated for domain(%s)", principal, domainId));
|
||||||
|
}
|
||||||
return true;
|
return true;
|
||||||
} catch (NamingException | IOException e) {
|
} catch (NamingException | IOException e) {/* AuthenticationException is caught as NamingException */
|
||||||
s_logger.debug("Exception while doing an LDAP bind for user "+" "+principal, e);
|
LOGGER.debug("Exception while doing an LDAP bind for user "+" "+principal, e);
|
||||||
s_logger.info("Failed to authenticate user: " + principal + ". incorrect password.");
|
LOGGER.info("Failed to authenticate user: " + principal + ". incorrect password.");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -158,7 +161,7 @@ public class LdapManagerImpl implements LdapManager, LdapValidator {
|
|||||||
context.close();
|
context.close();
|
||||||
}
|
}
|
||||||
} catch (final NamingException e) {
|
} catch (final NamingException e) {
|
||||||
s_logger.warn(e.getMessage(), e);
|
LOGGER.warn(e.getMessage(), e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -166,7 +169,10 @@ public class LdapManagerImpl implements LdapManager, LdapValidator {
|
|||||||
public LdapConfigurationResponse createLdapConfigurationResponse(final LdapConfigurationVO configuration) {
|
public LdapConfigurationResponse createLdapConfigurationResponse(final LdapConfigurationVO configuration) {
|
||||||
String domainUuid = null;
|
String domainUuid = null;
|
||||||
if(configuration.getDomainId() != null) {
|
if(configuration.getDomainId() != null) {
|
||||||
domainUuid = domainDao.findById(configuration.getDomainId()).getUuid();
|
DomainVO domain = domainDao.findById(configuration.getDomainId());
|
||||||
|
if (domain != null) {
|
||||||
|
domainUuid = domain.getUuid();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return new LdapConfigurationResponse(configuration.getHostname(), configuration.getPort(), domainUuid);
|
return new LdapConfigurationResponse(configuration.getHostname(), configuration.getPort(), domainUuid);
|
||||||
}
|
}
|
||||||
@ -199,7 +205,7 @@ public class LdapManagerImpl implements LdapManager, LdapValidator {
|
|||||||
throw new InvalidParameterValueException("Cannot find configuration with hostname " + hostname);
|
throw new InvalidParameterValueException("Cannot find configuration with hostname " + hostname);
|
||||||
} else {
|
} else {
|
||||||
_ldapConfigurationDao.remove(configuration.getId());
|
_ldapConfigurationDao.remove(configuration.getId());
|
||||||
s_logger.info("Removed ldap server with url: " + hostname + ':' + port + (domainId == null ? "" : " for domain id " + domainId));
|
LOGGER.info("Removed ldap server with url: " + hostname + ':' + port + (domainId == null ? "" : " for domain id " + domainId));
|
||||||
return createLdapConfigurationResponse(configuration);
|
return createLdapConfigurationResponse(configuration);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -231,7 +237,7 @@ public class LdapManagerImpl implements LdapManager, LdapValidator {
|
|||||||
return _ldapUserManagerFactory.getInstance(_ldapConfiguration.getLdapProvider(null)).getUser(escapedUsername, context, domainId);
|
return _ldapUserManagerFactory.getInstance(_ldapConfiguration.getLdapProvider(null)).getUser(escapedUsername, context, domainId);
|
||||||
|
|
||||||
} catch (NamingException | IOException e) {
|
} catch (NamingException | IOException e) {
|
||||||
s_logger.debug("ldap Exception: ",e);
|
LOGGER.debug("ldap Exception: ",e);
|
||||||
throw new NoLdapUserMatchingQueryException("No Ldap User found for username: "+username);
|
throw new NoLdapUserMatchingQueryException("No Ldap User found for username: "+username);
|
||||||
} finally {
|
} finally {
|
||||||
closeContext(context);
|
closeContext(context);
|
||||||
@ -244,9 +250,15 @@ public class LdapManagerImpl implements LdapManager, LdapValidator {
|
|||||||
try {
|
try {
|
||||||
context = _ldapContextFactory.createBindContext(domainId);
|
context = _ldapContextFactory.createBindContext(domainId);
|
||||||
final String escapedUsername = LdapUtils.escapeLDAPSearchFilter(username);
|
final String escapedUsername = LdapUtils.escapeLDAPSearchFilter(username);
|
||||||
return _ldapUserManagerFactory.getInstance(_ldapConfiguration.getLdapProvider(null)).getUser(escapedUsername, type, name, context, domainId);
|
LdapUserManager.Provider ldapProvider = _ldapConfiguration.getLdapProvider(domainId);
|
||||||
|
if (ldapProvider == null) {
|
||||||
|
// feeble second attempt?
|
||||||
|
ldapProvider = _ldapConfiguration.getLdapProvider(null);
|
||||||
|
}
|
||||||
|
LdapUserManager userManagerFactory = _ldapUserManagerFactory.getInstance(ldapProvider);
|
||||||
|
return userManagerFactory.getUser(escapedUsername, type, name, context, domainId);
|
||||||
} catch (NamingException | IOException e) {
|
} catch (NamingException | IOException e) {
|
||||||
s_logger.debug("ldap Exception: ",e);
|
LOGGER.debug("ldap Exception: ",e);
|
||||||
throw new NoLdapUserMatchingQueryException("No Ldap User found for username: "+username + " in group: " + name + " of type: " + type);
|
throw new NoLdapUserMatchingQueryException("No Ldap User found for username: "+username + " in group: " + name + " of type: " + type);
|
||||||
} finally {
|
} finally {
|
||||||
closeContext(context);
|
closeContext(context);
|
||||||
@ -260,7 +272,7 @@ public class LdapManagerImpl implements LdapManager, LdapValidator {
|
|||||||
context = _ldapContextFactory.createBindContext(domainId);
|
context = _ldapContextFactory.createBindContext(domainId);
|
||||||
return _ldapUserManagerFactory.getInstance(_ldapConfiguration.getLdapProvider(domainId)).getUsers(context, domainId);
|
return _ldapUserManagerFactory.getInstance(_ldapConfiguration.getLdapProvider(domainId)).getUsers(context, domainId);
|
||||||
} catch (NamingException | IOException e) {
|
} catch (NamingException | IOException e) {
|
||||||
s_logger.debug("ldap Exception: ",e);
|
LOGGER.debug("ldap Exception: ",e);
|
||||||
throw new NoLdapUserMatchingQueryException("*");
|
throw new NoLdapUserMatchingQueryException("*");
|
||||||
} finally {
|
} finally {
|
||||||
closeContext(context);
|
closeContext(context);
|
||||||
@ -274,7 +286,7 @@ public class LdapManagerImpl implements LdapManager, LdapValidator {
|
|||||||
context = _ldapContextFactory.createBindContext(domainId);
|
context = _ldapContextFactory.createBindContext(domainId);
|
||||||
return _ldapUserManagerFactory.getInstance(_ldapConfiguration.getLdapProvider(domainId)).getUsersInGroup(groupName, context, domainId);
|
return _ldapUserManagerFactory.getInstance(_ldapConfiguration.getLdapProvider(domainId)).getUsersInGroup(groupName, context, domainId);
|
||||||
} catch (NamingException | IOException e) {
|
} catch (NamingException | IOException e) {
|
||||||
s_logger.debug("ldap NamingException: ",e);
|
LOGGER.debug("ldap NamingException: ",e);
|
||||||
throw new NoLdapUserMatchingQueryException("groupName=" + groupName);
|
throw new NoLdapUserMatchingQueryException("groupName=" + groupName);
|
||||||
} finally {
|
} finally {
|
||||||
closeContext(context);
|
closeContext(context);
|
||||||
@ -286,6 +298,13 @@ public class LdapManagerImpl implements LdapManager, LdapValidator {
|
|||||||
return listConfigurations(new LdapListConfigurationCmd(this)).second() > 0;
|
return listConfigurations(new LdapListConfigurationCmd(this)).second() > 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isLdapEnabled(long domainId) {
|
||||||
|
LdapListConfigurationCmd cmd = new LdapListConfigurationCmd(this);
|
||||||
|
cmd.setDomainId(domainId);
|
||||||
|
return listConfigurations(cmd).second() > 0;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Pair<List<? extends LdapConfigurationVO>, Integer> listConfigurations(final LdapListConfigurationCmd cmd) {
|
public Pair<List<? extends LdapConfigurationVO>, Integer> listConfigurations(final LdapListConfigurationCmd cmd) {
|
||||||
final String hostname = cmd.getHostname();
|
final String hostname = cmd.getHostname();
|
||||||
@ -304,7 +323,7 @@ public class LdapManagerImpl implements LdapManager, LdapValidator {
|
|||||||
final String escapedUsername = LdapUtils.escapeLDAPSearchFilter(username);
|
final String escapedUsername = LdapUtils.escapeLDAPSearchFilter(username);
|
||||||
return _ldapUserManagerFactory.getInstance(_ldapConfiguration.getLdapProvider(null)).getUsers("*" + escapedUsername + "*", context, null);
|
return _ldapUserManagerFactory.getInstance(_ldapConfiguration.getLdapProvider(null)).getUsers("*" + escapedUsername + "*", context, null);
|
||||||
} catch (NamingException | IOException e) {
|
} catch (NamingException | IOException e) {
|
||||||
s_logger.debug("ldap Exception: ",e);
|
LOGGER.debug("ldap Exception: ",e);
|
||||||
throw new NoLdapUserMatchingQueryException(username);
|
throw new NoLdapUserMatchingQueryException(username);
|
||||||
} finally {
|
} finally {
|
||||||
closeContext(context);
|
closeContext(context);
|
||||||
@ -313,9 +332,13 @@ public class LdapManagerImpl implements LdapManager, LdapValidator {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public LinkDomainToLdapResponse linkDomainToLdap(LinkDomainToLdapCmd cmd) {
|
public LinkDomainToLdapResponse linkDomainToLdap(LinkDomainToLdapCmd cmd) {
|
||||||
Validate.isTrue(_ldapConfiguration.getBaseDn(cmd.getDomainId()) == null, "can not link a domain unless a basedn is configured for it.");
|
final Long domainId = cmd.getDomainId();
|
||||||
Validate.notEmpty(cmd.getLdapDomain(), "ldapDomain cannot be empty, please supply a GROUP or OU name");
|
final String baseDn = _ldapConfiguration.getBaseDn(domainId);
|
||||||
return linkDomainToLdap(cmd.getDomainId(),cmd.getType(),cmd.getLdapDomain(),cmd.getAccountType());
|
final String ldapDomain = cmd.getLdapDomain();
|
||||||
|
|
||||||
|
Validate.isTrue(baseDn != null, String.format("can not link a domain (with id = %d) unless a basedn (%s) is configured for it.", domainId, baseDn));
|
||||||
|
Validate.notEmpty(ldapDomain, "ldapDomain cannot be empty, please supply a GROUP or OU name");
|
||||||
|
return linkDomainToLdap(cmd.getDomainId(),cmd.getType(), ldapDomain,cmd.getAccountType());
|
||||||
}
|
}
|
||||||
|
|
||||||
private LinkDomainToLdapResponse linkDomainToLdap(Long domainId, String type, String name, short accountType) {
|
private LinkDomainToLdapResponse linkDomainToLdap(Long domainId, String type, String name, short accountType) {
|
||||||
@ -329,7 +352,7 @@ public class LdapManagerImpl implements LdapManager, LdapValidator {
|
|||||||
DomainVO domain = domainDao.findById(vo.getDomainId());
|
DomainVO domain = domainDao.findById(vo.getDomainId());
|
||||||
String domainUuid = "<unknown>";
|
String domainUuid = "<unknown>";
|
||||||
if (domain == null) {
|
if (domain == null) {
|
||||||
s_logger.error("no domain in database for id " + vo.getDomainId());
|
LOGGER.error("no domain in database for id " + vo.getDomainId());
|
||||||
} else {
|
} else {
|
||||||
domainUuid = domain.getUuid();
|
domainUuid = domain.getUuid();
|
||||||
}
|
}
|
||||||
@ -371,12 +394,14 @@ public class LdapManagerImpl implements LdapManager, LdapValidator {
|
|||||||
account = new AccountVO(cmd.getAccountName(), cmd.getDomainId(), null, cmd.getAccountType(), UUID.randomUUID().toString());
|
account = new AccountVO(cmd.getAccountName(), cmd.getDomainId(), null, cmd.getAccountType(), UUID.randomUUID().toString());
|
||||||
accountDao.persist((AccountVO)account);
|
accountDao.persist((AccountVO)account);
|
||||||
}
|
}
|
||||||
|
|
||||||
Long accountId = account.getAccountId();
|
Long accountId = account.getAccountId();
|
||||||
|
clearOldAccountMapping(cmd);
|
||||||
LdapTrustMapVO vo = _ldapTrustMapDao.persist(new LdapTrustMapVO(cmd.getDomainId(), linkType, cmd.getLdapDomain(), cmd.getAccountType(), accountId));
|
LdapTrustMapVO vo = _ldapTrustMapDao.persist(new LdapTrustMapVO(cmd.getDomainId(), linkType, cmd.getLdapDomain(), cmd.getAccountType(), accountId));
|
||||||
DomainVO domain = domainDao.findById(vo.getDomainId());
|
DomainVO domain = domainDao.findById(vo.getDomainId());
|
||||||
String domainUuid = "<unknown>";
|
String domainUuid = "<unknown>";
|
||||||
if (domain == null) {
|
if (domain == null) {
|
||||||
s_logger.error("no domain in database for id " + vo.getDomainId());
|
LOGGER.error("no domain in database for id " + vo.getDomainId());
|
||||||
} else {
|
} else {
|
||||||
domainUuid = domain.getUuid();
|
domainUuid = domain.getUuid();
|
||||||
}
|
}
|
||||||
@ -384,4 +409,29 @@ public class LdapManagerImpl implements LdapManager, LdapValidator {
|
|||||||
LinkAccountToLdapResponse response = new LinkAccountToLdapResponse(domainUuid, vo.getType().toString(), vo.getName(), vo.getAccountType(), account.getUuid(), cmd.getAccountName());
|
LinkAccountToLdapResponse response = new LinkAccountToLdapResponse(domainUuid, vo.getType().toString(), vo.getName(), vo.getAccountType(), account.getUuid(), cmd.getAccountName());
|
||||||
return response;
|
return response;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void clearOldAccountMapping(LinkAccountToLdapCmd cmd) {
|
||||||
|
// first find if exists log warning and update
|
||||||
|
LdapTrustMapVO oldVo = _ldapTrustMapDao.findGroupInDomain(cmd.getDomainId(), cmd.getLdapDomain());
|
||||||
|
if(oldVo != null) {
|
||||||
|
// deal with edge cases, i.e. check if the old account is indeed deleted etc.
|
||||||
|
if (oldVo.getAccountId() != 0l) {
|
||||||
|
AccountVO oldAcount = accountDao.findByIdIncludingRemoved(oldVo.getAccountId());
|
||||||
|
String msg = String.format("group %s is mapped to account %d in the current domain (%s)", cmd.getLdapDomain(), oldVo.getAccountId(), cmd.getDomainId());
|
||||||
|
if (null == oldAcount.getRemoved()) {
|
||||||
|
msg += ", delete the old map before mapping a new account to the same group.";
|
||||||
|
LOGGER.error(msg);
|
||||||
|
throw new CloudRuntimeException(msg);
|
||||||
|
} else {
|
||||||
|
msg += ", the old map is deleted.";
|
||||||
|
LOGGER.warn(msg);
|
||||||
|
_ldapTrustMapDao.expunge(oldVo.getId());
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
String msg = String.format("group %s is mapped to the current domain (%s) for autoimport and can not be used for autosync", cmd.getLdapDomain(), cmd.getDomainId());
|
||||||
|
LOGGER.error(msg);
|
||||||
|
throw new CloudRuntimeException(msg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -32,7 +32,7 @@ public class LdapUserManagerFactory implements ApplicationContextAware {
|
|||||||
|
|
||||||
public static final Logger s_logger = Logger.getLogger(LdapUserManagerFactory.class.getName());
|
public static final Logger s_logger = Logger.getLogger(LdapUserManagerFactory.class.getName());
|
||||||
|
|
||||||
private static Map<LdapUserManager.Provider, LdapUserManager> ldapUserManagerMap = new HashMap<>();
|
static Map<LdapUserManager.Provider, LdapUserManager> ldapUserManagerMap = new HashMap<>();
|
||||||
|
|
||||||
private ApplicationContext applicationCtx;
|
private ApplicationContext applicationCtx;
|
||||||
|
|
||||||
|
|||||||
@ -33,16 +33,20 @@ import javax.naming.ldap.LdapContext;
|
|||||||
import javax.naming.ldap.PagedResultsControl;
|
import javax.naming.ldap.PagedResultsControl;
|
||||||
import javax.naming.ldap.PagedResultsResponseControl;
|
import javax.naming.ldap.PagedResultsResponseControl;
|
||||||
|
|
||||||
|
import org.apache.cloudstack.ldap.dao.LdapTrustMapDao;
|
||||||
import org.apache.commons.collections.CollectionUtils;
|
import org.apache.commons.collections.CollectionUtils;
|
||||||
import org.apache.commons.lang.StringUtils;
|
import org.apache.commons.lang.StringUtils;
|
||||||
import org.apache.log4j.Logger;
|
import org.apache.log4j.Logger;
|
||||||
|
|
||||||
public class OpenLdapUserManagerImpl implements LdapUserManager {
|
public class OpenLdapUserManagerImpl implements LdapUserManager {
|
||||||
private static final Logger s_logger = Logger.getLogger(OpenLdapUserManagerImpl.class.getName());
|
private static final Logger LOGGER = Logger.getLogger(OpenLdapUserManagerImpl.class.getName());
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
protected LdapConfiguration _ldapConfiguration;
|
protected LdapConfiguration _ldapConfiguration;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
LdapTrustMapDao _ldapTrustMapDao;
|
||||||
|
|
||||||
public OpenLdapUserManagerImpl() {
|
public OpenLdapUserManagerImpl() {
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -82,25 +86,62 @@ public class OpenLdapUserManagerImpl implements LdapUserManager {
|
|||||||
usernameFilter.append((username == null ? "*" : username));
|
usernameFilter.append((username == null ? "*" : username));
|
||||||
usernameFilter.append(")");
|
usernameFilter.append(")");
|
||||||
|
|
||||||
final StringBuilder memberOfFilter = new StringBuilder();
|
String memberOfAttribute = _ldapConfiguration.getUserMemberOfAttribute(domainId);
|
||||||
if (_ldapConfiguration.getSearchGroupPrinciple(domainId) != null) {
|
StringBuilder ldapGroupsFilter = new StringBuilder();
|
||||||
if(s_logger.isDebugEnabled()) {
|
// this should get the trustmaps for this domain
|
||||||
s_logger.debug("adding search filter for '" + _ldapConfiguration.getSearchGroupPrinciple(domainId) +
|
List<String> ldapGroups = getMappedLdapGroups(domainId);
|
||||||
"', using " + _ldapConfiguration.getUserMemberOfAttribute(domainId));
|
if (null != ldapGroups && ldapGroups.size() > 0) {
|
||||||
|
ldapGroupsFilter.append("(|");
|
||||||
|
for (String ldapGroup : ldapGroups) {
|
||||||
|
ldapGroupsFilter.append(getMemberOfGroupString(ldapGroup, memberOfAttribute));
|
||||||
}
|
}
|
||||||
memberOfFilter.append("(" + _ldapConfiguration.getUserMemberOfAttribute(domainId) + "=");
|
ldapGroupsFilter.append(')');
|
||||||
memberOfFilter.append(_ldapConfiguration.getSearchGroupPrinciple(domainId));
|
}
|
||||||
memberOfFilter.append(")");
|
// make sure only users in the principle group are retrieved
|
||||||
|
String pricipleGroup = _ldapConfiguration.getSearchGroupPrinciple(domainId);
|
||||||
|
final StringBuilder principleGroupFilter = new StringBuilder();
|
||||||
|
if (null != pricipleGroup) {
|
||||||
|
principleGroupFilter.append(getMemberOfGroupString(pricipleGroup, memberOfAttribute));
|
||||||
}
|
}
|
||||||
|
|
||||||
final StringBuilder result = new StringBuilder();
|
final StringBuilder result = new StringBuilder();
|
||||||
result.append("(&");
|
result.append("(&");
|
||||||
result.append(userObjectFilter);
|
result.append(userObjectFilter);
|
||||||
result.append(usernameFilter);
|
result.append(usernameFilter);
|
||||||
result.append(memberOfFilter);
|
result.append(ldapGroupsFilter);
|
||||||
|
result.append(principleGroupFilter);
|
||||||
result.append(")");
|
result.append(")");
|
||||||
|
|
||||||
return result.toString();
|
String returnString = result.toString();
|
||||||
|
if (LOGGER.isTraceEnabled()) {
|
||||||
|
LOGGER.trace("constructed ldap query: " + returnString);
|
||||||
|
}
|
||||||
|
return returnString;
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<String> getMappedLdapGroups(Long domainId) {
|
||||||
|
List <String> ldapGroups = new ArrayList<>();
|
||||||
|
// first get the trustmaps
|
||||||
|
if (null != domainId) {
|
||||||
|
for (LdapTrustMapVO trustMap : _ldapTrustMapDao.searchByDomainId(domainId)) {
|
||||||
|
// then retrieve the string from it
|
||||||
|
ldapGroups.add(trustMap.getName());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ldapGroups;
|
||||||
|
}
|
||||||
|
|
||||||
|
private String getMemberOfGroupString(String group, String memberOfAttribute) {
|
||||||
|
final StringBuilder memberOfFilter = new StringBuilder();
|
||||||
|
if (null != group) {
|
||||||
|
if(LOGGER.isDebugEnabled()) {
|
||||||
|
LOGGER.debug("adding search filter for '" + group +
|
||||||
|
"', using '" + memberOfAttribute + "'");
|
||||||
|
}
|
||||||
|
memberOfFilter.append("(" + memberOfAttribute + "=");
|
||||||
|
memberOfFilter.append(group);
|
||||||
|
memberOfFilter.append(")");
|
||||||
|
}
|
||||||
|
return memberOfFilter.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
private String generateGroupSearchFilter(final String groupName, Long domainId) {
|
private String generateGroupSearchFilter(final String groupName, Long domainId) {
|
||||||
@ -212,7 +253,7 @@ public class OpenLdapUserManagerImpl implements LdapUserManager {
|
|||||||
try{
|
try{
|
||||||
users.add(getUserForDn(userdn, context, domainId));
|
users.add(getUserForDn(userdn, context, domainId));
|
||||||
} catch (NamingException e){
|
} catch (NamingException e){
|
||||||
s_logger.info("Userdn: " + userdn + " Not Found:: Exception message: " + e.getMessage());
|
LOGGER.info("Userdn: " + userdn + " Not Found:: Exception message: " + e.getMessage());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -251,8 +292,8 @@ public class OpenLdapUserManagerImpl implements LdapUserManager {
|
|||||||
searchControls.setReturningAttributes(_ldapConfiguration.getReturnAttributes(domainId));
|
searchControls.setReturningAttributes(_ldapConfiguration.getReturnAttributes(domainId));
|
||||||
|
|
||||||
NamingEnumeration<SearchResult> results = context.search(basedn, searchString, searchControls);
|
NamingEnumeration<SearchResult> results = context.search(basedn, searchString, searchControls);
|
||||||
if(s_logger.isDebugEnabled()) {
|
if(LOGGER.isDebugEnabled()) {
|
||||||
s_logger.debug("searching user(s) with filter: \"" + searchString + "\"");
|
LOGGER.debug("searching user(s) with filter: \"" + searchString + "\"");
|
||||||
}
|
}
|
||||||
final List<LdapUser> users = new ArrayList<LdapUser>();
|
final List<LdapUser> users = new ArrayList<LdapUser>();
|
||||||
while (results.hasMoreElements()) {
|
while (results.hasMoreElements()) {
|
||||||
@ -277,7 +318,7 @@ public class OpenLdapUserManagerImpl implements LdapUserManager {
|
|||||||
|
|
||||||
String basedn = _ldapConfiguration.getBaseDn(domainId);
|
String basedn = _ldapConfiguration.getBaseDn(domainId);
|
||||||
if (StringUtils.isBlank(basedn)) {
|
if (StringUtils.isBlank(basedn)) {
|
||||||
throw new IllegalArgumentException("ldap basedn is not configured");
|
throw new IllegalArgumentException(String.format("ldap basedn is not configured (for domain: %s)", domainId));
|
||||||
}
|
}
|
||||||
byte[] cookie = null;
|
byte[] cookie = null;
|
||||||
int pageSize = _ldapConfiguration.getLdapPageSize(domainId);
|
int pageSize = _ldapConfiguration.getLdapPageSize(domainId);
|
||||||
@ -301,7 +342,7 @@ public class OpenLdapUserManagerImpl implements LdapUserManager {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
s_logger.info("No controls were sent from the ldap server");
|
LOGGER.info("No controls were sent from the ldap server");
|
||||||
}
|
}
|
||||||
context.setRequestControls(new Control[] {new PagedResultsControl(pageSize, cookie, Control.CRITICAL)});
|
context.setRequestControls(new Control[] {new PagedResultsControl(pageSize, cookie, Control.CRITICAL)});
|
||||||
} while (cookie != null);
|
} while (cookie != null);
|
||||||
|
|||||||
@ -75,7 +75,7 @@ public class LdapConfigurationDaoImpl extends GenericDaoBase<LdapConfigurationVO
|
|||||||
private SearchCriteria<LdapConfigurationVO> getSearchCriteria(String hostname, int port, Long domainId) {
|
private SearchCriteria<LdapConfigurationVO> getSearchCriteria(String hostname, int port, Long domainId) {
|
||||||
SearchCriteria<LdapConfigurationVO> sc;
|
SearchCriteria<LdapConfigurationVO> sc;
|
||||||
if (domainId == null) {
|
if (domainId == null) {
|
||||||
sc = listDomainConfigurationsSearch.create();
|
sc = listGlobalConfigurationsSearch.create();
|
||||||
} else {
|
} else {
|
||||||
sc = listDomainConfigurationsSearch.create();
|
sc = listDomainConfigurationsSearch.create();
|
||||||
sc.setParameters("domain_id", domainId);
|
sc.setParameters("domain_id", domainId);
|
||||||
|
|||||||
@ -0,0 +1,466 @@
|
|||||||
|
// 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 com.cloud.domain.Domain;
|
||||||
|
import com.cloud.domain.DomainVO;
|
||||||
|
import com.cloud.user.Account;
|
||||||
|
import com.cloud.user.AccountVO;
|
||||||
|
import com.cloud.user.DomainService;
|
||||||
|
import com.cloud.user.User;
|
||||||
|
import org.apache.cloudstack.api.ResponseObject;
|
||||||
|
import org.apache.cloudstack.api.response.LdapUserResponse;
|
||||||
|
import org.apache.cloudstack.api.response.ListResponse;
|
||||||
|
import org.apache.cloudstack.api.response.UserResponse;
|
||||||
|
import org.apache.cloudstack.context.CallContext;
|
||||||
|
import org.apache.cloudstack.ldap.LdapManager;
|
||||||
|
import org.apache.cloudstack.ldap.LdapUser;
|
||||||
|
import org.apache.cloudstack.ldap.NoLdapUserMatchingQueryException;
|
||||||
|
import org.apache.cloudstack.query.QueryService;
|
||||||
|
import org.junit.Before;
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.junit.runner.RunWith;
|
||||||
|
import org.mockito.Mock;
|
||||||
|
import org.powermock.api.mockito.PowerMockito;
|
||||||
|
import org.powermock.core.classloader.annotations.PrepareForTest;
|
||||||
|
import org.powermock.modules.junit4.PowerMockRunner;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import static org.junit.Assert.assertEquals;
|
||||||
|
import static org.junit.Assert.assertFalse;
|
||||||
|
import static org.junit.Assert.assertNotEquals;
|
||||||
|
import static org.junit.Assert.assertTrue;
|
||||||
|
import static org.junit.Assert.fail;
|
||||||
|
import static org.mockito.Matchers.any;
|
||||||
|
import static org.mockito.Matchers.anyBoolean;
|
||||||
|
import static org.mockito.Matchers.anyLong;
|
||||||
|
import static org.mockito.Mockito.times;
|
||||||
|
import static org.mockito.Mockito.verify;
|
||||||
|
import static org.powermock.api.mockito.PowerMockito.doReturn;
|
||||||
|
import static org.powermock.api.mockito.PowerMockito.doThrow;
|
||||||
|
import static org.powermock.api.mockito.PowerMockito.spy;
|
||||||
|
import static org.powermock.api.mockito.PowerMockito.when;
|
||||||
|
|
||||||
|
@RunWith(PowerMockRunner.class)
|
||||||
|
@PrepareForTest(CallContext.class)
|
||||||
|
public class LdapListUsersCmdTest implements LdapConfigurationChanger {
|
||||||
|
|
||||||
|
public static final String LOCAL_DOMAIN_ID = "12345678-90ab-cdef-fedc-ba0987654321";
|
||||||
|
public static final String LOCAL_DOMAIN_NAME = "engineering";
|
||||||
|
@Mock
|
||||||
|
LdapManager ldapManager;
|
||||||
|
@Mock
|
||||||
|
QueryService queryService;
|
||||||
|
@Mock
|
||||||
|
DomainService domainService;
|
||||||
|
|
||||||
|
LdapListUsersCmd ldapListUsersCmd;
|
||||||
|
LdapListUsersCmd cmdSpy;
|
||||||
|
|
||||||
|
Domain localDomain;
|
||||||
|
|
||||||
|
@Before
|
||||||
|
public void setUp() throws NoSuchFieldException, IllegalAccessException {
|
||||||
|
ldapListUsersCmd = new LdapListUsersCmd(ldapManager, queryService);
|
||||||
|
cmdSpy = spy(ldapListUsersCmd);
|
||||||
|
|
||||||
|
PowerMockito.mockStatic(CallContext.class);
|
||||||
|
CallContext callContextMock = PowerMockito.mock(CallContext.class);
|
||||||
|
PowerMockito.when(CallContext.current()).thenReturn(callContextMock);
|
||||||
|
Account accountMock = PowerMockito.mock(Account.class);
|
||||||
|
PowerMockito.when(accountMock.getDomainId()).thenReturn(1l);
|
||||||
|
PowerMockito.when(callContextMock.getCallingAccount()).thenReturn(accountMock);
|
||||||
|
|
||||||
|
ldapListUsersCmd._domainService = domainService;
|
||||||
|
|
||||||
|
// no need to setHiddenField(ldapListUsersCmd, .... );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* given: "We have an LdapManager, QueryService and LdapListUsersCmd"
|
||||||
|
* when: "Get entity owner id is called"
|
||||||
|
* then: "a 1 should be returned"
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
public void getEntityOwnerIdisOne() {
|
||||||
|
long ownerId = ldapListUsersCmd.getEntityOwnerId();
|
||||||
|
assertEquals(ownerId, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* given: "We have an LdapManager with no users, QueryService and a LdapListUsersCmd"
|
||||||
|
* when: "LdapListUsersCmd is executed"
|
||||||
|
* then: "An array of size 0 is returned"
|
||||||
|
*
|
||||||
|
* @throws NoLdapUserMatchingQueryException
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
public void successfulEmptyResponseFromExecute() throws NoLdapUserMatchingQueryException {
|
||||||
|
doThrow(new NoLdapUserMatchingQueryException("")).when(ldapManager).getUsers(null);
|
||||||
|
ldapListUsersCmd.execute();
|
||||||
|
assertEquals(0, ((ListResponse)ldapListUsersCmd.getResponseObject()).getResponses().size());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* given: "We have an LdapManager, one user, QueryService and a LdapListUsersCmd"
|
||||||
|
* when: "LdapListUsersCmd is executed"
|
||||||
|
* then: "a list of size not 0 is returned"
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
public void successfulResponseFromExecute() throws NoLdapUserMatchingQueryException {
|
||||||
|
mockACSUserSearch();
|
||||||
|
|
||||||
|
mockResponseCreation();
|
||||||
|
|
||||||
|
useSubdomain();
|
||||||
|
|
||||||
|
ldapListUsersCmd.execute();
|
||||||
|
|
||||||
|
verify(queryService, times(1)).searchForUsers(anyLong(), anyBoolean());
|
||||||
|
assertNotEquals(0, ((ListResponse)ldapListUsersCmd.getResponseObject()).getResponses().size());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* given: "We have an LdapManager, QueryService and a LdapListUsersCmd"
|
||||||
|
* when: "Get command name is called"
|
||||||
|
* then: "ldapuserresponse is returned"
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
public void successfulReturnOfCommandName() {
|
||||||
|
String commandName = ldapListUsersCmd.getCommandName();
|
||||||
|
|
||||||
|
assertEquals("ldapuserresponse", commandName);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* given: "We have an LdapUser and a CloudStack user whose username match"
|
||||||
|
* when: "isACloudstackUser is executed"
|
||||||
|
* then: "The result is true"
|
||||||
|
*
|
||||||
|
* TODO: is this really the valid behaviour? shouldn't the user also be linked to ldap and not accidentally match?
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
public void isACloudstackUser() {
|
||||||
|
mockACSUserSearch();
|
||||||
|
|
||||||
|
LdapUser ldapUser = new LdapUser("rmurphy", "rmurphy@cloudstack.org", "Ryan", "Murphy", "cn=rmurphy,dc=cloudstack,dc=org", null, false, null);
|
||||||
|
|
||||||
|
boolean result = ldapListUsersCmd.isACloudstackUser(ldapUser);
|
||||||
|
|
||||||
|
assertTrue(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* given: "We have an LdapUser and not a matching CloudstackUser"
|
||||||
|
* when: "isACloudstackUser is executed"
|
||||||
|
* then: "The result is false"
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
public void isNotACloudstackUser() {
|
||||||
|
doReturn(new ListResponse<UserResponse>()).when(queryService).searchForUsers(anyLong(), anyBoolean());
|
||||||
|
|
||||||
|
LdapUser ldapUser = new LdapUser("rmurphy", "rmurphy@cloudstack.org", "Ryan", "Murphy", "cn=rmurphy,dc=cloudstack,dc=org", null, false, null);
|
||||||
|
|
||||||
|
boolean result = ldapListUsersCmd.isACloudstackUser(ldapUser);
|
||||||
|
|
||||||
|
assertFalse(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* test whether a value other than 'any' for 'listtype' leads to a good 'userfilter' value
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
public void getListtypeOther() {
|
||||||
|
when(cmdSpy.getListTypeString()).thenReturn("otHer", "anY");
|
||||||
|
|
||||||
|
String userfilter = cmdSpy.getUserFilterString();
|
||||||
|
assertEquals("AnyDomain", userfilter);
|
||||||
|
|
||||||
|
userfilter = cmdSpy.getUserFilterString();
|
||||||
|
assertEquals("AnyDomain", userfilter);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* test whether a value of 'any' for 'listtype' leads to a good 'userfilter' value
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
public void getListtypeAny() {
|
||||||
|
when(cmdSpy.getListTypeString()).thenReturn("all");
|
||||||
|
String userfilter = cmdSpy.getUserFilterString();
|
||||||
|
assertEquals("NoFilter", userfilter);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* test whether values for 'userfilter' yield the right filter
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
public void getUserFilter() throws NoSuchFieldException, IllegalAccessException {
|
||||||
|
when(cmdSpy.getListTypeString()).thenReturn("otHer");
|
||||||
|
LdapListUsersCmd.UserFilter userfilter = cmdSpy.getUserFilter();
|
||||||
|
|
||||||
|
assertEquals(LdapListUsersCmd.UserFilter.ANY_DOMAIN, userfilter);
|
||||||
|
|
||||||
|
when(cmdSpy.getListTypeString()).thenReturn("anY");
|
||||||
|
userfilter = cmdSpy.getUserFilter();
|
||||||
|
assertEquals(LdapListUsersCmd.UserFilter.ANY_DOMAIN, userfilter);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* test if the right exception is thrown on invalid input.
|
||||||
|
*/
|
||||||
|
@Test(expected = IllegalArgumentException.class)
|
||||||
|
public void getInvalidUserFilterValues() throws NoSuchFieldException, IllegalAccessException {
|
||||||
|
setHiddenField(ldapListUsersCmd, "userFilter", "flase");
|
||||||
|
// unused output: LdapListUsersCmd.UserFilter userfilter =
|
||||||
|
ldapListUsersCmd.getUserFilter();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void getUserFilterValues() {
|
||||||
|
assertEquals("PotentialImport", LdapListUsersCmd.UserFilter.POTENTIAL_IMPORT.toString());
|
||||||
|
assertEquals(LdapListUsersCmd.UserFilter.POTENTIAL_IMPORT, LdapListUsersCmd.UserFilter.fromString("PotentialImport"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test(expected = IllegalArgumentException.class)
|
||||||
|
public void getInvalidUserFilterStringValue() {
|
||||||
|
LdapListUsersCmd.UserFilter.fromString("PotentImport");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* apply no filter
|
||||||
|
*
|
||||||
|
* @throws NoSuchFieldException
|
||||||
|
* @throws IllegalAccessException
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
public void applyNoFilter() throws NoSuchFieldException, IllegalAccessException, NoLdapUserMatchingQueryException {
|
||||||
|
mockACSUserSearch();
|
||||||
|
mockResponseCreation();
|
||||||
|
|
||||||
|
useSubdomain();
|
||||||
|
|
||||||
|
setHiddenField(ldapListUsersCmd, "userFilter", "NoFilter");
|
||||||
|
ldapListUsersCmd.execute();
|
||||||
|
|
||||||
|
assertEquals(3, ((ListResponse)ldapListUsersCmd.getResponseObject()).getResponses().size());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* filter all acs users
|
||||||
|
*
|
||||||
|
* @throws NoSuchFieldException
|
||||||
|
* @throws IllegalAccessException
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
public void applyAnyDomain() throws NoSuchFieldException, IllegalAccessException, NoLdapUserMatchingQueryException {
|
||||||
|
mockACSUserSearch();
|
||||||
|
mockResponseCreation();
|
||||||
|
|
||||||
|
useSubdomain();
|
||||||
|
|
||||||
|
setHiddenField(ldapListUsersCmd, "userFilter", "AnyDomain");
|
||||||
|
setHiddenField(ldapListUsersCmd, "domainId", 2l /* not root */);
|
||||||
|
ldapListUsersCmd.execute();
|
||||||
|
|
||||||
|
// 'rmurphy' annotated with native
|
||||||
|
// 'bob' still in
|
||||||
|
// 'abhi' is filtered out
|
||||||
|
List<ResponseObject> responses = ((ListResponse)ldapListUsersCmd.getResponseObject()).getResponses();
|
||||||
|
assertEquals(2, responses.size());
|
||||||
|
for(ResponseObject response : responses) {
|
||||||
|
if(!(response instanceof LdapUserResponse)) {
|
||||||
|
fail("unexpected return-type from API backend method");
|
||||||
|
} else {
|
||||||
|
LdapUserResponse userResponse = (LdapUserResponse)response;
|
||||||
|
// further validate this user
|
||||||
|
if ("rmurphy".equals(userResponse.getUsername()) &&
|
||||||
|
! User.Source.NATIVE.toString().equalsIgnoreCase(userResponse.getUserSource())) {
|
||||||
|
fail("expected murphy from ldap");
|
||||||
|
}
|
||||||
|
if ("bob".equals(userResponse.getUsername()) &&
|
||||||
|
! "".equals(userResponse.getUserSource())) {
|
||||||
|
fail("expected bob from without usersource");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* filter out acs users for the requested domain
|
||||||
|
*
|
||||||
|
* @throws NoSuchFieldException
|
||||||
|
* @throws IllegalAccessException
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
public void applyLocalDomainForASubDomain() throws NoSuchFieldException, IllegalAccessException, NoLdapUserMatchingQueryException {
|
||||||
|
mockACSUserSearch();
|
||||||
|
mockResponseCreation();
|
||||||
|
|
||||||
|
setHiddenField(ldapListUsersCmd, "userFilter", "LocalDomain");
|
||||||
|
setHiddenField(ldapListUsersCmd, "domainId", 2l /* not root */);
|
||||||
|
|
||||||
|
localDomain = useSubdomain();
|
||||||
|
|
||||||
|
ldapListUsersCmd.execute();
|
||||||
|
|
||||||
|
// 'rmurphy' filtered out 'bob' still in
|
||||||
|
assertEquals(2, ((ListResponse)ldapListUsersCmd.getResponseObject()).getResponses().size());
|
||||||
|
// todo: assert user sources
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* filter out acs users for the default domain
|
||||||
|
*
|
||||||
|
* @throws NoSuchFieldException
|
||||||
|
* @throws IllegalAccessException
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
public void applyLocalDomainForTheCallersDomain() throws NoSuchFieldException, IllegalAccessException, NoLdapUserMatchingQueryException {
|
||||||
|
mockACSUserSearch();
|
||||||
|
mockResponseCreation();
|
||||||
|
|
||||||
|
setHiddenField(ldapListUsersCmd, "userFilter", "LocalDomain");
|
||||||
|
|
||||||
|
AccountVO account = new AccountVO();
|
||||||
|
setHiddenField(account, "accountName", "admin");
|
||||||
|
setHiddenField(account, "domainId", 1l);
|
||||||
|
final CallContext callContext = CallContext.current();
|
||||||
|
setHiddenField(callContext, "account", account);
|
||||||
|
DomainVO domainVO = useDomain("ROOT", 1l);
|
||||||
|
localDomain = domainVO;
|
||||||
|
|
||||||
|
ldapListUsersCmd.execute();
|
||||||
|
|
||||||
|
// 'rmurphy' filtered out 'bob' still in
|
||||||
|
assertEquals(2, ((ListResponse)ldapListUsersCmd.getResponseObject()).getResponses().size());
|
||||||
|
// todo: assert usersources
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* todo generate an extensive configuration and check with an extensive user list
|
||||||
|
*
|
||||||
|
* @throws NoSuchFieldException
|
||||||
|
* @throws IllegalAccessException
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
public void applyPotentialImport() throws NoSuchFieldException, IllegalAccessException, NoLdapUserMatchingQueryException {
|
||||||
|
mockACSUserSearch();
|
||||||
|
mockResponseCreation();
|
||||||
|
|
||||||
|
useSubdomain();
|
||||||
|
|
||||||
|
setHiddenField(ldapListUsersCmd, "userFilter", "PotentialImport");
|
||||||
|
ldapListUsersCmd.execute();
|
||||||
|
|
||||||
|
assertEquals(2, ((ListResponse)ldapListUsersCmd.getResponseObject()).getResponses().size());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* unknown filter
|
||||||
|
*
|
||||||
|
* @throws NoSuchFieldException
|
||||||
|
* @throws IllegalAccessException
|
||||||
|
*/
|
||||||
|
@Test(expected = IllegalArgumentException.class)
|
||||||
|
public void applyUnknownFilter() throws NoSuchFieldException, IllegalAccessException {
|
||||||
|
setHiddenField(ldapListUsersCmd, "userFilter", "UnknownFilter");
|
||||||
|
ldapListUsersCmd.execute();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* make sure there are no unimplemented filters
|
||||||
|
*
|
||||||
|
* This was created to deal with the possible {code}NoSuchMethodException{code} that won't be dealt with in regular coverage
|
||||||
|
*
|
||||||
|
* @throws NoSuchFieldException
|
||||||
|
* @throws IllegalAccessException
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
public void applyUnimplementedFilter() throws NoSuchFieldException, IllegalAccessException {
|
||||||
|
useSubdomain();
|
||||||
|
for (LdapListUsersCmd.UserFilter UNIMPLEMENTED_FILTER : LdapListUsersCmd.UserFilter.values()) {
|
||||||
|
setHiddenField(ldapListUsersCmd, "userFilter", UNIMPLEMENTED_FILTER.toString());
|
||||||
|
ldapListUsersCmd.getUserFilter().filter(ldapListUsersCmd,new ArrayList<LdapUserResponse>());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// helper methods //
|
||||||
|
////////////////////
|
||||||
|
private DomainVO useSubdomain() {
|
||||||
|
DomainVO domainVO = useDomain(LOCAL_DOMAIN_NAME, 2l);
|
||||||
|
return domainVO;
|
||||||
|
}
|
||||||
|
|
||||||
|
private DomainVO useDomain(String domainName, long domainId) {
|
||||||
|
DomainVO domainVO = new DomainVO();
|
||||||
|
domainVO.setName(domainName);
|
||||||
|
domainVO.setId(domainId);
|
||||||
|
domainVO.setUuid(LOCAL_DOMAIN_ID);
|
||||||
|
when(domainService.getDomain(anyLong())).thenReturn(domainVO);
|
||||||
|
return domainVO;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void mockACSUserSearch() {
|
||||||
|
UserResponse rmurphy = createMockUserResponse("rmurphy", User.Source.NATIVE);
|
||||||
|
UserResponse rohit = createMockUserResponse("rohit", User.Source.SAML2);
|
||||||
|
UserResponse abhi = createMockUserResponse("abhi", User.Source.LDAP);
|
||||||
|
|
||||||
|
ArrayList<UserResponse> responses = new ArrayList<>();
|
||||||
|
responses.add(rmurphy);
|
||||||
|
responses.add(rohit);
|
||||||
|
responses.add(abhi);
|
||||||
|
|
||||||
|
ListResponse<UserResponse> queryServiceResponse = new ListResponse<>();
|
||||||
|
queryServiceResponse.setResponses(responses);
|
||||||
|
|
||||||
|
doReturn(queryServiceResponse).when(queryService).searchForUsers(anyLong(), anyBoolean());
|
||||||
|
}
|
||||||
|
|
||||||
|
private UserResponse createMockUserResponse(String uid, User.Source source) {
|
||||||
|
UserResponse userResponse = new UserResponse();
|
||||||
|
userResponse.setUsername(uid);
|
||||||
|
userResponse.setUserSource(source);
|
||||||
|
|
||||||
|
// for now:
|
||||||
|
userResponse.setDomainId(LOCAL_DOMAIN_ID);
|
||||||
|
userResponse.setDomainName(LOCAL_DOMAIN_NAME);
|
||||||
|
|
||||||
|
return userResponse;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void mockResponseCreation() throws NoLdapUserMatchingQueryException {
|
||||||
|
List<LdapUser> users = new ArrayList();
|
||||||
|
LdapUser murphy = new LdapUser("rmurphy", "rmurphy@test.com", "Ryan", "Murphy", "cn=rmurphy,dc=cloudstack,dc=org", "mythical", false, null);
|
||||||
|
LdapUser bob = new LdapUser("bob", "bob@test.com", "Robert", "Young", "cn=bob,ou=engineering,dc=cloudstack,dc=org", LOCAL_DOMAIN_NAME, false, null);
|
||||||
|
LdapUser abhi = new LdapUser("abhi", "abhi@test.com", "Abhi", "YoungOrOld", "cn=abhi,ou=engineering,dc=cloudstack,dc=org", LOCAL_DOMAIN_NAME, false, null);
|
||||||
|
users.add(murphy);
|
||||||
|
users.add(bob);
|
||||||
|
users.add(abhi);
|
||||||
|
|
||||||
|
doReturn(users).when(ldapManager).getUsers(any());
|
||||||
|
|
||||||
|
LdapUserResponse response = new LdapUserResponse("rmurphy", "rmurphy@test.com", "Ryan", "Murphy", "cn=rmurphy,dc=cloudstack,dc=org", null);
|
||||||
|
doReturn(response).when(ldapManager).createLdapUserResponse(murphy);
|
||||||
|
LdapUserResponse bobResponse = new LdapUserResponse("bob", "bob@test.com", "Robert", "Young", "cn=bob,ou=engineering,dc=cloudstack,dc=org", LOCAL_DOMAIN_NAME);
|
||||||
|
doReturn(bobResponse).when(ldapManager).createLdapUserResponse(bob);
|
||||||
|
LdapUserResponse abhiResponse = new LdapUserResponse("abhi", "abhi@test.com", "Abhi", "YoungOrOld", "cn=abhi,ou=engineering,dc=cloudstack,dc=org", LOCAL_DOMAIN_NAME);
|
||||||
|
doReturn(abhiResponse).when(ldapManager).createLdapUserResponse(abhi);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,326 @@
|
|||||||
|
/*-
|
||||||
|
* 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 org.apache.commons.io.FileUtils;
|
||||||
|
import org.apache.directory.api.ldap.model.entry.Entry;
|
||||||
|
import org.apache.directory.api.ldap.model.exception.LdapException;
|
||||||
|
import org.apache.directory.api.ldap.model.schema.registries.DefaultSchema;
|
||||||
|
import org.apache.directory.api.ldap.model.schema.registries.Schema;
|
||||||
|
import org.apache.directory.api.ldap.schema.loader.JarLdifSchemaLoader;
|
||||||
|
import org.apache.directory.api.ldap.schema.loader.LdifSchemaLoader;
|
||||||
|
import org.apache.directory.server.core.api.CoreSession;
|
||||||
|
import org.apache.directory.server.core.api.DirectoryService;
|
||||||
|
import org.apache.directory.server.core.factory.DefaultDirectoryServiceFactory;
|
||||||
|
import org.apache.directory.server.core.factory.JdbmPartitionFactory;
|
||||||
|
import org.apache.directory.server.core.partition.impl.btree.jdbm.JdbmIndex;
|
||||||
|
import org.apache.directory.server.core.partition.impl.btree.jdbm.JdbmPartition;
|
||||||
|
import org.apache.directory.server.ldap.LdapServer;
|
||||||
|
import org.apache.directory.server.protocol.shared.transport.TcpTransport;
|
||||||
|
import org.apache.directory.server.xdbm.Index;
|
||||||
|
import org.apache.directory.server.xdbm.IndexNotFoundException;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Iterator;
|
||||||
|
import java.util.LinkedHashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Call init() to start the server and destroy() to shut it down.
|
||||||
|
*/
|
||||||
|
public class EmbeddedLdapServer {
|
||||||
|
// API References:
|
||||||
|
// http://directory.apache.org/apacheds/gen-docs/latest/apidocs/
|
||||||
|
// http://directory.apache.org/api/gen-docs/latest/apidocs/
|
||||||
|
|
||||||
|
private static final String BASE_PARTITION_NAME = "mydomain";
|
||||||
|
private static final String BASE_DOMAIN = "org";
|
||||||
|
private static final String BASE_STRUCTURE = "dc=" + BASE_PARTITION_NAME + ",dc=" + BASE_DOMAIN;
|
||||||
|
|
||||||
|
private static final int LDAP_SERVER_PORT = 10389;
|
||||||
|
private static final int BASE_CACHE_SIZE = 1000;
|
||||||
|
private static final List<String> ATTR_NAMES_TO_INDEX = new ArrayList<String>(Arrays.asList("uid"));
|
||||||
|
|
||||||
|
private DirectoryService _directoryService;
|
||||||
|
private LdapServer _ldapServer;
|
||||||
|
private JdbmPartition _basePartition;
|
||||||
|
private boolean _deleteInstanceDirectoryOnStartup = true;
|
||||||
|
private boolean _deleteInstanceDirectoryOnShutdown = true;
|
||||||
|
|
||||||
|
public String getBasePartitionName() {
|
||||||
|
return BASE_PARTITION_NAME;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getBaseStructure() {
|
||||||
|
return BASE_STRUCTURE;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getBaseCacheSize() {
|
||||||
|
return BASE_CACHE_SIZE;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getLdapServerPort() {
|
||||||
|
return LDAP_SERVER_PORT;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<String> getAttrNamesToIndex() {
|
||||||
|
return ATTR_NAMES_TO_INDEX;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void addSchemaExtensions() throws LdapException, IOException {
|
||||||
|
// override to add custom attributes to the schema
|
||||||
|
}
|
||||||
|
|
||||||
|
public void init() throws Exception {
|
||||||
|
if (getDirectoryService() == null) {
|
||||||
|
if (getDeleteInstanceDirectoryOnStartup()) {
|
||||||
|
deleteDirectory(getGuessedInstanceDirectory());
|
||||||
|
}
|
||||||
|
|
||||||
|
DefaultDirectoryServiceFactory serviceFactory = new DefaultDirectoryServiceFactory();
|
||||||
|
serviceFactory.init(getDirectoryServiceName());
|
||||||
|
setDirectoryService(serviceFactory.getDirectoryService());
|
||||||
|
|
||||||
|
getDirectoryService().getChangeLog().setEnabled(false);
|
||||||
|
getDirectoryService().setDenormalizeOpAttrsEnabled(true);
|
||||||
|
|
||||||
|
createBasePartition();
|
||||||
|
|
||||||
|
getDirectoryService().startup();
|
||||||
|
|
||||||
|
createRootEntry();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (getLdapServer() == null) {
|
||||||
|
setLdapServer(new LdapServer());
|
||||||
|
getLdapServer().setDirectoryService(getDirectoryService());
|
||||||
|
getLdapServer().setTransports(new TcpTransport(getLdapServerPort()));
|
||||||
|
getLdapServer().start();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void destroy() throws Exception {
|
||||||
|
File instanceDirectory = getDirectoryService().getInstanceLayout().getInstanceDirectory();
|
||||||
|
getLdapServer().stop();
|
||||||
|
getDirectoryService().shutdown();
|
||||||
|
setLdapServer(null);
|
||||||
|
setDirectoryService(null);
|
||||||
|
if (getDeleteInstanceDirectoryOnShutdown()) {
|
||||||
|
deleteDirectory(instanceDirectory);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getDirectoryServiceName() {
|
||||||
|
return getBasePartitionName() + "DirectoryService";
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void deleteDirectory(File path) throws IOException {
|
||||||
|
FileUtils.deleteDirectory(path);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void createBasePartition() throws Exception {
|
||||||
|
JdbmPartitionFactory jdbmPartitionFactory = new JdbmPartitionFactory();
|
||||||
|
setBasePartition(jdbmPartitionFactory.createPartition(getDirectoryService().getSchemaManager(), getDirectoryService().getDnFactory(), getBasePartitionName(), getBaseStructure(), getBaseCacheSize(), getBasePartitionPath()));
|
||||||
|
addSchemaExtensions();
|
||||||
|
createBaseIndices();
|
||||||
|
getDirectoryService().addPartition(getBasePartition());
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void createBaseIndices() throws Exception {
|
||||||
|
//
|
||||||
|
// Default indices, that can be seen with getSystemIndexMap() and
|
||||||
|
// getUserIndexMap(), are minimal. There are no user indices by
|
||||||
|
// default and the default system indices are:
|
||||||
|
//
|
||||||
|
// apacheOneAlias, entryCSN, apacheSubAlias, apacheAlias,
|
||||||
|
// objectClass, apachePresence, apacheRdn, administrativeRole
|
||||||
|
//
|
||||||
|
for (String attrName : getAttrNamesToIndex()) {
|
||||||
|
getBasePartition().addIndex(createIndexObjectForAttr(attrName));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected JdbmIndex<?> createIndexObjectForAttr(String attrName, boolean withReverse) throws LdapException {
|
||||||
|
String oid = getOidByAttributeName(attrName);
|
||||||
|
if (oid == null) {
|
||||||
|
throw new RuntimeException("OID could not be found for attr " + attrName);
|
||||||
|
}
|
||||||
|
return new JdbmIndex(oid, withReverse);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected JdbmIndex<?> createIndexObjectForAttr(String attrName) throws LdapException {
|
||||||
|
return createIndexObjectForAttr(attrName, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void createRootEntry() throws LdapException {
|
||||||
|
Entry entry = getDirectoryService().newEntry(getDirectoryService().getDnFactory().create(getBaseStructure()));
|
||||||
|
entry.add("objectClass", "top", "domain", "extensibleObject");
|
||||||
|
entry.add("dc", getBasePartitionName());
|
||||||
|
CoreSession session = getDirectoryService().getAdminSession();
|
||||||
|
try {
|
||||||
|
session.add(entry);
|
||||||
|
} finally {
|
||||||
|
session.unbind();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return A map where the key is the attribute name the value is the
|
||||||
|
* oid.
|
||||||
|
*/
|
||||||
|
public Map<String, String> getSystemIndexMap() throws IndexNotFoundException {
|
||||||
|
Map<String, String> result = new LinkedHashMap<>();
|
||||||
|
Iterator<String> it = getBasePartition().getSystemIndices();
|
||||||
|
while (it.hasNext()) {
|
||||||
|
String oid = it.next();
|
||||||
|
Index<?, String> index = getBasePartition().getSystemIndex(getDirectoryService().getSchemaManager().getAttributeType(oid));
|
||||||
|
result.put(index.getAttribute().getName(), index.getAttributeId());
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return A map where the key is the attribute name the value is the
|
||||||
|
* oid.
|
||||||
|
*/
|
||||||
|
public Map<String, String> getUserIndexMap() throws IndexNotFoundException {
|
||||||
|
Map<String, String> result = new LinkedHashMap<>();
|
||||||
|
Iterator<String> it = getBasePartition().getUserIndices();
|
||||||
|
while (it.hasNext()) {
|
||||||
|
String oid = it.next();
|
||||||
|
Index<?, String> index = getBasePartition().getUserIndex(getDirectoryService().getSchemaManager().getAttributeType(oid));
|
||||||
|
result.put(index.getAttribute().getName(), index.getAttributeId());
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public File getPartitionsDirectory() {
|
||||||
|
return getDirectoryService().getInstanceLayout().getPartitionsDirectory();
|
||||||
|
}
|
||||||
|
|
||||||
|
public File getBasePartitionPath() {
|
||||||
|
return new File(getPartitionsDirectory(), getBasePartitionName());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Used at init time to clear out the likely instance directory before
|
||||||
|
* anything is created.
|
||||||
|
*/
|
||||||
|
public File getGuessedInstanceDirectory() {
|
||||||
|
// See source code for DefaultDirectoryServiceFactory
|
||||||
|
// buildInstanceDirectory. ApacheDS looks at the workingDirectory
|
||||||
|
// system property first and then defers to the java.io.tmpdir
|
||||||
|
// system property.
|
||||||
|
final String property = System.getProperty("workingDirectory");
|
||||||
|
return new File(property != null ? property : System.getProperty("java.io.tmpdir") + File.separator + "server-work-" + getDirectoryServiceName());
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getOidByAttributeName(String attrName) throws LdapException {
|
||||||
|
return getDirectoryService().getSchemaManager().getAttributeTypeRegistry().getOidByName(attrName);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add additional schemas to the directory server. This takes a path to
|
||||||
|
* the schema directory and uses the LdifSchemaLoader.
|
||||||
|
*
|
||||||
|
* @param schemaLocation The path to the directory containing the
|
||||||
|
* "ou=schema" directory for an additional schema
|
||||||
|
* @param schemaName The name of the schema
|
||||||
|
* @return true if the schemas have been loaded and the registries is
|
||||||
|
* consistent
|
||||||
|
*/
|
||||||
|
public boolean addSchemaFromPath(File schemaLocation, String schemaName) throws LdapException, IOException {
|
||||||
|
LdifSchemaLoader schemaLoader = new LdifSchemaLoader(schemaLocation);
|
||||||
|
DefaultSchema schema = new DefaultSchema(schemaLoader, schemaName);
|
||||||
|
return getDirectoryService().getSchemaManager().load(schema);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add additional schemas to the directory server. This uses
|
||||||
|
* JarLdifSchemaLoader, which will search for the "ou=schema" directory
|
||||||
|
* within "/schema" on the classpath. If packaging the schema as part of
|
||||||
|
* a jar using Gradle or Maven, you'd probably want to put your
|
||||||
|
* "ou=schema" directory in src/main/resources/schema.
|
||||||
|
* <p/>
|
||||||
|
* It's also required that a META-INF/apacheds-schema.index be present in
|
||||||
|
* your classpath that lists each LDIF file in your schema directory.
|
||||||
|
*
|
||||||
|
* @param schemaName The name of the schema
|
||||||
|
* @return true if the schemas have been loaded and the registries is
|
||||||
|
* consistent
|
||||||
|
*/
|
||||||
|
public boolean addSchemaFromClasspath(String schemaName) throws LdapException, IOException {
|
||||||
|
// To debug if your apacheds-schema.index isn't found:
|
||||||
|
// Enumeration<URL> indexes = getClass().getClassLoader().getResources("META-INF/apacheds-schema.index");
|
||||||
|
JarLdifSchemaLoader schemaLoader = new JarLdifSchemaLoader();
|
||||||
|
Schema schema = schemaLoader.getSchema(schemaName);
|
||||||
|
return schema != null && getDirectoryService().getSchemaManager().load(schema);
|
||||||
|
}
|
||||||
|
|
||||||
|
public DirectoryService getDirectoryService() {
|
||||||
|
return _directoryService;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setDirectoryService(DirectoryService directoryService) {
|
||||||
|
this._directoryService = directoryService;
|
||||||
|
}
|
||||||
|
|
||||||
|
public LdapServer getLdapServer() {
|
||||||
|
return _ldapServer;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setLdapServer(LdapServer ldapServer) {
|
||||||
|
this._ldapServer = ldapServer;
|
||||||
|
}
|
||||||
|
|
||||||
|
public JdbmPartition getBasePartition() {
|
||||||
|
return _basePartition;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setBasePartition(JdbmPartition basePartition) {
|
||||||
|
this._basePartition = basePartition;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean getDeleteInstanceDirectoryOnStartup() {
|
||||||
|
return _deleteInstanceDirectoryOnStartup;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setDeleteInstanceDirectoryOnStartup(boolean deleteInstanceDirectoryOnStartup) {
|
||||||
|
this._deleteInstanceDirectoryOnStartup = deleteInstanceDirectoryOnStartup;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean getDeleteInstanceDirectoryOnShutdown() {
|
||||||
|
return _deleteInstanceDirectoryOnShutdown;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setDeleteInstanceDirectoryOnShutdown(boolean deleteInstanceDirectoryOnShutdown) {
|
||||||
|
this._deleteInstanceDirectoryOnShutdown = deleteInstanceDirectoryOnShutdown;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void main (String[] args) {
|
||||||
|
EmbeddedLdapServer embeddedLdapServer = new EmbeddedLdapServer();
|
||||||
|
try {
|
||||||
|
embeddedLdapServer.init();
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -18,6 +18,9 @@ package org.apache.cloudstack.ldap;
|
|||||||
|
|
||||||
|
|
||||||
import com.cloud.server.auth.UserAuthenticator;
|
import com.cloud.server.auth.UserAuthenticator;
|
||||||
|
import com.cloud.user.AccountManager;
|
||||||
|
import com.cloud.user.AccountVO;
|
||||||
|
import com.cloud.user.User;
|
||||||
import com.cloud.user.UserAccount;
|
import com.cloud.user.UserAccount;
|
||||||
import com.cloud.user.UserAccountVO;
|
import com.cloud.user.UserAccountVO;
|
||||||
import com.cloud.user.dao.UserAccountDao;
|
import com.cloud.user.dao.UserAccountDao;
|
||||||
@ -25,13 +28,20 @@ import com.cloud.utils.Pair;
|
|||||||
import org.junit.Before;
|
import org.junit.Before;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.junit.runner.RunWith;
|
import org.junit.runner.RunWith;
|
||||||
|
import org.mockito.InjectMocks;
|
||||||
import org.mockito.Mock;
|
import org.mockito.Mock;
|
||||||
import org.mockito.runners.MockitoJUnitRunner;
|
import org.mockito.runners.MockitoJUnitRunner;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
import static org.junit.Assert.assertEquals;
|
import static org.junit.Assert.assertEquals;
|
||||||
import static org.junit.Assert.assertFalse;
|
import static org.junit.Assert.assertFalse;
|
||||||
import static org.junit.Assert.assertNull;
|
import static org.junit.Assert.assertNull;
|
||||||
import static org.junit.Assert.assertTrue;
|
import static org.junit.Assert.assertTrue;
|
||||||
|
import static org.mockito.Matchers.anyLong;
|
||||||
|
import static org.mockito.Mockito.spy;
|
||||||
import static org.mockito.Mockito.when;
|
import static org.mockito.Mockito.when;
|
||||||
|
|
||||||
|
|
||||||
@ -43,9 +53,12 @@ public class LdapAuthenticatorTest {
|
|||||||
@Mock
|
@Mock
|
||||||
UserAccountDao userAccountDao;
|
UserAccountDao userAccountDao;
|
||||||
@Mock
|
@Mock
|
||||||
|
AccountManager accountManager;
|
||||||
|
@Mock
|
||||||
UserAccount user = new UserAccountVO();
|
UserAccount user = new UserAccountVO();
|
||||||
|
|
||||||
LdapAuthenticator ldapAuthenticator;
|
@InjectMocks
|
||||||
|
LdapAuthenticator ldapAuthenticator = new LdapAuthenticator();
|
||||||
private String username = "bbanner";
|
private String username = "bbanner";
|
||||||
private String principal = "cd=bbanner";
|
private String principal = "cd=bbanner";
|
||||||
private String hardcoded = "password";
|
private String hardcoded = "password";
|
||||||
@ -53,7 +66,18 @@ public class LdapAuthenticatorTest {
|
|||||||
|
|
||||||
@Before
|
@Before
|
||||||
public void setUp() throws Exception {
|
public void setUp() throws Exception {
|
||||||
ldapAuthenticator = new LdapAuthenticator(ldapManager, userAccountDao);
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void authenticateAsNativeUser() throws Exception {
|
||||||
|
final UserAccountVO user = new UserAccountVO();
|
||||||
|
user.setSource(User.Source.NATIVE);
|
||||||
|
|
||||||
|
when(userAccountDao.getUserAccount(username, domainId)).thenReturn(user);
|
||||||
|
Pair<Boolean, UserAuthenticator.ActionOnFailedAuthentication> rc;
|
||||||
|
rc = ldapAuthenticator.authenticate(username, "password", domainId, (Map<String, Object[]>)null);
|
||||||
|
assertFalse("authentication succeeded when it should have failed", rc.first());
|
||||||
|
assertEquals("We should not have tried to authenticate", null,rc.second());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@ -62,9 +86,39 @@ public class LdapAuthenticatorTest {
|
|||||||
Pair<Boolean, UserAuthenticator.ActionOnFailedAuthentication> rc;
|
Pair<Boolean, UserAuthenticator.ActionOnFailedAuthentication> rc;
|
||||||
when(ldapManager.getUser(username, domainId)).thenReturn(ldapUser);
|
when(ldapManager.getUser(username, domainId)).thenReturn(ldapUser);
|
||||||
rc = ldapAuthenticator.authenticate(username, "password", domainId, user);
|
rc = ldapAuthenticator.authenticate(username, "password", domainId, user);
|
||||||
assertFalse("authentication succeded when it should have failed", rc.first());
|
assertFalse("authentication succeeded when it should have failed", rc.first());
|
||||||
assertEquals("", UserAuthenticator.ActionOnFailedAuthentication.INCREMENT_INCORRECT_LOGIN_ATTEMPT_COUNT,rc.second());
|
assertEquals("", UserAuthenticator.ActionOnFailedAuthentication.INCREMENT_INCORRECT_LOGIN_ATTEMPT_COUNT,rc.second());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void authenticateFailingOnSyncedAccount() throws Exception {
|
||||||
|
Pair<Boolean, UserAuthenticator.ActionOnFailedAuthentication> rc;
|
||||||
|
|
||||||
|
List<String> memberships = new ArrayList<>();
|
||||||
|
memberships.add("g1");
|
||||||
|
List<String> mappedGroups = new ArrayList<>();
|
||||||
|
mappedGroups.add("g1");
|
||||||
|
mappedGroups.add("g2");
|
||||||
|
|
||||||
|
LdapUser ldapUser = new LdapUser(username,"a@b","b","banner",principal,"",false,null);
|
||||||
|
LdapUser userSpy = spy(ldapUser);
|
||||||
|
when(userSpy.getMemberships()).thenReturn(memberships);
|
||||||
|
|
||||||
|
List<LdapTrustMapVO> maps = new ArrayList<>();
|
||||||
|
LdapAuthenticator auth = spy(ldapAuthenticator);
|
||||||
|
when(auth.getMappedGroups(maps)).thenReturn(mappedGroups);
|
||||||
|
|
||||||
|
LdapTrustMapVO trustMap = new LdapTrustMapVO(domainId, LdapManager.LinkType.GROUP, "cn=name", (short)2, 1l);
|
||||||
|
|
||||||
|
AccountVO account = new AccountVO("accountName" , domainId, "domain.net", (short)2, "final String uuid");
|
||||||
|
when(accountManager.getAccount(anyLong())).thenReturn(account);
|
||||||
|
when(ldapManager.getUser(username, domainId)).thenReturn(userSpy);
|
||||||
|
when(ldapManager.getLinkedLdapGroup(domainId, "g1")).thenReturn(trustMap);
|
||||||
|
rc = auth.authenticate(username, "password", domainId, user, maps);
|
||||||
|
assertFalse("authentication succeeded when it should have failed", rc.first());
|
||||||
|
assertEquals("", UserAuthenticator.ActionOnFailedAuthentication.INCREMENT_INCORRECT_LOGIN_ATTEMPT_COUNT,rc.second());
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void authenticate() throws Exception {
|
public void authenticate() throws Exception {
|
||||||
LdapUser ldapUser = new LdapUser(username, "a@b", "b", "banner", principal, "", false, null);
|
LdapUser ldapUser = new LdapUser(username, "a@b", "b", "banner", principal, "", false, null);
|
||||||
|
|||||||
@ -16,7 +16,6 @@
|
|||||||
// under the License.
|
// under the License.
|
||||||
package org.apache.cloudstack.ldap;
|
package org.apache.cloudstack.ldap;
|
||||||
|
|
||||||
import org.apache.cloudstack.framework.config.ConfigKey;
|
|
||||||
import org.apache.cloudstack.ldap.dao.LdapConfigurationDao;
|
import org.apache.cloudstack.ldap.dao.LdapConfigurationDao;
|
||||||
import org.apache.cloudstack.ldap.dao.LdapConfigurationDaoImpl;
|
import org.apache.cloudstack.ldap.dao.LdapConfigurationDaoImpl;
|
||||||
import org.junit.Before;
|
import org.junit.Before;
|
||||||
@ -24,118 +23,98 @@ import org.junit.Test;
|
|||||||
import org.junit.runner.RunWith;
|
import org.junit.runner.RunWith;
|
||||||
import org.mockito.runners.MockitoJUnitRunner;
|
import org.mockito.runners.MockitoJUnitRunner;
|
||||||
|
|
||||||
import java.lang.reflect.Field;
|
|
||||||
import java.lang.reflect.Modifier;
|
|
||||||
|
|
||||||
import static org.junit.Assert.assertEquals;
|
import static org.junit.Assert.assertEquals;
|
||||||
import static org.junit.Assert.assertTrue;
|
import static org.junit.Assert.assertTrue;
|
||||||
|
|
||||||
@RunWith(MockitoJUnitRunner.class)
|
@RunWith(MockitoJUnitRunner.class)
|
||||||
public class LdapConfigurationTest {
|
public class LdapConfigurationTest {
|
||||||
|
|
||||||
|
private final LdapTestConfigTool ldapTestConfigTool = new LdapTestConfigTool();
|
||||||
LdapConfigurationDao ldapConfigurationDao;
|
LdapConfigurationDao ldapConfigurationDao;
|
||||||
LdapConfiguration ldapConfiguration;
|
LdapConfiguration ldapConfiguration;
|
||||||
|
|
||||||
private void overrideConfigValue(final String configKeyName, final Object o) throws IllegalAccessException, NoSuchFieldException {
|
private void overrideConfigValue(LdapConfiguration ldapConfiguration, final String configKeyName, final Object o) throws IllegalAccessException, NoSuchFieldException
|
||||||
Field configKey = LdapConfiguration.class.getDeclaredField(configKeyName);
|
{
|
||||||
configKey.setAccessible(true);
|
ldapTestConfigTool.overrideConfigValue(ldapConfiguration, configKeyName, o);
|
||||||
|
|
||||||
ConfigKey key = (ConfigKey)configKey.get(ldapConfiguration);
|
|
||||||
|
|
||||||
Field modifiersField = Field.class.getDeclaredField("modifiers");
|
|
||||||
modifiersField.setAccessible(true);
|
|
||||||
modifiersField.setInt(configKey, configKey.getModifiers() & ~Modifier.FINAL);
|
|
||||||
|
|
||||||
Field f = ConfigKey.class.getDeclaredField("_value");
|
|
||||||
f.setAccessible(true);
|
|
||||||
modifiersField.setInt(f, f.getModifiers() & ~Modifier.FINAL);
|
|
||||||
f.set(key, o);
|
|
||||||
|
|
||||||
Field dynamic = ConfigKey.class.getDeclaredField("_isDynamic");
|
|
||||||
dynamic.setAccessible(true);
|
|
||||||
modifiersField.setInt(dynamic, dynamic.getModifiers() & ~Modifier.FINAL);
|
|
||||||
dynamic.setBoolean(key, false);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Before
|
@Before public void init() throws Exception {
|
||||||
public void init() throws Exception {
|
ldapConfigurationDao = new LdapConfigurationDaoImpl();
|
||||||
ldapConfigurationDao = new LdapConfigurationDaoImpl();
|
ldapConfiguration = new LdapConfiguration(ldapConfigurationDao);
|
||||||
ldapConfiguration = new LdapConfiguration(ldapConfigurationDao);;
|
;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test public void getAuthenticationReturnsSimple() throws Exception {
|
||||||
public void getAuthenticationReturnsSimple() throws Exception {
|
ldapTestConfigTool.overrideConfigValue(ldapConfiguration, "ldapBindPrincipal", "cn=bla");
|
||||||
overrideConfigValue("ldapBindPrincipal", "cn=bla");
|
ldapTestConfigTool.overrideConfigValue(ldapConfiguration, "ldapBindPassword", "pw");
|
||||||
overrideConfigValue("ldapBindPassword", "pw");
|
|
||||||
String authentication = ldapConfiguration.getAuthentication(null);
|
String authentication = ldapConfiguration.getAuthentication(null);
|
||||||
assertEquals("authentication should be set to simple", "simple", authentication);
|
assertEquals("authentication should be set to simple", "simple", authentication);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test public void getBaseDnReturnsABaseDn() throws Exception {
|
||||||
@Test
|
ldapTestConfigTool.overrideConfigValue(ldapConfiguration, "ldapBaseDn", "dc=cloudstack,dc=org");
|
||||||
public void getBaseDnReturnsABaseDn() throws Exception {
|
|
||||||
overrideConfigValue("ldapBaseDn", "dc=cloudstack,dc=org");
|
|
||||||
String baseDn = ldapConfiguration.getBaseDn(null);
|
String baseDn = ldapConfiguration.getBaseDn(null);
|
||||||
assertEquals("The set baseDn should be returned","dc=cloudstack,dc=org", baseDn);
|
assertEquals("The set baseDn should be returned", "dc=cloudstack,dc=org", baseDn);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test public void getGroupUniqueMemberAttribute() throws Exception {
|
||||||
public void getGroupUniqueMemberAttribute() throws Exception {
|
String[] groupNames = {"bla", "uniquemember", "memberuid", "", null};
|
||||||
String [] groupNames = {"bla", "uniquemember", "memberuid", "", null};
|
for (String groupObject : groupNames) {
|
||||||
for (String groupObject: groupNames) {
|
ldapTestConfigTool.overrideConfigValue(ldapConfiguration, "ldapGroupUniqueMemberAttribute", groupObject);
|
||||||
overrideConfigValue("ldapGroupUniqueMemberAttribute", groupObject);
|
|
||||||
String expectedResult = null;
|
String expectedResult = null;
|
||||||
if(groupObject == null) {
|
if (groupObject == null) {
|
||||||
expectedResult = "uniquemember";
|
expectedResult = "uniquemember";
|
||||||
} else {
|
} else {
|
||||||
expectedResult = groupObject;
|
expectedResult = groupObject;
|
||||||
};
|
}
|
||||||
|
;
|
||||||
String result = ldapConfiguration.getGroupUniqueMemberAttribute(null);
|
String result = ldapConfiguration.getGroupUniqueMemberAttribute(null);
|
||||||
assertEquals("testing for " + groupObject, expectedResult, result);
|
assertEquals("testing for " + groupObject, expectedResult, result);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test public void getSSLStatusCanBeTrue() throws Exception {
|
||||||
public void getSSLStatusCanBeTrue() throws Exception {
|
|
||||||
// given: "We have a ConfigDao with values for truststore and truststore password set"
|
// given: "We have a ConfigDao with values for truststore and truststore password set"
|
||||||
overrideConfigValue("ldapTrustStore", "/tmp/ldap.ts");
|
ldapTestConfigTool.overrideConfigValue(ldapConfiguration, "ldapTrustStore", "/tmp/ldap.ts");
|
||||||
overrideConfigValue("ldapTrustStorePassword", "password");
|
ldapTestConfigTool.overrideConfigValue(ldapConfiguration, "ldapTrustStorePassword", "password");
|
||||||
|
|
||||||
assertTrue("A request is made to get the status of SSL should result in true", ldapConfiguration.getSSLStatus());
|
assertTrue("A request is made to get the status of SSL should result in true", ldapConfiguration.getSSLStatus());
|
||||||
}
|
}
|
||||||
@Test
|
|
||||||
public void getSearchGroupPrincipleReturnsSuccessfully() throws Exception {
|
@Test public void getSearchGroupPrincipleReturnsSuccessfully() throws Exception {
|
||||||
// We have a ConfigDao with a value for ldap.search.group.principle and an LdapConfiguration
|
// We have a ConfigDao with a value for ldap.search.group.principle and an LdapConfiguration
|
||||||
overrideConfigValue("ldapSearchGroupPrinciple", "cn=cloudstack,cn=users,dc=cloudstack,dc=org");
|
ldapTestConfigTool.overrideConfigValue(ldapConfiguration, "ldapSearchGroupPrinciple", "cn=cloudstack,cn=users,dc=cloudstack,dc=org");
|
||||||
String result = ldapConfiguration.getSearchGroupPrinciple(null);
|
String result = ldapConfiguration.getSearchGroupPrinciple(null);
|
||||||
|
|
||||||
assertEquals("The result holds the same value configDao did", "cn=cloudstack,cn=users,dc=cloudstack,dc=org",result);
|
assertEquals("The result holds the same value configDao did", "cn=cloudstack,cn=users,dc=cloudstack,dc=org", result);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test public void getTrustStorePasswordResopnds() throws Exception {
|
||||||
public void getTrustStorePasswordResopnds() throws Exception {
|
|
||||||
// We have a ConfigDao with a value for truststore password
|
// We have a ConfigDao with a value for truststore password
|
||||||
overrideConfigValue("ldapTrustStorePassword", "password");
|
ldapTestConfigTool.overrideConfigValue(ldapConfiguration, "ldapTrustStorePassword", "password");
|
||||||
|
|
||||||
String result = ldapConfiguration.getTrustStorePassword();
|
String result = ldapConfiguration.getTrustStorePassword();
|
||||||
|
|
||||||
assertEquals("The result is password", "password", result);
|
assertEquals("The result is password", "password", result);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test public void getGroupObject() throws Exception {
|
||||||
@Test
|
String[] groupNames = {"bla", "groupOfUniqueNames", "groupOfNames", "", null};
|
||||||
public void getGroupObject() throws Exception {
|
for (String groupObject : groupNames) {
|
||||||
String [] groupNames = {"bla", "groupOfUniqueNames", "groupOfNames", "", null};
|
ldapTestConfigTool.overrideConfigValue(ldapConfiguration, "ldapGroupObject", groupObject);
|
||||||
for (String groupObject: groupNames) {
|
|
||||||
overrideConfigValue("ldapGroupObject", groupObject);
|
|
||||||
String expectedResult = null;
|
String expectedResult = null;
|
||||||
if(groupObject == null) {
|
if (groupObject == null) {
|
||||||
expectedResult = "groupOfUniqueNames";
|
expectedResult = "groupOfUniqueNames";
|
||||||
} else {
|
} else {
|
||||||
expectedResult = groupObject;
|
expectedResult = groupObject;
|
||||||
};
|
}
|
||||||
|
;
|
||||||
String result = ldapConfiguration.getGroupObject(null);
|
String result = ldapConfiguration.getGroupObject(null);
|
||||||
assertEquals("testing for " + groupObject, expectedResult, result);
|
assertEquals("testing for " + groupObject, expectedResult, result);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test public void getNullLdapProvider() {
|
||||||
|
assertEquals(LdapUserManager.Provider.OPENLDAP, ldapConfiguration.getLdapProvider(null));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@ -0,0 +1,210 @@
|
|||||||
|
/*
|
||||||
|
* 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 com.cloud.utils.Pair;
|
||||||
|
import org.apache.cloudstack.ldap.dao.LdapConfigurationDao;
|
||||||
|
import org.apache.directory.api.ldap.model.entry.DefaultEntry;
|
||||||
|
import org.apache.directory.api.ldap.model.entry.Entry;
|
||||||
|
import org.apache.directory.api.ldap.model.exception.LdapException;
|
||||||
|
import org.apache.directory.api.ldap.model.message.AddRequest;
|
||||||
|
import org.apache.directory.api.ldap.model.message.AddRequestImpl;
|
||||||
|
import org.apache.directory.api.ldap.model.message.AddResponse;
|
||||||
|
import org.apache.directory.ldap.client.api.LdapConnection;
|
||||||
|
import org.apache.directory.ldap.client.api.LdapNetworkConnection;
|
||||||
|
import org.apache.directory.server.core.api.DirectoryService;
|
||||||
|
import org.apache.directory.server.core.api.changelog.ChangeLog;
|
||||||
|
import org.apache.directory.server.ldap.LdapServer;
|
||||||
|
import org.apache.directory.server.xdbm.IndexNotFoundException;
|
||||||
|
import org.junit.After;
|
||||||
|
import org.junit.AfterClass;
|
||||||
|
import org.junit.Before;
|
||||||
|
import org.junit.BeforeClass;
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.junit.runner.RunWith;
|
||||||
|
import org.mockito.InjectMocks;
|
||||||
|
import org.mockito.Mock;
|
||||||
|
import org.mockito.runners.MockitoJUnitRunner;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import static org.junit.Assert.assertEquals;
|
||||||
|
import static org.junit.Assert.assertFalse;
|
||||||
|
import static org.junit.Assert.assertNotNull;
|
||||||
|
import static org.junit.Assert.assertTrue;
|
||||||
|
import static org.junit.Assert.fail;
|
||||||
|
import static org.mockito.Mockito.when;
|
||||||
|
|
||||||
|
@RunWith(MockitoJUnitRunner.class)
|
||||||
|
public class LdapDirectoryServerConnectionTest {
|
||||||
|
|
||||||
|
static EmbeddedLdapServer embeddedLdapServer;
|
||||||
|
|
||||||
|
@Mock
|
||||||
|
LdapConfigurationDao configurationDao;
|
||||||
|
|
||||||
|
LdapContextFactory contextFactory;
|
||||||
|
|
||||||
|
@Mock
|
||||||
|
LdapUserManagerFactory userManagerFactory;
|
||||||
|
|
||||||
|
@InjectMocks
|
||||||
|
LdapConfiguration configuration;
|
||||||
|
|
||||||
|
@InjectMocks
|
||||||
|
private LdapManagerImpl ldapManager;
|
||||||
|
|
||||||
|
private final LdapTestConfigTool ldapTestConfigTool = new LdapTestConfigTool();
|
||||||
|
|
||||||
|
@BeforeClass
|
||||||
|
public static void start() throws Exception {
|
||||||
|
embeddedLdapServer = new EmbeddedLdapServer();
|
||||||
|
embeddedLdapServer.init();
|
||||||
|
}
|
||||||
|
@Before
|
||||||
|
public void setup() throws Exception {
|
||||||
|
LdapConfigurationVO configurationVO = new LdapConfigurationVO("localhost",10389,null);
|
||||||
|
when(configurationDao.find("localhost",10389,null)).thenReturn(configurationVO);
|
||||||
|
ldapTestConfigTool.overrideConfigValue(configuration, "ldapBaseDn", "ou=system");
|
||||||
|
ldapTestConfigTool.overrideConfigValue(configuration, "ldapBindPassword", "secret");
|
||||||
|
ldapTestConfigTool.overrideConfigValue(configuration, "ldapBindPrincipal", "uid=admin,ou=system");
|
||||||
|
ldapTestConfigTool.overrideConfigValue(configuration, "ldapMemberOfAttribute", "memberOf");
|
||||||
|
when(userManagerFactory.getInstance(LdapUserManager.Provider.OPENLDAP)).thenReturn(new OpenLdapUserManagerImpl(configuration));
|
||||||
|
// construct an ellaborate structure around a single object
|
||||||
|
Pair<List<LdapConfigurationVO>, Integer> vos = new Pair<List<LdapConfigurationVO>, Integer>( Collections.singletonList(configurationVO),1);
|
||||||
|
when(configurationDao.searchConfigurations(null, 0, 1L)).thenReturn(vos);
|
||||||
|
|
||||||
|
contextFactory = new LdapContextFactory(configuration);
|
||||||
|
ldapManager = new LdapManagerImpl(configurationDao, contextFactory, userManagerFactory, configuration);
|
||||||
|
}
|
||||||
|
|
||||||
|
@After
|
||||||
|
public void cleanup() throws Exception {
|
||||||
|
contextFactory = null;
|
||||||
|
ldapManager = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@AfterClass
|
||||||
|
public static void stop() throws Exception {
|
||||||
|
embeddedLdapServer.destroy();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testEmbeddedLdapServerInitialization() throws IndexNotFoundException {
|
||||||
|
LdapServer ldapServer = embeddedLdapServer.getLdapServer();
|
||||||
|
assertNotNull(ldapServer);
|
||||||
|
|
||||||
|
DirectoryService directoryService = embeddedLdapServer.getDirectoryService();
|
||||||
|
assertNotNull(directoryService);
|
||||||
|
assertNotNull(directoryService.getSchemaPartition());
|
||||||
|
assertNotNull(directoryService.getSystemPartition());
|
||||||
|
assertNotNull(directoryService.getSchemaManager());
|
||||||
|
assertNotNull(directoryService.getDnFactory());
|
||||||
|
|
||||||
|
assertNotNull(directoryService.isDenormalizeOpAttrsEnabled());
|
||||||
|
|
||||||
|
ChangeLog changeLog = directoryService.getChangeLog();
|
||||||
|
|
||||||
|
assertNotNull(changeLog);
|
||||||
|
assertFalse(changeLog.isEnabled());
|
||||||
|
|
||||||
|
assertNotNull(directoryService.isStarted());
|
||||||
|
assertNotNull(ldapServer.isStarted());
|
||||||
|
|
||||||
|
List userList = new ArrayList(embeddedLdapServer.getUserIndexMap().keySet());
|
||||||
|
java.util.Collections.sort(userList);
|
||||||
|
List checkList = Arrays.asList("uid");
|
||||||
|
assertEquals(userList, checkList);
|
||||||
|
}
|
||||||
|
|
||||||
|
// @Test
|
||||||
|
public void testEmbeddedLdapAvailable() {
|
||||||
|
try {
|
||||||
|
List<LdapUser> usahs = ldapManager.getUsers(1L);
|
||||||
|
assertFalse("should find at least the admin user", usahs.isEmpty());
|
||||||
|
} catch (NoLdapUserMatchingQueryException e) {
|
||||||
|
fail(e.getLocalizedMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testSchemaLoading() {
|
||||||
|
try {
|
||||||
|
assertTrue("standard not loaded", embeddedLdapServer.addSchemaFromClasspath("other"));
|
||||||
|
// we need member of in ACS nowadays (backwards comptability broken):
|
||||||
|
// assertTrue("memberOf schema not loaded", embeddedLdapServer.addSchemaFromPath(new File("src/test/resources/memberOf"), "microsoft"));
|
||||||
|
} catch (LdapException | IOException e) {
|
||||||
|
fail(e.getLocalizedMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// @Test
|
||||||
|
public void testUserCreation() {
|
||||||
|
LdapConnection connection = new LdapNetworkConnection( "localhost", 10389 );
|
||||||
|
try {
|
||||||
|
connection.bind( "uid=admin,ou=system", "secret" );
|
||||||
|
|
||||||
|
connection.add(new DefaultEntry(
|
||||||
|
"ou=acsadmins,ou=users,ou=system",
|
||||||
|
"objectClass: organizationalUnit",
|
||||||
|
// might also need to be objectClass: top
|
||||||
|
"ou: acsadmins"
|
||||||
|
));
|
||||||
|
connection.add(new DefaultEntry(
|
||||||
|
"uid=dahn,ou=acsadmins,ou=users,ou=system",
|
||||||
|
"objectClass: inetOrgPerson",
|
||||||
|
"objectClass: top",
|
||||||
|
"cn: dahn",
|
||||||
|
"sn: Hoogland",
|
||||||
|
"givenName: Daan",
|
||||||
|
"mail: d@b.c",
|
||||||
|
"uid: dahn"
|
||||||
|
));
|
||||||
|
|
||||||
|
connection.add(
|
||||||
|
new DefaultEntry(
|
||||||
|
"cn=JuniorAdmins,ou=groups,ou=system", // The Dn
|
||||||
|
"objectClass: groupOfUniqueNames",
|
||||||
|
"ObjectClass: top",
|
||||||
|
"cn: JuniorAdmins",
|
||||||
|
"uniqueMember: uid=dahn,ou=acsadmins,ou=system,ou=users") );
|
||||||
|
|
||||||
|
assertTrue( connection.exists( "cn=JuniorAdmins,ou=groups,ou=system" ) );
|
||||||
|
assertTrue( connection.exists( "uid=dahn,ou=acsadmins,ou=users,ou=system" ) );
|
||||||
|
|
||||||
|
Entry ourUser = connection.lookup("uid=dahn,ou=acsadmins,ou=users,ou=system");
|
||||||
|
ourUser.add("memberOf", "cn=JuniorAdmins,ou=groups,ou=system");
|
||||||
|
AddRequest addRequest = new AddRequestImpl();
|
||||||
|
addRequest.setEntry( ourUser );
|
||||||
|
AddResponse response = connection.add( addRequest );
|
||||||
|
assertNotNull( response );
|
||||||
|
// We would need to either
|
||||||
|
// assertEquals( ResultCodeEnum.SUCCESS, response.getLdapResult().getResultCode() );
|
||||||
|
// or have the automatic virtual attribute
|
||||||
|
|
||||||
|
List<LdapUser> usahs = ldapManager.getUsers(1L);
|
||||||
|
assertEquals("now an admin and a normal user should be present",2, usahs.size());
|
||||||
|
|
||||||
|
} catch (LdapException | NoLdapUserMatchingQueryException e) {
|
||||||
|
fail(e.getLocalizedMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,48 @@
|
|||||||
|
/*
|
||||||
|
* 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 org.apache.cloudstack.framework.config.ConfigKey;
|
||||||
|
|
||||||
|
import java.lang.reflect.Field;
|
||||||
|
import java.lang.reflect.Modifier;
|
||||||
|
|
||||||
|
public class LdapTestConfigTool {
|
||||||
|
public LdapTestConfigTool() {
|
||||||
|
}
|
||||||
|
|
||||||
|
void overrideConfigValue(LdapConfiguration ldapConfiguration, final String configKeyName, final Object o) throws IllegalAccessException, NoSuchFieldException {
|
||||||
|
Field configKey = LdapConfiguration.class.getDeclaredField(configKeyName);
|
||||||
|
configKey.setAccessible(true);
|
||||||
|
|
||||||
|
ConfigKey key = (ConfigKey)configKey.get(ldapConfiguration);
|
||||||
|
|
||||||
|
Field modifiersField = Field.class.getDeclaredField("modifiers");
|
||||||
|
modifiersField.setAccessible(true);
|
||||||
|
modifiersField.setInt(configKey, configKey.getModifiers() & ~Modifier.FINAL);
|
||||||
|
|
||||||
|
Field f = ConfigKey.class.getDeclaredField("_value");
|
||||||
|
f.setAccessible(true);
|
||||||
|
modifiersField.setInt(f, f.getModifiers() & ~Modifier.FINAL);
|
||||||
|
f.set(key, o);
|
||||||
|
|
||||||
|
Field dynamic = ConfigKey.class.getDeclaredField("_isDynamic");
|
||||||
|
dynamic.setAccessible(true);
|
||||||
|
modifiersField.setInt(dynamic, dynamic.getModifiers() & ~Modifier.FINAL);
|
||||||
|
dynamic.setBoolean(key, false);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -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 org.apache.cloudstack.ldap;
|
||||||
|
|
||||||
|
import com.google.common.collect.Iterators;
|
||||||
|
import com.unboundid.ldap.sdk.LDAPConnection;
|
||||||
|
import com.unboundid.ldap.sdk.LDAPInterface;
|
||||||
|
import com.unboundid.ldap.sdk.SearchResult;
|
||||||
|
import com.unboundid.ldap.sdk.SearchScope;
|
||||||
|
import org.junit.Rule;
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.junit.runner.RunWith;
|
||||||
|
import org.mockito.runners.MockitoJUnitRunner;
|
||||||
|
import org.zapodot.junit.ldap.EmbeddedLdapRule;
|
||||||
|
import org.zapodot.junit.ldap.EmbeddedLdapRuleBuilder;
|
||||||
|
|
||||||
|
import javax.naming.Context;
|
||||||
|
import javax.naming.NamingEnumeration;
|
||||||
|
import javax.naming.directory.DirContext;
|
||||||
|
import javax.naming.directory.SearchControls;
|
||||||
|
|
||||||
|
import static org.junit.Assert.assertEquals;
|
||||||
|
import static org.junit.Assert.assertNotNull;
|
||||||
|
|
||||||
|
@RunWith(MockitoJUnitRunner.class)
|
||||||
|
public class LdapUnboundidZapdotConnectionTest {
|
||||||
|
private static final String DOMAIN_DSN;
|
||||||
|
|
||||||
|
static {
|
||||||
|
DOMAIN_DSN = "dc=cloudstack,dc=org";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Rule
|
||||||
|
public EmbeddedLdapRule embeddedLdapRule = EmbeddedLdapRuleBuilder
|
||||||
|
.newInstance()
|
||||||
|
.usingDomainDsn(DOMAIN_DSN)
|
||||||
|
.importingLdifs("unboundid.ldif")
|
||||||
|
.build();
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testLdapInteface() throws Exception {
|
||||||
|
// Test using the UnboundID LDAP SDK directly
|
||||||
|
final LDAPInterface ldapConnection = embeddedLdapRule.ldapConnection();
|
||||||
|
final SearchResult searchResult = ldapConnection.search(DOMAIN_DSN, SearchScope.SUB, "(objectClass=person)");
|
||||||
|
assertEquals(24, searchResult.getEntryCount());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testUnsharedLdapConnection() throws Exception {
|
||||||
|
// Test using the UnboundID LDAP SDK directly by using the UnboundID LDAPConnection type
|
||||||
|
final LDAPConnection ldapConnection = embeddedLdapRule.unsharedLdapConnection();
|
||||||
|
final SearchResult searchResult = ldapConnection.search(DOMAIN_DSN, SearchScope.SUB, "(objectClass=person)");
|
||||||
|
assertEquals(24, searchResult.getEntryCount());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testDirContext() throws Exception {
|
||||||
|
|
||||||
|
// Test using the good ol' JDNI-LDAP integration
|
||||||
|
final DirContext dirContext = embeddedLdapRule.dirContext();
|
||||||
|
final SearchControls searchControls = new SearchControls();
|
||||||
|
searchControls.setSearchScope(SearchControls.SUBTREE_SCOPE);
|
||||||
|
final NamingEnumeration<javax.naming.directory.SearchResult> resultNamingEnumeration =
|
||||||
|
dirContext.search(DOMAIN_DSN, "(objectClass=person)", searchControls);
|
||||||
|
assertEquals(24, Iterators.size(Iterators.forEnumeration(resultNamingEnumeration)));
|
||||||
|
}
|
||||||
|
@Test
|
||||||
|
public void testContext() throws Exception {
|
||||||
|
|
||||||
|
// Another test using the good ol' JDNI-LDAP integration, this time with the Context interface
|
||||||
|
final Context context = embeddedLdapRule.context();
|
||||||
|
final Object user = context.lookup("cn=Cammy Petri,dc=cloudstack,dc=org");
|
||||||
|
assertNotNull(user);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,62 @@
|
|||||||
|
// 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 org.junit.After;
|
||||||
|
import org.junit.Before;
|
||||||
|
import org.junit.Rule;
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.junit.runner.RunWith;
|
||||||
|
import org.mockito.runners.MockitoJUnitRunner;
|
||||||
|
|
||||||
|
import com.btmatthews.ldapunit.DirectoryTester;
|
||||||
|
import com.btmatthews.ldapunit.DirectoryServerConfiguration;
|
||||||
|
import com.btmatthews.ldapunit.DirectoryServerRule;
|
||||||
|
|
||||||
|
@RunWith(MockitoJUnitRunner.class)
|
||||||
|
@DirectoryServerConfiguration(ldifFiles = {LdapUnitConnectionTest.LDIF_FILE_NAME},
|
||||||
|
baseDN = LdapUnitConnectionTest.DOMAIN_DSN,
|
||||||
|
port = LdapUnitConnectionTest.PORT,
|
||||||
|
authDN = LdapUnitConnectionTest.BIND_DN,
|
||||||
|
authPassword = LdapUnitConnectionTest.SECRET)
|
||||||
|
public class LdapUnitConnectionTest {
|
||||||
|
static final String LDIF_FILE_NAME = "ldapunit.ldif";
|
||||||
|
static final String DOMAIN_DSN = "dc=am,dc=echt,dc=net";
|
||||||
|
static final String BIND_DN = "uid=admin,ou=cloudstack";
|
||||||
|
static final String SECRET = "secretzz";
|
||||||
|
static final int PORT =11389;
|
||||||
|
|
||||||
|
@Rule
|
||||||
|
public DirectoryServerRule directoryServerRule = new DirectoryServerRule();
|
||||||
|
|
||||||
|
private DirectoryTester directoryTester;
|
||||||
|
|
||||||
|
@Before
|
||||||
|
public void setUp() {
|
||||||
|
directoryTester = new DirectoryTester("localhost", PORT, BIND_DN, SECRET);
|
||||||
|
}
|
||||||
|
|
||||||
|
@After
|
||||||
|
public void tearDown() {
|
||||||
|
directoryTester.disconnect();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testLdapInteface() throws Exception {
|
||||||
|
directoryTester.assertDNExists("dc=am,dc=echt,dc=net");
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -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 org.junit.Before;
|
||||||
|
import org.junit.BeforeClass;
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.junit.runner.RunWith;
|
||||||
|
import org.mockito.InjectMocks;
|
||||||
|
import org.mockito.Mock;
|
||||||
|
import org.mockito.Spy;
|
||||||
|
import org.mockito.runners.MockitoJUnitRunner;
|
||||||
|
import org.springframework.beans.factory.config.AutowireCapableBeanFactory;
|
||||||
|
import org.springframework.context.ApplicationContext;
|
||||||
|
|
||||||
|
import static org.junit.Assert.assertTrue;
|
||||||
|
|
||||||
|
@RunWith(MockitoJUnitRunner.class)
|
||||||
|
public class LdapUserManagerFactoryTest {
|
||||||
|
|
||||||
|
@Mock
|
||||||
|
ApplicationContext applicationCtx;
|
||||||
|
|
||||||
|
@Mock
|
||||||
|
AutowireCapableBeanFactory autowireCapableBeanFactory;
|
||||||
|
|
||||||
|
@Mock
|
||||||
|
protected LdapConfiguration _ldapConfiguration;
|
||||||
|
|
||||||
|
@Spy
|
||||||
|
@InjectMocks
|
||||||
|
static LdapUserManagerFactory ldapUserManagerFactory = new LdapUserManagerFactory();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* circumvent springframework for these {code ManagerImpl}
|
||||||
|
*/
|
||||||
|
@BeforeClass
|
||||||
|
public static void init()
|
||||||
|
{
|
||||||
|
ldapUserManagerFactory.ldapUserManagerMap.put(LdapUserManager.Provider.MICROSOFTAD, new ADLdapUserManagerImpl());
|
||||||
|
ldapUserManagerFactory.ldapUserManagerMap.put(LdapUserManager.Provider.OPENLDAP, new OpenLdapUserManagerImpl());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Before
|
||||||
|
public void setup() {
|
||||||
|
|
||||||
|
}
|
||||||
|
@Test
|
||||||
|
public void getOpenLdapInstance() {
|
||||||
|
LdapUserManager userManager = ldapUserManagerFactory.getInstance(LdapUserManager.Provider.OPENLDAP);
|
||||||
|
assertTrue("x dude", userManager instanceof OpenLdapUserManagerImpl);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void getMSADInstance() {
|
||||||
|
LdapUserManager userManager = ldapUserManagerFactory.getInstance(LdapUserManager.Provider.MICROSOFTAD);
|
||||||
|
assertTrue("wrong dude", userManager instanceof ADLdapUserManagerImpl);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,151 @@
|
|||||||
|
# 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.
|
||||||
|
version: 1
|
||||||
|
|
||||||
|
dn: ou=groups,dc=am,dc=echt,dc=net
|
||||||
|
objectClass: organizationalUnit
|
||||||
|
objectClass: top
|
||||||
|
ou: Groups
|
||||||
|
|
||||||
|
dn: cn=JuniorAdmins,ou=groups,dc=am,dc=echt,dc=net
|
||||||
|
objectClass: groupOfUniqueNames
|
||||||
|
objectClass: top
|
||||||
|
cn: JuniorAdmins
|
||||||
|
uniqueMember: uid=demo,ou=acsadmins,dc=am,dc=echt,dc=net
|
||||||
|
uniqueMember: uid=demo2,ou=acsadmins,dc=am,dc=echt,dc=net
|
||||||
|
uniqueMember: uid=double,ou=acsadmins,dc=am,dc=echt,dc=net
|
||||||
|
|
||||||
|
dn: ou=acsadmins,dc=am,dc=echt,dc=net
|
||||||
|
objectClass: organizationalUnit
|
||||||
|
objectClass: top
|
||||||
|
ou: acsadmins
|
||||||
|
|
||||||
|
dn: uid=dahn,ou=acsadmins,dc=am,dc=echt,dc=net
|
||||||
|
objectClass: person
|
||||||
|
objectClass: organizationalPerson
|
||||||
|
objectClass: top
|
||||||
|
objectClass: inetOrgPerson
|
||||||
|
cn: dahn
|
||||||
|
sn: Hoogland
|
||||||
|
givenName: Daan
|
||||||
|
mail: d@b.c
|
||||||
|
uid: dahn
|
||||||
|
|
||||||
|
dn: uid=demo,ou=acsadmins,dc=am,dc=echt,dc=net
|
||||||
|
objectClass: person
|
||||||
|
objectClass: organizationalPerson
|
||||||
|
objectClass: top
|
||||||
|
objectClass: inetOrgPerson
|
||||||
|
cn: demo
|
||||||
|
sn: User
|
||||||
|
givenName: demo
|
||||||
|
mail: d@b.c
|
||||||
|
uid: demo
|
||||||
|
|
||||||
|
dn: cn=SeniorAdmins,ou=groups,dc=am,dc=echt,dc=net
|
||||||
|
objectClass: groupOfUniqueNames
|
||||||
|
objectClass: top
|
||||||
|
cn: SeniorAdmins
|
||||||
|
uniqueMember: uid=pga,ou=acsadmins,dc=am,dc=echt,dc=net
|
||||||
|
uniqueMember: uid=demo4,ou=acsadmins,dc=am,dc=echt,dc=net
|
||||||
|
|
||||||
|
dn: cn=admins,ou=acsadmins,dc=am,dc=echt,dc=net
|
||||||
|
objectClass: groupOfNames
|
||||||
|
objectClass: top
|
||||||
|
cn: admins
|
||||||
|
member: uid=dahn,ou=acsadmins,dc=am,dc=echt,dc=net
|
||||||
|
member: uid=demo,ou=acsadmins,dc=am,dc=echt,dc=net
|
||||||
|
member: uid=demo2,ou=acsadmins,dc=am,dc=echt,dc=net
|
||||||
|
member: uid=demo3,ou=acsadmins,dc=am,dc=echt,dc=net
|
||||||
|
member: uid=demo4,ou=acsadmins,dc=am,dc=echt,dc=net
|
||||||
|
member: uid=pga,ou=acsadmins,dc=am,dc=echt,dc=net
|
||||||
|
member: uid=double,ou=acsadmins,dc=am,dc=echt,dc=net
|
||||||
|
|
||||||
|
dn: uid=pga,ou=acsadmins,dc=am,dc=echt,dc=net
|
||||||
|
objectClass: person
|
||||||
|
objectClass: organizationalperson
|
||||||
|
objectClass: top
|
||||||
|
objectClass: inetorgperson
|
||||||
|
cn: Paul Angus
|
||||||
|
sn: angus
|
||||||
|
givenName: paul
|
||||||
|
mail: paul.angus@shapeblue.com
|
||||||
|
uid: pga
|
||||||
|
|
||||||
|
dn: uid=demo2,ou=acsadmins,dc=am,dc=echt,dc=net
|
||||||
|
objectClass: person
|
||||||
|
objectClass: organizationalPerson
|
||||||
|
objectClass: top
|
||||||
|
objectClass: inetOrgPerson
|
||||||
|
cn: demo
|
||||||
|
sn: User
|
||||||
|
givenName: demo
|
||||||
|
mail: d@b.c
|
||||||
|
uid: demo2
|
||||||
|
|
||||||
|
dn: uid=demo3,ou=acsadmins,dc=am,dc=echt,dc=net
|
||||||
|
objectClass: person
|
||||||
|
objectClass: organizationalPerson
|
||||||
|
objectClass: top
|
||||||
|
objectClass: inetOrgPerson
|
||||||
|
cn: demo
|
||||||
|
sn: User
|
||||||
|
givenName: demo
|
||||||
|
mail: d@b.c
|
||||||
|
uid: demo3
|
||||||
|
|
||||||
|
dn: uid=demo4,ou=acsadmins,dc=am,dc=echt,dc=net
|
||||||
|
objectClass: person
|
||||||
|
objectClass: organizationalPerson
|
||||||
|
objectClass: top
|
||||||
|
objectClass: inetOrgPerson
|
||||||
|
cn: demo
|
||||||
|
sn: User
|
||||||
|
givenName: demo
|
||||||
|
mail: d@b.c
|
||||||
|
uid: demo4
|
||||||
|
|
||||||
|
dn: cn=Admins,ou=groups,dc=am,dc=echt,dc=net
|
||||||
|
objectClass: groupOfUniqueNames
|
||||||
|
objectClass: top
|
||||||
|
cn: Admins
|
||||||
|
uniqueMember: uid=dahn,ou=acsadmins,dc=am,dc=echt,dc=net
|
||||||
|
uniqueMember: uid=demo3,ou=acsadmins,dc=am,dc=echt,dc=net
|
||||||
|
uniqueMember: uid=double,ou=acsadmins,dc=am,dc=echt,dc=net
|
||||||
|
uniqueMember: uid=noadmin,ou=acsadmins,dc=am,dc=echt,dc=net
|
||||||
|
|
||||||
|
dn: uid=double,ou=acsadmins,dc=am,dc=echt,dc=net
|
||||||
|
objectClass: person
|
||||||
|
objectClass: organizationalPerson
|
||||||
|
objectClass: top
|
||||||
|
objectClass: inetOrgPerson
|
||||||
|
cn: demo
|
||||||
|
sn: User
|
||||||
|
givenName: demo
|
||||||
|
mail: d@b.c
|
||||||
|
uid: double
|
||||||
|
|
||||||
|
dn: uid=noadmin,ou=acsadmins,dc=am,dc=echt,dc=net
|
||||||
|
objectClass: person
|
||||||
|
objectClass: organizationalPerson
|
||||||
|
objectClass: top
|
||||||
|
objectClass: inetOrgPerson
|
||||||
|
cn: demo
|
||||||
|
sn: User
|
||||||
|
givenName: demo
|
||||||
|
mail: d@b.c
|
||||||
|
uid: noadmin
|
||||||
78
plugins/user-authenticators/ldap/src/test/resources/log4j.xml
Executable file
78
plugins/user-authenticators/ldap/src/test/resources/log4j.xml
Executable file
@ -0,0 +1,78 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!--
|
||||||
|
|
||||||
|
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.
|
||||||
|
|
||||||
|
-->
|
||||||
|
<!DOCTYPE log4j:configuration SYSTEM "log4j.dtd">
|
||||||
|
|
||||||
|
<log4j:configuration xmlns:log4j="http://jakarta.apache.org/log4j/" debug="false">
|
||||||
|
|
||||||
|
<throwableRenderer class="com.cloud.utils.log.CglibThrowableRenderer"/>
|
||||||
|
<!-- ================================= -->
|
||||||
|
<!-- Preserve messages in a local file -->
|
||||||
|
<!-- ================================= -->
|
||||||
|
|
||||||
|
<!-- ============================== -->
|
||||||
|
<!-- Append messages to the console -->
|
||||||
|
<!-- ============================== -->
|
||||||
|
|
||||||
|
<appender name="CONSOLE" class="org.apache.log4j.ConsoleAppender">
|
||||||
|
<param name="Target" value="System.out"/>
|
||||||
|
<param name="Threshold" value="TRACE"/>
|
||||||
|
<layout class="org.apache.log4j.PatternLayout">
|
||||||
|
<param name="ConversionPattern" value="%d{ISO8601} %-5p [%c{3}] (%t:%x) %m%n"/>
|
||||||
|
</layout>
|
||||||
|
</appender>
|
||||||
|
|
||||||
|
<!-- ================ -->
|
||||||
|
<!-- Limit categories -->
|
||||||
|
<!-- ================ -->
|
||||||
|
|
||||||
|
<category name="com.cloud">
|
||||||
|
<priority value="DEBUG"/>
|
||||||
|
</category>
|
||||||
|
|
||||||
|
<!-- Limit the org.apache category to INFO as its DEBUG is verbose -->
|
||||||
|
<category name="org.apache.cloudstack">
|
||||||
|
<priority value="DEBUG"/>
|
||||||
|
</category>
|
||||||
|
|
||||||
|
<category name="org.apache.directory">
|
||||||
|
<priority value="WARN"/>
|
||||||
|
</category>
|
||||||
|
|
||||||
|
<category name="org.apache.directory.api.ldap.model.entry.Value">
|
||||||
|
<priority value="FATAL"/>
|
||||||
|
</category>
|
||||||
|
|
||||||
|
<category name="org.apache.directory.api.ldap.model.entry.DefaultAttribute">
|
||||||
|
<priority value="FATAL"/>
|
||||||
|
</category>
|
||||||
|
|
||||||
|
|
||||||
|
<!-- ======================= -->
|
||||||
|
<!-- Setup the Root category -->
|
||||||
|
<!-- ======================= -->
|
||||||
|
|
||||||
|
<root>
|
||||||
|
<level value="INFO"/>
|
||||||
|
<appender-ref ref="CONSOLE"/>
|
||||||
|
</root>
|
||||||
|
|
||||||
|
</log4j:configuration>
|
||||||
243
plugins/user-authenticators/ldap/src/test/resources/minimal.ldif
Normal file
243
plugins/user-authenticators/ldap/src/test/resources/minimal.ldif
Normal file
@ -0,0 +1,243 @@
|
|||||||
|
# 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.
|
||||||
|
version: 1
|
||||||
|
|
||||||
|
dn: dc=am,dc=echt,dc=net
|
||||||
|
objectClass: domain
|
||||||
|
objectClass: top
|
||||||
|
dc: am
|
||||||
|
|
||||||
|
dn: ou=groups,dc=am,dc=echt,dc=net
|
||||||
|
objectClass: organizationalUnit
|
||||||
|
objectClass: top
|
||||||
|
ou: Groups
|
||||||
|
|
||||||
|
dn: cn=JuniorAdmins,ou=groups,dc=am,dc=echt,dc=net
|
||||||
|
objectClass: groupOfUniqueNames
|
||||||
|
objectClass: top
|
||||||
|
cn: JuniorAdmins
|
||||||
|
uniqueMember: uid=demo,ou=acsadmins,dc=am,dc=echt,dc=net
|
||||||
|
uniqueMember: uid=demo2,ou=acsadmins,dc=am,dc=echt,dc=net
|
||||||
|
uniqueMember: uid=double,ou=acsadmins,dc=am,dc=echt,dc=net
|
||||||
|
|
||||||
|
dn: ou=acsadmins,dc=am,dc=echt,dc=net
|
||||||
|
objectClass: organizationalUnit
|
||||||
|
objectClass: top
|
||||||
|
ou: acsadmins
|
||||||
|
|
||||||
|
dn: uid=dahn,ou=acsadmins,dc=am,dc=echt,dc=net
|
||||||
|
objectClass: person
|
||||||
|
objectClass: organizationalPerson
|
||||||
|
objectClass: top
|
||||||
|
objectClass: inetOrgPerson
|
||||||
|
objectClass: sunFederationManagerDataStore
|
||||||
|
cn: dahn
|
||||||
|
sn: Hoogland
|
||||||
|
givenName: Daan
|
||||||
|
inetUserStatus: Active
|
||||||
|
mail: d@b.c
|
||||||
|
uid: dahn
|
||||||
|
|
||||||
|
dn: uid=demo,ou=acsadmins,dc=am,dc=echt,dc=net
|
||||||
|
objectClass: iplanet-am-user-service
|
||||||
|
objectClass: person
|
||||||
|
objectClass: organizationalPerson
|
||||||
|
objectClass: sunAMAuthAccountLockout
|
||||||
|
objectClass: iPlanetPreferences
|
||||||
|
objectClass: top
|
||||||
|
objectClass: sunIdentityServerLibertyPPService
|
||||||
|
objectClass: sunFMSAML2NameIdentifier
|
||||||
|
objectClass: forgerock-am-dashboard-service
|
||||||
|
objectClass: inetOrgPerson
|
||||||
|
objectClass: sunFederationManagerDataStore
|
||||||
|
objectClass: devicePrintProfilesContainer
|
||||||
|
objectClass: iplanet-am-auth-configuration-service
|
||||||
|
objectClass: iplanet-am-managed-person
|
||||||
|
objectClass: inetuser
|
||||||
|
cn: demo
|
||||||
|
sn: User
|
||||||
|
givenName: demo
|
||||||
|
inetUserStatus: Active
|
||||||
|
mail: d@b.c
|
||||||
|
uid: demo
|
||||||
|
|
||||||
|
dn: cn=SeniorAdmins,ou=groups,dc=am,dc=echt,dc=net
|
||||||
|
objectClass: groupOfUniqueNames
|
||||||
|
objectClass: top
|
||||||
|
cn: SeniorAdmins
|
||||||
|
uniqueMember: uid=pga,ou=acsadmins,dc=am,dc=echt,dc=net
|
||||||
|
uniqueMember: uid=demo4,ou=acsadmins,dc=am,dc=echt,dc=net
|
||||||
|
|
||||||
|
dn: cn=admins,ou=acsadmins,dc=am,dc=echt,dc=net
|
||||||
|
objectClass: groupOfNames
|
||||||
|
objectClass: top
|
||||||
|
cn: admins
|
||||||
|
member: uid=dahn,ou=acsadmins,dc=am,dc=echt,dc=net
|
||||||
|
member: uid=demo,ou=acsadmins,dc=am,dc=echt,dc=net
|
||||||
|
member: uid=demo2,ou=acsadmins,dc=am,dc=echt,dc=net
|
||||||
|
member: uid=demo3,ou=acsadmins,dc=am,dc=echt,dc=net
|
||||||
|
member: uid=demo4,ou=acsadmins,dc=am,dc=echt,dc=net
|
||||||
|
member: uid=pga,ou=acsadmins,dc=am,dc=echt,dc=net
|
||||||
|
member: uid=double,ou=acsadmins,dc=am,dc=echt,dc=net
|
||||||
|
|
||||||
|
dn: uid=pga,ou=acsadmins,dc=am,dc=echt,dc=net
|
||||||
|
objectClass: iplanet-am-user-service
|
||||||
|
objectClass: person
|
||||||
|
objectClass: organizationalperson
|
||||||
|
objectClass: sunAMAuthAccountLockout
|
||||||
|
objectClass: iPlanetPreferences
|
||||||
|
objectClass: top
|
||||||
|
objectClass: sunIdentityServerLibertyPPService
|
||||||
|
objectClass: sunFMSAML2NameIdentifier
|
||||||
|
objectClass: forgerock-am-dashboard-service
|
||||||
|
objectClass: inetorgperson
|
||||||
|
objectClass: sunFederationManagerDataStore
|
||||||
|
objectClass: devicePrintProfilesContainer
|
||||||
|
objectClass: iplanet-am-auth-configuration-service
|
||||||
|
objectClass: iplanet-am-managed-person
|
||||||
|
objectClass: inetuser
|
||||||
|
cn: Paul Angus
|
||||||
|
sn: angus
|
||||||
|
givenName: paul
|
||||||
|
inetUserStatus: Active
|
||||||
|
mail: paul.angus@shapeblue.com
|
||||||
|
uid: pga
|
||||||
|
|
||||||
|
dn: uid=demo2,ou=acsadmins,dc=am,dc=echt,dc=net
|
||||||
|
objectClass: iplanet-am-user-service
|
||||||
|
objectClass: person
|
||||||
|
objectClass: organizationalPerson
|
||||||
|
objectClass: sunAMAuthAccountLockout
|
||||||
|
objectClass: iPlanetPreferences
|
||||||
|
objectClass: top
|
||||||
|
objectClass: sunIdentityServerLibertyPPService
|
||||||
|
objectClass: sunFMSAML2NameIdentifier
|
||||||
|
objectClass: forgerock-am-dashboard-service
|
||||||
|
objectClass: inetOrgPerson
|
||||||
|
objectClass: sunFederationManagerDataStore
|
||||||
|
objectClass: devicePrintProfilesContainer
|
||||||
|
objectClass: iplanet-am-auth-configuration-service
|
||||||
|
objectClass: iplanet-am-managed-person
|
||||||
|
objectClass: inetuser
|
||||||
|
cn: demo
|
||||||
|
sn: User
|
||||||
|
givenName: demo
|
||||||
|
inetUserStatus: Active
|
||||||
|
mail: d@b.c
|
||||||
|
uid: demo2
|
||||||
|
|
||||||
|
dn: uid=demo3,ou=acsadmins,dc=am,dc=echt,dc=net
|
||||||
|
objectClass: iplanet-am-user-service
|
||||||
|
objectClass: person
|
||||||
|
objectClass: organizationalPerson
|
||||||
|
objectClass: sunAMAuthAccountLockout
|
||||||
|
objectClass: iPlanetPreferences
|
||||||
|
objectClass: top
|
||||||
|
objectClass: sunIdentityServerLibertyPPService
|
||||||
|
objectClass: sunFMSAML2NameIdentifier
|
||||||
|
objectClass: forgerock-am-dashboard-service
|
||||||
|
objectClass: inetOrgPerson
|
||||||
|
objectClass: sunFederationManagerDataStore
|
||||||
|
objectClass: devicePrintProfilesContainer
|
||||||
|
objectClass: iplanet-am-auth-configuration-service
|
||||||
|
objectClass: iplanet-am-managed-person
|
||||||
|
objectClass: inetuser
|
||||||
|
cn: demo
|
||||||
|
sn: User
|
||||||
|
givenName: demo
|
||||||
|
inetUserStatus: Active
|
||||||
|
mail: d@b.c
|
||||||
|
uid: demo3
|
||||||
|
|
||||||
|
dn: uid=demo4,ou=acsadmins,dc=am,dc=echt,dc=net
|
||||||
|
objectClass: iplanet-am-user-service
|
||||||
|
objectClass: person
|
||||||
|
objectClass: organizationalPerson
|
||||||
|
objectClass: sunAMAuthAccountLockout
|
||||||
|
objectClass: iPlanetPreferences
|
||||||
|
objectClass: top
|
||||||
|
objectClass: sunIdentityServerLibertyPPService
|
||||||
|
objectClass: sunFMSAML2NameIdentifier
|
||||||
|
objectClass: forgerock-am-dashboard-service
|
||||||
|
objectClass: inetOrgPerson
|
||||||
|
objectClass: sunFederationManagerDataStore
|
||||||
|
objectClass: devicePrintProfilesContainer
|
||||||
|
objectClass: iplanet-am-auth-configuration-service
|
||||||
|
objectClass: iplanet-am-managed-person
|
||||||
|
objectClass: inetuser
|
||||||
|
cn: demo
|
||||||
|
sn: User
|
||||||
|
givenName: demo
|
||||||
|
inetUserStatus: Active
|
||||||
|
mail: d@b.c
|
||||||
|
uid: demo4
|
||||||
|
|
||||||
|
dn: cn=Admins,ou=groups,dc=am,dc=echt,dc=net
|
||||||
|
objectClass: groupOfUniqueNames
|
||||||
|
objectClass: top
|
||||||
|
cn: Admins
|
||||||
|
uniqueMember: uid=dahn,ou=acsadmins,dc=am,dc=echt,dc=net
|
||||||
|
uniqueMember: uid=demo3,ou=acsadmins,dc=am,dc=echt,dc=net
|
||||||
|
uniqueMember: uid=double,ou=acsadmins,dc=am,dc=echt,dc=net
|
||||||
|
uniqueMember: uid=noadmin,ou=acsadmins,dc=am,dc=echt,dc=net
|
||||||
|
|
||||||
|
dn: uid=double,ou=acsadmins,dc=am,dc=echt,dc=net
|
||||||
|
objectClass: iplanet-am-user-service
|
||||||
|
objectClass: person
|
||||||
|
objectClass: organizationalPerson
|
||||||
|
objectClass: sunAMAuthAccountLockout
|
||||||
|
objectClass: iPlanetPreferences
|
||||||
|
objectClass: top
|
||||||
|
objectClass: sunIdentityServerLibertyPPService
|
||||||
|
objectClass: sunFMSAML2NameIdentifier
|
||||||
|
objectClass: forgerock-am-dashboard-service
|
||||||
|
objectClass: inetOrgPerson
|
||||||
|
objectClass: sunFederationManagerDataStore
|
||||||
|
objectClass: devicePrintProfilesContainer
|
||||||
|
objectClass: iplanet-am-auth-configuration-service
|
||||||
|
objectClass: iplanet-am-managed-person
|
||||||
|
objectClass: inetuser
|
||||||
|
cn: demo
|
||||||
|
sn: User
|
||||||
|
givenName: demo
|
||||||
|
inetUserStatus: Active
|
||||||
|
mail: d@b.c
|
||||||
|
uid: double
|
||||||
|
|
||||||
|
dn: uid=noadmin,ou=acsadmins,dc=am,dc=echt,dc=net
|
||||||
|
objectClass: iplanet-am-user-service
|
||||||
|
objectClass: person
|
||||||
|
objectClass: organizationalPerson
|
||||||
|
objectClass: sunAMAuthAccountLockout
|
||||||
|
objectClass: iPlanetPreferences
|
||||||
|
objectClass: top
|
||||||
|
objectClass: sunIdentityServerLibertyPPService
|
||||||
|
objectClass: sunFMSAML2NameIdentifier
|
||||||
|
objectClass: forgerock-am-dashboard-service
|
||||||
|
objectClass: inetOrgPerson
|
||||||
|
objectClass: sunFederationManagerDataStore
|
||||||
|
objectClass: devicePrintProfilesContainer
|
||||||
|
objectClass: iplanet-am-auth-configuration-service
|
||||||
|
objectClass: iplanet-am-managed-person
|
||||||
|
objectClass: inetuser
|
||||||
|
cn: demo
|
||||||
|
sn: User
|
||||||
|
givenName: demo
|
||||||
|
inetUserStatus: Active
|
||||||
|
mail: d@b.c
|
||||||
|
uid: noadmin
|
||||||
|
|
||||||
@ -0,0 +1,37 @@
|
|||||||
|
<!--
|
||||||
|
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.
|
||||||
|
-->
|
||||||
|
<beans xmlns="http://www.springframework.org/schema/beans"
|
||||||
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
xmlns:context="http://www.springframework.org/schema/context"
|
||||||
|
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
|
||||||
|
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
|
||||||
|
|
||||||
|
<bean id="LdapAuthenticator" class="org.apache.cloudstack.ldap.LdapAuthenticator">
|
||||||
|
<property name="name" value="LDAP" />
|
||||||
|
</bean>
|
||||||
|
|
||||||
|
<bean id="LdapManager" class="org.apache.cloudstack.ldap.LdapManagerImpl" />
|
||||||
|
<bean id="LdapUserManagerFactory" class="org.apache.cloudstack.ldap.LdapUserManagerFactory" />
|
||||||
|
<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" />
|
||||||
|
<bean id="LdapTrustMapDao" class="org.apache.cloudstack.ldap.dao.LdapTrustMapDaoImpl" />
|
||||||
|
|
||||||
|
</beans>
|
||||||
@ -0,0 +1,311 @@
|
|||||||
|
# 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.
|
||||||
|
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=
|
||||||
|
|
||||||
|
dn: cn=Barbara Brewer,dc=cloudstack,dc=org
|
||||||
|
objectClass: inetOrgPerson
|
||||||
|
objectClass: organizationalPerson
|
||||||
|
objectClass: person
|
||||||
|
objectClass: top
|
||||||
|
cn: Barbara Brewer
|
||||||
|
sn: Brewer
|
||||||
|
mail: bbrewer@cloudstack.org
|
||||||
|
uid: bbrewer
|
||||||
|
userpassword:: cGFzc3dvcmQ=
|
||||||
|
|
||||||
|
dn: cn=Zak Wilkinson,dc=cloudstack,dc=org
|
||||||
|
objectClass: inetOrgPerson
|
||||||
|
objectClass: organizationalPerson
|
||||||
|
objectClass: person
|
||||||
|
objectClass: top
|
||||||
|
cn: Zak Wilkinson
|
||||||
|
givenname: Zak
|
||||||
|
sn: Wilkinson
|
||||||
|
uid: zwilkinson
|
||||||
|
userpassword:: cGFzc3dvcmQ=
|
||||||
|
|
||||||
|
dn: cn=Archie Shingleton,dc=cloudstack,dc=org
|
||||||
|
objectClass: inetOrgPerson
|
||||||
|
objectClass: organizationalPerson
|
||||||
|
objectClass: person
|
||||||
|
objectClass: top
|
||||||
|
cn: Archie Shingleton
|
||||||
|
sn: Shingleton
|
||||||
|
givenName: Archie
|
||||||
|
mail: ashingleton@cloudstack.org
|
||||||
|
uid: ashingleton
|
||||||
|
userpassword:: cGFzc3dvcmQ=
|
||||||
|
|
||||||
|
dn: cn=Cletus Pears,dc=cloudstack,dc=org
|
||||||
|
objectClass: inetOrgPerson
|
||||||
|
objectClass: organizationalPerson
|
||||||
|
objectClass: person
|
||||||
|
objectClass: top
|
||||||
|
cn: Cletus Pears
|
||||||
|
sn: Pears
|
||||||
|
givenName: Cletus
|
||||||
|
mail: cpears@cloudstack.org
|
||||||
|
uid: cpears
|
||||||
|
userpassword:: cGFzc3dvcmQ=
|
||||||
|
|
||||||
|
dn: cn=Teisha Milewski,dc=cloudstack,dc=org
|
||||||
|
objectClass: inetOrgPerson
|
||||||
|
objectClass: organizationalPerson
|
||||||
|
objectClass: person
|
||||||
|
objectClass: top
|
||||||
|
cn: Teisha Milewski
|
||||||
|
sn: Milewski
|
||||||
|
givenName: Teisha
|
||||||
|
mail: tmilewski@cloudstack.org
|
||||||
|
uid: tmilewski
|
||||||
|
userpassword:: cGFzc3dvcmQ=
|
||||||
|
|
||||||
|
dn: cn=Eloy Para,dc=cloudstack,dc=org
|
||||||
|
objectClass: inetOrgPerson
|
||||||
|
objectClass: organizationalPerson
|
||||||
|
objectClass: person
|
||||||
|
objectClass: top
|
||||||
|
cn: Eloy Para
|
||||||
|
sn: Para
|
||||||
|
givenName: Eloy
|
||||||
|
mail: epara@cloudstack.org
|
||||||
|
uid: epara
|
||||||
|
userpassword:: cGFzc3dvcmQ=
|
||||||
|
|
||||||
|
dn: cn=Elaine Lamb,dc=cloudstack,dc=org
|
||||||
|
objectClass: inetOrgPerson
|
||||||
|
objectClass: organizationalPerson
|
||||||
|
objectClass: person
|
||||||
|
objectClass: top
|
||||||
|
cn: Elaine Lamb
|
||||||
|
sn: Lamb
|
||||||
|
givenName: Elaine
|
||||||
|
mail: elamb@cloudstack.org
|
||||||
|
uid: elamb
|
||||||
|
userpassword:: cGFzc3dvcmQ=
|
||||||
|
|
||||||
|
dn: cn=Soon Griffen,dc=cloudstack,dc=org
|
||||||
|
objectClass: inetOrgPerson
|
||||||
|
objectClass: organizationalPerson
|
||||||
|
objectClass: person
|
||||||
|
objectClass: top
|
||||||
|
cn: Soon Griffen
|
||||||
|
sn: Griffen
|
||||||
|
givenName: Soon
|
||||||
|
mail: sgriffen@cloudstack.org
|
||||||
|
uid: sgriffen
|
||||||
|
userpassword:: cGFzc3dvcmQ=
|
||||||
|
|
||||||
|
dn: cn=Tran Neisler,dc=cloudstack,dc=org
|
||||||
|
objectClass: inetOrgPerson
|
||||||
|
objectClass: organizationalPerson
|
||||||
|
objectClass: person
|
||||||
|
objectClass: top
|
||||||
|
cn: Tran Neisler
|
||||||
|
sn: Neisler
|
||||||
|
givenName: Tran
|
||||||
|
mail: tneisler@cloudstack.org
|
||||||
|
uid: tneisler
|
||||||
|
userpassword:: cGFzc3dvcmQ=
|
||||||
|
|
||||||
|
dn: cn=Mirella Zeck,dc=cloudstack,dc=org
|
||||||
|
objectClass: inetOrgPerson
|
||||||
|
objectClass: organizationalPerson
|
||||||
|
objectClass: person
|
||||||
|
objectClass: top
|
||||||
|
cn: Mirella Zeck
|
||||||
|
sn: Zeck
|
||||||
|
givenName: Mirella
|
||||||
|
mail: mzeck@cloudstack.org
|
||||||
|
uid: mzeck
|
||||||
|
userpassword:: cGFzc3dvcmQ=
|
||||||
|
|
||||||
|
dn: cn=Greg Hoskin,dc=cloudstack,dc=org
|
||||||
|
objectClass: inetOrgPerson
|
||||||
|
objectClass: organizationalPerson
|
||||||
|
objectClass: person
|
||||||
|
objectClass: top
|
||||||
|
cn: Greg Hoskin
|
||||||
|
sn: Hoskin
|
||||||
|
givenName: Greg
|
||||||
|
mail: ghoskin@cloudstack.org
|
||||||
|
uid: ghoskin
|
||||||
|
userpassword:: cGFzc3dvcmQ=
|
||||||
|
|
||||||
|
dn: cn=Johanne Runyon,dc=cloudstack,dc=org
|
||||||
|
objectClass: inetOrgPerson
|
||||||
|
objectClass: organizationalPerson
|
||||||
|
objectClass: person
|
||||||
|
objectClass: top
|
||||||
|
cn: Johanne Runyon
|
||||||
|
sn: Runyon
|
||||||
|
givenName: Johanne
|
||||||
|
mail: jrunyon@cloudstack.org
|
||||||
|
uid: jrunyon
|
||||||
|
userpassword:: cGFzc3dvcmQ=
|
||||||
|
|
||||||
|
dn: cn=Mabelle Waiters,dc=cloudstack,dc=org
|
||||||
|
objectClass: inetOrgPerson
|
||||||
|
objectClass: organizationalPerson
|
||||||
|
objectClass: person
|
||||||
|
objectClass: top
|
||||||
|
cn: Mabelle Waiters
|
||||||
|
sn: Waiters
|
||||||
|
givenName: Mabelle
|
||||||
|
mail: mwaiters@cloudstack.org
|
||||||
|
uid: mwaiters
|
||||||
|
userpassword:: cGFzc3dvcmQ=
|
||||||
|
|
||||||
|
dn: cn=Phillip Fruge,dc=cloudstack,dc=org
|
||||||
|
objectClass: inetOrgPerson
|
||||||
|
objectClass: organizationalPerson
|
||||||
|
objectClass: person
|
||||||
|
objectClass: top
|
||||||
|
cn: Phillip Fruge
|
||||||
|
sn: Fruge
|
||||||
|
givenName: Phillip
|
||||||
|
mail: pfruge@cloudstack.org
|
||||||
|
uid: pfruge
|
||||||
|
userpassword:: cGFzc3dvcmQ=
|
||||||
|
|
||||||
|
dn: cn=Jayna Ridenhour,dc=cloudstack,dc=org
|
||||||
|
objectClass: inetOrgPerson
|
||||||
|
objectClass: organizationalPerson
|
||||||
|
objectClass: person
|
||||||
|
objectClass: top
|
||||||
|
cn: Jayna Ridenhour
|
||||||
|
sn: Ridenhour
|
||||||
|
givenName: Jayna
|
||||||
|
mail: jridenhour@cloudstack.org
|
||||||
|
uid: jridenhour
|
||||||
|
userpassword:: cGFzc3dvcmQ=
|
||||||
|
|
||||||
|
dn: cn=Marlyn Mandujano,dc=cloudstack,dc=org
|
||||||
|
objectClass: inetOrgPerson
|
||||||
|
objectClass: organizationalPerson
|
||||||
|
objectClass: person
|
||||||
|
objectClass: top
|
||||||
|
cn: Marlyn Mandujano
|
||||||
|
sn: Mandujano
|
||||||
|
givenName: Marlyn
|
||||||
|
mail: mmandujano@cloudstack.org
|
||||||
|
uid: mmandujano
|
||||||
|
userpassword:: cGFzc3dvcmQ=
|
||||||
|
|
||||||
|
dn: cn=Shaunna Scherer,dc=cloudstack,dc=org
|
||||||
|
objectClass: inetOrgPerson
|
||||||
|
objectClass: organizationalPerson
|
||||||
|
objectClass: person
|
||||||
|
objectClass: top
|
||||||
|
cn: Shaunna Scherer
|
||||||
|
sn: Scherer
|
||||||
|
givenName: Shaunna
|
||||||
|
mail: sscherer@cloudstack.org
|
||||||
|
uid: sscherer
|
||||||
|
userpassword:: cGFzc3dvcmQ=
|
||||||
|
|
||||||
|
dn: cn=Adriana Bozek,dc=cloudstack,dc=org
|
||||||
|
objectClass: inetOrgPerson
|
||||||
|
objectClass: organizationalPerson
|
||||||
|
objectClass: person
|
||||||
|
objectClass: top
|
||||||
|
cn: Adriana Bozek
|
||||||
|
sn: Bozek
|
||||||
|
givenName: Adriana
|
||||||
|
mail: abozek@cloudstack.org
|
||||||
|
uid: abozek
|
||||||
|
userpassword:: cGFzc3dvcmQ=
|
||||||
|
|
||||||
|
dn: cn=Silvana Chipman,dc=cloudstack,dc=org
|
||||||
|
objectClass: inetOrgPerson
|
||||||
|
objectClass: organizationalPerson
|
||||||
|
objectClass: person
|
||||||
|
objectClass: top
|
||||||
|
cn: Silvana Chipman
|
||||||
|
sn: Chipman
|
||||||
|
givenName: Silvana
|
||||||
|
mail: schipman@cloudstack.org
|
||||||
|
uid: schipman
|
||||||
|
userpassword:: cGFzc3dvcmQ=
|
||||||
|
|
||||||
|
dn: cn=Marion Wasden,dc=cloudstack,dc=org
|
||||||
|
objectClass: inetOrgPerson
|
||||||
|
objectClass: organizationalPerson
|
||||||
|
objectClass: person
|
||||||
|
objectClass: top
|
||||||
|
cn: Marion Wasden
|
||||||
|
sn: Wasden
|
||||||
|
givenName: Marion
|
||||||
|
mail: mwasden@cloudstack.org
|
||||||
|
uid: mwasden
|
||||||
|
userpassword:: cGFzc3dvcmQ=
|
||||||
|
|
||||||
|
dn: cn=Anisa Casson,dc=cloudstack,dc=org
|
||||||
|
objectClass: inetOrgPerson
|
||||||
|
objectClass: organizationalPerson
|
||||||
|
objectClass: person
|
||||||
|
objectClass: top
|
||||||
|
cn: Anisa Casson
|
||||||
|
sn: Casson
|
||||||
|
givenName: Anisa
|
||||||
|
mail: acasson@cloudstack.org
|
||||||
|
uid: acasson
|
||||||
|
userpassword:: cGFzc3dvcmQ=
|
||||||
|
|
||||||
|
dn: cn=Noel King,dc=cloudstack,dc=org
|
||||||
|
objectClass: inetOrgPerson
|
||||||
|
objectClass: organizationalPerson
|
||||||
|
objectClass: person
|
||||||
|
objectClass: top
|
||||||
|
cn: Noel King
|
||||||
|
sn: King
|
||||||
|
givenName: Noel
|
||||||
|
mail: nking@cloudstack.org
|
||||||
|
uid: nking
|
||||||
|
userpassword:: cGFzc3dvcmQ=
|
||||||
|
|
||||||
|
|
||||||
|
dn: cn=Cammy Petri,dc=cloudstack,dc=org
|
||||||
|
objectClass: inetOrgPerson
|
||||||
|
objectClass: organizationalPerson
|
||||||
|
objectClass: person
|
||||||
|
objectClass: top
|
||||||
|
cn: Cammy Petri
|
||||||
|
sn: Petri
|
||||||
|
givenName: Cammy
|
||||||
|
mail: cpetri@cloudstack.org
|
||||||
|
uid: cpetri
|
||||||
|
userpassword:: cGFzc3dvcmQ=
|
||||||
|
|
||||||
@ -413,6 +413,36 @@ public class QueryManagerImpl extends MutualExclusiveIdsManagerBase implements Q
|
|||||||
return response;
|
return response;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public ListResponse<UserResponse> searchForUsers(Long domainId, boolean recursive) throws PermissionDeniedException {
|
||||||
|
Account caller = CallContext.current().getCallingAccount();
|
||||||
|
|
||||||
|
List<Long> permittedAccounts = new ArrayList<Long>();
|
||||||
|
|
||||||
|
boolean listAll = true;
|
||||||
|
Long id = null;
|
||||||
|
|
||||||
|
if (caller.getType() == Account.ACCOUNT_TYPE_NORMAL) {
|
||||||
|
long currentId = CallContext.current().getCallingUser().getId();
|
||||||
|
if (id != null && currentId != id.longValue()) {
|
||||||
|
throw new PermissionDeniedException("Calling user is not authorized to see the user requested by id");
|
||||||
|
}
|
||||||
|
id = currentId;
|
||||||
|
}
|
||||||
|
Object username = null;
|
||||||
|
Object type = null;
|
||||||
|
String accountName = null;
|
||||||
|
Object state = null;
|
||||||
|
Object keyword = null;
|
||||||
|
|
||||||
|
Pair<List<UserAccountJoinVO>, Integer> result = getUserListInternal(caller, permittedAccounts, listAll, id, username, type, accountName, state, keyword, domainId, recursive,
|
||||||
|
null);
|
||||||
|
ListResponse<UserResponse> response = new ListResponse<UserResponse>();
|
||||||
|
List<UserResponse> userResponses = ViewResponseHelper.createUserResponse(CallContext.current().getCallingAccount().getDomainId(),
|
||||||
|
result.first().toArray(new UserAccountJoinVO[result.first().size()]));
|
||||||
|
response.setResponses(userResponses, result.second());
|
||||||
|
return response;
|
||||||
|
}
|
||||||
|
|
||||||
private Pair<List<UserAccountJoinVO>, Integer> searchForUsersInternal(ListUsersCmd cmd) throws PermissionDeniedException {
|
private Pair<List<UserAccountJoinVO>, Integer> searchForUsersInternal(ListUsersCmd cmd) throws PermissionDeniedException {
|
||||||
Account caller = CallContext.current().getCallingAccount();
|
Account caller = CallContext.current().getCallingAccount();
|
||||||
|
|
||||||
@ -427,42 +457,52 @@ public class QueryManagerImpl extends MutualExclusiveIdsManagerBase implements Q
|
|||||||
}
|
}
|
||||||
id = currentId;
|
id = currentId;
|
||||||
}
|
}
|
||||||
Ternary<Long, Boolean, ListProjectResourcesCriteria> domainIdRecursiveListProject = new Ternary<Long, Boolean, ListProjectResourcesCriteria>(cmd.getDomainId(), cmd.isRecursive(), null);
|
|
||||||
_accountMgr.buildACLSearchParameters(caller, id, cmd.getAccountName(), null, permittedAccounts, domainIdRecursiveListProject, listAll, false);
|
|
||||||
Long domainId = domainIdRecursiveListProject.first();
|
|
||||||
Boolean isRecursive = domainIdRecursiveListProject.second();
|
|
||||||
ListProjectResourcesCriteria listProjectResourcesCriteria = domainIdRecursiveListProject.third();
|
|
||||||
|
|
||||||
Filter searchFilter = new Filter(UserAccountJoinVO.class, "id", true, cmd.getStartIndex(), cmd.getPageSizeVal());
|
|
||||||
|
|
||||||
Object username = cmd.getUsername();
|
Object username = cmd.getUsername();
|
||||||
Object type = cmd.getAccountType();
|
Object type = cmd.getAccountType();
|
||||||
Object accountName = cmd.getAccountName();
|
String accountName = cmd.getAccountName();
|
||||||
Object state = cmd.getState();
|
Object state = cmd.getState();
|
||||||
Object keyword = cmd.getKeyword();
|
Object keyword = cmd.getKeyword();
|
||||||
|
|
||||||
|
Long domainId = cmd.getDomainId();
|
||||||
|
boolean recursive = cmd.isRecursive();
|
||||||
|
Long pageSizeVal = cmd.getPageSizeVal();
|
||||||
|
Long startIndex = cmd.getStartIndex();
|
||||||
|
|
||||||
|
Filter searchFilter = new Filter(UserAccountJoinVO.class, "id", true, startIndex, pageSizeVal);
|
||||||
|
|
||||||
|
return getUserListInternal(caller, permittedAccounts, listAll, id, username, type, accountName, state, keyword, domainId, recursive, searchFilter);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Pair<List<UserAccountJoinVO>, Integer> getUserListInternal(Account caller, List<Long> permittedAccounts, boolean listAll, Long id, Object username, Object type,
|
||||||
|
String accountName, Object state, Object keyword, Long domainId, boolean recursive, Filter searchFilter) {
|
||||||
|
Ternary<Long, Boolean, ListProjectResourcesCriteria> domainIdRecursiveListProject = new Ternary<Long, Boolean, ListProjectResourcesCriteria>(domainId, recursive, null);
|
||||||
|
_accountMgr.buildACLSearchParameters(caller, id, accountName, null, permittedAccounts, domainIdRecursiveListProject, listAll, false);
|
||||||
|
domainId = domainIdRecursiveListProject.first();
|
||||||
|
Boolean isRecursive = domainIdRecursiveListProject.second();
|
||||||
|
ListProjectResourcesCriteria listProjectResourcesCriteria = domainIdRecursiveListProject.third();
|
||||||
|
|
||||||
SearchBuilder<UserAccountJoinVO> sb = _userAccountJoinDao.createSearchBuilder();
|
SearchBuilder<UserAccountJoinVO> sb = _userAccountJoinDao.createSearchBuilder();
|
||||||
_accountMgr.buildACLViewSearchBuilder(sb, domainId, isRecursive, permittedAccounts, listProjectResourcesCriteria);
|
_accountMgr.buildACLViewSearchBuilder(sb, domainId, isRecursive, permittedAccounts, listProjectResourcesCriteria);
|
||||||
sb.and("username", sb.entity().getUsername(), SearchCriteria.Op.LIKE);
|
sb.and("username", sb.entity().getUsername(), Op.LIKE);
|
||||||
if (id != null && id == 1) {
|
if (id != null && id == 1) {
|
||||||
// system user should NOT be searchable
|
// system user should NOT be searchable
|
||||||
List<UserAccountJoinVO> emptyList = new ArrayList<UserAccountJoinVO>();
|
List<UserAccountJoinVO> emptyList = new ArrayList<UserAccountJoinVO>();
|
||||||
return new Pair<List<UserAccountJoinVO>, Integer>(emptyList, 0);
|
return new Pair<List<UserAccountJoinVO>, Integer>(emptyList, 0);
|
||||||
} else if (id != null) {
|
} else if (id != null) {
|
||||||
sb.and("id", sb.entity().getId(), SearchCriteria.Op.EQ);
|
sb.and("id", sb.entity().getId(), Op.EQ);
|
||||||
} else {
|
} else {
|
||||||
// this condition is used to exclude system user from the search
|
// this condition is used to exclude system user from the search
|
||||||
// results
|
// results
|
||||||
sb.and("id", sb.entity().getId(), SearchCriteria.Op.NEQ);
|
sb.and("id", sb.entity().getId(), Op.NEQ);
|
||||||
}
|
}
|
||||||
|
|
||||||
sb.and("type", sb.entity().getAccountType(), SearchCriteria.Op.EQ);
|
sb.and("type", sb.entity().getAccountType(), Op.EQ);
|
||||||
sb.and("domainId", sb.entity().getDomainId(), SearchCriteria.Op.EQ);
|
sb.and("domainId", sb.entity().getDomainId(), Op.EQ);
|
||||||
sb.and("accountName", sb.entity().getAccountName(), SearchCriteria.Op.EQ);
|
sb.and("accountName", sb.entity().getAccountName(), Op.EQ);
|
||||||
sb.and("state", sb.entity().getState(), SearchCriteria.Op.EQ);
|
sb.and("state", sb.entity().getState(), Op.EQ);
|
||||||
|
|
||||||
if ((accountName == null) && (domainId != null)) {
|
if ((accountName == null) && (domainId != null)) {
|
||||||
sb.and("domainPath", sb.entity().getDomainPath(), SearchCriteria.Op.LIKE);
|
sb.and("domainPath", sb.entity().getDomainPath(), Op.LIKE);
|
||||||
}
|
}
|
||||||
|
|
||||||
SearchCriteria<UserAccountJoinVO> sc = sb.create();
|
SearchCriteria<UserAccountJoinVO> sc = sb.create();
|
||||||
@ -472,15 +512,15 @@ public class QueryManagerImpl extends MutualExclusiveIdsManagerBase implements Q
|
|||||||
|
|
||||||
if (keyword != null) {
|
if (keyword != null) {
|
||||||
SearchCriteria<UserAccountJoinVO> ssc = _userAccountJoinDao.createSearchCriteria();
|
SearchCriteria<UserAccountJoinVO> ssc = _userAccountJoinDao.createSearchCriteria();
|
||||||
ssc.addOr("username", SearchCriteria.Op.LIKE, "%" + keyword + "%");
|
ssc.addOr("username", Op.LIKE, "%" + keyword + "%");
|
||||||
ssc.addOr("firstname", SearchCriteria.Op.LIKE, "%" + keyword + "%");
|
ssc.addOr("firstname", Op.LIKE, "%" + keyword + "%");
|
||||||
ssc.addOr("lastname", SearchCriteria.Op.LIKE, "%" + keyword + "%");
|
ssc.addOr("lastname", Op.LIKE, "%" + keyword + "%");
|
||||||
ssc.addOr("email", SearchCriteria.Op.LIKE, "%" + keyword + "%");
|
ssc.addOr("email", Op.LIKE, "%" + keyword + "%");
|
||||||
ssc.addOr("state", SearchCriteria.Op.LIKE, "%" + keyword + "%");
|
ssc.addOr("state", Op.LIKE, "%" + keyword + "%");
|
||||||
ssc.addOr("accountName", SearchCriteria.Op.LIKE, "%" + keyword + "%");
|
ssc.addOr("accountName", Op.LIKE, "%" + keyword + "%");
|
||||||
ssc.addOr("accountType", SearchCriteria.Op.LIKE, "%" + keyword + "%");
|
ssc.addOr("accountType", Op.LIKE, "%" + keyword + "%");
|
||||||
|
|
||||||
sc.addAnd("username", SearchCriteria.Op.SC, ssc);
|
sc.addAnd("username", Op.SC, ssc);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (username != null) {
|
if (username != null) {
|
||||||
|
|||||||
189
test/integration/plugins/ldap/ldap_test_data.py
Normal file
189
test/integration/plugins/ldap/ldap_test_data.py
Normal file
@ -0,0 +1,189 @@
|
|||||||
|
# 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.
|
||||||
|
|
||||||
|
class LdapTestData:
|
||||||
|
#constants
|
||||||
|
configuration = "ldap_configuration"
|
||||||
|
syncAccounts = "accountsToSync"
|
||||||
|
parentDomain = "LDAP"
|
||||||
|
manualDomain = "manual"
|
||||||
|
importDomain = "import"
|
||||||
|
syncDomain = "sync"
|
||||||
|
name = "name"
|
||||||
|
id = "id"
|
||||||
|
notAvailable = "N/A"
|
||||||
|
groups = "groups"
|
||||||
|
group = "group"
|
||||||
|
seniorAccount = "seniors"
|
||||||
|
juniorAccount = "juniors"
|
||||||
|
|
||||||
|
ldap_ip_address = "localhost"
|
||||||
|
ldap_port = 389
|
||||||
|
hostname = "hostname"
|
||||||
|
port = "port"
|
||||||
|
dn = "dn"
|
||||||
|
ou = "ou"
|
||||||
|
cn = "cn"
|
||||||
|
member = "uniqueMember"
|
||||||
|
basedn = "basedn"
|
||||||
|
basednConfig = "ldap.basedn"
|
||||||
|
ldapPw = "ldapPassword"
|
||||||
|
ldapPwConfig = "ldap.bind.password"
|
||||||
|
principal = "ldapUsername"
|
||||||
|
principalConfig = "ldap.bind.principal"
|
||||||
|
users = "users"
|
||||||
|
objectClass = "objectClass"
|
||||||
|
sn = "sn"
|
||||||
|
givenName = "givenName"
|
||||||
|
uid = "uid"
|
||||||
|
domains = "domains"
|
||||||
|
type = "accounttype"
|
||||||
|
password = "userPassword"
|
||||||
|
mail = "email"
|
||||||
|
groupPrinciple = "ldap.search.group.principle"
|
||||||
|
|
||||||
|
basednValue = "dc=echt,dc=net"
|
||||||
|
people_dn = "ou=people,"+basednValue
|
||||||
|
groups_dn = "ou=groups,"+basednValue
|
||||||
|
admins = "ou=admins,"+groups_dn
|
||||||
|
juniors = "ou=juniors,"+groups_dn
|
||||||
|
seniors = "ou=seniors,"+groups_dn
|
||||||
|
userObject = "userObject"
|
||||||
|
usernameAttribute = "usernameAttribute"
|
||||||
|
memberAttribute = "memberAttribute"
|
||||||
|
mailAttribute = "emailAttribute"
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
self.testdata = {
|
||||||
|
LdapTestData.configuration: {
|
||||||
|
LdapTestData.mailAttribute: "mail",
|
||||||
|
LdapTestData.userObject: "person",
|
||||||
|
LdapTestData.usernameAttribute: LdapTestData.uid,
|
||||||
|
LdapTestData.memberAttribute: LdapTestData.member,
|
||||||
|
# global values for use in all domains
|
||||||
|
LdapTestData.hostname: LdapTestData.ldap_ip_address,
|
||||||
|
LdapTestData.port: LdapTestData.ldap_port,
|
||||||
|
LdapTestData.basedn: LdapTestData.basednValue,
|
||||||
|
LdapTestData.ldapPw: "secret",
|
||||||
|
LdapTestData.principal: "cn=willie,"+LdapTestData.basednValue,
|
||||||
|
},
|
||||||
|
LdapTestData.groups: [
|
||||||
|
{
|
||||||
|
LdapTestData.dn : LdapTestData.people_dn,
|
||||||
|
LdapTestData.objectClass: ["organizationalUnit", "top"],
|
||||||
|
LdapTestData.ou : "People"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
LdapTestData.dn : LdapTestData.groups_dn,
|
||||||
|
LdapTestData.objectClass: ["organizationalUnit", "top"],
|
||||||
|
LdapTestData.ou : "Groups"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
LdapTestData.dn : LdapTestData.seniors,
|
||||||
|
LdapTestData.objectClass: ["groupOfUniqueNames", "top"],
|
||||||
|
LdapTestData.ou : "seniors",
|
||||||
|
LdapTestData.cn : "seniors",
|
||||||
|
LdapTestData.member : ["uid=bobby,ou=people,"+LdapTestData.basednValue, "uid=rohit,ou=people,"+LdapTestData.basednValue]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
LdapTestData.dn : LdapTestData.juniors,
|
||||||
|
LdapTestData.objectClass : ["groupOfUniqueNames", "top"],
|
||||||
|
LdapTestData.ou : "juniors",
|
||||||
|
LdapTestData.cn : "juniors",
|
||||||
|
LdapTestData.member : ["uid=dahn,ou=people,"+LdapTestData.basednValue, "uid=paul,ou=people,"+LdapTestData.basednValue]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
LdapTestData.users: [
|
||||||
|
{
|
||||||
|
LdapTestData.dn : "uid=bobby,ou=people,"+LdapTestData.basednValue,
|
||||||
|
LdapTestData.objectClass : ["inetOrgPerson", "top", "person"],
|
||||||
|
LdapTestData.cn : "bobby",
|
||||||
|
LdapTestData.sn: "Stoyanov",
|
||||||
|
LdapTestData.givenName : "Boris",
|
||||||
|
LdapTestData.uid : "bobby",
|
||||||
|
LdapTestData.mail: "bobby@echt.net"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
LdapTestData.dn : "uid=dahn,ou=people,"+LdapTestData.basednValue,
|
||||||
|
LdapTestData.objectClass : ["inetOrgPerson", "top", "person"],
|
||||||
|
LdapTestData.cn : "dahn",
|
||||||
|
LdapTestData.sn: "Hoogland",
|
||||||
|
LdapTestData.givenName : "Daan",
|
||||||
|
LdapTestData.uid : "dahn",
|
||||||
|
LdapTestData.mail: "dahn@echt.net"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
LdapTestData.dn : "uid=paul,ou=people,"+LdapTestData.basednValue,
|
||||||
|
LdapTestData.objectClass : ["inetOrgPerson", "top", "person"],
|
||||||
|
LdapTestData.cn : "Paul",
|
||||||
|
LdapTestData.sn: "Angus",
|
||||||
|
LdapTestData.givenName : "Paul",
|
||||||
|
LdapTestData.uid : "paul",
|
||||||
|
LdapTestData.mail: "paul@echt.net"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
LdapTestData.dn : "uid=rohit,ou=people,"+LdapTestData.basednValue,
|
||||||
|
LdapTestData.objectClass : ["inetOrgPerson", "top", "person"],
|
||||||
|
LdapTestData.cn : "rhtyd",
|
||||||
|
LdapTestData.sn: "Yadav",
|
||||||
|
LdapTestData.givenName : "Rohit",
|
||||||
|
LdapTestData.uid : "rohit",
|
||||||
|
LdapTestData.mail: "rhtyd@echt.net"
|
||||||
|
},
|
||||||
|
# extra test user (just in case)
|
||||||
|
# {
|
||||||
|
# LdapTestData.dn : "uid=noone,ou=people,"+LdapTestData.basednValue,
|
||||||
|
# LdapTestData.objectClass : ["inetOrgPerson", "person"],
|
||||||
|
# LdapTestData.cn : "noone",
|
||||||
|
# LdapTestData.sn: "a User",
|
||||||
|
# LdapTestData.givenName : "Not",
|
||||||
|
# LdapTestData.uid : "noone",
|
||||||
|
# LdapTestData.mail: "noone@echt.net",
|
||||||
|
# LdapTestData.password: 'password'
|
||||||
|
# },
|
||||||
|
],
|
||||||
|
LdapTestData.domains : [
|
||||||
|
{
|
||||||
|
LdapTestData.name : LdapTestData.parentDomain,
|
||||||
|
LdapTestData.id : LdapTestData.notAvailable
|
||||||
|
},
|
||||||
|
{
|
||||||
|
LdapTestData.name : LdapTestData.manualDomain,
|
||||||
|
LdapTestData.id : LdapTestData.notAvailable
|
||||||
|
},
|
||||||
|
{
|
||||||
|
LdapTestData.name : LdapTestData.importDomain,
|
||||||
|
LdapTestData.id : LdapTestData.notAvailable
|
||||||
|
},
|
||||||
|
{
|
||||||
|
LdapTestData.name : LdapTestData.syncDomain,
|
||||||
|
LdapTestData.id : LdapTestData.notAvailable
|
||||||
|
},
|
||||||
|
],
|
||||||
|
LdapTestData.syncAccounts : [
|
||||||
|
{
|
||||||
|
LdapTestData.name : LdapTestData.juniorAccount,
|
||||||
|
LdapTestData.type : 0,
|
||||||
|
LdapTestData.group : LdapTestData.juniors
|
||||||
|
},
|
||||||
|
{
|
||||||
|
LdapTestData.name : LdapTestData.seniorAccount,
|
||||||
|
LdapTestData.type : 2,
|
||||||
|
LdapTestData.group : LdapTestData.seniors
|
||||||
|
}
|
||||||
|
],
|
||||||
|
}
|
||||||
476
test/integration/plugins/ldap/test_ldap.py
Normal file
476
test/integration/plugins/ldap/test_ldap.py
Normal file
@ -0,0 +1,476 @@
|
|||||||
|
# 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.
|
||||||
|
|
||||||
|
#Import Local Modules
|
||||||
|
from .ldap_test_data import LdapTestData
|
||||||
|
from marvin.cloudstackTestCase import cloudstackTestCase
|
||||||
|
from marvin.lib.utils import (cleanup_resources)
|
||||||
|
from marvin.lib.base import (listLdapUsers,
|
||||||
|
ldapCreateAccount,
|
||||||
|
importLdapUsers,
|
||||||
|
User,
|
||||||
|
Domain,
|
||||||
|
Account,
|
||||||
|
addLdapConfiguration,
|
||||||
|
deleteLdapConfiguration,
|
||||||
|
linkAccountToLdap,
|
||||||
|
linkDomainToLdap,
|
||||||
|
updateConfiguration)
|
||||||
|
from marvin.lib.common import (get_domain,
|
||||||
|
get_zone)
|
||||||
|
from nose.plugins.attrib import attr
|
||||||
|
|
||||||
|
# for login validation
|
||||||
|
import requests
|
||||||
|
|
||||||
|
import logging
|
||||||
|
|
||||||
|
class TestLDAP(cloudstackTestCase):
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def setUpClass(cls):
|
||||||
|
'''
|
||||||
|
needs to
|
||||||
|
- create the applicable ldap accounts in the directory server
|
||||||
|
- create three domains:
|
||||||
|
-- LDAP/manual
|
||||||
|
-- LDAP/import
|
||||||
|
-- LDAP/sync
|
||||||
|
'''
|
||||||
|
cls.logger = logging.getLogger(__name__)
|
||||||
|
stream_handler = logging.StreamHandler()
|
||||||
|
logger_formatter = logging.Formatter('%(asctime)s %(levelname)s %(message)s')
|
||||||
|
stream_handler.setFormatter(logger_formatter)
|
||||||
|
cls.logger.setLevel(logging.DEBUG)
|
||||||
|
cls.logger.addHandler(stream_handler)
|
||||||
|
|
||||||
|
cls.logger.info("Setting up Class")
|
||||||
|
testClient = super(TestLDAP, cls).getClsTestClient()
|
||||||
|
cls.apiclient = testClient.getApiClient()
|
||||||
|
|
||||||
|
try:
|
||||||
|
# Setup test data
|
||||||
|
cls.testdata = LdapTestData()
|
||||||
|
if cls.config.TestData and cls.config.TestData.Path:
|
||||||
|
cls.logger.debug("reading extra config from '" + cls.config.TestData.Path + "'")
|
||||||
|
cls.testdata.update(cls.config.TestData.Path)
|
||||||
|
cls.logger.debug(cls.testdata)
|
||||||
|
|
||||||
|
cls.services = testClient.getParsedTestDataConfig()
|
||||||
|
cls.services["configurableData"]["ldap_configuration"] = cls.testdata.testdata["ldap_configuration"]
|
||||||
|
cls.logger.debug(cls.services["configurableData"]["ldap_configuration"])
|
||||||
|
|
||||||
|
# Get Zone, Domain
|
||||||
|
cls.domain = get_domain(cls.apiclient)
|
||||||
|
cls.logger.debug("standard domain: %s" % cls.domain.id)
|
||||||
|
cls.zone = get_zone(cls.apiclient, cls.testClient.getZoneForTests())
|
||||||
|
|
||||||
|
cls._cleanup = []
|
||||||
|
|
||||||
|
# Build the test env
|
||||||
|
cls.create_domains(cls.testdata)
|
||||||
|
cls.configure_ldap_for_domains(cls.testdata)
|
||||||
|
|
||||||
|
cls.test_user = [
|
||||||
|
cls.testdata.testdata[LdapTestData.users][0][LdapTestData.uid],
|
||||||
|
cls.testdata.testdata[LdapTestData.users][1][LdapTestData.uid],
|
||||||
|
cls.testdata.testdata[LdapTestData.users][2][LdapTestData.uid]
|
||||||
|
]
|
||||||
|
except Exception as e:
|
||||||
|
cls.logger.debug("Exception in setUpClass(cls): %s" % e)
|
||||||
|
cls.tearDownClass()
|
||||||
|
raise Exception("setup failed due to %s", e)
|
||||||
|
|
||||||
|
return
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def tearDownClass(cls):
|
||||||
|
cls.logger.info("Tearing Down Class")
|
||||||
|
try:
|
||||||
|
cleanup_resources(cls.apiclient, reversed(cls._cleanup))
|
||||||
|
cls.remove_ldap_configuration_for_domains()
|
||||||
|
cls.logger.debug("done cleaning up resources in tearDownClass(cls) %s")
|
||||||
|
except Exception as e:
|
||||||
|
cls.logger.debug("Exception in tearDownClass(cls): %s" % e)
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
self.cleanup = []
|
||||||
|
|
||||||
|
self.server_details = self.config.__dict__["mgtSvr"][0].__dict__
|
||||||
|
self.server_url = "http://%s:8080/client/api" % self.server_details['mgtSvrIp']
|
||||||
|
|
||||||
|
return
|
||||||
|
|
||||||
|
def tearDown(self):
|
||||||
|
try:
|
||||||
|
cleanup_resources(self.apiclient, self.cleanup)
|
||||||
|
except Exception as e:
|
||||||
|
raise Exception("Warning: Exception during cleanup : %s" % e)
|
||||||
|
return
|
||||||
|
|
||||||
|
@attr(tags=["smoke", "advanced"], required_hardware="false")
|
||||||
|
def test_01_manual(self):
|
||||||
|
'''
|
||||||
|
test if an account can be imported
|
||||||
|
|
||||||
|
prerequisite
|
||||||
|
a ldap host is configured
|
||||||
|
a domain is linked to cloudstack
|
||||||
|
'''
|
||||||
|
cmd = listLdapUsers.listLdapUsersCmd()
|
||||||
|
cmd.domainid = self.manualDomain.id
|
||||||
|
cmd.userfilter = "LocalDomain"
|
||||||
|
response = self.apiclient.listLdapUsers(cmd)
|
||||||
|
self.logger.info("users found for linked domain %s" % response)
|
||||||
|
self.assertEqual(len(response), len(self.testdata.testdata[LdapTestData.users]), "unexpected number (%d) of ldap users" % len(self.testdata.testdata[LdapTestData.users]))
|
||||||
|
|
||||||
|
cmd = ldapCreateAccount.ldapCreateAccountCmd()
|
||||||
|
cmd.domainid = self.manualDomain.id
|
||||||
|
cmd.accounttype = 0
|
||||||
|
cmd.username = self.test_user[1]
|
||||||
|
create_response = self.apiclient.ldapCreateAccount(cmd)
|
||||||
|
|
||||||
|
# cleanup
|
||||||
|
# last results id should be the account
|
||||||
|
list_response = Account.list(self.apiclient, id=create_response.id)
|
||||||
|
account_created = Account(list_response[0].__dict__)
|
||||||
|
self.cleanup.append(account_created)
|
||||||
|
|
||||||
|
self.assertEqual(len(create_response.user), 1, "only one user %s should be present" % self.test_user[1])
|
||||||
|
|
||||||
|
self.assertEqual(len(list_response),
|
||||||
|
1,
|
||||||
|
"only one account (for user %s) should be present" % self.test_user[1])
|
||||||
|
|
||||||
|
return
|
||||||
|
|
||||||
|
@attr(tags=["smoke", "advanced"], required_hardware="false")
|
||||||
|
def test_02_import(self):
|
||||||
|
'''
|
||||||
|
test if components are synced
|
||||||
|
|
||||||
|
prerequisite
|
||||||
|
a ldap host is configured
|
||||||
|
a domain is linked to cloudstack
|
||||||
|
'''
|
||||||
|
domainid = self.importDomain.id
|
||||||
|
|
||||||
|
cmd = importLdapUsers.importLdapUsersCmd()
|
||||||
|
cmd.domainid = domainid
|
||||||
|
cmd.accounttype = 0
|
||||||
|
import_response = self.apiclient.importLdapUsers(cmd)
|
||||||
|
|
||||||
|
# this is needed purely for cleanup:
|
||||||
|
# cleanup
|
||||||
|
list_response = Account.list(self.apiclient, domainid=domainid)
|
||||||
|
for account in list_response:
|
||||||
|
account_created = Account(account.__dict__)
|
||||||
|
self.logger.debug("account to clean: %s (id: %s)" % (account_created.name, account_created.id))
|
||||||
|
self.cleanup.append(account_created)
|
||||||
|
|
||||||
|
self.assertEqual(len(import_response), len(self.testdata.testdata[LdapTestData.users]), "unexpected number of ldap users")
|
||||||
|
|
||||||
|
self.assertEqual(len(list_response), len(self.testdata.testdata[LdapTestData.users]), "only one account (for user %s) should be present" % self.test_user[1])
|
||||||
|
|
||||||
|
return
|
||||||
|
|
||||||
|
@attr(tags=["smoke", "advanced"], required_hardware="false")
|
||||||
|
def test_03_sync(self):
|
||||||
|
'''
|
||||||
|
test if components are synced
|
||||||
|
|
||||||
|
prerequisite
|
||||||
|
a ldap host is configured
|
||||||
|
a domain is linked to cloudstack
|
||||||
|
some accounts in that domain are linked to groups in ldap
|
||||||
|
'''
|
||||||
|
domainid = self.syncDomain.id
|
||||||
|
username = self.test_user[1]
|
||||||
|
|
||||||
|
# validate the user doesn't exist
|
||||||
|
response = User.list(self.apiclient,domainid=domainid,username=username)
|
||||||
|
self.assertEqual(response, None, "user should not exist yet")
|
||||||
|
|
||||||
|
self.logon_test_user(username)
|
||||||
|
|
||||||
|
# now validate the user exists in domain
|
||||||
|
response = User.list(self.apiclient,domainid=domainid,username=username)
|
||||||
|
for user in response:
|
||||||
|
user_created = User(user.__dict__)
|
||||||
|
self.logger.debug("user to clean: %s (id: %s)" % (user_created.username, user_created.id))
|
||||||
|
self.cleanup.append(user_created)
|
||||||
|
|
||||||
|
# now verify the creation of the user
|
||||||
|
self.assertEqual(len(response), 1, "user should exist by now")
|
||||||
|
|
||||||
|
return
|
||||||
|
|
||||||
|
@attr(tags=["smoke", "advanced"], required_hardware="false")
|
||||||
|
def test_04_filtered_list_of_users(self):
|
||||||
|
'''
|
||||||
|
test if we can get a filtered list of ldap users
|
||||||
|
|
||||||
|
prerequisite
|
||||||
|
a ldap host is configured
|
||||||
|
a couple of ldapdomains are linked to cloudstack domains
|
||||||
|
some accounts in those domain are linked to groups in ldap
|
||||||
|
some ldap accounts are linked and present with the same uid
|
||||||
|
some ldap accounts are not yet linked but present at other locations in cloudstack
|
||||||
|
|
||||||
|
NOTE 1: if this test is run last only the explicitely imported test user from test_03_sync
|
||||||
|
is in the system. The accounts from test_01_manual and test_02_import should have been cleared
|
||||||
|
by the test tearDown(). We can not depend on test_03_sync having run so the test must avoid
|
||||||
|
depending on it either being available or not.
|
||||||
|
|
||||||
|
NOTE 2: this test will not work if the ldap users UIDs are already present in the ACS instance
|
||||||
|
against which is being tested
|
||||||
|
'''
|
||||||
|
cmd = listLdapUsers.listLdapUsersCmd()
|
||||||
|
cmd.userfilter = "NoFilter"
|
||||||
|
cmd.domainid = self.manualDomain.id
|
||||||
|
response = self.apiclient.listLdapUsers(cmd)
|
||||||
|
self.logger.debug(cmd.userfilter + " : " + str(response))
|
||||||
|
self.assertEqual(len(response), len(self.testdata.testdata[LdapTestData.users]), "unexpected number of ldap users")
|
||||||
|
|
||||||
|
# create a non ldap user with the uid of cls.test_user[0] in parentDomain
|
||||||
|
# create a manual import of a cls.test_user[1] in manualDomain
|
||||||
|
# log on with test_user[2] in an syncDomain
|
||||||
|
|
||||||
|
# we can now test all four filtertypes in syncDomain and inspect the respective outcomes for validity
|
||||||
|
|
||||||
|
self.logon_test_user(self.test_user[2])
|
||||||
|
|
||||||
|
cmd.userfilter = "LocalDomain"
|
||||||
|
cmd.domainid = self.syncDomain.id
|
||||||
|
response = self.apiclient.listLdapUsers(cmd)
|
||||||
|
self.logger.debug(cmd.userfilter + " : " + str(response))
|
||||||
|
self.assertEqual(len(response),
|
||||||
|
len(self.testdata.testdata[LdapTestData.users]) - 1,
|
||||||
|
"unexpected number of ldap users")
|
||||||
|
|
||||||
|
@attr(tags=["smoke", "advanced"], required_hardware="false")
|
||||||
|
def test_05_relink_account_and_reuse_user(self):
|
||||||
|
'''
|
||||||
|
test if an account and thus a user can be removed and re-added
|
||||||
|
|
||||||
|
test if components still are synced
|
||||||
|
|
||||||
|
prerequisite
|
||||||
|
a ldap host is configured
|
||||||
|
a domain is linked to cloudstack
|
||||||
|
some accounts in that domain are linked to groups in ldap
|
||||||
|
'''
|
||||||
|
domainid = self.syncDomain.id
|
||||||
|
username = self.test_user[1]
|
||||||
|
|
||||||
|
# validate the user doesn't exist
|
||||||
|
response = User.list(self.apiclient,domainid=domainid,username=username)
|
||||||
|
self.assertEqual(response, None, "user should not exist yet")
|
||||||
|
|
||||||
|
self.logon_test_user(username)
|
||||||
|
|
||||||
|
# now validate the user exists in domain
|
||||||
|
response = User.list(self.apiclient,domainid=domainid,username=username)
|
||||||
|
# for user in response:
|
||||||
|
# user_created = User(user.__dict__)
|
||||||
|
# self.debug("user to clean: %s (id: %s)" % (user_created.username, user_created.id))
|
||||||
|
# # we don't cleanup to test if re-adding fails
|
||||||
|
# self.cleanup.append(user_created)
|
||||||
|
|
||||||
|
# now verify the creation of the user
|
||||||
|
self.assertEqual(len(response), 1, "user should exist by now")
|
||||||
|
|
||||||
|
# delete the account - quick implementation: user[1] happens to be a junior
|
||||||
|
self.junior_account.delete(self.apiclient)
|
||||||
|
|
||||||
|
# add the account with the same ldap group
|
||||||
|
self.bind_account_to_ldap(
|
||||||
|
account=self.testdata.testdata[LdapTestData.syncAccounts][0]["name"],
|
||||||
|
ldapdomain=self.testdata.testdata[LdapTestData.syncAccounts][0]["group"],
|
||||||
|
accounttype=self.testdata.testdata[LdapTestData.syncAccounts][0]["accounttype"])
|
||||||
|
|
||||||
|
# logon the user - should succeed - reported to fail
|
||||||
|
self.logon_test_user(username)
|
||||||
|
|
||||||
|
# now verify the creation of the user
|
||||||
|
response = User.list(self.apiclient,domainid=domainid,username=username)
|
||||||
|
for user in response:
|
||||||
|
user_created = User(user.__dict__)
|
||||||
|
self.debug("user to clean: %s (id: %s)" % (user_created.username, user_created.id))
|
||||||
|
# we don't cleanup to test if re-adding fails
|
||||||
|
# self.cleanup.append(user_created)
|
||||||
|
self.assertEqual(len(response), 1, "user should exist again")
|
||||||
|
return
|
||||||
|
|
||||||
|
|
||||||
|
def logon_test_user(self, username, domain = None):
|
||||||
|
# login of dahn should create a user in account juniors
|
||||||
|
args = {}
|
||||||
|
args["command"] = 'login'
|
||||||
|
args["username"] = username
|
||||||
|
args["password"] = 'password'
|
||||||
|
if domain == None:
|
||||||
|
args["domain"] = "/" + self.parentDomain.name + "/" + self.syncDomain.name
|
||||||
|
else:
|
||||||
|
args["domain"] = domain
|
||||||
|
args["response"] = "json"
|
||||||
|
session = requests.Session()
|
||||||
|
try:
|
||||||
|
resp = session.post(self.server_url, params=args, verify=False)
|
||||||
|
except requests.exceptions.ConnectionError as e:
|
||||||
|
self.fail("Failed to attempt login request to mgmt server")
|
||||||
|
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def create_domains(cls, td):
|
||||||
|
# create a parent domain
|
||||||
|
cls.parentDomain = cls.create_domain(td.testdata["domains"][0], parent_domain=cls.domain.id)
|
||||||
|
cls.manualDomain = cls.create_domain(td.testdata["domains"][1], parent_domain=cls.parentDomain.id)
|
||||||
|
cls.importDomain = cls.create_domain(td.testdata["domains"][2], parent_domain=cls.parentDomain.id)
|
||||||
|
cls.syncDomain = cls.create_domain(td.testdata["domains"][3], parent_domain=cls.parentDomain.id)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def create_domain(cls, domain_to_create, parent_domain = None):
|
||||||
|
cls.logger.debug("Creating domain: %s under %s" % (domain_to_create[LdapTestData.name], parent_domain))
|
||||||
|
if parent_domain:
|
||||||
|
domain_to_create["parentdomainid"] = parent_domain
|
||||||
|
tmpDomain = Domain.create(cls.apiclient, domain_to_create)
|
||||||
|
cls.logger.debug("Created domain %s with id %s " % (tmpDomain.name, tmpDomain.id))
|
||||||
|
cls._cleanup.append(tmpDomain)
|
||||||
|
return tmpDomain
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def configure_ldap_for_domains(cls, td) :
|
||||||
|
cmd = addLdapConfiguration.addLdapConfigurationCmd()
|
||||||
|
cmd.hostname = td.testdata[LdapTestData.configuration][LdapTestData.hostname]
|
||||||
|
cmd.port = td.testdata[LdapTestData.configuration][LdapTestData.port]
|
||||||
|
|
||||||
|
cls.logger.debug("configuring ldap server for domain %s" % LdapTestData.manualDomain)
|
||||||
|
cmd.domainid = cls.manualDomain.id
|
||||||
|
response = cls.apiclient.addLdapConfiguration(cmd)
|
||||||
|
cls.manualLdap = response
|
||||||
|
|
||||||
|
cls.logger.debug("configuring ldap server for domain %s" % LdapTestData.importDomain)
|
||||||
|
cmd.domainid = cls.importDomain.id
|
||||||
|
response = cls.apiclient.addLdapConfiguration(cmd)
|
||||||
|
cls.importLdap = response
|
||||||
|
|
||||||
|
cls.logger.debug("configuring ldap server for domain %s" % LdapTestData.syncDomain)
|
||||||
|
cmd.domainid = cls.syncDomain.id
|
||||||
|
response = cls.apiclient.addLdapConfiguration(cmd)
|
||||||
|
cls.syncLdap = response
|
||||||
|
|
||||||
|
cls.set_ldap_settings_on_domain(domainid=cls.manualDomain.id)
|
||||||
|
cls.bind_domain_to_ldap(domainid=cls.manualDomain.id, ldapdomain=cls.testdata.admins)
|
||||||
|
|
||||||
|
cls.set_ldap_settings_on_domain(domainid=cls.importDomain.id)
|
||||||
|
cls.bind_domain_to_ldap(domainid=cls.importDomain.id, ldapdomain=cls.testdata.admins,
|
||||||
|
accounttype=2, type="Group") # just to be testing different types
|
||||||
|
|
||||||
|
cls.set_ldap_settings_on_domain(domainid=cls.syncDomain.id)
|
||||||
|
cls.create_sync_accounts()
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def remove_ldap_configuration_for_domains(cls) :
|
||||||
|
cls.logger.debug("deleting configurations for ldap server")
|
||||||
|
cmd = deleteLdapConfiguration.deleteLdapConfigurationCmd()
|
||||||
|
|
||||||
|
cmd.hostname = cls.manualLdap.hostname
|
||||||
|
cmd.port = cls.manualLdap.port
|
||||||
|
cmd.domainid = cls.manualLdap.domainid
|
||||||
|
response = cls.apiclient.deleteLdapConfiguration(cmd)
|
||||||
|
cls.logger.debug("configuration deleted for %s" % response)
|
||||||
|
|
||||||
|
cmd.hostname = cls.importLdap.hostname
|
||||||
|
cmd.port = cls.importLdap.port
|
||||||
|
cmd.domainid = cls.importLdap.domainid
|
||||||
|
response = cls.apiclient.deleteLdapConfiguration(cmd)
|
||||||
|
cls.logger.debug("configuration deleted for %s" % response)
|
||||||
|
|
||||||
|
cmd.hostname = cls.syncLdap.hostname
|
||||||
|
cmd.port = cls.syncLdap.port
|
||||||
|
cmd.domainid = cls.syncLdap.domainid
|
||||||
|
cls.logger.debug("deleting configuration %s" % cmd)
|
||||||
|
response = cls.apiclient.deleteLdapConfiguration(cmd)
|
||||||
|
cls.logger.debug("configuration deleted for %s" % response)
|
||||||
|
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def create_sync_accounts(cls):
|
||||||
|
cls.logger.debug("creating account: %s" % LdapTestData.seniors)
|
||||||
|
cls.senior_account = cls.bind_account_to_ldap(
|
||||||
|
account=cls.testdata.testdata[LdapTestData.syncAccounts][1]["name"],
|
||||||
|
ldapdomain=cls.testdata.testdata[LdapTestData.syncAccounts][1]["group"],
|
||||||
|
accounttype=cls.testdata.testdata[LdapTestData.syncAccounts][1]["accounttype"])
|
||||||
|
cls.junior_account = cls.bind_account_to_ldap(
|
||||||
|
account=cls.testdata.testdata[LdapTestData.syncAccounts][0]["name"],
|
||||||
|
ldapdomain=cls.testdata.testdata[LdapTestData.syncAccounts][0]["group"],
|
||||||
|
accounttype=cls.testdata.testdata[LdapTestData.syncAccounts][0]["accounttype"])
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def bind_account_to_ldap(cls, account, ldapdomain, type="Group", accounttype=0):
|
||||||
|
cmd = linkAccountToLdap.linkAccountToLdapCmd()
|
||||||
|
|
||||||
|
cmd.domainid = cls.syncDomain.id
|
||||||
|
cmd.account = account
|
||||||
|
cmd.ldapdomain = ldapdomain
|
||||||
|
cmd.type = type
|
||||||
|
cmd.accounttype = accounttype
|
||||||
|
|
||||||
|
response = cls.apiclient.linkAccountToLdap(cmd)
|
||||||
|
cls.logger.info("account linked to ladp %s" % response)
|
||||||
|
|
||||||
|
# this is needed purely for cleanup:
|
||||||
|
response = Account.list(cls.apiclient, id=response.accountid)
|
||||||
|
account_created = Account(response[0].__dict__)
|
||||||
|
cls._cleanup.append(account_created)
|
||||||
|
return account_created
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def bind_domain_to_ldap(cls, domainid, ldapdomain, type="OU", accounttype=0):
|
||||||
|
cmd = linkDomainToLdap.linkDomainToLdapCmd()
|
||||||
|
cmd.domainid = domainid
|
||||||
|
cmd.type = type
|
||||||
|
cmd.accounttype = accounttype
|
||||||
|
cmd.ldapdomain = ldapdomain
|
||||||
|
response = cls.apiclient.linkDomainToLdap(cmd)
|
||||||
|
cls.logger.info("domain linked to ladp %s" % response)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def set_ldap_settings_on_domain(cls, domainid):
|
||||||
|
cmd = updateConfiguration.updateConfigurationCmd()
|
||||||
|
cmd.domainid = domainid
|
||||||
|
cmd.name = LdapTestData.basednConfig
|
||||||
|
cmd.value = cls.testdata.testdata[LdapTestData.configuration][LdapTestData.basedn]
|
||||||
|
response = cls.apiclient.updateConfiguration(cmd)
|
||||||
|
cls.logger.debug("set the basedn: %s" % response)
|
||||||
|
cmd.name = LdapTestData.ldapPwConfig
|
||||||
|
cmd.value = cls.testdata.testdata[LdapTestData.configuration][LdapTestData.ldapPw]
|
||||||
|
response = cls.apiclient.updateConfiguration(cmd)
|
||||||
|
cls.logger.debug("set the pw: %s" % response)
|
||||||
|
cmd.name = LdapTestData.principalConfig
|
||||||
|
cmd.value = cls.testdata.testdata[LdapTestData.configuration][LdapTestData.principal]
|
||||||
|
response = cls.apiclient.updateConfiguration(cmd)
|
||||||
|
cls.logger.debug("set the id: %s" % response)
|
||||||
|
if cls.testdata.testdata[LdapTestData.configuration].has_key(LdapTestData.groupPrinciple) :
|
||||||
|
cmd.name = LdapTestData.groupPrinciple
|
||||||
|
cmd.value = cls.testdata.testdata[LdapTestData.configuration][LdapTestData.groupPrinciple]
|
||||||
|
response = cls.apiclient.updateConfiguration(cmd)
|
||||||
|
cls.logger.debug("set the id: %s" % response)
|
||||||
|
|
||||||
|
|
||||||
|
## python ldap utility functions
|
||||||
@ -181,7 +181,7 @@ class Account:
|
|||||||
self.__dict__.update(items)
|
self.__dict__.update(items)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def create(cls, apiclient, services, admin=False, domainid=None, roleid=None):
|
def create(cls, apiclient, services, admin=False, domainid=None, roleid=None, account=None):
|
||||||
"""Creates an account"""
|
"""Creates an account"""
|
||||||
cmd = createAccount.createAccountCmd()
|
cmd = createAccount.createAccountCmd()
|
||||||
|
|
||||||
@ -213,6 +213,9 @@ class Account:
|
|||||||
if roleid:
|
if roleid:
|
||||||
cmd.roleid = roleid
|
cmd.roleid = roleid
|
||||||
|
|
||||||
|
if account:
|
||||||
|
cmd.account = account
|
||||||
|
|
||||||
account = apiclient.createAccount(cmd)
|
account = apiclient.createAccount(cmd)
|
||||||
|
|
||||||
return Account(account.__dict__)
|
return Account(account.__dict__)
|
||||||
|
|||||||
@ -639,6 +639,7 @@
|
|||||||
<th><translate key="label.name"/></th>
|
<th><translate key="label.name"/></th>
|
||||||
<th><translate key="label.username"/></th>
|
<th><translate key="label.username"/></th>
|
||||||
<th><translate key="label.email"/></th>
|
<th><translate key="label.email"/></th>
|
||||||
|
<th><translate key="label.user.conflict"/></th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
|
|||||||
@ -1759,8 +1759,10 @@ var dictionary = {
|
|||||||
"label.use.vm.ips":"Use VM IPs",
|
"label.use.vm.ips":"Use VM IPs",
|
||||||
"label.used":"Used",
|
"label.used":"Used",
|
||||||
"label.user":"User",
|
"label.user":"User",
|
||||||
|
"label.user.conflict":"Conflict",
|
||||||
"label.user.data":"User Data",
|
"label.user.data":"User Data",
|
||||||
"label.user.details":"User details",
|
"label.user.details":"User details",
|
||||||
|
"label.user.source":"source",
|
||||||
"label.user.vm":"User VM",
|
"label.user.vm":"User VM",
|
||||||
"label.username":"Username",
|
"label.username":"Username",
|
||||||
"label.username.lower":"username",
|
"label.username.lower":"username",
|
||||||
|
|||||||
@ -68,10 +68,43 @@
|
|||||||
required: true
|
required: true
|
||||||
},
|
},
|
||||||
docID: 'helpAccountLastName'
|
docID: 'helpAccountLastName'
|
||||||
}
|
},
|
||||||
|
conflictingusersource: {
|
||||||
|
label: 'label.user.conflict',
|
||||||
|
validation: {
|
||||||
|
required: true
|
||||||
|
},
|
||||||
|
docID: 'helpConflictSource'
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
informationNotInLdap: {
|
informationNotInLdap: {
|
||||||
|
filter: {
|
||||||
|
label: 'label.filterBy',
|
||||||
|
docID: 'helpLdapUserFilter',
|
||||||
|
select: function(args) {
|
||||||
|
var items = [];
|
||||||
|
items.push({
|
||||||
|
id: "NoFilter",
|
||||||
|
description: "No filter"
|
||||||
|
});
|
||||||
|
items.push({
|
||||||
|
id: "LocalDomain",
|
||||||
|
description: "Local domain"
|
||||||
|
});
|
||||||
|
items.push({
|
||||||
|
id: "AnyDomain",
|
||||||
|
description: "Any domain"
|
||||||
|
});
|
||||||
|
items.push({
|
||||||
|
id: "PotentialImport",
|
||||||
|
description: "Potential import"
|
||||||
|
});
|
||||||
|
args.response.success({
|
||||||
|
data: items
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
domainid: {
|
domainid: {
|
||||||
label: 'label.domain',
|
label: 'label.domain',
|
||||||
docID: 'helpAccountDomain',
|
docID: 'helpAccountDomain',
|
||||||
|
|||||||
@ -110,6 +110,11 @@ cloudStack.docs = {
|
|||||||
},
|
},
|
||||||
|
|
||||||
//Ldap
|
//Ldap
|
||||||
|
helpLdapUserFilter: {
|
||||||
|
desc: 'Filter to apply to listing of ldap accounts\n\t"NoFilter": no filtering is done\n\t"LocalDomain": shows only users not in the current or requested domain\n\t"AnyDomain": shows only users not currently known to cloudstack (in any domain)\n\t"PotentialImport": shows all users that (would be) automatically imported to cloudstack with their current usersource',
|
||||||
|
externalLink: ''
|
||||||
|
},
|
||||||
|
|
||||||
helpLdapQueryFilter: {
|
helpLdapQueryFilter: {
|
||||||
|
|
||||||
desc: 'Query filter is used to find a mapped user in the external LDAP server.Cloudstack provides some wildchars to represent the unique attributes in its database . Example - If Cloudstack account-name is same as the LDAP uid, then following will be a valid filter: Queryfilter : (&(uid=%u) , Queryfilter: .incase of Active Directory , Email _ID :(&(mail=%e)) , displayName :(&(displayName=%u)',
|
desc: 'Query filter is used to find a mapped user in the external LDAP server.Cloudstack provides some wildchars to represent the unique attributes in its database . Example - If Cloudstack account-name is same as the LDAP uid, then following will be a valid filter: Queryfilter : (&(uid=%u) , Queryfilter: .incase of Active Directory , Email _ID :(&(mail=%e)) , displayName :(&(displayName=%u)',
|
||||||
@ -127,7 +132,7 @@ cloudStack.docs = {
|
|||||||
|
|
||||||
helpIPReservationNetworkCidr: {
|
helpIPReservationNetworkCidr: {
|
||||||
desc: 'The CIDR of the entire network when IP reservation is configured',
|
desc: 'The CIDR of the entire network when IP reservation is configured',
|
||||||
externalLink: ' '
|
externalLink: ''
|
||||||
|
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|||||||
@ -111,6 +111,14 @@
|
|||||||
});
|
});
|
||||||
|
|
||||||
if (ldapStatus) {
|
if (ldapStatus) {
|
||||||
|
var userFilter = $wizard.find('#label_filterBy').val();
|
||||||
|
if (userFilter == null) {
|
||||||
|
userFilter = 'AnyDomain';
|
||||||
|
}
|
||||||
|
var domainId = $wizard.find('#label_domain').val();
|
||||||
|
if (domainId == null) {
|
||||||
|
domainId = $.cookie('domainid');
|
||||||
|
}
|
||||||
var $table = $wizard.find('.ldap-account-choice tbody');
|
var $table = $wizard.find('.ldap-account-choice tbody');
|
||||||
$("#label_ldap_group_name").on("keypress", function(event) {
|
$("#label_ldap_group_name").on("keypress", function(event) {
|
||||||
if ($table.find("#tr-groupname-message").length === 0) {
|
if ($table.find("#tr-groupname-message").length === 0) {
|
||||||
@ -125,94 +133,13 @@
|
|||||||
$table.find("#tr-groupname-message").hide();
|
$table.find("#tr-groupname-message").hide();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
$.ajax({
|
loadList = function() { $.ajax({
|
||||||
url: createURL("listLdapUsers&listtype=new"),
|
url: createURL("listLdapUsers&listtype=new&domainid=" + domainId + "&userfilter=" + userFilter),
|
||||||
dataType: "json",
|
dataType: "json",
|
||||||
async: false,
|
async: false,
|
||||||
success: function(json) {
|
success: function(json) {
|
||||||
//for testing only (begin)
|
|
||||||
/*
|
|
||||||
json = {
|
|
||||||
"ldapuserresponse": {
|
|
||||||
"count": 11,
|
|
||||||
"LdapUser": [
|
|
||||||
{
|
|
||||||
"email": "test@test.com",
|
|
||||||
"principal": "CN=Administrator,CN=Users,DC=hyd-qa,DC=com",
|
|
||||||
"username": "Administrator",
|
|
||||||
"domain": "CN=Administrator"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"email": "test@test.com",
|
|
||||||
"principal": "CN=Guest,CN=Users,DC=hyd-qa,DC=com",
|
|
||||||
"username": "Guest",
|
|
||||||
"domain": "CN=Guest"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"email": "test@test.com",
|
|
||||||
"principal": "CN=IUSR_HYD-QA12,CN=Users,DC=hyd-qa,DC=com",
|
|
||||||
"username": "IUSR_HYD-QA12",
|
|
||||||
"domain": "CN=IUSR_HYD-QA12"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"email": "test@test.com",
|
|
||||||
"principal": "CN=IWAM_HYD-QA12,CN=Users,DC=hyd-qa,DC=com",
|
|
||||||
"username": "IWAM_HYD-QA12",
|
|
||||||
"domain": "CN=IWAM_HYD-QA12"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"email": "test@test.com",
|
|
||||||
"principal": "CN=SUPPORT_388945a0,CN=Users,DC=hyd-qa,DC=com",
|
|
||||||
"username": "SUPPORT_388945a0",
|
|
||||||
"domain": "CN=SUPPORT_388945a0"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"principal": "CN=jessica j,CN=Users,DC=hyd-qa,DC=com",
|
|
||||||
"firstname": "jessica",
|
|
||||||
"lastname": "j",
|
|
||||||
"username": "jessica",
|
|
||||||
"domain": "CN=jessica j"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"principal": "CN=krbtgt,CN=Users,DC=hyd-qa,DC=com",
|
|
||||||
"username": "krbtgt",
|
|
||||||
"domain": "CN=krbtgt"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"email": "sadhu@sadhu.com",
|
|
||||||
"principal": "CN=sadhu,CN=Users,DC=hyd-qa,DC=com",
|
|
||||||
"firstname": "sadhu",
|
|
||||||
"username": "sadhu",
|
|
||||||
"domain": "CN=sadhu"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"email": "test@test.com",
|
|
||||||
"principal": "CN=sangee1 hariharan,CN=Users,DC=hyd-qa,DC=com",
|
|
||||||
"firstname": "sangee1",
|
|
||||||
"lastname": "hariharan",
|
|
||||||
"username": "sangee1",
|
|
||||||
"domain": "CN=sangee1 hariharan"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"email": "test@test.com",
|
|
||||||
"principal": "CN=sanjeev n.,CN=Users,DC=hyd-qa,DC=com",
|
|
||||||
"firstname": "sanjeev",
|
|
||||||
"username": "sanjeev",
|
|
||||||
"domain": "CN=sanjeev n."
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"email": "test@test.com",
|
|
||||||
"principal": "CN=test1dddd,CN=Users,DC=hyd-qa,DC=com",
|
|
||||||
"firstname": "test1",
|
|
||||||
"username": "test1dddd",
|
|
||||||
"domain": "CN=test1dddd"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
};
|
|
||||||
*/
|
|
||||||
//for testing only (end)
|
|
||||||
|
|
||||||
|
$table.find('tr').remove();
|
||||||
if (json.ldapuserresponse.count > 0) {
|
if (json.ldapuserresponse.count > 0) {
|
||||||
$(json.ldapuserresponse.LdapUser).each(function() {
|
$(json.ldapuserresponse.LdapUser).each(function() {
|
||||||
var $result = $('<tr>');
|
var $result = $('<tr>');
|
||||||
@ -228,7 +155,9 @@
|
|||||||
$('<td>').addClass('username').html(_s(this.username))
|
$('<td>').addClass('username').html(_s(this.username))
|
||||||
.attr('title', this.username),
|
.attr('title', this.username),
|
||||||
$('<td>').addClass('email').html(_s(this.email))
|
$('<td>').addClass('email').html(_s(this.email))
|
||||||
.attr('title', _s(this.email))
|
.attr('title', _s(this.email)),
|
||||||
|
$('<td>').addClass('email').html(_s(this.conflictingusersource))
|
||||||
|
.attr('title', _s(this.conflictingusersource))
|
||||||
)
|
)
|
||||||
|
|
||||||
$table.append($result);
|
$table.append($result);
|
||||||
@ -243,14 +172,20 @@
|
|||||||
$table.append($result);
|
$table.append($result);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
}) };
|
||||||
|
loadList();
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
|
var informationWithinLdapFields = $.extend(true,{},args.informationWithinLdap);
|
||||||
|
// we are not in ldap so
|
||||||
|
delete informationWithinLdapFields.conflictingusersource;
|
||||||
|
|
||||||
var informationWithinLdap = cloudStack.dialog.createForm({
|
var informationWithinLdap = cloudStack.dialog.createForm({
|
||||||
context: context,
|
context: context,
|
||||||
noDialog: true,
|
noDialog: true,
|
||||||
form: {
|
form: {
|
||||||
title: '',
|
title: '',
|
||||||
fields: args.informationWithinLdap
|
fields: informationWithinLdapFields
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -267,13 +202,16 @@
|
|||||||
$wizard.removeClass('multi-wizard');
|
$wizard.removeClass('multi-wizard');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var informationNotInLdap = $.extend(true,{},args.informationNotInLdap);
|
||||||
|
|
||||||
if (!ldapStatus) {
|
if (!ldapStatus) {
|
||||||
delete args.informationNotInLdap.ldapGroupName;
|
delete informationNotInLdap.filter;
|
||||||
|
delete informationNotInLdap.ldapGroupName;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (g_idpList == null) {
|
if (g_idpList == null) {
|
||||||
delete args.informationNotInLdap.samlEnable;
|
delete informationNotInLdap.samlEnable;
|
||||||
delete args.informationNotInLdap.samlEntity;
|
delete informationNotInLdap.samlEntity;
|
||||||
}
|
}
|
||||||
|
|
||||||
var informationNotInLdap = cloudStack.dialog.createForm({
|
var informationNotInLdap = cloudStack.dialog.createForm({
|
||||||
@ -281,12 +219,21 @@
|
|||||||
noDialog: true,
|
noDialog: true,
|
||||||
form: {
|
form: {
|
||||||
title: '',
|
title: '',
|
||||||
fields: args.informationNotInLdap
|
fields: informationNotInLdap
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
var informationNotInLdapForm = informationNotInLdap.$formContainer.find('form .form-item');
|
var informationNotInLdapForm = informationNotInLdap.$formContainer.find('form .form-item');
|
||||||
|
informationNotInLdapForm.find('.value #label_filterBy').addClass('required');
|
||||||
|
informationNotInLdapForm.find('.value #label_filterBy').change(function() {
|
||||||
|
userFilter = $wizard.find('#label_filterBy').val();
|
||||||
|
loadList();
|
||||||
|
});
|
||||||
informationNotInLdapForm.find('.value #label_domain').addClass('required');
|
informationNotInLdapForm.find('.value #label_domain').addClass('required');
|
||||||
|
informationNotInLdapForm.find('.value #label_domain').change(function() {
|
||||||
|
domainId = $wizard.find('#label_domain').val();
|
||||||
|
loadList();
|
||||||
|
});
|
||||||
informationNotInLdapForm.find('.value #label_type').addClass('required');
|
informationNotInLdapForm.find('.value #label_type').addClass('required');
|
||||||
if (!ldapStatus) {
|
if (!ldapStatus) {
|
||||||
informationNotInLdapForm.css('background', 'none');
|
informationNotInLdapForm.css('background', 'none');
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user