package io.cattle.platform.iaas.api.auth.integration.internal.rancher;
import static io.cattle.platform.core.model.tables.AccountTable.*;
import static io.cattle.platform.core.model.tables.AgentTable.*;
import io.cattle.platform.archaius.util.ArchaiusUtil;
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.constants.ProjectConstants;
import io.cattle.platform.core.constants.ServiceConstants;
import io.cattle.platform.core.model.Account;
import io.cattle.platform.core.model.Agent;
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.interfaces.AccountLookup;
import io.cattle.platform.object.ObjectManager;
import io.cattle.platform.object.util.DataAccessor;
import io.cattle.platform.util.type.Priority;
import io.github.ibuildthecloud.gdapi.condition.Condition;
import io.github.ibuildthecloud.gdapi.condition.ConditionType;
import io.github.ibuildthecloud.gdapi.context.ApiContext;
import io.github.ibuildthecloud.gdapi.request.ApiRequest;
import java.io.UnsupportedEncodingException;
import java.util.ArrayList;
import java.util.List;
import javax.inject.Inject;
import javax.servlet.http.HttpServletResponse;
import org.apache.commons.codec.binary.Base64;
import org.apache.commons.lang3.StringUtils;
import com.netflix.config.DynamicStringProperty;
public class BasicAuthImpl implements AccountLookup, Priority {
public static final String AUTH_HEADER = "Authorization";
public static final String CHALLENGE_HEADER = "WWW-Authenticate";
public static final String BASIC = "Basic";
public static final String BASIC_REALM = "Basic realm=\"%s\"";
private static final String NO_CHALLENGE_HEADER = "X-API-No-Challenge";
private static final String CONNECTION = "Connection";
private static final DynamicStringProperty REALM = ArchaiusUtil.getString("api.auth.realm");
AuthDao authDao;
@Inject
AdminAuthLookUp adminAuthLookUp;
@Inject
ObjectManager objectManager;
@Inject
TokenAuthLookup tokenAuthLookUp;
@Override
public Account getAccount(ApiRequest request) {
String[] auth = getUsernamePassword(request.getServletContext().getRequest().getHeader(AUTH_HEADER));
if (auth == null) {
return null;
}
Account account = authDao.getAccountByKeys(auth[0], auth[1], ApiContext.getContext().getTransformationService());
if (account != null) {
return switchAccount(account, request);
} else if (auth[0].toLowerCase().startsWith(ProjectConstants.OAUTH_BASIC.toLowerCase()) && SecurityConstants.SECURITY.get()) {
String[] splits = auth[0].split("=");
String projectId = splits.length == 2 ? splits[1] : null;
request.setAttribute(ProjectConstants.PROJECT_HEADER, projectId);
account = tokenAuthLookUp.getAccountAccess(ProjectConstants.AUTH_TYPE + auth[1], request);
} else if (auth[0].toLowerCase().startsWith(ProjectConstants.OAUTH_BASIC.toLowerCase()) && !SecurityConstants.SECURITY.get()) {
String[] splits = auth[0].split("=");
String projectId = splits.length == 2 ? splits[1] : null;
request.setAttribute(ProjectConstants.PROJECT_HEADER, projectId);
account = adminAuthLookUp.getAccount(request);
}
return account;
}
protected Account switchAccount(Account account, ApiRequest request) {
boolean shouldSwitch = DataAccessor.fromDataFieldOf(account)
.withKey(AccountConstants.DATA_ACT_AS_RESOURCE_ACCOUNT)
.withDefault(false)
.as(Boolean.class);
boolean projectAdmin = false;
if (DataAccessor.fromDataFieldOf(account)
.withKey(AccountConstants.DATA_ACT_AS_RESOURCE_ADMIN_ACCOUNT)
.withDefault(false)
.as(Boolean.class)) {
shouldSwitch = true;
projectAdmin = true;
}
if (shouldSwitch) {
Long agentOwnerId = DataAccessor.fromDataFieldOf(account).withKey(AccountConstants.DATA_AGENT_OWNER_ID).as(Long.class);
Agent agent = null;
if (agentOwnerId != null) {
agent = objectManager.findAny(Agent.class, AGENT.ID, agentOwnerId);
} else {
agent = objectManager.findAny(Agent.class, AGENT.ACCOUNT_ID, account.getId());
}
if (agent != null) {
Long resourceAccId = DataAccessor.fromDataFieldOf(agent)
.withKey(AgentConstants.DATA_AGENT_RESOURCES_ACCOUNT_ID)
.as(Long.class);
if (resourceAccId != null) {
List<Object> activeStates = new ArrayList<>();
activeStates.add(CommonStatesConstants.ACTIVE);
activeStates.add(ServiceConstants.STATE_UPGRADING);
Account resourceAccount = objectManager.findAny(Account.class,
ACCOUNT.ID, resourceAccId,
ACCOUNT.STATE, new Condition(ConditionType.IN, activeStates));
if (resourceAccount == null) {
return null;
}
if (projectAdmin) {
resourceAccount.setKind("projectadmin");
} else {
resourceAccount.setKind("environment");
}
return resourceAccount;
}
}
}
return account;
}
@Override
public boolean challenge(ApiRequest request) {
if ("upgrade".equalsIgnoreCase(request.getServletContext().getRequest().getHeader(CONNECTION))) {
return false;
}
if ("true".equalsIgnoreCase(request.getServletContext().getRequest().getHeader(NO_CHALLENGE_HEADER))) {
return false;
}
HttpServletResponse response = request.getServletContext().getResponse();
String realm = REALM.get();
if (realm == null) {
response.setHeader(CHALLENGE_HEADER, BASIC);
} else {
response.setHeader(CHALLENGE_HEADER, String.format(BASIC_REALM, realm));
}
return true;
}
protected String getRealm(ApiRequest request) {
return REALM.get();
}
public static String[] getUsernamePassword(ApiRequest request) {
return getUsernamePassword(request.getServletContext().getRequest().getHeader(AUTH_HEADER));
}
public static String[] getUsernamePassword(String auth) {
if (auth == null)
return null;
String[] parts = StringUtils.split(auth);
if (parts.length != 2) {
return null;
}
if (!parts[0].equalsIgnoreCase(BASIC))
return null;
try {
String text = new String(Base64.decodeBase64(parts[1]), "UTF-8");
int i = text.indexOf(":");
if (i == -1) {
return null;
}
return new String[]{text.substring(0, i), text.substring(i + 1)};
} catch (UnsupportedEncodingException e) {
return null;
}
}
@Override
public int getPriority() {
return Priority.DEFAULT;
}
public AuthDao getAuthDao() {
return authDao;
}
@Inject
public void setAuthDao(AuthDao authDao) {
this.authDao = authDao;
}
@Override
public boolean isConfigured() {
return true;
}
@Override
public String getName() {
return "BasicAuth";
}
}