package io.cattle.platform.iaas.api.auth.impl; import io.cattle.platform.api.auth.Identity; import io.cattle.platform.api.auth.Policy; import io.cattle.platform.core.constants.AccountConstants; import io.cattle.platform.core.constants.ProjectConstants; import io.cattle.platform.core.dao.AccountDao; import io.cattle.platform.core.model.Account; import io.cattle.platform.iaas.api.auth.AuthorizationProvider; import io.cattle.platform.iaas.api.auth.SecurityConstants; import io.cattle.platform.iaas.api.auth.dao.AuthDao; import io.cattle.platform.iaas.api.auth.integration.external.ExternalServiceAuthProvider; import io.cattle.platform.iaas.api.auth.integration.interfaces.AccountLookup; import io.cattle.platform.iaas.api.auth.integration.interfaces.IdentityProvider; import io.cattle.platform.object.ObjectManager; import io.cattle.platform.util.type.CollectionUtils; import io.github.ibuildthecloud.gdapi.context.ApiContext; import io.github.ibuildthecloud.gdapi.exception.ClientVisibleException; import io.github.ibuildthecloud.gdapi.factory.SchemaFactory; import io.github.ibuildthecloud.gdapi.request.ApiRequest; import io.github.ibuildthecloud.gdapi.request.handler.AbstractApiRequestHandler; import io.github.ibuildthecloud.gdapi.util.ResponseCodes; import io.github.ibuildthecloud.gdapi.util.TransformationService; import java.io.IOException; import java.util.HashSet; import java.util.List; import java.util.Set; import javax.inject.Inject; import org.apache.commons.lang3.StringUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class ApiAuthenticator extends AbstractApiRequestHandler { private static final Logger log = LoggerFactory.getLogger(ApiAuthenticator.class); private static final String ACCOUNT_ID_HEADER = "X-API-ACCOUNT-ID"; private static final String USER_ID_HEADER = "X-API-USER-ID"; AuthDao authDao; List<AccountLookup> accountLookups; List<IdentityProvider> identityProviders; List<AuthorizationProvider> authorizationProviders; @Inject ObjectManager objectManager; @Inject TransformationService transformationService; @Inject ExternalServiceAuthProvider externalAuthProvider; @Inject AccountDao accountDao; @Override public void handle(ApiRequest request) throws IOException { if (ApiContext.getContext().getPolicy() != null) { return; } if (ApiContext.getContext().getTransformationService() == null){ ApiContext.getContext().setTransformationService(transformationService); } Account authenticatedAsAccount = getAccount(request); if (authenticatedAsAccount == null || !accountDao.isActiveAccount(authenticatedAsAccount)) { throw new ClientVisibleException(ResponseCodes.UNAUTHORIZED); } Set<Identity> identities = getIdentities(authenticatedAsAccount); if (identities == null || identities.size() == 0) { throw new ClientVisibleException(ResponseCodes.UNAUTHORIZED); } Account account = getAccountRequested(authenticatedAsAccount, identities, request); Policy policy = getPolicy(account, authenticatedAsAccount, identities, request); if (policy == null) { log.error("Failed to find policy for [{}]", account.getId()); throwUnauthorized(); } SchemaFactory schemaFactory = getSchemaFactory(account, policy, request); if (schemaFactory == null) { log.error("Failed to find a schema for account type [{}]", account.getKind()); if (SecurityConstants.SECURITY.get()) { throwUnauthorized(); } } else if (request.getType() != null && request.getType().endsWith("s")) { //In case the SchemaFactory didn't have the type before. //we need to make it singular. String singleType = schemaFactory.getSingularName(request.getType()); if (singleType != null) { request.setType(singleType); } } saveInContext(request, policy, schemaFactory); } protected void throwUnauthorized() { throw new ClientVisibleException(ResponseCodes.UNAUTHORIZED); } protected void saveInContext(ApiRequest request, Policy policy, SchemaFactory schemaFactory) { if (schemaFactory != null) { request.setSchemaFactory(schemaFactory); } String accountId = (String) ApiContext.getContext().getIdFormatter().formatId(objectManager.getType(Account.class), policy.getAccountId()); request.getServletContext().getResponse().addHeader(ACCOUNT_ID_HEADER, accountId); String userId = (String) ApiContext.getContext().getIdFormatter().formatId(objectManager.getType(Account.class), policy.getAuthenticatedAsAccountId()); request.getServletContext().getResponse().addHeader(USER_ID_HEADER, userId); ApiContext.getContext().setPolicy(policy); } protected Policy getPolicy(Account account, Account authenticatedAsAccount, Set<Identity> identities, ApiRequest request) { Policy policy = null; for (AuthorizationProvider auth : authorizationProviders) { policy = auth.getPolicy(account, authenticatedAsAccount, identities, request); if (policy != null) { break; } } return policy; } protected SchemaFactory getSchemaFactory(Account account, Policy policy, ApiRequest request) { SchemaFactory factory = null; for (AuthorizationProvider auth : authorizationProviders) { factory = auth.getSchemaFactory(account, policy, request); if (factory != null) { break; } } return factory; } protected Account getAccount(ApiRequest request) { Account account = null; for (AccountLookup lookup : accountLookups) { if (lookup.isConfigured()){ account = lookup.getAccount(request); if (account != null) { request.setAttribute(AccountConstants.AUTH_TYPE, lookup.getName()); break; } } } if (account != null) { return account; } if (SecurityConstants.SECURITY.get()) { for (AccountLookup lookup : accountLookups) { if (lookup.challenge(request)) { break; } } } return null; } private Set<Identity> getIdentities(Account account) { Set<Identity> identities = new HashSet<>(); for (IdentityProvider identityProvider : identityProviders) { identities.addAll(identityProvider.getIdentities(account)); } identities.addAll(externalAuthProvider.getIdentities(account)); identities.remove(null); return identities; } private Account getAccountRequested(Account authenticatedAsAccount, Set<Identity> identities, ApiRequest request) { Account project; String parsedProjectId = null; String projectId = request.getServletContext().getRequest().getHeader(ProjectConstants.PROJECT_HEADER); if (projectId == null || projectId.isEmpty()) { projectId = request.getServletContext().getRequest().getParameter("projectId"); } if (projectId == null || projectId.isEmpty()) { projectId = (String) request.getAttribute(ProjectConstants.PROJECT_HEADER); } if (projectId == null || projectId.isEmpty()) { String accessKey = request.getServletContext().getRequest().getHeader(ProjectConstants.CLIENT_ACCESS_KEY); if (StringUtils.isNotBlank(accessKey)) { Account account = authDao.getAccountByAccessKey(accessKey); if (account != null) { parsedProjectId = account.getId().toString(); } } } if (StringUtils.isBlank(projectId) && StringUtils.isBlank(parsedProjectId)) { return authenticatedAsAccount; } try { if (parsedProjectId == null) { parsedProjectId = ApiContext.getContext().getIdFormatter().parseId(projectId); } } catch (NumberFormatException e) { throw new ClientVisibleException(ResponseCodes.BAD_REQUEST, "InvalidFormat", "projectId header format is incorrect " + projectId, null); } if (StringUtils.isEmpty(parsedProjectId)) { throw new ClientVisibleException(ResponseCodes.FORBIDDEN); } try { project = authDao.getAccountById(new Long(parsedProjectId)); if (project == null || !accountDao.isActiveAccount(project)) { throw new ClientVisibleException(ResponseCodes.FORBIDDEN); } if (authenticatedAsAccount.getId().equals(project.getId())) { return authenticatedAsAccount; } } catch (NumberFormatException e) { throw new ClientVisibleException(ResponseCodes.FORBIDDEN); } Policy tempPolicy = getPolicy(authenticatedAsAccount, authenticatedAsAccount, identities, request); if (authDao.hasAccessToProject(project.getId(), authenticatedAsAccount.getId(), tempPolicy.isOption(Policy.AUTHORIZED_FOR_ALL_ACCOUNTS), identities)) { return project; } throw new ClientVisibleException(ResponseCodes.FORBIDDEN); } public AuthDao getAuthDao() { return authDao; } @Inject public void setAuthDao(AuthDao authDao) { this.authDao = authDao; } public List<AuthorizationProvider> getAuthorizationProviders() { return authorizationProviders; } @Inject public void setAuthorizationProviders(List<AuthorizationProvider> authorizationProviders) { this.authorizationProviders = CollectionUtils.orderList(AuthorizationProvider.class, authorizationProviders); } public List<AccountLookup> getAccountLookups() { return accountLookups; } @Inject public void setAccountLookups(List<AccountLookup> accountLookups) { this.accountLookups = CollectionUtils.orderList(AccountLookup.class, accountLookups); } public List<IdentityProvider> getIdentityProviders() { return identityProviders; } @Inject public void setIdentityProviders(List<IdentityProvider> identityProviders) { this.identityProviders = CollectionUtils.orderList(IdentityProvider.class, identityProviders); } }