@@ -22,6 +22,9 @@ server { | |||||
} | } | ||||
location /nodeman { | location /nodeman { | ||||
return 302 /nodeman/; | |||||
} | |||||
location /nodeman/ { | |||||
proxy_pass http://127.0.0.1:7800/; | proxy_pass http://127.0.0.1:7800/; | ||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; | proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; | ||||
proxy_set_header X-Real-IP $remote_addr; | proxy_set_header X-Real-IP $remote_addr; | ||||
@@ -22,6 +22,9 @@ server { | |||||
} | } | ||||
location /nodeman { | location /nodeman { | ||||
return 302 /nodeman/; | |||||
} | |||||
location /nodeman/ { | |||||
proxy_pass http://127.0.0.1:7800/; | proxy_pass http://127.0.0.1:7800/; | ||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; | proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; | ||||
proxy_set_header X-Real-IP $remote_addr; | proxy_set_header X-Real-IP $remote_addr; | ||||
@@ -19,6 +19,9 @@ server { | |||||
} | } | ||||
location /nodeman { | location /nodeman { | ||||
return 302 /nodeman/; | |||||
} | |||||
location /nodeman/ { | |||||
proxy_pass http://127.0.0.1:7800/; | proxy_pass http://127.0.0.1:7800/; | ||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; | proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; | ||||
proxy_set_header X-Real-IP $remote_addr; | proxy_set_header X-Real-IP $remote_addr; | ||||
@@ -19,6 +19,9 @@ server { | |||||
} | } | ||||
location /nodeman { | location /nodeman { | ||||
return 302 /nodeman/; | |||||
} | |||||
location /nodeman/ { | |||||
proxy_pass http://127.0.0.1:7800/; | proxy_pass http://127.0.0.1:7800/; | ||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; | proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; | ||||
proxy_set_header X-Real-IP $remote_addr; | proxy_set_header X-Real-IP $remote_addr; | ||||
@@ -7,12 +7,16 @@ package bubble.main.http; | |||||
import bubble.main.BubbleApiMain; | import bubble.main.BubbleApiMain; | ||||
import org.apache.commons.io.IOUtils; | import org.apache.commons.io.IOUtils; | ||||
import org.cobbzilla.util.http.HttpRequestBean; | import org.cobbzilla.util.http.HttpRequestBean; | ||||
import org.cobbzilla.util.string.Base64; | |||||
import org.cobbzilla.wizard.api.ApiException; | import org.cobbzilla.wizard.api.ApiException; | ||||
import org.cobbzilla.wizard.api.ForbiddenException; | import org.cobbzilla.wizard.api.ForbiddenException; | ||||
import org.cobbzilla.wizard.api.NotFoundException; | import org.cobbzilla.wizard.api.NotFoundException; | ||||
import org.cobbzilla.wizard.api.ValidationException; | import org.cobbzilla.wizard.api.ValidationException; | ||||
import org.cobbzilla.wizard.client.ApiClientBase; | |||||
import org.cobbzilla.wizard.util.RestResponse; | import org.cobbzilla.wizard.util.RestResponse; | ||||
import javax.ws.rs.core.HttpHeaders; | |||||
import static org.cobbzilla.util.daemon.ZillaRuntime.errorString; | import static org.cobbzilla.util.daemon.ZillaRuntime.errorString; | ||||
import static org.cobbzilla.util.http.HttpStatusCodes.OK; | import static org.cobbzilla.util.http.HttpStatusCodes.OK; | ||||
import static org.cobbzilla.util.json.JsonUtil.prettyJson; | import static org.cobbzilla.util.json.JsonUtil.prettyJson; | ||||
@@ -23,6 +27,16 @@ public abstract class BubbleHttpMain<OPT extends BubbleHttpOptions> extends Bubb | |||||
protected abstract String getMethod(); | protected abstract String getMethod(); | ||||
@Override protected ApiClientBase initApiClient() { | |||||
final ApiClientBase api = super.initApiClient(); | |||||
final OPT options = getOptions(); | |||||
if (options.hasHttpBasicUser() || options.hasHttpBasicPassword()) { | |||||
final String usernameAndPassword = options.getHttpBasicUser() + ":" + options.getHttpBasicPassword(); | |||||
api.setHeader(HttpHeaders.AUTHORIZATION, "Basic "+ Base64.encodeBytes(usernameAndPassword.getBytes())); | |||||
} | |||||
return api; | |||||
} | |||||
@Override protected void run() throws Exception { | @Override protected void run() throws Exception { | ||||
final OPT options = getOptions(); | final OPT options = getOptions(); | ||||
final String url = options.getUrl(); | final String url = options.getUrl(); | ||||
@@ -32,7 +46,11 @@ public abstract class BubbleHttpMain<OPT extends BubbleHttpOptions> extends Bubb | |||||
final String entity = options instanceof BubbleHttpEntityOptions | final String entity = options instanceof BubbleHttpEntityOptions | ||||
? ((BubbleHttpEntityOptions) options).getRequestJson() | ? ((BubbleHttpEntityOptions) options).getRequestJson() | ||||
: null; | : null; | ||||
IOUtils.copyLarge(getApiClient().getStream(new HttpRequestBean(getMethod(), requestUrl, entity)), System.out); | |||||
final HttpRequestBean request = new HttpRequestBean(getMethod(), requestUrl, entity); | |||||
if (options.hasHttpBasicUser()) request.setAuthUsername(options.getHttpBasicUser()); | |||||
if (options.hasHttpBasicPassword()) request.setAuthPassword(options.getHttpBasicPassword()); | |||||
IOUtils.copyLarge(getApiClient().getStream(request), System.out); | |||||
} else { | } else { | ||||
RestResponse response = null; | RestResponse response = null; | ||||
try { | try { | ||||
@@ -9,6 +9,8 @@ import lombok.Getter; | |||||
import lombok.Setter; | import lombok.Setter; | ||||
import org.kohsuke.args4j.Option; | import org.kohsuke.args4j.Option; | ||||
import static org.cobbzilla.util.daemon.ZillaRuntime.empty; | |||||
public class BubbleHttpOptions extends BubbleApiOptionsBase { | public class BubbleHttpOptions extends BubbleApiOptionsBase { | ||||
public static final String USAGE_URL = "URL to request"; | public static final String USAGE_URL = "URL to request"; | ||||
@@ -17,6 +19,20 @@ public class BubbleHttpOptions extends BubbleApiOptionsBase { | |||||
@Option(name=OPT_URL, aliases=LONGOPT_URL, usage=USAGE_URL) | @Option(name=OPT_URL, aliases=LONGOPT_URL, usage=USAGE_URL) | ||||
@Getter @Setter private String url; | @Getter @Setter private String url; | ||||
public static final String USAGE_HTTP_USER = "HTTP Basic Auth username"; | |||||
public static final String OPT_HTTP_USER = "-B"; | |||||
public static final String LONGOPT_HTTP_USER= "--user"; | |||||
@Option(name=OPT_HTTP_USER, aliases=LONGOPT_HTTP_USER, usage=USAGE_HTTP_USER) | |||||
@Getter @Setter private String httpBasicUser; | |||||
public boolean hasHttpBasicUser () { return !empty(httpBasicUser); } | |||||
public static final String USAGE_HTTP_PASS = "HTTP Basic Auth username"; | |||||
public static final String OPT_HTTP_PASS = "-W"; | |||||
public static final String LONGOPT_HTTP_PASS= "--password"; | |||||
@Option(name=OPT_HTTP_PASS, aliases=LONGOPT_HTTP_PASS, usage=USAGE_HTTP_PASS) | |||||
@Getter @Setter private String httpBasicPassword; | |||||
public boolean hasHttpBasicPassword () { return !empty(httpBasicPassword); } | |||||
public static final String USAGE_RAW = "Raw response: do not parse as JSON"; | public static final String USAGE_RAW = "Raw response: do not parse as JSON"; | ||||
public static final String OPT_RAW = "-R"; | public static final String OPT_RAW = "-R"; | ||||
public static final String LONGOPT_RAW= "--raw"; | public static final String LONGOPT_RAW= "--raw"; | ||||
@@ -14,6 +14,7 @@ import net.lingala.zip4j.exception.ZipException; | |||||
import org.cobbzilla.util.http.*; | import org.cobbzilla.util.http.*; | ||||
import org.cobbzilla.util.io.FileUtil; | import org.cobbzilla.util.io.FileUtil; | ||||
import org.cobbzilla.util.io.TempDir; | import org.cobbzilla.util.io.TempDir; | ||||
import org.cobbzilla.util.string.Base64; | |||||
import org.cobbzilla.wizard.auth.LoginRequest; | import org.cobbzilla.wizard.auth.LoginRequest; | ||||
import org.glassfish.jersey.media.multipart.FormDataParam; | import org.glassfish.jersey.media.multipart.FormDataParam; | ||||
import org.glassfish.jersey.server.ContainerRequest; | import org.glassfish.jersey.server.ContainerRequest; | ||||
@@ -21,6 +22,7 @@ import org.springframework.beans.factory.annotation.Autowired; | |||||
import javax.ws.rs.*; | import javax.ws.rs.*; | ||||
import javax.ws.rs.core.Context; | import javax.ws.rs.core.Context; | ||||
import javax.ws.rs.core.HttpHeaders; | |||||
import javax.ws.rs.core.Response; | import javax.ws.rs.core.Response; | ||||
import java.io.File; | import java.io.File; | ||||
import java.io.IOException; | import java.io.IOException; | ||||
@@ -45,6 +47,7 @@ public class NodeManagerResource { | |||||
public static final String ROOT_DIR_PREFIX = "root_dir/"; | public static final String ROOT_DIR_PREFIX = "root_dir/"; | ||||
public static final String COMPONENT_ROOT = "root"; | public static final String COMPONENT_ROOT = "root"; | ||||
public static final Set<String> PATCH_COMPONENTS = new HashSet<>(Arrays.asList(new String[]{COMPONENT_ROOT, "bubble", "mitmproxy"})); | public static final Set<String> PATCH_COMPONENTS = new HashSet<>(Arrays.asList(new String[]{COMPONENT_ROOT, "bubble", "mitmproxy"})); | ||||
public static final String AUTH_BASIC_PREFIX = "Basic "; | |||||
private BubbleNode node; | private BubbleNode node; | ||||
@@ -70,12 +73,12 @@ public class NodeManagerResource { | |||||
if (selfNode != null && selfNode.hasSageNode() && !selfNode.getUuid().equals(selfNode.getSageNode())) { | if (selfNode != null && selfNode.hasSageNode() && !selfNode.getUuid().equals(selfNode.getSageNode())) { | ||||
final BubbleNode sageNode = nodeDAO.findByUuid(selfNode.getSageNode()); | final BubbleNode sageNode = nodeDAO.findByUuid(selfNode.getSageNode()); | ||||
if (sageNode == null) { | if (sageNode == null) { | ||||
log.warn("setPassword: error finding sage to notify: "+selfNode.getSageNode()); | |||||
log.warn("setPassword: error finding sage to notify: " + selfNode.getSageNode()); | |||||
} else { | } else { | ||||
selfNode.setNodeManagerPassword(password); | selfNode.setNodeManagerPassword(password); | ||||
final NotificationReceipt receipt = notificationService.notify(sageNode, hello_to_sage, selfNode); | final NotificationReceipt receipt = notificationService.notify(sageNode, hello_to_sage, selfNode); | ||||
if (!receipt.isSuccess()) { | if (!receipt.isSuccess()) { | ||||
log.warn("setPassword: error notifying sage of new nodemanager password: "+receipt); | |||||
log.warn("setPassword: error notifying sage of new nodemanager password: " + receipt); | |||||
} | } | ||||
selfNode.setNodeManagerPassword(null); // just in case the object gets sync'd to db | selfNode.setNodeManagerPassword(null); // just in case the object gets sync'd to db | ||||
} | } | ||||
@@ -97,13 +100,26 @@ public class NodeManagerResource { | |||||
if (!caller.admin() && !caller.getUuid().equals(node.getAccount())) throw forbiddenEx(); | if (!caller.admin() && !caller.getUuid().equals(node.getAccount())) throw forbiddenEx(); | ||||
final BubbleNode n = nodeDAO.findByUuid(node.getUuid()); | final BubbleNode n = nodeDAO.findByUuid(node.getUuid()); | ||||
if (!n.hasNodeManagerPassword()) throw invalidEx("err.nodemanager.noPasswordSet"); | |||||
final String nodeManagerPassword; | |||||
if (n.hasNodeManagerPassword()) { | |||||
nodeManagerPassword = n.getNodeManagerPassword(); | |||||
} else { | |||||
final String authHeader = ctx.getHeaderString(HttpHeaders.AUTHORIZATION); | |||||
if (empty(authHeader)) throw invalidEx("err.nodemanager.noPasswordSet"); | |||||
try { | |||||
final String userNameAndPassword = new String(Base64.decode(authHeader.substring(authHeader.indexOf(AUTH_BASIC_PREFIX)+AUTH_BASIC_PREFIX.length()))); | |||||
nodeManagerPassword = userNameAndPassword.substring(userNameAndPassword.indexOf(':') + 1); | |||||
} catch (Exception e) { | |||||
throw invalidEx("err.nodemanager.noPasswordSet"); | |||||
} | |||||
} | |||||
final String url = "https://" + node.getFqdn() + ":" + node.getAdminPort() + "/nodeman/" + path; | |||||
final String url = "https://" + node.getFqdn() + ":" + node.getSslPort() + "/nodeman/" + path; | |||||
log.info("validateNodeManagerRequest: requesting URL: "+url); | |||||
return new HttpRequestBean(url) | return new HttpRequestBean(url) | ||||
.setAuthType(HttpAuthType.basic) | .setAuthType(HttpAuthType.basic) | ||||
.setAuthUsername(BUBBLE_NODE_ADMIN) | .setAuthUsername(BUBBLE_NODE_ADMIN) | ||||
.setAuthPassword(n.getNodeManagerPassword()); | |||||
.setAuthPassword(nodeManagerPassword); | |||||
} | } | ||||
public Response callNodeManager(HttpRequestBean request, String prefix) { | public Response callNodeManager(HttpRequestBean request, String prefix) { | ||||
@@ -1 +1 @@ | |||||
bubble.version=0.9.17 | |||||
bubble.version=0.9.18 |
@@ -1 +1 @@ | |||||
Subproject commit 2d2f7169b6e4135c16a2ac2ddae7862d973387e5 | |||||
Subproject commit 1bdd2482ba5188c5f36a20a1fadbbb2a18ca50f4 |
@@ -1 +1 @@ | |||||
Subproject commit 7461458467963a9da89477ce0b12a4f8fa15dd7b | |||||
Subproject commit 7d6a220fb6327638100014fae0445de44a6424aa |