/* * Copyright (c) 2012-2013 EMC Corporation * All Rights Reserved */ package com.emc.storageos.security.validator; import com.emc.storageos.coordinator.client.service.CoordinatorClient; import com.emc.storageos.db.client.model.TenantOrg; import com.emc.storageos.model.auth.AuthnProviderParamsToValidate; import com.emc.storageos.model.auth.PrincipalsToValidate; import com.emc.storageos.model.errorhandling.ServiceErrorRestRep; import com.emc.storageos.security.authentication.AuthSvcEndPointLocator; import com.emc.storageos.security.authentication.AuthSvcInternalApiClientIterator; import com.emc.storageos.security.authentication.ServiceLocatorInfo; import com.emc.storageos.security.authentication.StorageOSUserRepository; import com.emc.storageos.security.exceptions.FatalSecurityException; import com.emc.storageos.security.exceptions.SecurityException; import com.emc.storageos.security.resource.UserInfoPage.UserTenantList; import com.emc.storageos.svcs.errorhandling.resources.APIException; import com.emc.storageos.svcs.errorhandling.resources.InternalServerErrorException; import com.sun.jersey.api.client.ClientResponse; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.io.UnsupportedEncodingException; import java.net.URI; import java.net.URLEncoder; import java.util.ArrayList; import java.util.HashSet; import java.util.List; /** * validates if a user/group is valid for the given domain of AD/LDAP */ public class Validator { private static final int _MAX_VALIDATION_RETRIES = 5; private static final Logger _log = LoggerFactory .getLogger(Validator.class); private static final URI _URI_VALIDATE = URI.create("/internal/principalValidate"); private static final URI _URI_VALIDATE_PRINCIPALS = URI .create("/internal/principalsValidate"); private static final URI _URI_REFRESH = URI.create("/internal/refreshUser"); private static final URI _URI_USERTENANT = URI.create("/internal/userTenant"); private static final URI _URI_VALIDATE_AUTHNPROVIDER = URI.create("/internal/authnProviderValidate"); private static AuthSvcEndPointLocator _authSvcEndPointLocator; private static CoordinatorClient _coordinator; private static StorageOSUserRepository _repository; /** * Validates the principal within the tenant * * @param principal * @param tenantId : tenant id * @return true if the principal is valid within the tenant */ public static boolean isValidPrincipal(StorageOSPrincipal principal, URI tenantId) { StringBuilder error = new StringBuilder(); return Validator.isValidPrincipal(principal, tenantId, error); } /** * Validates the principal within the tenant * * @param principal * @param tenantId * : tenant id * @param error * : a string representing that error that happened. * @return true if the principal is valid within the tenant */ public static boolean isValidPrincipal(StorageOSPrincipal principal, URI tenantId, StringBuilder error) { String queryParams = null; switch (principal.getType()) { case User: String encodedPrincipal; String encodedTenant; try { encodedPrincipal = URLEncoder.encode(principal.getName(), "UTF-8"); } catch (UnsupportedEncodingException e) { throw APIException.badRequests.unableToEncodeString(principal.getName(), e); } try { encodedTenant = URLEncoder.encode(tenantId.toString(), "UTF-8"); } catch (UnsupportedEncodingException e) { throw APIException.badRequests.unableToEncodeString(tenantId.toString(), e); } queryParams = "?subject_id=" + encodedPrincipal + "&tenant_id=" + encodedTenant; break; case Group: try { queryParams = "?group=" + URLEncoder.encode(principal.getName(), "UTF-8"); } catch (UnsupportedEncodingException e) { throw APIException.badRequests.unableToEncodeString(principal.getName(), e); } break; } String endpoint = null; int attempts = 0; while (attempts < _MAX_VALIDATION_RETRIES) { _log.debug("Validation attempt {}", ++attempts); AuthSvcInternalApiClientIterator authSvcClientItr = new AuthSvcInternalApiClientIterator(_authSvcEndPointLocator, _coordinator); try { if (authSvcClientItr.hasNext()) { endpoint = authSvcClientItr.peek().toString(); _log.info("isValidPrincipal(): {}", endpoint); final ClientResponse response = authSvcClientItr.get(URI.create(_URI_VALIDATE + queryParams)); final int status = response.getStatus(); _log.debug("Status: {}", status); if (status == ClientResponse.Status.OK.getStatusCode()) { return true; } else if (status == ClientResponse.Status.BAD_REQUEST.getStatusCode() || status == ClientResponse.Status.INTERNAL_SERVER_ERROR.getStatusCode()) { ServiceErrorRestRep errorXml = response .getEntity(ServiceErrorRestRep.class); error.append(errorXml.getDetailedMessage()); return false; } else { _log.info("Unexpected response code {}.", status); } } } catch (Exception e) { _log.info("Exception connecting to {}. ", endpoint, e); } } return false; } /** * Validates the principals within the tenant * * @param principalsToValidate * @param error * :a string representing that error that happened. * @return true if all the principal are valid within the tenant */ public static boolean validatePrincipals(PrincipalsToValidate principalsToValidate, StringBuilder error) { String endpoint = null; principalsToValidate.setUsers(deDuplicate(principalsToValidate.getUsers())); principalsToValidate.setGroups(deDuplicate(principalsToValidate.getGroups())); principalsToValidate.setAltTenantUsers(deDuplicate(principalsToValidate.getAltTenantUsers())); int attempts = 0; while (attempts < _MAX_VALIDATION_RETRIES) { _log.debug("Validation attempt {}", ++attempts); AuthSvcInternalApiClientIterator authSvcClientItr = new AuthSvcInternalApiClientIterator(_authSvcEndPointLocator, _coordinator); try { if (authSvcClientItr.hasNext()) { endpoint = authSvcClientItr.peek().toString(); _log.info("validatePrincipals(): {}", endpoint); final ClientResponse response = authSvcClientItr.post(_URI_VALIDATE_PRINCIPALS, principalsToValidate); final int status = response.getStatus(); _log.debug("Status: {}", status); if (status == ClientResponse.Status.OK.getStatusCode()) { return true; } else if (status == ClientResponse.Status.BAD_REQUEST .getStatusCode() || status == ClientResponse.Status.INTERNAL_SERVER_ERROR .getStatusCode()) { ServiceErrorRestRep errorXml = response.getEntity(ServiceErrorRestRep.class); error.append(errorXml.getDetailedMessage()); return false; } else { _log.info("Unexpected response code {}.", status); } } } catch (Exception e) { _log.info("Exception connecting to {}. ", endpoint, e); if (e.getMessage().contains("Read timed out")) { throw InternalServerErrorException.internalServerErrors.authTimeout(); } } } return false; } /** * Sends an internal api call to authsvc to validate authentication provider * basic connectivity parameters * * @param param has the basic connectivity parameters * @param errorString will be set to an error message if the validation fails * @return true if validation succeeded. False otherwise. */ public static boolean isUsableAuthenticationProvider(AuthnProviderParamsToValidate param, StringBuilder errorString) { String endpoint = null; int attempts = 0; while (attempts < _MAX_VALIDATION_RETRIES) { _log.debug("Validation attempt {}", ++attempts); AuthSvcInternalApiClientIterator authSvcClientItr = new AuthSvcInternalApiClientIterator(_authSvcEndPointLocator, _coordinator); try { if (authSvcClientItr.hasNext()) { endpoint = authSvcClientItr.peek().toString(); _log.info("isAuthenticationProvider(): {}", endpoint); final ClientResponse response = authSvcClientItr.post(URI.create(_URI_VALIDATE_AUTHNPROVIDER.toString()), param); final int status = response.getStatus(); String errorRaw = response.getEntity(String.class); _log.debug("Status: {}", status); _log.debug("Response entity: {}", errorRaw); if (status == ClientResponse.Status.OK.getStatusCode()) { return true; } else if (status == ClientResponse.Status.BAD_REQUEST.getStatusCode()) { errorString.append(errorRaw); return false; } else { _log.info("Unexpected response code {}.", status); } } } catch (Exception e) { _log.info("Exception connecting to {}. ", endpoint, e); } } return false; } public synchronized static void setAuthSvcEndPointLocator(AuthSvcEndPointLocator authSvcEndPointLocator) { _authSvcEndPointLocator = authSvcEndPointLocator; } public synchronized static void setCoordinator(CoordinatorClient coordinator) { _coordinator = coordinator; } public synchronized static void setStorageOSUserRepository(StorageOSUserRepository repo) { _repository = repo; } /** * determines if a username exists in the local storageos user repository * * @param name * @return true if yes, false if no */ public static boolean isUserLocal(String name) { return _repository.isUserLocal(name); } /** * Make an internal REST API call to the authsvc in order to get the user's * tenant mapping * * @param username * @return List of tenancies the user maps to with the applied mapping */ public static UserTenantList getUserTenants(String username) { return getUserTenants(username, null); } public static UserTenantList getUserTenants(String username, TenantOrg tenant) { String endpoint = null; int attempts = 0; while (attempts < _MAX_VALIDATION_RETRIES) { _log.debug("Get user tenants attempt {}", ++attempts); AuthSvcInternalApiClientIterator authSvcClientItr = new AuthSvcInternalApiClientIterator(_authSvcEndPointLocator, _coordinator); try { if (authSvcClientItr.hasNext()) { endpoint = authSvcClientItr.peek().toString(); // String queryParameters = "?username=" + username; if (tenant != null) { queryParameters += "&tenantURI=" + tenant.getId(); if (tenant.getUserMappings() != null) { String userMappingStr = MarshallUtil.convertTenantUserMappingToString(tenant); String encodedUserMapping = URLEncoder.encode(userMappingStr); queryParameters += "&usermappings=" + encodedUserMapping; } } final ClientResponse response = authSvcClientItr.get(URI.create(_URI_USERTENANT + queryParameters)); final int status = response.getStatus(); _log.debug("Status: {}", status); if (status == ClientResponse.Status.OK.getStatusCode()) { return response.getEntity(UserTenantList.class); } else if (status == ClientResponse.Status.BAD_REQUEST.getStatusCode()) { throw APIException.badRequests.theParametersAreNotValid(response.hasEntity() ? response.getEntity(String.class) : "Bad request"); } else { _log.info("Unexpected response code {}.", status); } } } catch (APIException e) { throw e; } catch (Exception e) { _log.info("Exception connecting to {}. ", endpoint, e); } } throw SecurityException.retryables .requiredServiceUnvailable(ServiceLocatorInfo.AUTH_SVC.getServiceName()); } /** * Make an internal REST API call to the authsvc in order to reload the user in the * DB. * * @param username */ public static void refreshUser(String username) { String endpoint = null; int attempts = 0; while (attempts < _MAX_VALIDATION_RETRIES) { _log.debug("Refresh user, attempt {}", ++attempts); AuthSvcInternalApiClientIterator authSvcClientItr = new AuthSvcInternalApiClientIterator( _authSvcEndPointLocator, _coordinator); try { if (authSvcClientItr.hasNext()) { endpoint = authSvcClientItr.peek().toString(); final ClientResponse response = authSvcClientItr .put(URI.create(_URI_REFRESH + "?username=" + URLEncoder.encode(username, "UTF-8")), null); final int status = response.getStatus(); _log.debug("Status: {}", status); if (status == ClientResponse.Status.OK.getStatusCode()) { return; } else if (status == ClientResponse.Status.BAD_REQUEST .getStatusCode()) { throw APIException.badRequests.principalSearchFailed(username); } else if (status == ClientResponse.Status.INTERNAL_SERVER_ERROR .getStatusCode()) { ServiceErrorRestRep error = response.getEntity(ServiceErrorRestRep.class); // if we got here, it means that we refresh user has failed throw SecurityException.fatals.failedToRefreshUser(error .getDetailedMessage()); } else { _log.error("Unexpected response code {}.", status); } } } catch (APIException e) { throw e; } catch (FatalSecurityException e) { throw e; } catch (Exception e) { _log.info("Exception connecting to {}. ", endpoint, e); } } throw SecurityException.retryables .requiredServiceUnvailable(ServiceLocatorInfo.AUTH_SVC.getServiceName()); } /** * remove duplicated string from a list * @param input * @return */ private static List<String> deDuplicate(List<String> input) { if (input == null) { return input; } HashSet hs = new HashSet(); hs.addAll(input); List<String> result = new ArrayList<String>(); result.addAll(hs); return result; } }