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.api.auth.impl.OptionCallback; import io.cattle.platform.api.auth.impl.PolicyOptions; import io.cattle.platform.api.pubsub.model.Subscribe; import io.cattle.platform.api.pubsub.util.SubscriptionUtils; import io.cattle.platform.api.pubsub.util.SubscriptionUtils.SubscriptionStyle; import io.cattle.platform.core.constants.AccountConstants; import io.cattle.platform.core.constants.AgentConstants; import io.cattle.platform.core.constants.CommonStatesConstants; import io.cattle.platform.core.model.Account; import io.cattle.platform.core.model.Agent; import io.cattle.platform.iaas.api.auth.AchaiusPolicyOptionsFactory; import io.cattle.platform.iaas.api.auth.AuthorizationProvider; import io.cattle.platform.iaas.event.IaasEvents; import io.cattle.platform.object.process.ObjectProcessManager; import io.github.ibuildthecloud.gdapi.context.ApiContext; import io.github.ibuildthecloud.gdapi.exception.ClientVisibleException; import io.github.ibuildthecloud.gdapi.exception.ValidationErrorException; import io.github.ibuildthecloud.gdapi.factory.SchemaFactory; import io.github.ibuildthecloud.gdapi.model.Pagination; import io.github.ibuildthecloud.gdapi.request.ApiRequest; import io.github.ibuildthecloud.gdapi.request.resource.ResourceManager; import io.github.ibuildthecloud.gdapi.request.resource.ResourceManagerLocator; import io.github.ibuildthecloud.gdapi.util.ResponseCodes; import io.github.ibuildthecloud.gdapi.validation.ValidationErrorCodes; import java.util.Arrays; import java.util.HashSet; import java.util.Set; import javax.inject.Inject; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class AgentQualifierAuthorizationProvider implements AuthorizationProvider { private static final Logger log = LoggerFactory.getLogger(AgentQualifierAuthorizationProvider.class); private static final Set<String> STATES = new HashSet<String>(Arrays.asList( CommonStatesConstants.ACTIVATING, CommonStatesConstants.ACTIVE, AgentConstants.STATE_RECONNECTING, AgentConstants.STATE_DISCONNECTED, AgentConstants.STATE_DISCONNECTING, AgentConstants.STATE_FINISHING_RECONNECT, AgentConstants.STATE_RECONNECTED )); private static final Set<String> DISCONNECTED = new HashSet<String>(Arrays.asList( AgentConstants.STATE_DISCONNECTED, AgentConstants.STATE_DISCONNECTING )); AchaiusPolicyOptionsFactory optionsFactory; ResourceManagerLocator locator; @Inject ObjectProcessManager processManager; @Override public Policy getPolicy(final Account account, Account authenticatedAsAccount, Set<Identity> identities, ApiRequest request) { PolicyOptions policyOptions = optionsFactory.getOptions(account); boolean apply = false; final SubscriptionStyle accountStyle = SubscriptionUtils.getSubscriptionStyle(policyOptions); /* This boolean logic could be optimized but this seems more readable. */ if (accountStyle == SubscriptionStyle.RAW) { apply = true; } else if (accountStyle == SubscriptionStyle.QUALIFIED && AccountConstants.AGENT_KIND.equals(account.getKind())) { apply = true; } if (!apply) { return null; } final PolicyOptionsWrapper options = new PolicyOptionsWrapper(policyOptions); AccountPolicy policy = new AccountPolicy(account, authenticatedAsAccount, identities, options); options.addCallback(Policy.AGENT_ID, new OptionCallback() { @Override public String getOption() { Long agentId = getAgent(); return agentId == null ? null : Long.toString(agentId); } }); options.addCallback(SubscriptionUtils.POLICY_SUBSCRIPTION_STYLE, new OptionCallback() { @Override public String getOption() { if (accountStyle == SubscriptionStyle.RAW && getRawAgentId() != null) { return SubscriptionStyle.QUALIFIED.toString(); } return accountStyle.toString(); } }); options.setOption(SubscriptionUtils.POLICY_SUBSCRIPTION_QUALIFIER, IaasEvents.AGENT_QUALIFIER); options.addCallback(SubscriptionUtils.POLICY_SUBSCRIPTION_QUALIFIER_VALUE, new OptionCallback() { @Override public String getOption() { String agentId = options.getOption(Policy.AGENT_ID); if (agentId == null) { log.error("Failed to determine the proper agent ID for subscription for account [{}]", account.getId()); throw new ClientVisibleException(ResponseCodes.FORBIDDEN); } return agentId; } }); return policy; } protected Long getRawAgentId() { ApiRequest request = ApiContext.getContext().getApiRequest(); Subscribe subscribe = request.proxyRequestObject(Subscribe.class); try { Long agentId = subscribe.getAgentId(); if (agentId != null) { return agentId; } } catch (Throwable t) { // ignore errors; } return null; } protected Long getAgent() { ApiRequest request = ApiContext.getContext().getApiRequest(); Long agentId = getRawAgentId(); if (agentId != null) { return agentId; } String type = request.getSchemaFactory().getSchemaName(Agent.class); ResourceManager rm = getLocator().getResourceManagerByType(type); Long id = null; /* This really isn't the best logic. Basically we are looking for agents with state in STATES */ for (Object obj : rm.list(type, null, Pagination.limit(2))) { if (!(obj instanceof Agent)) { continue; } Agent agent = (Agent)obj; if (STATES.contains(agent.getState())) { if (id != null) { throw new ValidationErrorException(ValidationErrorCodes.MISSING_REQUIRED, "agentId"); } else { if (DISCONNECTED.contains(agent.getState())) { processManager.scheduleProcessInstance(AgentConstants.PROCESS_RECONNECT, agent, null); } id = agent.getId(); } } } return id; } @Override public SchemaFactory getSchemaFactory(Account account, Policy policy, ApiRequest request) { return null; } @Override public String getRole(Account account, Policy policy, ApiRequest request) { return null; } public AchaiusPolicyOptionsFactory getOptionsFactory() { return optionsFactory; } @Inject public void setOptionsFactory(AchaiusPolicyOptionsFactory optionsFactory) { this.optionsFactory = optionsFactory; } public ResourceManagerLocator getLocator() { return locator; } @Inject public void setLocator(ResourceManagerLocator locator) { this.locator = locator; } }