промежуточный коммит
This commit is contained in:
parent
c9506224ea
commit
08c8fe2f7c
|
|
@ -0,0 +1,15 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||||
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||||
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
<parent>
|
||||||
|
<groupId>ru.kirillius</groupId>
|
||||||
|
<artifactId>pf-sdn</artifactId>
|
||||||
|
<version>0.1.0.0</version>
|
||||||
|
</parent>
|
||||||
|
|
||||||
|
<artifactId>core</artifactId>
|
||||||
|
|
||||||
|
|
||||||
|
</project>
|
||||||
|
|
@ -0,0 +1,72 @@
|
||||||
|
package ru.kirillius.pf.sdn.core.Auth;
|
||||||
|
|
||||||
|
import jakarta.servlet.http.HttpSession;
|
||||||
|
import ru.kirillius.pf.sdn.core.Context;
|
||||||
|
import ru.kirillius.pf.sdn.core.Util.HashUtil;
|
||||||
|
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
|
public class AuthManager {
|
||||||
|
private final Context context;
|
||||||
|
public final static String SESSION_AUTH_KEY = "auth";
|
||||||
|
public final static String SESSION_TOKEN = "token";
|
||||||
|
|
||||||
|
public AuthManager(Context context) {
|
||||||
|
this.context = context;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean validatePassword(String pass) {
|
||||||
|
var config = context.getConfig();
|
||||||
|
return HashUtil.hash(pass, config.getPasswordSalt()).equals(config.getPasswordHash());
|
||||||
|
}
|
||||||
|
|
||||||
|
public void updatePassword(String pass) {
|
||||||
|
var config = context.getConfig();
|
||||||
|
config.setPasswordHash(
|
||||||
|
HashUtil.hash(pass, config.getPasswordSalt())
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public AuthToken createToken(String description) {
|
||||||
|
var config = context.getConfig();
|
||||||
|
var token = new AuthToken();
|
||||||
|
token.setDescripton(description);
|
||||||
|
config.getTokens().add(token);
|
||||||
|
config.update();
|
||||||
|
return token;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean validateToken(AuthToken token) {
|
||||||
|
return context.getConfig().getTokens().contains(token);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setSessionAuthState(HttpSession session, boolean state) {
|
||||||
|
session.setAttribute(SESSION_AUTH_KEY, state);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setSessionToken(HttpSession session, AuthToken token) {
|
||||||
|
session.setAttribute(SESSION_TOKEN, token);
|
||||||
|
}
|
||||||
|
|
||||||
|
public AuthToken getSessionToken(HttpSession session) {
|
||||||
|
return (AuthToken) session.getAttribute(SESSION_TOKEN);
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean getSessionAuthState(HttpSession session) {
|
||||||
|
return Objects.equals(session.getAttribute(SESSION_AUTH_KEY), Boolean.TRUE);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void invalidateToken(AuthToken token) {
|
||||||
|
var config = context.getConfig();
|
||||||
|
config.getTokens().remove(token);
|
||||||
|
config.update();
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean validateToken(String token) {
|
||||||
|
return validateToken(new AuthToken(token));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void invalidateToken(String token) {
|
||||||
|
invalidateToken(new AuthToken(token));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,40 @@
|
||||||
|
package ru.kirillius.pf.sdn.core.Auth;
|
||||||
|
|
||||||
|
import lombok.Getter;
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
import lombok.Setter;
|
||||||
|
import ru.kirillius.json.JSONProperty;
|
||||||
|
import ru.kirillius.json.JSONSerializable;
|
||||||
|
|
||||||
|
import java.util.Objects;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
@NoArgsConstructor
|
||||||
|
@JSONSerializable
|
||||||
|
public class AuthToken {
|
||||||
|
public AuthToken(String token) {
|
||||||
|
this.token = token;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Setter
|
||||||
|
@Getter
|
||||||
|
@JSONProperty
|
||||||
|
private String descripton = "untitled";
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object o) {
|
||||||
|
if (o == null || getClass() != o.getClass()) return false;
|
||||||
|
AuthToken authToken = (AuthToken) o;
|
||||||
|
return Objects.equals(token, authToken.token);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
return Objects.hashCode(token);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Setter
|
||||||
|
@Getter
|
||||||
|
@JSONProperty
|
||||||
|
private String token = UUID.randomUUID().toString();
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,84 @@
|
||||||
|
package ru.kirillius.pf.sdn.core;
|
||||||
|
|
||||||
|
import lombok.Getter;
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
import lombok.Setter;
|
||||||
|
import org.json.JSONObject;
|
||||||
|
import org.json.JSONTokener;
|
||||||
|
import ru.kirillius.json.JSONArrayProperty;
|
||||||
|
import ru.kirillius.json.JSONProperty;
|
||||||
|
import ru.kirillius.json.JSONSerializable;
|
||||||
|
import ru.kirillius.json.JSONUtility;
|
||||||
|
import ru.kirillius.pf.sdn.core.Auth.AuthToken;
|
||||||
|
|
||||||
|
import java.io.*;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
@NoArgsConstructor
|
||||||
|
@JSONSerializable
|
||||||
|
public class Config {
|
||||||
|
@Getter
|
||||||
|
private File loadedConfigFile = null;
|
||||||
|
@Setter
|
||||||
|
@Getter
|
||||||
|
@JSONProperty
|
||||||
|
private String host = "0.0.0.0";
|
||||||
|
|
||||||
|
@Setter
|
||||||
|
@Getter
|
||||||
|
@JSONArrayProperty(type = AuthToken.class)
|
||||||
|
private List<AuthToken> tokens = Collections.emptyList();
|
||||||
|
|
||||||
|
@Setter
|
||||||
|
@Getter
|
||||||
|
@JSONProperty
|
||||||
|
private String passwordSalt = UUID.randomUUID().toString();
|
||||||
|
|
||||||
|
@Setter
|
||||||
|
@Getter
|
||||||
|
@JSONProperty
|
||||||
|
private String passwordHash = null;
|
||||||
|
|
||||||
|
@Setter
|
||||||
|
@Getter
|
||||||
|
@JSONProperty
|
||||||
|
private int httpPort = 8081;
|
||||||
|
|
||||||
|
public void update() {
|
||||||
|
try {
|
||||||
|
store(this, loadedConfigFile);
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void store(Config config, File file) throws IOException {
|
||||||
|
try (var fileInputStream = new FileOutputStream(file)) {
|
||||||
|
try (var writer = new BufferedWriter(new OutputStreamWriter(fileInputStream))) {
|
||||||
|
writer.write(serialize(config).toString());
|
||||||
|
writer.flush();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static JSONObject serialize(Config config) {
|
||||||
|
return JSONUtility.serializeStructure(config);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Config deserialize(JSONObject object) {
|
||||||
|
return JSONUtility.deserializeStructure(object, Config.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Config load(File file) throws IOException {
|
||||||
|
try (var stream = new FileInputStream(file)) {
|
||||||
|
var json = new JSONObject(new JSONTokener(stream));
|
||||||
|
var config = deserialize(json);
|
||||||
|
config.loadedConfigFile = file;
|
||||||
|
return config;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,13 @@
|
||||||
|
package ru.kirillius.pf.sdn.core;
|
||||||
|
|
||||||
|
import org.eclipse.jetty.server.Server;
|
||||||
|
import ru.kirillius.pf.sdn.core.Auth.AuthManager;
|
||||||
|
|
||||||
|
public interface Context {
|
||||||
|
Config getConfig();
|
||||||
|
|
||||||
|
AuthManager getAuthManager();
|
||||||
|
|
||||||
|
Server getServer();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,56 @@
|
||||||
|
package ru.kirillius.pf.sdn.core.Networking;
|
||||||
|
|
||||||
|
import lombok.Getter;
|
||||||
|
import ru.kirillius.pf.sdn.core.Util.IPv4Util;
|
||||||
|
|
||||||
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
|
|
||||||
|
public class IPv4Subnet {
|
||||||
|
|
||||||
|
private final long address;
|
||||||
|
@Getter
|
||||||
|
private final int prefixLength;
|
||||||
|
|
||||||
|
|
||||||
|
public IPv4Subnet(String subnet) {
|
||||||
|
var split = subnet.split(Pattern.quote("/"));
|
||||||
|
if (split.length != 2) {
|
||||||
|
throw new IllegalArgumentException("Invalid subnet: " + subnet);
|
||||||
|
}
|
||||||
|
var prefix = Integer.parseInt(split[1]);
|
||||||
|
IPv4Util.validatePrefix(prefix);
|
||||||
|
|
||||||
|
address = IPv4Util.ipAddressToLong(split[0]);
|
||||||
|
prefixLength = prefix;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public IPv4Subnet(String address, int prefixLength) {
|
||||||
|
IPv4Util.validatePrefix(prefixLength);
|
||||||
|
|
||||||
|
this.address = IPv4Util.ipAddressToLong(address);
|
||||||
|
this.prefixLength = prefixLength;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getAddress() {
|
||||||
|
return IPv4Util.longToIpAddress(address);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return getAddress() + '/' + prefixLength;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean overlaps(IPv4Subnet subnet) {
|
||||||
|
var minPrefixLength = Math.min(prefixLength, subnet.prefixLength);
|
||||||
|
var commonMask = IPv4Util.calculateMask(minPrefixLength);
|
||||||
|
if (commonMask != prefixLength) {
|
||||||
|
return false; //can't overlap larger prefix
|
||||||
|
}
|
||||||
|
|
||||||
|
return (address & commonMask) == (subnet.address & commonMask);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,5 @@
|
||||||
|
package ru.kirillius.pf.sdn.core.Util;
|
||||||
|
|
||||||
|
public class BGPUtility {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,30 @@
|
||||||
|
package ru.kirillius.pf.sdn.core.Util;
|
||||||
|
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
|
import java.security.MessageDigest;
|
||||||
|
import java.security.NoSuchAlgorithmException;
|
||||||
|
|
||||||
|
public final class HashUtil {
|
||||||
|
private HashUtil() {
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static String hash(String data, String salt) {
|
||||||
|
String generatedPassword = null;
|
||||||
|
MessageDigest md = null;
|
||||||
|
try {
|
||||||
|
md = MessageDigest.getInstance("SHA-512");
|
||||||
|
} catch (NoSuchAlgorithmException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
md.update(salt.getBytes(StandardCharsets.UTF_8));
|
||||||
|
var bytes = md.digest(data.getBytes(StandardCharsets.UTF_8));
|
||||||
|
var sb = new StringBuilder();
|
||||||
|
for (byte aByte : bytes) {
|
||||||
|
sb.append(Integer.toString((aByte & 0xff) + 0x100, 16).substring(1));
|
||||||
|
}
|
||||||
|
generatedPassword = sb.toString();
|
||||||
|
return generatedPassword;
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,53 @@
|
||||||
|
package ru.kirillius.pf.sdn.core.Util;
|
||||||
|
|
||||||
|
import lombok.SneakyThrows;
|
||||||
|
|
||||||
|
import java.net.InetAddress;
|
||||||
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
|
|
||||||
|
public class IPv4Util {
|
||||||
|
|
||||||
|
private IPv4Util() {
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final Pattern pattern = Pattern.compile("^((25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.){3}(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$");
|
||||||
|
|
||||||
|
public static void validateAddress(String address) {
|
||||||
|
if (!pattern.matcher(address).matches()) {
|
||||||
|
throw new IllegalArgumentException("Invalid IPv4 address: " + address);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void validatePrefix(int prefix) {
|
||||||
|
if (prefix < 0 || prefix > 32) {
|
||||||
|
throw new IllegalArgumentException("Invalid IPv4 prefix: " + prefix);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@SneakyThrows
|
||||||
|
public static long ipAddressToLong(String address) {
|
||||||
|
validateAddress(address);
|
||||||
|
var ip = InetAddress.getByName(address);
|
||||||
|
var bytes = ip.getAddress();
|
||||||
|
var result = 0L;
|
||||||
|
for (var b : bytes) {
|
||||||
|
result = (result << 8) | (b & 0xFF);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static long calculateMask(int prefixLength) {
|
||||||
|
validatePrefix(prefixLength);
|
||||||
|
return 0xFFFFFFFFL << (32 - prefixLength);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static String longToIpAddress(long ipLong) {
|
||||||
|
if (ipLong < 0 || ipLong > 0xFFFFFFFFL) {
|
||||||
|
throw new IllegalArgumentException("Address number should be in range 0 - 4294967295");
|
||||||
|
}
|
||||||
|
return ((ipLong >> 24) & 0xFF) + "." + ((ipLong >> 16) & 0xFF) + "." + ((ipLong >> 8) & 0xFF) + "." + (ipLong & 0xFF);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,77 @@
|
||||||
|
package ru.kirillius.pf.sdn.core.Networking;
|
||||||
|
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
|
|
||||||
|
class IPv4SubnetTest {
|
||||||
|
@SuppressWarnings("CatchMayIgnoreException")
|
||||||
|
@Test
|
||||||
|
void testInit() {
|
||||||
|
var validPrefixes = List.of(0, 5, 20, 32);
|
||||||
|
var invalidPrefixes = List.of(-1, 33, 800);
|
||||||
|
var validAddresses = List.of("1.2.3.4", "0.0.0.0", "255.255.255.255");
|
||||||
|
var invalidAddresses = List.of("1.2.3.04", "0.0.0", "255.255.255.255.255", "1.2.3.256", "-1.0.0.0");
|
||||||
|
|
||||||
|
|
||||||
|
validPrefixes.forEach(prefix -> {
|
||||||
|
validAddresses.forEach(address -> {
|
||||||
|
var subnet = new IPv4Subnet(address, prefix);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
invalidPrefixes.forEach(prefix -> {
|
||||||
|
validAddresses.forEach(address -> {
|
||||||
|
try {
|
||||||
|
var subnet = new IPv4Subnet(address, prefix);
|
||||||
|
throw new AssertionError();
|
||||||
|
} catch (Exception e) {
|
||||||
|
assertThat(e).isInstanceOf(IllegalArgumentException.class);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
validPrefixes.forEach(prefix -> {
|
||||||
|
invalidAddresses.forEach(address -> {
|
||||||
|
try {
|
||||||
|
var subnet = new IPv4Subnet(address, prefix);
|
||||||
|
throw new AssertionError();
|
||||||
|
} catch (Exception e) {
|
||||||
|
assertThat(e).isInstanceOf(IllegalArgumentException.class);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
invalidPrefixes.forEach(prefix -> {
|
||||||
|
invalidAddresses.forEach(address -> {
|
||||||
|
try {
|
||||||
|
var subnet = new IPv4Subnet(address, prefix);
|
||||||
|
throw new AssertionError();
|
||||||
|
} catch (Exception e) {
|
||||||
|
assertThat(e).isInstanceOf(IllegalArgumentException.class);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void overlaps() {
|
||||||
|
checkOverlaps(
|
||||||
|
new IPv4Subnet("0.0.0.0", 0),
|
||||||
|
new IPv4Subnet("8.8.8.8", 32)
|
||||||
|
);
|
||||||
|
|
||||||
|
checkOverlaps(
|
||||||
|
new IPv4Subnet("192.168.0.0", 24),
|
||||||
|
new IPv4Subnet("192.168.0.128", 25)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void checkOverlaps(IPv4Subnet larger, IPv4Subnet smaller) {
|
||||||
|
assertThat(larger.overlaps(smaller)).isTrue();
|
||||||
|
assertThat(smaller.overlaps(larger)).isFalse();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,14 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||||
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||||
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
<parent>
|
||||||
|
<groupId>ru.kirillius</groupId>
|
||||||
|
<artifactId>pf-sdn</artifactId>
|
||||||
|
<version>0.1.0.0</version>
|
||||||
|
</parent>
|
||||||
|
|
||||||
|
<artifactId>launcher</artifactId>
|
||||||
|
|
||||||
|
</project>
|
||||||
107
pom.xml
107
pom.xml
|
|
@ -7,11 +7,114 @@
|
||||||
<groupId>ru.kirillius</groupId>
|
<groupId>ru.kirillius</groupId>
|
||||||
<artifactId>pf-sdn</artifactId>
|
<artifactId>pf-sdn</artifactId>
|
||||||
<version>0.1.0.0</version>
|
<version>0.1.0.0</version>
|
||||||
|
<packaging>pom</packaging>
|
||||||
|
<modules>
|
||||||
|
<module>web-client</module>
|
||||||
|
<module>launcher</module>
|
||||||
|
<module>core</module>
|
||||||
|
<module>web-server</module>
|
||||||
|
</modules>
|
||||||
|
|
||||||
<properties>
|
<properties>
|
||||||
<maven.compiler.source>24</maven.compiler.source>
|
<maven.compiler.source>21</maven.compiler.source>
|
||||||
<maven.compiler.target>24</maven.compiler.target>
|
<maven.compiler.target>21</maven.compiler.target>
|
||||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||||
</properties>
|
</properties>
|
||||||
|
|
||||||
|
|
||||||
|
<repositories>
|
||||||
|
<repository>
|
||||||
|
<id>kirillius</id>
|
||||||
|
<name>kirillius</name>
|
||||||
|
<url>https://repo.kirillius.ru/maven</url>
|
||||||
|
<releases>
|
||||||
|
<enabled>true</enabled>
|
||||||
|
<updatePolicy>always</updatePolicy>
|
||||||
|
<checksumPolicy>fail</checksumPolicy>
|
||||||
|
</releases>
|
||||||
|
<layout>default</layout>
|
||||||
|
</repository>
|
||||||
|
</repositories>
|
||||||
|
<dependencies>
|
||||||
|
|
||||||
|
<!-- https://mvnrepository.com/artifact/org.junit.jupiter/junit-jupiter-api -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.junit.jupiter</groupId>
|
||||||
|
<artifactId>junit-jupiter-api</artifactId>
|
||||||
|
<version>5.13.0-M2</version>
|
||||||
|
<scope>test</scope>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<!-- https://mvnrepository.com/artifact/org.assertj/assertj-core -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.assertj</groupId>
|
||||||
|
<artifactId>assertj-core</artifactId>
|
||||||
|
<version>4.0.0-M1</version>
|
||||||
|
<scope>test</scope>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.javatuples</groupId>
|
||||||
|
<artifactId>javatuples</artifactId>
|
||||||
|
<version>1.2</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.cronutils</groupId>
|
||||||
|
<artifactId>cron-utils</artifactId>
|
||||||
|
<version>9.2.0</version>
|
||||||
|
</dependency>
|
||||||
|
<!-- https://mvnrepository.com/artifact/org.snmp4j/snmp4j -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.snmp4j</groupId>
|
||||||
|
<artifactId>snmp4j</artifactId>
|
||||||
|
<version>3.7.7</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>ru.kirillius</groupId>
|
||||||
|
<artifactId>icmp4j</artifactId>
|
||||||
|
<version>1.0.0.0</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>ru.kirillius.util</groupId>
|
||||||
|
<artifactId>dynamic-types</artifactId>
|
||||||
|
<version>2.0.0.0</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>ru.kirillius</groupId>
|
||||||
|
<artifactId>json-rpc-servlet</artifactId>
|
||||||
|
<version>2.1.4.0</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>ru.kirillius.utils</groupId>
|
||||||
|
<artifactId>common-logging</artifactId>
|
||||||
|
<version>1.3.0.0</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>ru.kirillius</groupId>
|
||||||
|
<artifactId>hibernate-commons</artifactId>
|
||||||
|
<version>2.2.0.0</version>
|
||||||
|
</dependency>
|
||||||
|
<!-- https://mvnrepository.com/artifact/org.javassist/javassist -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.javassist</groupId>
|
||||||
|
<artifactId>javassist</artifactId>
|
||||||
|
<version>3.29.2-GA</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<!-- https://mvnrepository.com/artifact/org.eclipse.jetty/jetty-server -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.eclipse.jetty</groupId>
|
||||||
|
<artifactId>jetty-server</artifactId>
|
||||||
|
<version>12.0.12</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<!-- https://mvnrepository.com/artifact/org.projectlombok/lombok -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.projectlombok</groupId>
|
||||||
|
<artifactId>lombok</artifactId>
|
||||||
|
<version>1.18.34</version>
|
||||||
|
<scope>provided</scope>
|
||||||
|
</dependency>
|
||||||
|
</dependencies>
|
||||||
|
|
||||||
</project>
|
</project>
|
||||||
|
|
@ -0,0 +1,16 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||||
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||||
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
<parent>
|
||||||
|
<groupId>ru.kirillius</groupId>
|
||||||
|
<artifactId>pf-sdn</artifactId>
|
||||||
|
<version>0.1.0.0</version>
|
||||||
|
</parent>
|
||||||
|
|
||||||
|
<artifactId>web-client</artifactId>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
</project>
|
||||||
|
|
@ -0,0 +1,23 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||||
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||||
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
<parent>
|
||||||
|
<groupId>ru.kirillius</groupId>
|
||||||
|
<artifactId>pf-sdn</artifactId>
|
||||||
|
<version>0.1.0.0</version>
|
||||||
|
</parent>
|
||||||
|
|
||||||
|
<artifactId>web-server</artifactId>
|
||||||
|
<dependencies>
|
||||||
|
<dependency>
|
||||||
|
<groupId>ru.kirillius</groupId>
|
||||||
|
<artifactId>core</artifactId>
|
||||||
|
<version>0.1.0.0</version>
|
||||||
|
<scope>compile</scope>
|
||||||
|
</dependency>
|
||||||
|
</dependencies>
|
||||||
|
|
||||||
|
|
||||||
|
</project>
|
||||||
|
|
@ -0,0 +1,91 @@
|
||||||
|
package ru.kirillius.pf.sdn.web;
|
||||||
|
|
||||||
|
|
||||||
|
import org.eclipse.jetty.ee10.servlet.DefaultServlet;
|
||||||
|
import org.eclipse.jetty.ee10.servlet.ServletContextHandler;
|
||||||
|
import org.eclipse.jetty.server.Server;
|
||||||
|
import org.eclipse.jetty.server.ServerConnector;
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
import ru.kirillius.json.rpc.Servlet.JSONRPCServlet;
|
||||||
|
import ru.kirillius.pf.sdn.core.Context;
|
||||||
|
import ru.kirillius.utils.logging.SystemLogger;
|
||||||
|
import ru.kirillius.pf.sdn.web.RPC.AuthRPC;
|
||||||
|
import ru.kirillius.pf.sdn.web.RPC.RPC;
|
||||||
|
|
||||||
|
import java.net.MalformedURLException;
|
||||||
|
import java.net.URL;
|
||||||
|
import java.util.Objects;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
public class HTTPServer extends Server {
|
||||||
|
|
||||||
|
public final static String ANY_HOST = "0.0.0.0";
|
||||||
|
private final static String LOG_CONTEXT = HTTPServer.class.getSimpleName();
|
||||||
|
private final static String TOKEN_HEADER = "X-Auth-token";
|
||||||
|
|
||||||
|
private final JSONRPCServlet JSONRPC = new JSONRPCServlet();
|
||||||
|
|
||||||
|
private String getResourceBase() throws MalformedURLException {
|
||||||
|
var resourceFile = getClass().getClassLoader().getResource("htdocs/index.html");
|
||||||
|
return new URL(Objects.requireNonNull(resourceFile).getProtocol(), resourceFile.getHost(), resourceFile.getPath()
|
||||||
|
.substring(0, resourceFile.getPath().lastIndexOf("/")))
|
||||||
|
.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
private final static Set<Class<? extends RPC>> RPCHandlerTypes = Set.of(AuthRPC.class);
|
||||||
|
|
||||||
|
|
||||||
|
public HTTPServer(Context appContext, int port, @Nullable String host) throws Exception {
|
||||||
|
|
||||||
|
var connector = new ServerConnector(this);
|
||||||
|
connector.setPort(port);
|
||||||
|
if (host != null && !host.equals(ANY_HOST)) {
|
||||||
|
connector.setHost(host);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.addConnector(connector);
|
||||||
|
ServletContextHandler servletContext = new ServletContextHandler("/", ServletContextHandler.SESSIONS);
|
||||||
|
servletContext.addServlet(JSONRPC, JSONRPCServlet.CONTEXT_PATH);
|
||||||
|
var holder = servletContext.addServlet(DefaultServlet.class, "/");
|
||||||
|
holder.setInitParameter("resourceBase", getResourceBase());
|
||||||
|
this.setHandler(servletContext);
|
||||||
|
|
||||||
|
start();
|
||||||
|
|
||||||
|
|
||||||
|
JSONRPC.addRequestHandler((request, response, call) -> {
|
||||||
|
var authManager = appContext.getAuthManager();
|
||||||
|
var authorized = authManager.getSessionAuthState(call.getContext().getSession());
|
||||||
|
// Thread.sleep(100);//FIXME remove! debug only
|
||||||
|
|
||||||
|
//auth by token
|
||||||
|
if (!authorized) {
|
||||||
|
var headerToken = request.getHeader(TOKEN_HEADER);
|
||||||
|
if (headerToken != null) {
|
||||||
|
authorized = authManager.validateToken(headerToken);
|
||||||
|
authManager.setSessionAuthState(call.getContext().getSession(), authorized);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
var isProtectedAccess = call.getMethod().getAnnotation(ProtectedMethod.class);
|
||||||
|
if (isProtectedAccess != null) {
|
||||||
|
if (!authorized) throw new SecurityException("Forbidden");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
for (var handlerClass : RPCHandlerTypes) {
|
||||||
|
var instance = RPC.instantiate(handlerClass, appContext);
|
||||||
|
//noinspection unchecked
|
||||||
|
JSONRPC.addTargetInstance((Class<? super RPC>) handlerClass, instance);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
JSONRPC.getErrorHandler().add(throwable -> {
|
||||||
|
SystemLogger.error("JRPC Request " +
|
||||||
|
(throwable.getRequestData() == null ? "" : throwable.getRequestData().toString()) +
|
||||||
|
" has failed with error", LOG_CONTEXT, throwable.getError());
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,11 @@
|
||||||
|
package ru.kirillius.pf.sdn.web;
|
||||||
|
|
||||||
|
import java.lang.annotation.ElementType;
|
||||||
|
import java.lang.annotation.Retention;
|
||||||
|
import java.lang.annotation.RetentionPolicy;
|
||||||
|
import java.lang.annotation.Target;
|
||||||
|
|
||||||
|
@Retention(RetentionPolicy.RUNTIME)
|
||||||
|
@Target(ElementType.METHOD)
|
||||||
|
public @interface ProtectedMethod {
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,52 @@
|
||||||
|
package ru.kirillius.pf.sdn.web.RPC;
|
||||||
|
|
||||||
|
import ru.kirillius.json.rpc.Annotations.JRPCArgument;
|
||||||
|
import ru.kirillius.json.rpc.Annotations.JRPCContext;
|
||||||
|
import ru.kirillius.json.rpc.Annotations.JRPCMethod;
|
||||||
|
import ru.kirillius.json.rpc.Servlet.CallContext;
|
||||||
|
import ru.kirillius.pf.sdn.core.Auth.AuthToken;
|
||||||
|
import ru.kirillius.pf.sdn.core.Context;
|
||||||
|
import ru.kirillius.pf.sdn.web.ProtectedMethod;
|
||||||
|
|
||||||
|
public class AuthRPC implements RPC {
|
||||||
|
private final Context context;
|
||||||
|
|
||||||
|
public AuthRPC(Context context) {
|
||||||
|
this.context = context;
|
||||||
|
}
|
||||||
|
|
||||||
|
@ProtectedMethod
|
||||||
|
@JRPCMethod
|
||||||
|
public AuthToken getAuthToken(@JRPCContext CallContext call) {
|
||||||
|
return context.getAuthManager().getSessionToken(call.getSession());
|
||||||
|
}
|
||||||
|
|
||||||
|
@ProtectedMethod
|
||||||
|
@JRPCMethod
|
||||||
|
public AuthToken rememberCurrentUser(@JRPCContext CallContext call) {
|
||||||
|
var UA = call.getRequest().getHeader("User-Agent");
|
||||||
|
if (UA == null) {
|
||||||
|
UA = "Unknown user agent";
|
||||||
|
}
|
||||||
|
var authManager = context.getAuthManager();
|
||||||
|
var token = authManager.createToken(UA);
|
||||||
|
authManager.setSessionToken(call.getSession(), token);
|
||||||
|
return token;
|
||||||
|
}
|
||||||
|
|
||||||
|
@JRPCMethod
|
||||||
|
public boolean auth(@JRPCArgument(name = "password") String password, @JRPCContext CallContext call) {
|
||||||
|
var authManager = context.getAuthManager();
|
||||||
|
if (authManager.validatePassword(password)) {
|
||||||
|
authManager.setSessionAuthState(call.getSession(), true);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@JRPCMethod
|
||||||
|
public void logout(@JRPCContext CallContext call) {
|
||||||
|
var authManager = context.getAuthManager();
|
||||||
|
authManager.setSessionAuthState(call.getSession(), false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,16 @@
|
||||||
|
package ru.kirillius.pf.sdn.web.RPC;
|
||||||
|
|
||||||
|
import ru.kirillius.pf.sdn.core.Context;
|
||||||
|
|
||||||
|
import java.lang.reflect.InvocationTargetException;
|
||||||
|
|
||||||
|
public interface RPC {
|
||||||
|
static <T extends RPC> T instantiate(Class<T> type, Context context) {
|
||||||
|
try {
|
||||||
|
return type.getConstructor(Context.class).newInstance(context);
|
||||||
|
} catch (NoSuchMethodException | InvocationTargetException | InstantiationException |
|
||||||
|
IllegalAccessException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue