Skip to content

Commit

Permalink
auto authorize by HTTP URL and some more
Browse files Browse the repository at this point in the history
-auto authorize by HTTP URL
-HTTP URL by description
-Small refactors
-Public folder
  • Loading branch information
aalku committed Dec 17, 2023
1 parent b471b0c commit 7fe61de
Show file tree
Hide file tree
Showing 14 changed files with 393 additions and 136 deletions.
25 changes: 25 additions & 0 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,31 @@
<release>${java.version}</release>
</configuration>
</plugin>
<plugin>
<artifactId>maven-jar-plugin</artifactId>
<executions>
<execution>
<id>sb-client</id>
<goals>
<goal>jar</goal>
</goals>
<phase>package</phase>
<configuration>
<classifier>sb-client</classifier>
<includes>
<include>**/SwitchboardExternalClient.class</include>
<include>**/SwitchboardExternalClient$$*.class</include>
</includes>
<archive>
<manifest>
<addClasspath>true</addClasspath>
<mainClass>org.aalku.joatse.cloud.tools.io.SwitchboardExternalClient</mainClass>
</manifest>
</archive>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
<repositories>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package org.aalku.joatse.cloud.config;

import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.Map;
Expand All @@ -21,6 +22,7 @@
@Configuration
public class ListenPortConfigurator implements InitializingBean {


@Value("${cloud.port.open.range:}")
private String openPortRangeString;

Expand Down Expand Up @@ -73,7 +75,11 @@ PortRange httpUnsafePortRange(@Autowired @Qualifier("openPortRange") PortRange o

@Bean("switchboardPortListener")
AsyncTcpPortListener<Void> switchboardPortListener() {
return new AsyncTcpPortListener<Void>(InetAddress.getLoopbackAddress(), 0);
try {
return new AsyncTcpPortListener<Void>(InetAddress.getByName(System.getProperty("switchboard.host", "localhost")), Integer.getInteger("switchboard.port", 0));
} catch (UnknownHostException e) {
throw new RuntimeException(e);
}
}

@Bean("httpHosts")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,11 +43,12 @@ public class WebSecurityConfiguration {
public static final String PATH_LOGIN_POST = "/loginPost";
public static final String PATH_POST_LOGIN = "/postLogin";
public static final String PATH_PASSWORD_RESET = "/resetPassword";
public static final String PATH_PUBLIC = "/public/**";

@SuppressWarnings("unused")
private Logger log = LoggerFactory.getLogger(WebSecurityConfiguration.class);

@Bean
@Bean // Must be public
public ApplicationListener<WebServerInitializedEvent> webInitializedListener() {
return new ApplicationListener<WebServerInitializedEvent>() {

Expand All @@ -72,7 +73,7 @@ SecurityFilterChain filterChain(HttpSecurity http, JWTAuthorizationFilter jwtAut
.addFilterBefore(jwtAuthorizationFilter, UsernamePasswordAuthenticationFilter.class)
.authorizeHttpRequests(t -> t.requestMatchers(WebSocketConfig.CONNECTION_HTTP_PATH).anonymous()
//
.requestMatchers(HttpMethod.GET, "/ws-test.html").permitAll()
.requestMatchers(HttpMethod.GET, PATH_PUBLIC).permitAll()
.requestMatchers(HttpMethod.GET, "/login.html", "/css/**", "/header.js", "/lib/*.js").permitAll()
.requestMatchers(HttpMethod.GET, "/user").permitAll()
.requestMatchers("/error").permitAll()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -212,7 +212,7 @@ public TunnelCreationResponse requestTunnel(Principal principal, LotSharingReque
} else {
rejectConnectionRequest(request.getUuid(), "Invalid preconfirmation");
}
return new TunnelCreationResponse(request.getUuid(), null, request.future);
return new TunnelCreationResponse(request.getUuid(), null, request.getFuture());
} else if (automaticTunnelAcceptance != null) { // TODO remove this mock
new Thread() {
public void run() {
Expand All @@ -226,10 +226,11 @@ public void run() {
};
}.start();
}
return new TunnelCreationResponse(request.getUuid(), buildConfirmationUri(request), request.future);
return new TunnelCreationResponse(request.getUuid(), buildConfirmationUri(request), request.getFuture());
}

private String checkPreconfirmation(PreconfirmedShare saved, LotSharingRequest request) {
// TODO autoAuthorizeByHttpUrl
LotSharingRequest savedReq;
try {
savedReq = LotSharingRequest.fromJson(new JSONObject(saved.getResources()), saved.getRequesterAddress());
Expand Down Expand Up @@ -283,11 +284,11 @@ public void acceptTunnelRequest(UUID uuid, JoatseUser user) {
} finally {
lock.writeLock().unlock();
}
request.future.complete(newTunnel); // Without lock
request.getFuture().complete(newTunnel); // Without lock
} catch (Exception e) {
if (request != null) {
// Reject
request.future.completeExceptionally(e);
request.getFuture().completeExceptionally(e);
return;
} else {
throw e;
Expand Down Expand Up @@ -332,7 +333,7 @@ public void rejectConnectionRequest(UUID uuid, String reason) {
lock.writeLock().unlock();
}
if (request != null) {
request.future.complete(new TunnelCreationResult.Rejected(request, reason)); // Out of lock
request.getFuture().complete(new TunnelCreationResult.Rejected(request, reason)); // Out of lock
}
}

Expand Down Expand Up @@ -383,21 +384,33 @@ public void receivedTcpConnection(int port, AsynchronousSocketChannel channel,
* A httpClientEnd connection has arrived. Returns a context object to be used
* on the next call.
*/
public Object httpClientEndConnected(UUID uuid, long targetId) {
HttpTunnel httpTarget = tunnelRegistry.getHttpTunnel(uuid, targetId);
if (httpTarget == null) {
public Object switchboardConnected(UUID uuid, long targetId) {
Object tunnel = tunnelRegistry.getTunnel(uuid, targetId);
if (tunnel == null) {
log.warn("Unexpected connection or wrong protocol: {}.{}", uuid, targetId);
return null;
} else {
log.info("Switchboard Received Tunnel connection: {}.{}", uuid, targetId);
return httpTarget;
return tunnel;
}
}

/** The httpClientEnd connection is ready */
public void httpClientEndConnectionReady(Object context, AsynchronousSocketChannel channel) {
HttpTunnel httpTarget = (HttpTunnel) context;
httpTarget.getTunnel().tunnelTcpConnection(httpTarget.getTargetId(), channel);
public void switchboardConnectionReady(Object context, AsynchronousSocketChannel channel) {
SharedResourceLot tunnel = null;
long targetId = -1;
if (context instanceof HttpTunnel) {
HttpTunnel httpTarget = (HttpTunnel) context;
tunnel = httpTarget.getTunnel();
targetId = httpTarget.getTargetId();
} else if (context instanceof TcpTunnel) {
TcpTunnel tcpTarget = (TcpTunnel) context;
tunnel = tcpTarget.getTunnel();
targetId = tcpTarget.getTargetId();
} else {
throw new RuntimeException();
}
tunnel.tunnelTcpConnection(targetId, channel);
// TODO schedule a periodic check to log the disconnection
}

Expand All @@ -411,14 +424,21 @@ public void httpClientEndConnectionReady(Object context, AsynchronousSocketChann
public HttpTunnel getTunnelForHttpRequest(InetAddress remoteAddress, int serverPort, String serverName, String protocol) {
List<HttpTunnel> res = tunnelRegistry.findMatchingHttpTunnel(remoteAddress, serverPort, serverName, protocol);
if (res.size() > 0) {
return res.get(0);
HttpTunnel ht = res.get(0);
SharedResourceLot srl = ht.getTunnel();
/* If not authorized maybe it should be */
if (!srl.getAllowedAddresses().contains(remoteAddress)
&& srl.isAuthorizeByHttpUrl()) {
srl.addAllowedAddress(remoteAddress);
}
return ht;
} else {
return null;
}
}

public HttpTunnel getHttpTunnelById(UUID uuid, long httpTunnelId) {
HttpTunnel res = tunnelRegistry.getHttpTunnel(uuid, httpTunnelId);
HttpTunnel res = tunnelRegistry.getTunnel(uuid, httpTunnelId);
return res;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.UUID;
import java.util.stream.Collectors;

Expand All @@ -26,9 +25,16 @@ public class TunnelRegistry {
public synchronized List<HttpTunnel> findMatchingHttpTunnel(InetAddress remoteAddress, int serverPort, String serverName, String protocol) {
ListenAddress reachedAddress = new ListenAddress(serverPort, serverName, protocol);
List<HttpTunnel> tunnelsMatching = tunnelsByUUID.values().stream()
.filter(t -> t.getAllowedAddresses().contains(remoteAddress))
.flatMap(x -> x.getHttpItems().stream())
.filter(http->http.getListenAddress().equals(reachedAddress))
.filter(http -> {
SharedResourceLot t = http.getTunnel();
if (t.isAuthorizeByHttpUrl()) {
return true;
} else {
return t.getAllowedAddresses().contains(remoteAddress);
}
})
.collect(Collectors.toList());
return tunnelsMatching;
}
Expand All @@ -42,8 +48,18 @@ public synchronized List<TcpTunnel> findMatchingTcpTunnel(InetAddress remoteAddr
return tunnelsMatching;
}

public synchronized HttpTunnel getHttpTunnel(UUID uuid, long targetId) {
return Optional.ofNullable(tunnelsByUUID.get(uuid)).map(t -> t.getHttpItem(targetId)).orElse(null);
@SuppressWarnings("unchecked")
public synchronized <E> E getTunnel(UUID uuid, long targetId) {
SharedResourceLot srl = tunnelsByUUID.get(uuid);
if (srl == null) {
return null;
} else {
Object res = srl.getHttpItem(targetId);
if (res == null) {
res = srl.getTcpItem(targetId);
}
return (E) res;
}
}

public synchronized void removeTunnel(UUID uuid) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,16 @@
import org.aalku.joatse.cloud.tools.io.PortRange;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Configuration;

@Configuration
public class HttpEndpointGenerator {

public enum PrefixStrategy {
URL, DESCRIPTION
}

private static final String DYNAMIC_PREFIX = "*.";

@Autowired
Expand All @@ -34,6 +39,12 @@ public class HttpEndpointGenerator {
@Autowired
private ListenerConfigurationDetector listenerConfigurationDetector;

@Value("${httpEndpointGenerator.prefixStrategy:DESCRIPTION}")
private PrefixStrategy prefixStrategy;

@Value("${httpEndpointGenerator.prefixHashLength:6}")
private int prefixHashLength;

public ListenAddress generateListenAddress(HttpTunnel tunnel, LinkedHashSet<ListenAddress> forbiddenAddresses,
String askedCloudHostname) {
PortRange portRange = tunnel.isUnsafe() ? httpUnsafePortRange : httpPortRange;
Expand Down Expand Up @@ -77,14 +88,27 @@ public ListenAddress generateListenAddress(HttpTunnel tunnel, LinkedHashSet<List
}

private String generatePrefix(HttpTunnel tunnel) {
return generateSummaryPreffix(tunnel) + "-" + generateHashPrefix(tunnel);
String prefixBase;
if (this.prefixStrategy == PrefixStrategy.URL) {
prefixBase = generateSummaryPreffixWithURL(tunnel);
} else if (this.prefixStrategy == PrefixStrategy.DESCRIPTION) {
prefixBase = generateSummaryPreffixWithDescription(tunnel);
} else {
throw new IllegalArgumentException();
}
return prefixBase + "-" + generateHashPrefix(tunnel, this.prefixHashLength);
}

private static String cleanString(String str) {
return str.replaceAll("[^a-zA-Z0-9]+", "-").replaceFirst("^-", "").replaceFirst("-$", "").toLowerCase();
return str.replaceAll("[^a-zA-Z0-9]+", "-").replaceFirst("^-", "").replaceFirst("-$", "").replaceAll("([a-z])([A-Z])", "$1-$2").toLowerCase();
}

private static String generateSummaryPreffix(HttpTunnel tunnel) {
private static String generateSummaryPreffixWithDescription(HttpTunnel tunnel) {
StringBuilder sb = new StringBuilder(cleanString(tunnel.getTargetDescription()));
return sb.toString();
}

private static String generateSummaryPreffixWithURL(HttpTunnel tunnel) {
URL url = tunnel.getTargetURL();
StringBuilder sb = new StringBuilder(cleanString(url.getHost()));
Optional.of(url.getPort()).filter(p -> p >=0 && p != 80 && p != 443).ifPresent(p->{
Expand All @@ -93,17 +117,20 @@ private static String generateSummaryPreffix(HttpTunnel tunnel) {
return sb.toString();
}

private static String generateHashPrefix(HttpTunnel tunnel) {
private static String generateHashPrefix(HttpTunnel tunnel, int hashLength) {
int targetPort = tunnel.getTargetPort();
try {
MessageDigest md = MessageDigest.getInstance("SHA-1");
md.update(tunnel.getTunnel().getRequesterAddress().getAddress().getAddress());
md.update(new byte[] {(byte)(targetPort & 0xff), (byte)((targetPort >> 8) & 0xff)});
md.update(tunnel.getTargetURL().toExternalForm().getBytes(StandardCharsets.UTF_8));
byte[] digest = md.digest();
if (hashLength > digest.length) {
throw new IllegalArgumentException("Selected hash algorithm can only generate " + digest.length + " chars");
}
StringBuilder sb = new StringBuilder(2);
final String dictionary = "abcdefghmprstxyz";
for (int i = 0; i < 4; i++) {
for (int i = 0; i < hashLength; i++) {
char c = dictionary.charAt(digest[i] & 0x0F);
sb.append(c);
}
Expand Down
Loading

0 comments on commit 7fe61de

Please sign in to comment.