| // Licensed to the Apache Software Foundation (ASF) under one |
| // or more contributor license agreements. See the NOTICE file |
| // distributed with this work for additional information |
| // regarding copyright ownership. The ASF licenses this file |
| // to you under the Apache License, Version 2.0 (the |
| // "License"); you may not use this file except in compliance |
| // with the License. You may obtain a copy of the License at |
| // |
| // http://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, |
| // software distributed under the License is distributed on an |
| // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| // KIND, either express or implied. See the License for the |
| // specific language governing permissions and limitations |
| // under the License. |
| package org.apache.cloudstack.api.command; |
| |
| import java.io.UnsupportedEncodingException; |
| import java.security.NoSuchAlgorithmException; |
| import java.security.SecureRandom; |
| import java.util.ArrayList; |
| import java.util.List; |
| import java.util.Map; |
| import java.util.UUID; |
| |
| import javax.inject.Inject; |
| |
| import com.cloud.user.Account; |
| import com.cloud.user.User; |
| import com.cloud.user.UserAccount; |
| import org.apache.cloudstack.acl.RoleType; |
| import org.apache.cloudstack.api.APICommand; |
| import org.apache.cloudstack.api.ApiConstants; |
| import org.apache.cloudstack.api.ApiErrorCode; |
| import org.apache.cloudstack.api.BaseListCmd; |
| import org.apache.cloudstack.api.Parameter; |
| import org.apache.cloudstack.api.ServerApiException; |
| import org.apache.cloudstack.api.response.DomainResponse; |
| import org.apache.cloudstack.api.response.LdapUserResponse; |
| import org.apache.cloudstack.api.response.ListResponse; |
| import org.apache.cloudstack.api.response.RoleResponse; |
| import org.apache.cloudstack.ldap.LdapManager; |
| import org.apache.cloudstack.ldap.LdapUser; |
| import org.apache.cloudstack.ldap.NoLdapUserMatchingQueryException; |
| import org.apache.commons.lang.StringUtils; |
| import org.apache.log4j.Logger; |
| import org.bouncycastle.util.encoders.Base64; |
| |
| import com.cloud.domain.Domain; |
| import com.cloud.exception.ConcurrentOperationException; |
| import com.cloud.exception.InsufficientCapacityException; |
| import com.cloud.exception.InvalidParameterValueException; |
| import com.cloud.exception.NetworkRuleConflictException; |
| import com.cloud.exception.ResourceAllocationException; |
| import com.cloud.exception.ResourceUnavailableException; |
| import com.cloud.user.AccountService; |
| import com.cloud.user.DomainService; |
| |
| @APICommand(name = "importLdapUsers", description = "Import LDAP users", responseObject = LdapUserResponse.class, since = "4.3.0", |
| requestHasSensitiveInfo = false, responseHasSensitiveInfo = false) |
| public class LdapImportUsersCmd extends BaseListCmd { |
| |
| public static final Logger s_logger = Logger.getLogger(LdapImportUsersCmd.class.getName()); |
| |
| private static final String s_name = "ldapuserresponse"; |
| |
| @Parameter(name = ApiConstants.TIMEZONE, |
| type = CommandType.STRING, |
| description = "Specifies a timezone for this command. For more information on the timezone parameter, see Time Zone Format.") |
| private String timezone; |
| |
| @Parameter(name = ApiConstants.ACCOUNT_TYPE, |
| type = CommandType.SHORT, |
| description = "Type of the account. Specify 0 for user, 1 for root admin, and 2 for domain admin") |
| private Short accountType; |
| |
| @Parameter(name = ApiConstants.ROLE_ID, type = CommandType.UUID, entityType = RoleResponse.class, description = "Creates the account under the specified role.") |
| private Long roleId; |
| |
| @Parameter(name = ApiConstants.ACCOUNT_DETAILS, type = CommandType.MAP, description = "details for account used to store specific parameters") |
| private Map<String, String> details; |
| |
| @Parameter(name = ApiConstants.DOMAIN_ID, |
| type = CommandType.UUID, |
| entityType = DomainResponse.class, |
| description = "Specifies the domain to which the ldap users are to be " |
| + "imported. If no domain is specified, a domain will created using group parameter. If the group is also not specified, a domain name based on the OU information will be " |
| + "created. If no OU hierarchy exists, will be defaulted to ROOT domain") |
| private Long domainId; |
| |
| @Parameter(name = ApiConstants.GROUP, type = CommandType.STRING, description = "Specifies the group name from which the ldap users are to be imported. " |
| + "If no group is specified, all the users will be imported.") |
| private String groupName; |
| |
| private Domain _domain; |
| |
| @Parameter(name = ApiConstants.ACCOUNT, type = CommandType.STRING, description = "Creates the user under the specified account. If no account is specified, the username will be used as the account name.") |
| private String accountName; |
| |
| @Inject |
| private LdapManager _ldapManager; |
| |
| public LdapImportUsersCmd() { |
| super(); |
| } |
| |
| public LdapImportUsersCmd(final LdapManager ldapManager, final DomainService domainService, final AccountService accountService) { |
| super(); |
| _ldapManager = ldapManager; |
| _domainService = domainService; |
| _accountService = accountService; |
| } |
| |
| private void createCloudstackUserAccount(LdapUser user, String accountName, Domain domain) { |
| Account account = _accountService.getActiveAccountByName(accountName, domain.getId()); |
| if (account == null) { |
| s_logger.debug("No account exists with name: " + accountName + " creating the account and an user with name: " + user.getUsername() + " in the account"); |
| _accountService.createUserAccount(user.getUsername(), generatePassword(), user.getFirstname(), user.getLastname(), user.getEmail(), timezone, accountName, getAccountType(), getRoleId(), |
| domain.getId(), domain.getNetworkDomain(), details, UUID.randomUUID().toString(), UUID.randomUUID().toString(), User.Source.LDAP); |
| } else { |
| // check if the user exists. if yes, call update |
| UserAccount csuser = _accountService.getActiveUserAccount(user.getUsername(), domain.getId()); |
| if(csuser == null) { |
| s_logger.debug("No user exists with name: " + user.getUsername() + " creating a user in the account: " + accountName); |
| _accountService.createUser(user.getUsername(), generatePassword(), user.getFirstname(), user.getLastname(), user.getEmail(), timezone, accountName, domain.getId(), |
| UUID.randomUUID().toString(), User.Source.LDAP); |
| } else { |
| s_logger.debug("account with name: " + accountName + " exist and user with name: " + user.getUsername() + " exists in the account. Updating the account."); |
| _accountService.updateUser(csuser.getId(), user.getFirstname(), user.getLastname(), user.getEmail(), null, null, null, null, null); |
| } |
| } |
| } |
| |
| @Override |
| public void execute() throws ResourceUnavailableException, InsufficientCapacityException, ServerApiException, ConcurrentOperationException, |
| ResourceAllocationException, NetworkRuleConflictException { |
| if (getAccountType() == null && getRoleId() == null) { |
| throw new ServerApiException(ApiErrorCode.PARAM_ERROR, "Both account type and role ID are not provided"); |
| } |
| List<LdapUser> users; |
| try { |
| if (StringUtils.isNotBlank(groupName)) { |
| |
| users = _ldapManager.getUsersInGroup(groupName, domainId); |
| } else { |
| users = _ldapManager.getUsers(domainId); |
| } |
| } catch (NoLdapUserMatchingQueryException ex) { |
| users = new ArrayList<LdapUser>(); |
| s_logger.info("No Ldap user matching query. " + " ::: " + ex.getMessage()); |
| } |
| |
| List<LdapUser> addedUsers = new ArrayList<LdapUser>(); |
| for (LdapUser user : users) { |
| Domain domain = getDomain(user); |
| try { |
| createCloudstackUserAccount(user, getAccountName(user), domain); |
| addedUsers.add(user); |
| } catch (InvalidParameterValueException ex) { |
| s_logger.error("Failed to create user with username: " + user.getUsername() + " ::: " + ex.getMessage()); |
| } |
| } |
| ListResponse<LdapUserResponse> response = new ListResponse<LdapUserResponse>(); |
| response.setResponses(createLdapUserResponse(addedUsers)); |
| response.setResponseName(getCommandName()); |
| setResponseObject(response); |
| } |
| |
| public Short getAccountType() { |
| return RoleType.getAccountTypeByRole(roleService.findRole(roleId), accountType); |
| } |
| |
| public Long getRoleId() { |
| return RoleType.getRoleByAccountType(roleId, accountType); |
| } |
| |
| private String getAccountName(LdapUser user) { |
| String finalAccountName = accountName; |
| if(finalAccountName == null ) { |
| finalAccountName = user.getUsername(); |
| } |
| return finalAccountName; |
| } |
| |
| private Domain getDomainForName(String name) { |
| Domain domain = null; |
| if (StringUtils.isNotBlank(name)) { |
| //removing all the special characters and trimming its length to 190 to make the domain valid. |
| String domainName = StringUtils.substring(name.replaceAll("\\W", ""), 0, 190); |
| if (StringUtils.isNotBlank(domainName)) { |
| domain = _domainService.getDomainByName(domainName, Domain.ROOT_DOMAIN); |
| if (domain == null) { |
| domain = _domainService.createDomain(domainName, Domain.ROOT_DOMAIN, domainName, UUID.randomUUID().toString()); |
| } |
| } |
| } |
| return domain; |
| } |
| |
| private Domain getDomain(LdapUser user) { |
| Domain domain; |
| if (_domain != null) { |
| //this means either domain id or groupname is passed and this will be same for all the users in this call. hence returning it. |
| domain = _domain; |
| } else { |
| if (domainId != null) { |
| // a domain Id is passed. use it for this user and all the users in the same api call (by setting _domain) |
| domain = _domain = _domainService.getDomain(domainId); |
| } else { |
| // a group name is passed. use it for this user and all the users in the same api call(by setting _domain) |
| domain = _domain = getDomainForName(groupName); |
| if (domain == null) { |
| //use the domain from the LDAP for this user |
| domain = getDomainForName(user.getDomain()); |
| } |
| } |
| if (domain == null) { |
| // could not get a domain using domainId / LDAP group / OU of LDAP user. using ROOT domain for this user |
| domain = _domainService.getDomain(Domain.ROOT_DOMAIN); |
| } |
| } |
| return domain; |
| } |
| |
| private List<LdapUserResponse> createLdapUserResponse(List<LdapUser> users) { |
| final List<LdapUserResponse> ldapResponses = new ArrayList<LdapUserResponse>(); |
| for (final LdapUser user : users) { |
| final LdapUserResponse ldapResponse = _ldapManager.createLdapUserResponse(user); |
| ldapResponse.setObjectName("LdapUser"); |
| ldapResponses.add(ldapResponse); |
| } |
| return ldapResponses; |
| } |
| |
| @Override |
| public String getCommandName() { |
| return s_name; |
| } |
| |
| private String generatePassword() throws ServerApiException { |
| try { |
| final SecureRandom randomGen = SecureRandom.getInstance("SHA1PRNG"); |
| final byte bytes[] = new byte[20]; |
| randomGen.nextBytes(bytes); |
| return new String(Base64.encode(bytes), "UTF-8"); |
| } catch ( NoSuchAlgorithmException | UnsupportedEncodingException e) { |
| throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to generate random password"); |
| } |
| } |
| } |