import org.apache.commons.cli.CommandLine;
import org.apache.commons.cli.Option;
import org.apache.commons.cli.Options;
-import org.argeo.api.cli.CommandArgsException;
import org.argeo.api.cli.CommandsCli;
import org.argeo.api.cli.DescribedCommand;
-import org.argeo.cms.client.WebSocketEventClient;
+import org.argeo.cms.client.CmsClient;
import org.argeo.cms.client.WebSocketPing;
+/** Commands dealing with CMS. */
public class CmsCommands extends CommandsCli {
final static Option connectOption = Option.builder().option("c").longOpt("connect").desc("server to connect to")
.hasArg(true).build();
public CmsCommands(String commandName) {
super(commandName);
addCommand("ping", new Ping());
- addCommand("event", new Events());
+ addCommand("get", new Get());
+ addCommand("status", new Status());
+ addCommand("event", new EventCommands("event"));
}
@Override
}
- class Events implements DescribedCommand<Void> {
+ class Get implements DescribedCommand<String> {
@Override
public Options getOptions() {
}
@Override
- public Void apply(List<String> t) {
+ public String apply(List<String> t) {
CommandLine line = toCommandLine(t);
List<String> remaining = line.getArgList();
- if (remaining.size() == 0) {
- throw new CommandArgsException("There must be at least one argument");
+ String additionalUri = null;
+ if (remaining.size() != 0) {
+ additionalUri = remaining.get(0);
}
- String topic = remaining.get(0);
- String uriArg = line.getOptionValue(connectOption);
- // TODO make it more robust (trailing /, etc.)
- URI uri = URI.create(uriArg);
- if ("".equals(uri.getPath())) {
- uri = URI.create(uri.toString() + "/cms/status/event/" + topic);
- }
- new WebSocketEventClient(uri).run();
- return null;
+ String connectUri = line.getOptionValue(connectOption);
+ CmsClient cmsClient = new CmsClient(URI.create(connectUri));
+ return additionalUri != null ? cmsClient.getAsString(URI.create(additionalUri)) : cmsClient.getAsString();
+ }
+
+ @Override
+ public String getUsage() {
+ return "[URI]";
+ }
+
+ @Override
+ public String getDescription() {
+ return "Retrieve this URI as a string";
+ }
+
+ }
+
+ class Status implements DescribedCommand<String> {
+
+ @Override
+ public Options getOptions() {
+ Options options = new Options();
+ options.addOption(connectOption);
+ return options;
+ }
+
+ @Override
+ public String apply(List<String> t) {
+ CommandLine line = toCommandLine(t);
+ String connectUri = line.getOptionValue(connectOption);
+ CmsClient cmsClient = new CmsClient(URI.create(connectUri));
+ return cmsClient.getAsString(URI.create("/cms/status"));
}
@Override
public String getUsage() {
- return "TOPIC";
+ return "[URI]";
}
@Override
public String getDescription() {
- return "Listen to events on a topic";
+ return "Retrieve the CMS status as a string";
}
}
--- /dev/null
+package org.argeo.cms.cli;
+
+import java.net.URI;
+import java.util.List;
+
+import org.apache.commons.cli.CommandLine;
+import org.apache.commons.cli.Options;
+import org.argeo.api.cli.CommandArgsException;
+import org.argeo.api.cli.CommandsCli;
+import org.argeo.api.cli.DescribedCommand;
+import org.argeo.cms.client.WebSocketEventClient;
+
+/** Commands dealing with CMS events. */
+public class EventCommands extends CommandsCli {
+ public EventCommands(String commandName) {
+ super(commandName);
+ addCommand("listen", new EventListent());
+ }
+
+ @Override
+ public String getDescription() {
+ return "Utilities related to an Argeo CMS";
+ }
+
+ class EventListent implements DescribedCommand<Void> {
+
+ @Override
+ public Options getOptions() {
+ Options options = new Options();
+ options.addOption(CmsCommands.connectOption);
+ return options;
+ }
+
+ @Override
+ public Void apply(List<String> t) {
+ CommandLine line = toCommandLine(t);
+ List<String> remaining = line.getArgList();
+ if (remaining.size() == 0) {
+ throw new CommandArgsException("There must be at least one argument");
+ }
+ String topic = remaining.get(0);
+
+ String uriArg = line.getOptionValue(CmsCommands.connectOption);
+ // TODO make it more robust (trailing /, etc.)
+ URI uri = URI.create(uriArg);
+ if ("".equals(uri.getPath())) {
+ uri = URI.create(uri.toString() + "/cms/status/event/" + topic);
+ }
+ new WebSocketEventClient(uri).run();
+ return null;
+ }
+
+ @Override
+ public String getUsage() {
+ return "TOPIC";
+ }
+
+ @Override
+ public String getDescription() {
+ return "Listen to events on a topic";
+ }
+
+ }
+}
--- /dev/null
+package org.argeo.cms.client;
+
+import java.io.BufferedInputStream;
+import java.io.IOException;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.net.URL;
+import java.net.http.HttpClient;
+import java.net.http.HttpRequest;
+import java.net.http.HttpResponse;
+import java.net.http.HttpResponse.BodyHandler;
+import java.net.http.HttpResponse.BodyHandlers;
+import java.net.http.WebSocket;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.security.KeyManagementException;
+import java.security.NoSuchAlgorithmException;
+import java.security.cert.CertificateException;
+import java.security.cert.CertificateFactory;
+import java.security.cert.X509Certificate;
+import java.util.Collection;
+import java.util.concurrent.CompletableFuture;
+
+import javax.net.ssl.SSLContext;
+import javax.net.ssl.TrustManager;
+import javax.net.ssl.X509TrustManager;
+import javax.security.auth.login.LoginContext;
+import javax.security.auth.login.LoginException;
+
+import org.argeo.cms.auth.ConsoleCallbackHandler;
+import org.argeo.cms.auth.RemoteAuthUtils;
+import org.argeo.util.http.HttpHeader;
+
+/** Utility to connect to a remote CMS node. */
+public class CmsClient {
+ public final static String CLIENT_LOGIN_CONTEXT = "CLIENT";
+
+ private URI uri;
+
+ private HttpClient httpClient;
+ private String gssToken;
+
+ public CmsClient(URI uri) {
+ this.uri = uri;
+ }
+
+ public void login() {
+ String server = uri.getHost();
+
+ URL jaasUrl = CmsClient.class.getResource("jaas-client-ipa.cfg");
+ System.setProperty("java.security.auth.login.config", jaasUrl.toExternalForm());
+ try {
+ LoginContext lc = new LoginContext(CLIENT_LOGIN_CONTEXT, new ConsoleCallbackHandler());
+ lc.login();
+ gssToken = RemoteAuthUtils.createGssToken(lc.getSubject(), "HTTP", server);
+ } catch (LoginException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ } finally {
+
+ }
+ }
+
+ public String getAsString() {
+ return getAsString(uri);
+ }
+
+ public String getAsString(URI uri) {
+ uri = normalizeUri(uri);
+ try {
+ HttpClient httpClient = getHttpClient();
+
+ HttpRequest request = HttpRequest.newBuilder().uri(uri) //
+ .header(HttpHeader.AUTHORIZATION.getHeaderName(), HttpHeader.NEGOTIATE + " " + getGssToken()) //
+ .build();
+ BodyHandler<String> bodyHandler = BodyHandlers.ofString();
+ HttpResponse<String> response = httpClient.send(request, bodyHandler);
+ return response.body();
+// int responseCode = response.statusCode();
+// System.exit(responseCode);
+ } catch (IOException | InterruptedException e) {
+ throw new RuntimeException("Cannot read " + uri + " as a string", e);
+ }
+ }
+
+ protected URI normalizeUri(URI uri) {
+ if (uri.getHost() != null)
+ return uri;
+ try {
+ String path = uri.getPath();
+ if (path.startsWith("/")) {// absolute
+ return new URI(this.uri.getScheme(), this.uri.getUserInfo(), this.uri.getHost(), this.uri.getPort(),
+ path, uri.getQuery(), uri.getFragment());
+ } else {
+ String thisUriStr = this.uri.toString();
+ if (!thisUriStr.endsWith("/"))
+ thisUriStr = thisUriStr + "/";
+ return URI.create(thisUriStr + path);
+ }
+ } catch (URISyntaxException e) {
+ throw new IllegalArgumentException("Cannot interpret " + uri, e);
+ }
+ }
+
+ public URI getUri() {
+ return uri;
+ }
+
+ String getGssToken() {
+ return gssToken;
+ }
+
+ public HttpClient getHttpClient() {
+ if (httpClient == null) {
+ login();
+ HttpClient client = HttpClient.newBuilder() //
+ .sslContext(ipaSslContext()) //
+ .version(HttpClient.Version.HTTP_1_1) //
+ .build();
+ httpClient = client;
+ }
+ return httpClient;
+ }
+
+ public CompletableFuture<WebSocket> newWebSocket(WebSocket.Listener listener) {
+ return newWebSocket(uri, listener);
+ }
+
+ public CompletableFuture<WebSocket> newWebSocket(URI uri, WebSocket.Listener listener) {
+ CompletableFuture<WebSocket> ws = getHttpClient().newWebSocketBuilder()
+ .header(HttpHeader.AUTHORIZATION.getHeaderName(), HttpHeader.NEGOTIATE + " " + getGssToken())
+ .buildAsync(uri, listener);
+ return ws;
+ }
+
+ @SuppressWarnings("unchecked")
+ protected SSLContext ipaSslContext() {
+ try {
+ final Collection<X509Certificate> certificates;
+ Path caCertificatePath = Paths.get("/etc/ipa/ca.crt");
+ if (Files.exists(caCertificatePath)) {
+ CertificateFactory certificateFactory = CertificateFactory.getInstance("X509");
+ try (BufferedInputStream in = new BufferedInputStream(Files.newInputStream(caCertificatePath))) {
+ certificates = (Collection<X509Certificate>) certificateFactory.generateCertificates(in);
+ }
+ } else {
+ certificates = null;
+ }
+ TrustManager[] noopTrustManager = new TrustManager[] { new X509TrustManager() {
+ public void checkClientTrusted(X509Certificate[] xcs, String string) {
+ }
+
+ public void checkServerTrusted(X509Certificate[] xcs, String string) {
+ }
+
+ public X509Certificate[] getAcceptedIssuers() {
+ if (certificates == null)
+ return null;
+ return certificates.toArray(new X509Certificate[certificates.size()]);
+ }
+ } };
+
+ SSLContext sc = SSLContext.getInstance("ssl");
+ sc.init(null, noopTrustManager, null);
+ return sc;
+ } catch (KeyManagementException | NoSuchAlgorithmException | CertificateException | IOException e) {
+ throw new IllegalStateException("Cannot create SSL context ", e);
+ }
+ }
+
+}
+++ /dev/null
-package org.argeo.cms.client;
-
-import java.io.BufferedInputStream;
-import java.io.IOException;
-import java.net.MalformedURLException;
-import java.net.URL;
-import java.net.http.HttpClient;
-import java.net.http.HttpRequest;
-import java.net.http.HttpResponse;
-import java.net.http.HttpResponse.BodyHandler;
-import java.net.http.HttpResponse.BodyHandlers;
-import java.nio.file.Files;
-import java.nio.file.Path;
-import java.nio.file.Paths;
-import java.security.KeyManagementException;
-import java.security.NoSuchAlgorithmException;
-import java.security.cert.CertificateException;
-import java.security.cert.CertificateFactory;
-import java.security.cert.X509Certificate;
-import java.util.Collection;
-
-import javax.net.ssl.SSLContext;
-import javax.net.ssl.TrustManager;
-import javax.net.ssl.X509TrustManager;
-import javax.security.auth.Subject;
-import javax.security.auth.login.LoginContext;
-
-import org.argeo.cms.auth.ConsoleCallbackHandler;
-import org.argeo.cms.auth.RemoteAuthUtils;
-import org.argeo.util.http.HttpHeader;
-
-public class SpnegoHttpClient {
- public final static String CLIENT_LOGIN_CONTEXT = "CLIENT";
-
- public static void main(String[] args) throws MalformedURLException {
-// String principal = System.getProperty("javax.security.auth.login.name");
- if (args.length == 0) {
- System.err.println("usage: java -Djavax.security.auth.login.name=<principal@REALM> "
- + SpnegoHttpClient.class.getName() + " <url>");
- System.exit(1);
- return;
- }
- String url = args[0];
- URL u = new URL(url);
- String server = u.getHost();
-
- URL jaasUrl = SpnegoHttpClient.class.getResource("jaas-client-ipa.cfg");
- System.setProperty("java.security.auth.login.config", jaasUrl.toExternalForm());
- try {
- LoginContext lc = new LoginContext(CLIENT_LOGIN_CONTEXT, new ConsoleCallbackHandler());
- lc.login();
-
- HttpClient httpClient = openHttpClient(lc.getSubject());
- String token = RemoteAuthUtils.createGssToken(lc.getSubject(), "HTTP", server);
-
- HttpRequest request = HttpRequest.newBuilder().uri(u.toURI()) //
- .header(HttpHeader.AUTHORIZATION.getHeaderName(), HttpHeader.NEGOTIATE + " " + token) //
- .build();
- BodyHandler<String> bodyHandler = BodyHandlers.ofString();
- HttpResponse<String> response = httpClient.send(request, bodyHandler);
- System.out.println(response.body());
- int responseCode = response.statusCode();
- System.exit(responseCode);
- } catch (Exception e) {
- e.printStackTrace();
- }
- }
-
- static HttpClient openHttpClient(Subject subject) {
- HttpClient client = HttpClient.newBuilder() //
- .sslContext(ipaSslContext()) //
- .version(HttpClient.Version.HTTP_1_1) //
- .build();
-
- return client;
- }
-
- @SuppressWarnings("unchecked")
- static SSLContext ipaSslContext() {
- try {
- final Collection<X509Certificate> certificates;
- Path caCertificatePath = Paths.get("/etc/ipa/ca.crt");
- if (Files.exists(caCertificatePath)) {
- CertificateFactory certificateFactory = CertificateFactory.getInstance("X509");
- try (BufferedInputStream in = new BufferedInputStream(Files.newInputStream(caCertificatePath))) {
- certificates = (Collection<X509Certificate>) certificateFactory.generateCertificates(in);
- }
- } else {
- certificates = null;
- }
- TrustManager[] noopTrustManager = new TrustManager[] { new X509TrustManager() {
- public void checkClientTrusted(X509Certificate[] xcs, String string) {
- }
-
- public void checkServerTrusted(X509Certificate[] xcs, String string) {
- }
-
- public X509Certificate[] getAcceptedIssuers() {
- if (certificates == null)
- return null;
- return certificates.toArray(new X509Certificate[certificates.size()]);
- }
- } };
-
- SSLContext sc = SSLContext.getInstance("ssl");
- sc.init(null, noopTrustManager, null);
- return sc;
- } catch (KeyManagementException | NoSuchAlgorithmException | CertificateException | IOException e) {
- throw new IllegalStateException("Cannot create SSL context ", e);
- }
- }
-
-}
package org.argeo.cms.client;
import java.net.URI;
-import java.net.URL;
-import java.net.http.HttpClient;
import java.net.http.WebSocket;
import java.nio.ByteBuffer;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CompletionStage;
import java.util.concurrent.ExecutionException;
-import javax.security.auth.login.LoginContext;
-import javax.security.auth.login.LoginException;
-
-import org.argeo.cms.auth.RemoteAuthUtils;
-import org.argeo.util.http.HttpHeader;
-
/** Tests connectivity to the web socket server. */
public class WebSocketEventClient implements Runnable {
private final URI uri;
private WebSocket webSocket;
-
+
+ private CmsClient cmsClient;
+
public WebSocketEventClient(URI uri) {
this.uri = uri;
+ cmsClient = new CmsClient(uri);
}
@Override
public void run() {
try {
- WebSocket.Listener listener = new WebSocket.Listener() {
-
- public CompletionStage<?> onText(WebSocket webSocket, CharSequence message, boolean last) {
- System.out.println(message);
- CompletionStage<String> res = CompletableFuture.completedStage(message.toString());
- return res;
- }
-
- @Override
- public CompletionStage<?> onPong(WebSocket webSocket, ByteBuffer message) {
- // System.out.println("Pong received.");
- return null;
- }
-
- };
-
- // SPNEGO
- URL jaasUrl = SpnegoHttpClient.class.getResource("jaas-client-ipa.cfg");
- System.setProperty("java.security.auth.login.config", jaasUrl.toExternalForm());
- LoginContext lc = new LoginContext(SpnegoHttpClient.CLIENT_LOGIN_CONTEXT);
- lc.login();
- String token = RemoteAuthUtils.createGssToken(lc.getSubject(), "HTTP", uri.getHost());
-
- HttpClient client = SpnegoHttpClient.openHttpClient(lc.getSubject());
- CompletableFuture<WebSocket> ws = client.newWebSocketBuilder()
- .header(HttpHeader.AUTHORIZATION.getHeaderName(), HttpHeader.NEGOTIATE + " " + token)
- .buildAsync(uri, listener);
+ CompletableFuture<WebSocket> ws = cmsClient.newWebSocket(new WsEventListener());
WebSocket webSocket = ws.get();
webSocket.request(Long.MAX_VALUE);
webSocket.sendPing(ByteBuffer.allocate(0));
Thread.sleep(10000);
}
- }catch (InterruptedException e) {
+ } catch (InterruptedException e) {
if (webSocket != null)
webSocket.sendClose(WebSocket.NORMAL_CLOSURE, "");
- } catch (ExecutionException | LoginException e) {
+ } catch (ExecutionException e) {
throw new RuntimeException("Cannot listent to " + uri, e.getCause());
}
}
-// public static void main(String[] args) throws Exception {
-// if (args.length == 0) {
-// System.err.println("usage: java " + WebSocketEventClient.class.getName() + " <url>");
-// System.exit(1);
-// return;
-// }
-// URI uri = URI.create(args[0]);
-// }
+ private class WsEventListener implements WebSocket.Listener {
+ public CompletionStage<?> onText(WebSocket webSocket, CharSequence message, boolean last) {
+ System.out.println(message);
+ CompletionStage<String> res = CompletableFuture.completedStage(message.toString());
+ return res;
+ }
+
+ @Override
+ public CompletionStage<?> onPong(WebSocket webSocket, ByteBuffer message) {
+ // System.out.println("Pong received.");
+ return null;
+ }
+ }
}
CLIENT {
com.sun.security.auth.module.Krb5LoginModule required
- useTicketCache=true;
+ useTicketCache=true;
};
@Override
public Result authenticate(HttpExchange exch) {
-// if (log.isTraceEnabled())
-// HttpUtils.logRequestHeaders(log, request);
RemoteAuthHttpExchange remoteAuthExchange = new RemoteAuthHttpExchange(exch);
ClassLoader currentThreadContextClassLoader = Thread.currentThread().getContextClassLoader();
Thread.currentThread().setContextClassLoader(CmsAuthenticator.class.getClassLoader());
Subject subject = lc.getSubject();
-// CurrentSubject.callAs(subject, () -> {
-// RemoteAuthUtils.configureRequestSecurity(remoteAuthExchange);
-// return null;
-// });
-// Subject.doAs(subject, new PrivilegedAction<Void>() {
-//
-// @Override
-// public Void run() {
-// // TODO also set login context in order to log out ?
-// RemoteAuthUtils.configureRequestSecurity(new ServletHttpRequest(request));
-// return null;
-// }
-//
-// });
String username = CurrentUser.getUsername(subject);
HttpPrincipal httpPrincipal = new HttpPrincipal(username, httpAuthRealm);
return new Authenticator.Success(httpPrincipal);