From ae1da5f2b2875b03a49966da143b09c9f77f7d41 Mon Sep 17 00:00:00 2001 From: zontreck Date: Tue, 2 Jul 2024 17:48:06 -0700 Subject: [PATCH] Adds a full access control list editor --- bin/server.dart | 7 +- lib/main.dart | 2 + lib/pages/ACL.dart | 180 ++++++++++++++++++++++++++++++ lib/pages/credentials_prompt.dart | 2 +- lib/pages/home.dart | 13 ++- lib/structs/settings.dart | 3 +- 6 files changed, 201 insertions(+), 6 deletions(-) create mode 100644 lib/pages/ACL.dart diff --git a/bin/server.dart b/bin/server.dart index 1a109f2..cedf5dd 100644 --- a/bin/server.dart +++ b/bin/server.dart @@ -36,8 +36,11 @@ void main() async { settings.Write(); print("Wrote settings.dat"); - settings.superuser = User.make(settings.serverLoginCreds.username, - settings.serverLoginCreds.password, UserLevel.Super_User); + if (settings.FTS) + settings.superuser = User.make( + settings.serverLoginCreds.username, + settings.serverLoginCreds.password, + UserLevel.Super_User); // Initialize the super-user account print("Initializing SteamCMD"); await settings.initializeSteamCmd(); diff --git a/lib/main.dart b/lib/main.dart index d1f260a..6504f64 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -1,6 +1,7 @@ import 'package:flutter/material.dart'; import 'package:libac_dart/packets/packets.dart'; import 'package:servermanager/packets/ClientPackets.dart'; +import 'package:servermanager/pages/ACL.dart'; import 'package:servermanager/pages/Constants.dart'; import 'package:servermanager/pages/DiscordConfigPage.dart'; import 'package:servermanager/pages/GameServerPage.dart'; @@ -30,6 +31,7 @@ class MyApp extends StatelessWidget { "/": (context) => ServerPage(), "/home": (context) => HomePage(settings: appSettings), "/creds": (context) => CredentialsPage(), + "/acl": (context) => AccessControlListPage(), "/server": (context) => GameServerPage(settings: appSettings), "/server/autorestart": (context) => AutoRestartPage(), "/server/ports": (context) => ServerSettingsPage(), diff --git a/lib/pages/ACL.dart b/lib/pages/ACL.dart new file mode 100644 index 0000000..e8fdfca --- /dev/null +++ b/lib/pages/ACL.dart @@ -0,0 +1,180 @@ +import 'package:flutter/material.dart'; +import 'package:servermanager/pages/Constants.dart'; +import 'package:servermanager/structs/credentials.dart'; +import 'package:servermanager/structs/settings.dart'; + +class AccessControlListPage extends StatefulWidget { + @override + State createState() { + return AccessControlState(); + } +} + +class AccessControlState extends State { + Settings settings = Settings(); + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + title: Text("Conan Exiles Server Manager - ACL Editor"), + backgroundColor: Constants.TITLEBAR_COLOR, + ), + floatingActionButton: ElevatedButton( + onPressed: () { + // Show the add new user page + Navigator.pushNamed(context, "/acl/edit"); + }, + child: Icon(Icons.add), + ), + body: Padding( + padding: EdgeInsets.all(8), + child: ListView.builder( + itemBuilder: (ctx, index) { + User user = settings.inst!.admins[index]; + return ListTile( + title: Text(user.name), + subtitle: Text("Access Level: ${user.permissions.name}"), + onTap: () async { + var edited = await Navigator.pushNamed(context, "/acl/edit", + arguments: user); + if (edited == null) return; + + if (edited is bool) { + settings.inst!.admins.remove(user); + setState(() {}); + return; + } + + setState(() { + settings.inst!.admins[index] = edited as User; + }); + }, + ); + }, + itemCount: settings.inst!.admins.length, + )), + ); + } +} + +class ACLEdit extends StatefulWidget { + @override + State createState() { + return ACLEditorState(); + } +} + +class ACLEditorState extends State { + Settings settings = Settings(); + var newUser = false; + TextEditingController username = TextEditingController(); + TextEditingController password = TextEditingController(); + + User? current; + + @override + void didChangeDependencies() { + var args = ModalRoute.of(context)!.settings.arguments; + if (args == null) { + newUser = true; + } else { + User usr = args as User; + + username.text = usr.name; + current = usr; + } + + setState(() {}); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + title: Text( + "CE SM - ACL Edit - ${newUser ? "Create A User" : "Edit User - ${username.text}"}"), + backgroundColor: Constants.TITLEBAR_COLOR, + ), + floatingActionButton: ElevatedButton( + onPressed: () { + User? user; + if (!newUser) { + if (password.text.isEmpty) { + user = User( + name: username.text, + passwordHash: current!.passwordHash, + passwordSalt: current!.passwordSalt, + permissions: current!.permissions, + userHash: User.generateValidityCode( + username.text, + current!.passwordHash, + current!.passwordSalt, + current!.permissions)); + } else { + user = User.make( + username.text, password.text, UserLevel.Administrator); + } + } else { + user = User.make( + username.text, password.text, UserLevel.Administrator); + } + + Navigator.pop(context, user); + }, + child: Text("Save"), + ), + body: Padding( + padding: EdgeInsets.all(8), + child: SingleChildScrollView( + child: Column( + children: [ + Row( + children: [ + SizedBox( + width: 150, + child: ListTile( + title: Text("Username"), + ), + ), + TextField( + controller: username, + ) + ], + ), + if (!newUser) + ListTile( + title: Text("Warning"), + subtitle: Text( + "The password is encrypted, if you wish to change the password do so below, but the field is intentionally not filled in. If left blank, it will remain unchanged."), + ), + Row( + children: [ + SizedBox( + width: 150, + child: ListTile( + title: Text("Password"), + ), + ), + TextField( + controller: password, + obscureText: true, + ) + ], + ), + if (!newUser) + ListTile( + title: Text("DELETE USER"), + leading: Icon(Icons.delete), + subtitle: Text("Delete the current user entirely"), + onTap: () { + Navigator.pop(context, false); + }, + ) + ], + ), + ), + ), + ); + } +} diff --git a/lib/pages/credentials_prompt.dart b/lib/pages/credentials_prompt.dart index 8df9ee9..760fc3a 100644 --- a/lib/pages/credentials_prompt.dart +++ b/lib/pages/credentials_prompt.dart @@ -32,7 +32,7 @@ class CredentialsPrompt extends State { Widget build(BuildContext context) { return Scaffold( appBar: AppBar( - title: Text("Conan Exiles Server Manager - Credentials (Super User)"), + title: Text("Conan Exiles Server Manager - Credentials"), backgroundColor: Color.fromARGB(255, 100, 0, 0), ), floatingActionButton: ElevatedButton( diff --git a/lib/pages/home.dart b/lib/pages/home.dart index 6bbe22e..cb537ac 100644 --- a/lib/pages/home.dart +++ b/lib/pages/home.dart @@ -93,17 +93,26 @@ class HomePageState extends State { ), ListTile( title: Text("Manager Credentials"), - subtitle: Text("Edit ServerManager credentials"), + subtitle: + Text("Edit ServerManager credentials (SUPER USER ONLY)"), leading: Icon(Icons.key), onTap: () async { var reply = await Navigator.pushNamed(context, "/creds", - arguments: settings.serverLoginCreds); + arguments: settings.superuser.name); if (reply != null) { Credentials creds = reply as Credentials; settings.serverLoginCreds = creds; } }, ), + ListTile( + title: Text("Manage Access Control List"), + subtitle: Text("Non-Super User access manager"), + leading: Icon(Icons.list), + onTap: () async { + Navigator.pushNamed(context, "/acl"); + }, + ), ListTile( title: Text("Save Changes"), subtitle: Text( diff --git a/lib/structs/settings.dart b/lib/structs/settings.dart index 5117fb7..4f18ddb 100644 --- a/lib/structs/settings.dart +++ b/lib/structs/settings.dart @@ -40,7 +40,7 @@ class Settings { UUID remoteLoginToken = UUID.ZERO; PacketClient? client; - User? superuser; + User superuser = User.make("admin", "changeMe123", UserLevel.Super_User); User? loggedInUser; @@ -59,6 +59,7 @@ class Settings { NbtUtils.writeBoolean(tag, "fts", FTS); tag.put("server_creds", serverLoginCreds.save()); + tag.put("superuser", superuser.serialize()); NbtUtils.writeUUID(tag, "token", NbtUUID.fromUUID(remoteLoginToken)); if (inst != null) tag.put("main", inst!.serialize());