Basic implementation of first login handshake
This commit is contained in:
parent
6ea48dd4e7
commit
88a7a0a9c6
8 changed files with 314 additions and 13 deletions
12
Dockerfile
12
Dockerfile
|
@ -1,17 +1,7 @@
|
||||||
FROM git.zontreck.com/packages/flutter:latest as BUILDER
|
FROM git.zontreck.com/packages/flutter:latest as BUILDER
|
||||||
|
|
||||||
WORKDIR /app
|
WORKDIR /app
|
||||||
COPY ./pubspec.yaml ./
|
COPY ./* /app/
|
||||||
COPY ./cli ./
|
|
||||||
COPY ./lib ./
|
|
||||||
COPY ./android ./
|
|
||||||
COPY ./linux ./
|
|
||||||
COPY ./ios ./
|
|
||||||
COPY ./macos ./
|
|
||||||
COPY ./test ./
|
|
||||||
COPY ./web ./
|
|
||||||
COPY ./windows ./
|
|
||||||
COPY ./analysis_options.yaml ./
|
|
||||||
|
|
||||||
|
|
||||||
RUN dart pub get
|
RUN dart pub get
|
||||||
|
|
|
@ -1,8 +1,44 @@
|
||||||
|
import 'dart:io';
|
||||||
|
|
||||||
|
import 'package:bugvault/Constants.dart';
|
||||||
|
import 'package:bugvault/SessionData.dart';
|
||||||
|
import 'package:bugvault/server/main.dart';
|
||||||
|
import 'package:libac_dart/nbt/NbtIo.dart';
|
||||||
|
import 'package:libac_dart/nbt/impl/CompoundTag.dart';
|
||||||
|
import 'package:libac_dart/packets/packets.dart';
|
||||||
|
|
||||||
Future<int> main(List<String> args) async {
|
Future<int> main(List<String> args) async {
|
||||||
print("Starting BugVault Server...");
|
print("Starting BugVault Server...");
|
||||||
|
|
||||||
// Check for settings.dat, which should contain all basic information.
|
// Check for settings.dat, which should contain all basic information.
|
||||||
// Then check if any data exists. If not, create data hierarchy
|
// Then check if any data exists. If not, create data hierarchy
|
||||||
|
File settings = File("settings.dat");
|
||||||
|
if (await settings.exists()) {
|
||||||
|
// Load!
|
||||||
|
SessionData.g_nbtConfiguration =
|
||||||
|
await NbtIo.read("settings.dat") as CompoundTag;
|
||||||
|
} else {
|
||||||
|
SessionData.g_nbtConfiguration = CompoundTag();
|
||||||
|
print("No existing configuration found");
|
||||||
|
}
|
||||||
|
await BugVaultServer.InstantiateSettings(SessionData.g_nbtConfiguration);
|
||||||
|
print("Loaded settings...");
|
||||||
|
|
||||||
|
print("Registering packet handlers...");
|
||||||
|
await BugVaultServer.RegisterPacketHandlers();
|
||||||
|
|
||||||
|
print("Opening data port...");
|
||||||
|
|
||||||
|
print("\n\n");
|
||||||
|
print("=".padLeft(20, "="));
|
||||||
|
print("BugVault Dedicated Server\nVersion: ${Constants.VERSION}");
|
||||||
|
print("=".padLeft(20, "="));
|
||||||
|
|
||||||
|
while (!SessionData.g_bShutdownPending) {
|
||||||
|
try {
|
||||||
|
await PacketServer.start(BugVaultServer.g_iPortNumber);
|
||||||
|
} catch (E) {}
|
||||||
|
}
|
||||||
|
|
||||||
print("Thank you for choosing BugVault! - Shutting down...");
|
print("Thank you for choosing BugVault! - Shutting down...");
|
||||||
return 0;
|
return 0;
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
class Constants {
|
class Constants {
|
||||||
static const VERSION = "1.0.031525+0003";
|
static const VERSION = "1.0.031525+0137";
|
||||||
static const APP_NAME = "BugVault";
|
static const APP_NAME = "BugVault";
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,4 +3,7 @@ import 'package:libac_dart/nbt/impl/CompoundTag.dart';
|
||||||
class SessionData {
|
class SessionData {
|
||||||
static var g_bDarkMode = true;
|
static var g_bDarkMode = true;
|
||||||
static CompoundTag g_nbtConfiguration = CompoundTag();
|
static CompoundTag g_nbtConfiguration = CompoundTag();
|
||||||
|
|
||||||
|
/// This flag is only used by the server to indicate if a packet has demanded the server shut down.
|
||||||
|
static var g_bShutdownPending = false;
|
||||||
}
|
}
|
||||||
|
|
61
lib/server/main.dart
Normal file
61
lib/server/main.dart
Normal file
|
@ -0,0 +1,61 @@
|
||||||
|
import 'dart:io';
|
||||||
|
|
||||||
|
import 'package:bugvault/server/packets.dart';
|
||||||
|
import 'package:bugvault/users/User.dart';
|
||||||
|
import 'package:libac_dart/nbt/NbtIo.dart';
|
||||||
|
import 'package:libac_dart/nbt/NbtUtils.dart';
|
||||||
|
import 'package:libac_dart/nbt/impl/CompoundTag.dart';
|
||||||
|
import 'package:libac_dart/nbt/impl/ListTag.dart';
|
||||||
|
import 'package:libac_dart/packets/packets.dart';
|
||||||
|
|
||||||
|
class BugVaultServer {
|
||||||
|
static int g_iPortNumber = 8372;
|
||||||
|
static bool g_bAllowAnonymousLogin = true;
|
||||||
|
static List<DBUser> UserDB = List.empty();
|
||||||
|
|
||||||
|
static Future<void> InstantiateSettings(CompoundTag ct) async {
|
||||||
|
g_iPortNumber = ct.get("port")?.asInt() ?? 8372;
|
||||||
|
if (ct.containsKey("anonymous"))
|
||||||
|
g_bAllowAnonymousLogin = NbtUtils.readBoolean(ct, "anonymous");
|
||||||
|
|
||||||
|
await _loadUserDB();
|
||||||
|
}
|
||||||
|
|
||||||
|
static Future<void> _loadUserDB() async {
|
||||||
|
File users = File("users.dat");
|
||||||
|
if (await users.exists()) {
|
||||||
|
CompoundTag ct = await NbtIo.read("users.dat") as CompoundTag;
|
||||||
|
ListTag userList = ct.get("users")! as ListTag;
|
||||||
|
for (var tag in userList.value) {
|
||||||
|
CompoundTag userEntry = tag.asCompoundTag();
|
||||||
|
UserDB.add(DBUser.load(serialized: userEntry));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static Future<void> RegisterPacketHandlers() async {
|
||||||
|
PacketRegistry registry = PacketRegistry();
|
||||||
|
BVSPacketImpl.Register(registry);
|
||||||
|
}
|
||||||
|
|
||||||
|
static DBUser? TryGetUser(String name) {
|
||||||
|
for (var entry in UserDB) {
|
||||||
|
if (entry.sName == name) {
|
||||||
|
return entry;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class BVSPacketImpl {
|
||||||
|
static Future<void> Register(PacketRegistry registry) async {
|
||||||
|
registry.register(C2SPacketLogin(), () {
|
||||||
|
return C2SPacketLogin();
|
||||||
|
});
|
||||||
|
registry.register(S2CLoginReply(), () {
|
||||||
|
return S2CLoginReply();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
165
lib/server/packets.dart
Normal file
165
lib/server/packets.dart
Normal file
|
@ -0,0 +1,165 @@
|
||||||
|
import 'dart:convert';
|
||||||
|
|
||||||
|
import 'package:bugvault/server/main.dart';
|
||||||
|
import 'package:bugvault/users/User.dart';
|
||||||
|
import 'package:libac_dart/nbt/Tag.dart';
|
||||||
|
import 'package:libac_dart/nbt/impl/CompoundTag.dart';
|
||||||
|
import 'package:libac_dart/nbt/impl/IntTag.dart';
|
||||||
|
import 'package:libac_dart/nbt/impl/StringTag.dart';
|
||||||
|
import 'package:libac_dart/packets/packets.dart';
|
||||||
|
|
||||||
|
class C2SPacketLogin implements IPacket {
|
||||||
|
String username = "";
|
||||||
|
|
||||||
|
@override
|
||||||
|
void decodeJson(String params) {
|
||||||
|
fromJson(json.decode(params));
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void decodeTag(Tag tag) {
|
||||||
|
CompoundTag ct = tag.asCompoundTag();
|
||||||
|
username = ct.get("user")?.asString() ?? "";
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
NetworkDirection direction() {
|
||||||
|
return NetworkDirection.ClientToServer;
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
String encodeJson() {
|
||||||
|
return json.encode(toJson());
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Tag encodeTag() {
|
||||||
|
CompoundTag ct = CompoundTag();
|
||||||
|
ct.put("user", StringTag.valueOf(username));
|
||||||
|
|
||||||
|
return ct;
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void fromJson(Map<String, dynamic> js) {
|
||||||
|
username = js['user'] as String;
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
String getChannelID() {
|
||||||
|
return "BVPKTLOGIN";
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<void> handleClientPacket() async {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<PacketResponse> handleServerPacket() async {
|
||||||
|
S2CResponse response = S2CResponse();
|
||||||
|
S2CLoginReply loginReply = S2CLoginReply();
|
||||||
|
loginReply.username = username;
|
||||||
|
|
||||||
|
// Check if the user exists
|
||||||
|
DBUser? user = BugVaultServer.TryGetUser(username);
|
||||||
|
if (user == null && username != "") {
|
||||||
|
loginReply.g_ixLoginState |= LoginStates.NOT_FOUND;
|
||||||
|
loginReply.g_ixLoginState |= LoginStates.PROVIDE_EMAIL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (user != null) {
|
||||||
|
// Tell the user to send their TOTP Code
|
||||||
|
loginReply.g_ixLoginState |= LoginStates.FOUND;
|
||||||
|
loginReply.g_ixLoginState |= LoginStates.REQUIRE_MFA;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (username == "") {
|
||||||
|
if (BugVaultServer.g_bAllowAnonymousLogin) {
|
||||||
|
loginReply.g_ixLoginState = LoginStates.LOGGED_IN;
|
||||||
|
} else {
|
||||||
|
loginReply.g_ixLoginState = LoginStates.ANONYMOUS_NOT_ALLOWED;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
response.contents = loginReply.encodeTag().asCompoundTag();
|
||||||
|
return PacketResponse(replyDataTag: response.encodeTag().asCompoundTag());
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Map<String, dynamic> toJson() {
|
||||||
|
return {"user": username};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class LoginStates {
|
||||||
|
static const NOT_FOUND = 1;
|
||||||
|
static const FOUND = 2;
|
||||||
|
static const REQUIRE_MFA = 4;
|
||||||
|
static const PROVIDE_EMAIL = 8;
|
||||||
|
static const REQUIRE_EMAIL_CODE = 16;
|
||||||
|
static const LOGGED_IN = 32;
|
||||||
|
static const ANONYMOUS_NOT_ALLOWED = 64;
|
||||||
|
}
|
||||||
|
|
||||||
|
class S2CLoginReply implements IPacket {
|
||||||
|
String username = "";
|
||||||
|
int g_ixLoginState = 0;
|
||||||
|
|
||||||
|
@override
|
||||||
|
void decodeJson(String params) {
|
||||||
|
return fromJson(json.decode(params));
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void decodeTag(Tag tag) {
|
||||||
|
CompoundTag ct = tag.asCompoundTag();
|
||||||
|
username = ct.get("user")?.asString() ?? "";
|
||||||
|
g_ixLoginState = ct.get("state")?.asInt() ?? 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
NetworkDirection direction() {
|
||||||
|
return NetworkDirection.ServerToClient;
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
String encodeJson() {
|
||||||
|
return json.encode(toJson());
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Tag encodeTag() {
|
||||||
|
CompoundTag ct = CompoundTag();
|
||||||
|
ct.put("user", StringTag.valueOf(username));
|
||||||
|
ct.put("state", IntTag.valueOf(g_ixLoginState));
|
||||||
|
|
||||||
|
return ct;
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void fromJson(Map<String, dynamic> js) {
|
||||||
|
username = js['user'] as String;
|
||||||
|
g_ixLoginState = js['state'] as int;
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
String getChannelID() {
|
||||||
|
return "BVSPKTLOGINREPLY";
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<void> handleClientPacket() async {
|
||||||
|
// do handling stuff
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<PacketResponse> handleServerPacket() async {
|
||||||
|
throw UnimplementedError(); // Client side only, so this never gets invoked.
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Map<String, dynamic> toJson() {
|
||||||
|
return {"user": username, "state": g_ixLoginState};
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,3 +1,10 @@
|
||||||
|
import 'dart:math';
|
||||||
|
import 'dart:typed_data';
|
||||||
|
|
||||||
|
import 'package:base32/base32.dart';
|
||||||
|
import 'package:libac_dart/nbt/NbtUtils.dart';
|
||||||
|
import 'package:libac_dart/nbt/impl/CompoundTag.dart';
|
||||||
|
import 'package:libac_dart/utils/uuid/NbtUUID.dart';
|
||||||
import 'package:libac_dart/utils/uuid/UUID.dart';
|
import 'package:libac_dart/utils/uuid/UUID.dart';
|
||||||
|
|
||||||
/// The user class is a user object.
|
/// The user class is a user object.
|
||||||
|
@ -9,3 +16,39 @@ class User {
|
||||||
|
|
||||||
User({required this.sName, required this.ID});
|
User({required this.sName, required this.ID});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
String generateTOTPSecret({int length = 32}) {
|
||||||
|
final random = Random.secure();
|
||||||
|
final List<int> bytes = List.generate(length, (_) => random.nextInt(256));
|
||||||
|
return base32.encode(Uint8List.fromList(bytes));
|
||||||
|
}
|
||||||
|
|
||||||
|
class DBUser {
|
||||||
|
String sName;
|
||||||
|
UUID ID;
|
||||||
|
late String TOTPSecret;
|
||||||
|
|
||||||
|
DBUser({required this.sName, required this.ID, String? totp}) {
|
||||||
|
if (totp == null)
|
||||||
|
TOTPSecret = generateTOTPSecret();
|
||||||
|
else
|
||||||
|
TOTPSecret = totp!;
|
||||||
|
}
|
||||||
|
|
||||||
|
factory DBUser.load({required CompoundTag serialized}) {
|
||||||
|
UUID IDv4 = UUID.generate(4);
|
||||||
|
NbtUUID saved = NbtUtils.readUUID(serialized, "id");
|
||||||
|
IDv4 = saved.toUUID();
|
||||||
|
DBUser user = DBUser(
|
||||||
|
sName: serialized.get("name")?.asString() ?? "",
|
||||||
|
ID: IDv4,
|
||||||
|
totp: serialized.get("mfa_secret")?.asString() ?? "",
|
||||||
|
);
|
||||||
|
|
||||||
|
return user;
|
||||||
|
}
|
||||||
|
|
||||||
|
void regenerateTOTP() {
|
||||||
|
TOTPSecret = generateTOTPSecret();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -16,7 +16,7 @@ publish_to: "none" # Remove this line if you wish to publish to pub.dev
|
||||||
# https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html
|
# https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html
|
||||||
# In Windows, build-name is used as the major, minor, and patch parts
|
# In Windows, build-name is used as the major, minor, and patch parts
|
||||||
# of the product and file versions while build-number is used as the build suffix.
|
# of the product and file versions while build-number is used as the build suffix.
|
||||||
version: 1.0.031525+0003
|
version: 1.0.031525+0137
|
||||||
|
|
||||||
environment:
|
environment:
|
||||||
sdk: ^3.7.0
|
sdk: ^3.7.0
|
||||||
|
@ -40,7 +40,10 @@ dependencies:
|
||||||
libac_dart:
|
libac_dart:
|
||||||
hosted: https://git.zontreck.com/api/packages/Packages/pub/
|
hosted: https://git.zontreck.com/api/packages/Packages/pub/
|
||||||
version: 1.4.20325+1215
|
version: 1.4.20325+1215
|
||||||
|
base32: ^2.1.3
|
||||||
totp: ^0.1.0
|
totp: ^0.1.0
|
||||||
|
hotp: ^0.1.0
|
||||||
|
barcode: ^2.2.9
|
||||||
|
|
||||||
dev_dependencies:
|
dev_dependencies:
|
||||||
flutter_test:
|
flutter_test:
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue