Basic implementation of first login handshake
This commit is contained in:
parent
6ea48dd4e7
commit
88a7a0a9c6
8 changed files with 314 additions and 13 deletions
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};
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue