Finish making account registration possible

This commit is contained in:
zontreck 2024-05-16 00:42:40 -07:00
parent f37af74687
commit fb6928cb30
9 changed files with 375 additions and 40 deletions

View file

@ -8,7 +8,7 @@ class Constants {
static const DRAWER_COLOR = Color.fromARGB(148, 0, 97, 97);
static const PORTFOLIO_CARD_COLOR = Color.fromARGB(255, 0, 71, 97);
static const VERSION = "1.0.051524.1622";
static const VERSION = "Version 1.0.051524.2243";
static const COPYRIGHT = "Copyright 2024 - Tara Piccari. All rights Reserved";
static const CLIENTPSK =
"f5c6caf3efe1ec5aa4b7c572f92aa14782b7be34b4c7844fa9c6d47fdf94246";
@ -16,6 +16,8 @@ class Constants {
static const SERVICES_JSON =
"https://raw.githubusercontent.com/AriasCreations/AriasCreations/main/services.json";
static const ALLOW_ANY_LAST_NAME = false;
static Future<Map<String, dynamic>> pullServicesJson() async {
Settings settings = Settings();
var reply = await settings.dio.get(SERVICES_JSON);

View file

@ -186,3 +186,60 @@ class C2SPingPacket implements IPacket {
return json.encode({"client": client});
}
}
class C2SRegisterAccountPacket implements IPacket {
final String firstName;
final String lastName;
final String passwordHash;
final String email;
final int level;
final String title;
final String clientKey;
C2SRegisterAccountPacket({
required this.firstName,
required this.lastName,
required this.passwordHash,
required this.email,
required this.level,
required this.title,
required this.clientKey,
});
@override
HTTPMethod method() {
return HTTPMethod.Post;
}
@override
String getType() {
return "C2SRegisterAccount";
}
@override
String encode() {
return json.encode({
"first": firstName,
"last": lastName,
"password": passwordHash,
"email": email,
"type": getType(),
"level": level,
"title": title,
"clientKey": clientKey,
});
}
static C2SRegisterAccountPacket decode(String params) {
var map = json.decode(params);
return C2SRegisterAccountPacket(
firstName: map['first'] as String,
lastName: map['last'] as String,
passwordHash: map['password'] as String,
email: map['email'] as String,
level: map['level'] as int,
title: map['title'] as String,
clientKey: map['clientKey'] as String);
}
}

View file

@ -9,6 +9,7 @@ enum APIEndpoint {
SetupCheck(script: "SetupCheck.php", path: "/ac/home/supports/"),
Ping(script: "Ping.php", path: "/ac/home/supports/"),
Setup(script: "Setup.php", path: "/ac/home/supports/"),
Register(script: "Register.php", path: "/ac/home/supports/"),
Login(script: "Login.php", path: "/ac/home/supports/");
final String script;
@ -21,6 +22,15 @@ enum APIEndpoint {
}
}
enum UserTitles {
OPERATOR(title: "Grid Operator"),
ADMIN(title: "Grid Admin"),
USER(title: "Resident");
final String title;
const UserTitles({required this.title});
}
enum HTTPMethod { Get, Post, Put, Delete }
class Settings {
@ -56,6 +66,9 @@ class Settings {
String displayName = "";
int totalGridUsers = 0;
bool get hasUsers => totalGridUsers != 0;
bool get hasNoUsers => totalGridUsers == 0;
void setServices(Map<String, dynamic> js) {
var protocol = js['api']['protocol'] as String;
var port = js['api']['port'] as int;

View file

@ -4,6 +4,7 @@ import 'package:footer/footer_view.dart';
import 'package:zontreck/Constants.dart';
import 'package:zontreck/pages/OpenSim.dart';
import 'package:zontreck/pages/Portfolio.dart';
import 'package:zontreck/pages/RegisterAccount.dart';
class MainPage extends StatelessWidget {
const MainPage({super.key});
@ -14,6 +15,7 @@ class MainPage extends StatelessWidget {
routes: {
"/": (context) => const HomePage(),
"/opensim": (context) => const OpenSimPage(),
"/opensim/register": (context) => RegisterAccountPage(),
"/portfolio": (context) => PortfolioPage(),
"/portfolio/coun": (context) => CardsOfUtterNonsense()
},

View file

@ -84,7 +84,10 @@ class OpenSimPageState extends State<OpenSimPage> {
ElevatedButton(
onPressed: () {}, child: Text("Login")),
ElevatedButton(
onPressed: () {},
onPressed: () {
Navigator.pushNamed(
context, "/opensim/register");
},
child: Text("Register Account"))
],
))

View file

@ -1,4 +1,6 @@
import 'package:flutter/material.dart';
import 'package:footer/footer.dart';
import 'package:footer/footer_view.dart';
import 'package:zontreck/Constants.dart';
class PortfolioPage extends StatelessWidget {
@ -7,33 +9,45 @@ class PortfolioPage extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("Zontreck.com - Portfolio of Tara Piccari"),
backgroundColor: Constants.TITLEBAR_COLOR,
),
body: Padding(
padding: EdgeInsets.all(8),
child: SingleChildScrollView(
child: Row(
children: [
PortfolioEntry(
title: ListTile(title: Text("Cards of Utter Nonsense")),
body: Text(
"A product I created for Second Life, but may port to the mobile phone at some point"),
onTap: () {
Navigator.pushNamed(context, "/portfolio/coun");
},
),
PortfolioEntry(
title: Text("Zontreck.com"),
body: Text(
("This website, which is written entirely in Flutter, with some supporting API files in PHP")),
onTap: () {})
],
),
appBar: AppBar(
title: Text("Zontreck.com - Portfolio of Tara Piccari"),
backgroundColor: Constants.TITLEBAR_COLOR,
),
),
);
body: FooterView(
footer: Footer(
alignment: Alignment.center,
backgroundColor: ThemeData.dark().focusColor,
child:
const Text("${Constants.COPYRIGHT}\n${Constants.VERSION}")),
children: [
Padding(
padding: EdgeInsets.all(8),
child: SingleChildScrollView(
child: Row(
children: [
PortfolioEntry(
title: ListTile(title: Text("Cards of Utter Nonsense")),
body: Text(
"A product I created for Second Life, but may port to the mobile phone at some point"),
onTap: () {
Navigator.pushNamed(context, "/portfolio/coun");
},
),
PortfolioEntry(
title: Text("Zontreck.com"),
body: Text(
("This website, which is written entirely in Flutter, with some supporting API files in PHP")),
onTap: () {}),
PortfolioEntry(
title: Text("Minecraft Modding"),
body: Text(
"These mods are all written in Java. The various mods I currently maintain, previously maintained, or have contributed to are: Thresholds, Aria's Essentials, LibZontreck, Let's Do Beachparty, WatchMyDurability"),
onTap: () {})
],
),
),
),
]));
}
}

View file

@ -1,5 +1,10 @@
import 'package:flutter/material.dart';
import 'package:footer/footer.dart';
import 'package:footer/footer_view.dart';
import 'package:libac_flutter/utils/Hashing.dart';
import 'package:zontreck/Constants.dart';
import 'package:zontreck/Packets.dart';
import 'package:zontreck/Settings.dart';
class RegisterAccountPage extends StatefulWidget {
RegisterAccountPage({super.key});
@ -11,21 +16,193 @@ class RegisterAccountPage extends StatefulWidget {
class RegisterAccountState extends State<RegisterAccountPage> {
RegisterAccountState();
Settings settings = Settings();
TextEditingController firstNameController = TextEditingController();
TextEditingController lastNameController = TextEditingController();
TextEditingController password = TextEditingController();
TextEditingController confirm = TextEditingController();
TextEditingController email = TextEditingController();
bool get passwordMatches =>
password.text == confirm.text &&
password.text != "" &&
confirm.text != "";
bool get canSubmit =>
firstNameController.text != "" &&
lastNameController.text != "" &&
passwordMatches &&
email.text != "";
@override
void didChangeDependencies() {
lastNameController.text = settings.hasNoUsers ? "Piccari" : "";
setState(() {});
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("OpenSim - Register Account"),
backgroundColor: Constants.TITLEBAR_COLOR,
),
body: Padding(
padding: EdgeInsets.all(8),
child: SingleChildScrollView(
child: Column(
children: [],
),
appBar: AppBar(
title: Text("OpenSim - Register Account"),
backgroundColor: Constants.TITLEBAR_COLOR,
),
),
);
floatingActionButton: canSubmit
? ElevatedButton(
onPressed: () async {
C2SRegisterAccountPacket packet = C2SRegisterAccountPacket(
firstName: firstNameController.text,
lastName: lastNameController.text,
passwordHash: Hashing.md5Hash(password.text),
email: email.text,
level: settings.hasNoUsers ? 240 : 1,
title: settings.hasNoUsers
? UserTitles.OPERATOR.title
: UserTitles.USER.title,
clientKey: Constants.CLIENTPSK,
);
var response = await settings.sendPacketToEndpoint(
APIEndpoint.Register, packet) as S2CSimpleReplyPacket;
if (response.done) {
ScaffoldMessenger.of(context).showSnackBar(SnackBar(
content: Text(
"User Account Created. You must now login to finish setting up the account")));
Navigator.pop(context);
} else {
ScaffoldMessenger.of(context).showSnackBar(SnackBar(
content:
Text("Fatal error when creating user account")));
}
},
child: Text("Create my Account"))
: null,
body: FooterView(
footer: Footer(
alignment: Alignment.center,
backgroundColor: ThemeData.dark().focusColor,
child:
const Text("${Constants.COPYRIGHT}\n${Constants.VERSION}")),
children: [
Padding(
padding: EdgeInsets.all(8),
child: SingleChildScrollView(
child: Column(
children: [
settings.hasNoUsers
? ListTile(
title: Text("There are no users on this grid."),
tileColor: Constants.TITLEBAR_COLOR,
subtitle: Text(
"This account will be granted Level 240, and the User Title : ${UserTitles.OPERATOR.title}"),
)
: SizedBox(),
ListTile(
title: Text("First Name"),
subtitle: TextField(
controller: firstNameController,
onChanged: (v) {
setState(() {});
},
),
),
ListTile(
title: Text("Last Name"),
subtitle: Constants.ALLOW_ANY_LAST_NAME ||
settings.hasNoUsers
? TextField(
controller: lastNameController,
onChanged: (v) {
setState(() {});
},
)
: DropdownMenu(
onSelected: (V) {
setState(() {
lastNameController.text = V as String;
});
},
dropdownMenuEntries:
LastNames.getCurrentNames(),
)),
ListTile(
title: Text("Password"),
subtitle: TextField(
controller: password,
decoration: InputDecoration(
hintText: "*******",
),
obscureText: true,
obscuringCharacter: "*",
onChanged: (V) {
setState(() {});
},
),
),
ListTile(
title: Text("Password Confirmation"),
subtitle: TextField(
controller: confirm,
decoration: InputDecoration(
hintText: "*******",
),
obscureText: true,
obscuringCharacter: "*",
onChanged: (V) {
setState(() {});
},
),
),
passwordMatches ||
password.text == "" && confirm.text == ""
? Divider(
thickness: 2,
)
: ListTile(
title: Text("Passwords do not match"),
tileColor: Constants.TITLEBAR_COLOR,
),
ListTile(
title: Text("Email Address"),
subtitle: TextField(
onChanged: (V) {
setState(() {});
},
controller: email,
decoration: InputDecoration(
hintText:
"Your email address. It is not used by zontreck.com, but if needed, can be used to reach you"),
),
)
],
),
),
),
]));
}
}
enum LastNames {
Aabye,
Aarde,
Bailey,
Caballero;
static List<DropdownMenuEntry<Object?>> getCurrentNames() {
return [
LastNames.Aabye.getEntry(),
LastNames.Aarde.getEntry(),
LastNames.Bailey.getEntry(),
LastNames.Caballero.getEntry()
];
}
DropdownMenuEntry<String> getEntry() {
return DropdownMenuEntry(value: this.name, label: this.name);
}
}

View file

@ -2,6 +2,9 @@
if(defined("COMMON")) return;
define("COMMON", 1);
define("NULLKEY", "00000000-0000-0000-0000-000000000000");
function get_DB() {
return mysqli_connect(DB_HOST, DB_USER, DB_PASS, DB_NAME);
@ -45,5 +48,33 @@ if(file_exists("../system.user.php"))
require("../system.user.php");
function gen_uuid()
{
return sprintf(
'%04x%04x-%04x-%04x-%04x-%04x%04x%04x',
// 32 bits for "time_low"
mt_rand(0, 0xffff),
mt_rand(0, 0xffff),
// 16 bits for "time_mid"
mt_rand(0, 0xffff),
// 16 bits for "time_hi_and_version",
// four most significant bits holds version number 4
mt_rand(0, 0x0fff) | 0x4000,
// 16 bits, 8 bits for "clk_seq_hi_res",
// 8 bits for "clk_seq_low",
// two most significant bits holds zero and one for variant DCE1.1
mt_rand(0, 0x3fff) | 0x8000,
// 48 bits for "node"
mt_rand(0, 0xffff),
mt_rand(0, 0xffff),
mt_rand(0, 0xffff)
);
}
session_start();
?>

36
php/Register.php Normal file
View file

@ -0,0 +1,36 @@
<?php
if(!defined("COMMON"))
require("Common.php");
$js = getJsonizedInput();
$first = $js['first'];
$last = $js['last'];
$password = $js['password'];
$ID = gen_uuid();
$level = $js['level'];
$title = $js['title'];
$email = $js['email'];
// Make salt
$salt = md5(time().":".md5(time().":".gen_uuid()));
$pwhash = md5($password.":".$salt);
$clientKey = $js['clientKey'];
if($clientKey == CLIENTPSK) {
// Perform registration
$DB = get_DB();
$DB->query("INSERT INTO `auth` (UUID, passwordHash, passwordSalt, webLoginKey, accountType) VALUES ('$ID', '$pwhash', '$salt', '".NULLKEY."', 'UserAccount');");
$DB->query("INSERT INTO `UserAccounts` (PrincipalID, ScopeID, FirstName, LastName, Email, ServiceURLs, Created, UserLevel, UserFlags, UserTitle, active) VALUES ('$ID', '".NULLKEY."', '$first', '$last', '$email', '', '".time()."', '$level', '0', '$title', '1');");
die(json_encode(array("done"=>true, "type"=> "S2CSimpleReply")));
}else {
die(json_encode(array("done"=>false, "type"=> "S2CSimpleReply")));
}
?>