diff --git a/lib/Packets.dart b/lib/Packets.dart index 2634744..2a23ebd 100644 --- a/lib/Packets.dart +++ b/lib/Packets.dart @@ -1,5 +1,7 @@ import 'dart:convert'; +import 'package:libac_flutter/utils/Hashing.dart'; +import 'package:libac_flutter/utils/uuid/UUID.dart'; import 'package:zontreck/Constants.dart'; import 'package:zontreck/pages/OpenSim.dart'; @@ -300,3 +302,55 @@ class S2CLoginResponsePacket implements IPacket { user: User.parseJson(map['user'])); } } + +class C2SSessionCheckPacket extends IPacket { + UUID sessionToken; + C2SSessionCheckPacket({required this.sessionToken}); + + @override + HTTPMethod method() { + return HTTPMethod.Post; + } + + @override + String getType() { + return "C2SSessionCheck"; + } + + @override + String encode() { + return json.encode({ + "token": Hashing.md5Hash(sessionToken.toString()), + "client": Constants.CLIENTPSK + }); + } +} + +class S2CSessionCheckPacket extends IPacket { + String id; + final bool valid; + S2CSessionCheckPacket({required this.id, required this.valid}); + + @override + HTTPMethod method() { + return HTTPMethod.Get; + } + + @override + String getType() { + return "S2CSessionCheck"; + } + + @override + String encode() { + return json.encode({"id": id, "type": getType()}); + } + + static S2CSessionCheckPacket decode(String params) { + var map = json.decode(params); + var id = map['id'] as String; + return S2CSessionCheckPacket( + id: id, + valid: id == Hashing.md5Hash(Settings().currentUser!.ID.toString())); + } +} diff --git a/lib/Settings.dart b/lib/Settings.dart index 06b72c8..6e42f33 100644 --- a/lib/Settings.dart +++ b/lib/Settings.dart @@ -1,7 +1,9 @@ import 'dart:convert'; import 'package:dio/dio.dart'; +import 'package:libac_flutter/nbt/NbtUtils.dart'; import 'package:libac_flutter/nbt/impl/CompoundTag.dart'; +import 'package:libac_flutter/nbt/impl/StringTag.dart'; import 'package:libac_flutter/utils/Hashing.dart'; import 'package:zontreck/Packets.dart'; import 'package:zontreck/pages/OpenSim.dart'; @@ -12,7 +14,8 @@ enum APIEndpoint { Setup(script: "Setup.php", path: "/ac/home/supports/"), Register(script: "Register.php", path: "/ac/home/supports/"), Logout(script: "Logout.php", path: "/ac/home/supports/"), - Login(script: "Login.php", path: "/ac/home/supports/"); + Login(script: "Login.php", path: "/ac/home/supports/"), + ValidateSession(script: "ValidateToken.php", path: "/ac/home/supports/"); final String script; final String path; @@ -54,6 +57,12 @@ class Settings { static CompoundTag save() { CompoundTag tag = CompoundTag(); + Settings settings = Settings(); + NbtUtils.writeBoolean(tag, "loggedIn", settings.loggedIn); + tag.put("name", StringTag.valueOf(settings.userName)); + tag.put("display", StringTag.valueOf(settings.displayName)); + if (settings.currentUser != null) + tag.put("user", settings.currentUser!.save()); return tag; } @@ -146,6 +155,11 @@ class Settings { S2CLoginResponsePacket.decode(reply); return response; } + case "S2CSessionCheck": + { + S2CSessionCheckPacket response = S2CSessionCheckPacket.decode(reply); + return response; + } default: { return NullPacket(); diff --git a/lib/pages/LoginAccount.dart b/lib/pages/LoginAccount.dart index 2afa328..26e8b3c 100644 --- a/lib/pages/LoginAccount.dart +++ b/lib/pages/LoginAccount.dart @@ -1,5 +1,8 @@ import 'package:flutter/material.dart'; +import 'package:libac_flutter/nbt/NbtIo.dart'; +import 'package:libac_flutter/nbt/impl/CompoundTag.dart'; import 'package:libac_flutter/utils/Hashing.dart'; +import 'package:shared_preferences/shared_preferences.dart'; import 'package:zontreck/Constants.dart'; import 'package:zontreck/Packets.dart'; import 'package:zontreck/Settings.dart'; @@ -69,6 +72,14 @@ class LoginAccountState extends State { settings.loggedIn = true; settings.currentUser = response.user; + + CompoundTag setting = Settings.save(); + // Save to cookie + var value = await NbtIo.writeBase64String(setting); + SharedPreferences prefs = + await SharedPreferences.getInstance(); + prefs.setString("settings", value); + Navigator.pop(context); } else { ScaffoldMessenger.of(context).showSnackBar(SnackBar( diff --git a/lib/pages/OpenSim.dart b/lib/pages/OpenSim.dart index 7929ea0..5cbe30c 100644 --- a/lib/pages/OpenSim.dart +++ b/lib/pages/OpenSim.dart @@ -3,7 +3,13 @@ import 'dart:convert'; import 'package:flutter/material.dart'; import 'package:footer/footer.dart'; import 'package:footer/footer_view.dart'; +import 'package:libac_flutter/nbt/NbtIo.dart'; +import 'package:libac_flutter/nbt/NbtUtils.dart'; +import 'package:libac_flutter/nbt/impl/CompoundTag.dart'; +import 'package:libac_flutter/nbt/impl/IntTag.dart'; +import 'package:libac_flutter/nbt/impl/StringTag.dart'; import 'package:libac_flutter/utils/uuid/UUID.dart'; +import 'package:shared_preferences/shared_preferences.dart'; import 'package:zontreck/Constants.dart'; import 'package:zontreck/Packets.dart'; import 'package:zontreck/Settings.dart'; @@ -15,6 +21,7 @@ class User { int createdAt; String userTitle; bool active; + UUID loginToken; User( {required this.ID, @@ -22,7 +29,8 @@ class User { required this.LastName, required this.createdAt, required this.userTitle, - required this.active}); + required this.active, + required this.loginToken}); static User parseJson(Map map) { return User( @@ -31,7 +39,8 @@ class User { LastName: map['last'] as String, createdAt: map['rez'] as int, userTitle: map['title'] as String, - active: map['active'] as bool); + active: map['active'] as bool, + loginToken: UUID.parse(map['token'] as String)); } String encode() { @@ -41,9 +50,34 @@ class User { "last": LastName, "rez": createdAt, "title": userTitle, - "active": active + "active": active, + "token": loginToken }); } + + CompoundTag save() { + CompoundTag tag = CompoundTag(); + NbtUtils.writeUUID(tag, "id", ID); + tag.put("first", StringTag.valueOf(FirstName)); + tag.put("last", StringTag.valueOf(LastName)); + tag.put("rez", IntTag.valueOf(createdAt)); + tag.put("title", StringTag.valueOf(userTitle)); + NbtUtils.writeBoolean(tag, "active", active); + NbtUtils.writeUUID(tag, "token", loginToken); + + return tag; + } + + static User load(CompoundTag tag) { + return User( + ID: NbtUtils.readUUID(tag, "id"), + FirstName: tag.get("first")!.asString(), + LastName: tag.get("last")!.asString(), + createdAt: tag.get("rez")!.asInt(), + userTitle: tag.get("title")!.asString(), + active: NbtUtils.readBoolean(tag, "active"), + loginToken: NbtUtils.readUUID(tag, "token")); + } } class OpenSimPage extends StatefulWidget { @@ -77,6 +111,29 @@ class OpenSimPageState extends State { settings.OpenSimSetupCompleted = false; } + if (!settings.loggedIn) { + SharedPreferences prefs = await SharedPreferences.getInstance(); + if (prefs.containsKey("settings")) { + String encoded = prefs.getString("settings")!; + CompoundTag tag = NbtIo.readBase64String(encoded) as CompoundTag; + if (tag.contains("user")) + settings.currentUser = User.load(tag.get("user") as CompoundTag); + + // Validate current session + var reply = await settings.sendPacketToEndpoint( + APIEndpoint.ValidateSession, + C2SSessionCheckPacket( + sessionToken: settings.currentUser!.loginToken)) + as S2CSessionCheckPacket; + if (reply.valid) { + // We're good to continue loading this user + } else { + settings.currentUser = null; + prefs.clear(); + } + } + } + var pong = await settings.sendPacketToEndpoint( APIEndpoint.Ping, NullPacket()) as S2CPongPacket; @@ -147,6 +204,11 @@ class OpenSimPageState extends State { APIEndpoint.Logout, NullPacket()); + SharedPreferences prefs = + await SharedPreferences + .getInstance(); + prefs.clear(); + didChangeDependencies(); }, child: Text("LOGOUT")) diff --git a/linux/CMakeLists.txt b/linux/CMakeLists.txt index 248f592..8234f28 100644 --- a/linux/CMakeLists.txt +++ b/linux/CMakeLists.txt @@ -7,7 +7,7 @@ project(runner LANGUAGES CXX) set(BINARY_NAME "zontreck") # The unique GTK application identifier for this application. See: # https://wiki.gnome.org/HowDoI/ChooseApplicationID -set(APPLICATION_ID "com.example.zontreck") +set(APPLICATION_ID "com.zontreck.zontreck") # Explicitly opt in to modern CMake behaviors to avoid warnings with recent # versions of CMake. diff --git a/php/Login.php b/php/Login.php index 0d2209c..6011e5b 100644 --- a/php/Login.php +++ b/php/Login.php @@ -20,6 +20,7 @@ $title = ""; $login = false; $reason = "Unauthorized"; $active = false; +$token = ""; $clientKey = $js['clientKey']; if($clientKey == CLIENTPSK) { @@ -43,6 +44,9 @@ if($clientKey == CLIENTPSK) { $reason = "success"; $login=true; + + $token = gen_uuid(); + $DB->query("UPDATE `auth` SET `webLoginKey`='$token' WHERE `UUID` = '$id';"); } else { $reason = "Invalid Password"; } @@ -64,7 +68,8 @@ die(json_encode( "last" => $last, "title" => $title, "rez" => $rez, - "active" => $active + "active" => $active, + "token" => $token ) ) )); diff --git a/php/ValidateToken.php b/php/ValidateToken.php new file mode 100644 index 0000000..7ae958f --- /dev/null +++ b/php/ValidateToken.php @@ -0,0 +1,29 @@ +query("SELECT * FROM `auth`;"); +if($res->num_rows != 0){ + while($row = $res->fetch_assoc()){ + if(md5($row['webLoginKey']) == $token) { + $id = $row["UUID"]; + } + } +} +die(json_encode( + array( + "id" => md5($id), + "type" => "S2CSessionCheck" + ) +)); + +?> \ No newline at end of file diff --git a/pubspec.yaml b/pubspec.yaml index 2940162..fc07550 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -37,7 +37,7 @@ dependencies: cupertino_icons: ^1.0.6 libac_flutter: hosted: https://git.zontreck.com/api/packages/AriasCreations/pub/ - version: 1.0.3 + version: 1.0.4 dio: ^5.4.3+1 shared_preferences: ^2.2.3 footer: ^0.0.4