package io.cattle.platform.host.service.impl;
import static io.cattle.platform.server.context.ServerContext.*;
import io.cattle.platform.archaius.util.ArchaiusUtil;
import io.cattle.platform.core.constants.CommonStatesConstants;
import io.cattle.platform.core.constants.HostConstants;
import io.cattle.platform.core.constants.IpAddressConstants;
import io.cattle.platform.core.model.Host;
import io.cattle.platform.core.model.IpAddress;
import io.cattle.platform.host.api.HostApiUtils;
import io.cattle.platform.host.model.HostApiAccess;
import io.cattle.platform.host.service.HostApiRSAKeyProvider;
import io.cattle.platform.host.service.HostApiService;
import io.cattle.platform.object.ObjectManager;
import io.cattle.platform.object.util.DataAccessor;
import io.cattle.platform.token.TokenService;
import io.github.ibuildthecloud.gdapi.exception.ClientVisibleException;
import io.github.ibuildthecloud.gdapi.request.ApiRequest;
import io.github.ibuildthecloud.gdapi.util.ResponseCodes;
import java.security.PublicKey;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import javax.inject.Inject;
import org.apache.commons.lang3.StringUtils;
import com.netflix.config.DynamicStringProperty;
public class HostApiServiceImpl implements HostApiService {
private static final String HOST_UUID = "hostUuid";
private static final DynamicStringProperty HEADER_AUTH = ArchaiusUtil.getString("host.api.auth.header");
private static final DynamicStringProperty HEADER_AUTH_VALUE = ArchaiusUtil.getString("host.api.auth.header.value");
ObjectManager objectManager;
TokenService tokenService;
HostApiRSAKeyProvider keyProvider;
@Override
public HostApiAccess getAccess(ApiRequest request, Long hostId, Map<String, Object> data, String... resourcePathSegments) {
return getAccess(request, hostId, data, null, resourcePathSegments);
}
@Override
public HostApiAccess getAccess(ApiRequest request, Long hostId, Map<String, Object> data, Date expiration, String... resourcePathSegments) {
Host host = objectManager.loadResource(Host.class, hostId);
if (host == null) {
return null;
}
String token = getToken(host, data, expiration);
if (token == null) {
return null;
}
Map<String, String> values = new HashMap<String, String>();
values.put(HEADER_AUTH.get(), String.format(HEADER_AUTH_VALUE.get(), token));
return new HostApiAccess(getHostAccessUrl(request, host, resourcePathSegments), token, values);
}
protected String getHostAccessUrl(ApiRequest request, Host host, String... segments) {
StringBuilder buffer = new StringBuilder();
switch (getHostApiProxyMode()) {
case HOST_API_PROXY_MODE_HA:
String proxyHost = HostApiUtils.HOST_API_PROXY_HOST.get();
if (StringUtils.isNotBlank(proxyHost)) {
String scheme = StringUtils.startsWithIgnoreCase(request.getResponseUrlBase(), "https") ? "wss://" : "ws://";
buffer.append(scheme).append(proxyHost);
break;
}
// Purposefully fall through
case HOST_API_PROXY_MODE_EMBEDDED:
String url = request.getResponseUrlBase().replaceFirst("http", "ws");
buffer.append(url);
break;
case HOST_API_PROXY_MODE_OFF:
throw new ClientVisibleException(501, "HostApiProxyDisabled");
}
if (buffer.length() <= 0) {
throw new ClientVisibleException(ResponseCodes.INTERNAL_SERVER_ERROR, "CantConstructUrl");
}
if (segments != null) {
for (String segment : segments) {
if (buffer.charAt(buffer.length() -1) != '/' && !segment.startsWith("/")) {
buffer.append("/");
}
buffer.append(segment);
}
}
return buffer.toString();
}
@Override
public Map<String, PublicKey> getPublicKeys() {
return keyProvider.getPublicKeys();
}
protected String getToken(Host host, Map<String, Object> inputData, Date expiration) {
Map<String, Object> data = new HashMap<String, Object>(inputData);
String uuid = DataAccessor.fields(host).withKey(HostConstants.FIELD_REPORTED_UUID).as(String.class);
if (uuid != null) {
data.put(HOST_UUID, uuid);
} else {
data.put(HOST_UUID, host.getUuid());
}
if (expiration == null) {
return tokenService.generateToken(data);
} else {
return tokenService.generateToken(data, expiration);
}
}
protected IpAddress getIpAddress(Host host) {
IpAddress choice = null;
for (IpAddress ip : objectManager.mappedChildren(host, IpAddress.class)) {
if (ip.getAddress() == null || !CommonStatesConstants.ACTIVE.equals(ip.getState())) {
continue;
}
if (IpAddressConstants.ROLE_PRIMARY.equals(ip.getRole())) {
choice = ip;
break;
} else if (choice == null || choice.getCreated() == null) {
choice = ip;
} else if (ip.getCreated() != null && ip.getCreated().before(choice.getCreated())) {
choice = ip;
}
}
return choice;
}
public ObjectManager getObjectManager() {
return objectManager;
}
@Inject
public void setObjectManager(ObjectManager objectManager) {
this.objectManager = objectManager;
}
public TokenService getTokenService() {
return tokenService;
}
@Inject
public void setTokenService(TokenService tokenService) {
this.tokenService = tokenService;
}
public HostApiRSAKeyProvider getKeyProvider() {
return keyProvider;
}
@Inject
public void setKeyProvider(HostApiRSAKeyProvider keyProvider) {
this.keyProvider = keyProvider;
}
}