mirror of
https://onedev.site.tesses.net/tytd2025
synced 2026-02-08 17:45:45 +00:00
Add login support and oobe
This commit is contained in:
@@ -1,4 +1,44 @@
|
||||
//DO NOT ADD A FLAG WITH ONES BIT SET AS THIS IS
|
||||
//TO DENOTE USER IS LOGGED IN
|
||||
class UserFlags {
|
||||
static getAdminFlag() 0b00000100;
|
||||
static getPluginFlag() 0b00000010;
|
||||
static getDatabaseFlag() 0b00001000;
|
||||
static getManagePluginFlag() 0b00100000;
|
||||
static IsAdmin(flags)
|
||||
{
|
||||
if(flags & UserFlags.AdminFlag) return true;
|
||||
return false;
|
||||
}
|
||||
static CanDownloadDB(flags)
|
||||
{
|
||||
if(flags & UserFlags.AdminFlag) return true;
|
||||
if(flags & UserFlags.DatabaseFlag) return true;
|
||||
return false;
|
||||
}
|
||||
static CanCreateUsers(flags)
|
||||
{
|
||||
if(flags & UserFlags.AdminFlag) return true;
|
||||
return false;
|
||||
}
|
||||
static CanUsePlugins(flags)
|
||||
{
|
||||
if(flags & UserFlags.AdminFlag) return true;
|
||||
if(flags & UserFlags.ManagePluginFlag) return true;
|
||||
if(flags & UserFlags.PluginFlag) return true;
|
||||
return false;
|
||||
}
|
||||
static CanManagePlugins(flags)
|
||||
{
|
||||
if(flags & UserFlags.AdminFlag) return true;
|
||||
if(flags & UserFlags.ManagePluginFlag) return true;
|
||||
return false;
|
||||
}
|
||||
static getITTR() 35000;
|
||||
}
|
||||
|
||||
class TYTD.Downloader {
|
||||
|
||||
/^
|
||||
The storage vfs that TYTD accesses
|
||||
^/
|
||||
@@ -715,7 +755,8 @@ class TYTD.Downloader {
|
||||
public Config = {
|
||||
TYTDTag = "UnknownPC",
|
||||
BellTimer = 10800,
|
||||
EnablePlugins=true
|
||||
EnablePlugins=true,
|
||||
OobeState = "oobe"
|
||||
};
|
||||
public SaveConfig()
|
||||
{
|
||||
@@ -1255,6 +1296,8 @@ class TYTD.Downloader {
|
||||
Sqlite.Exec(db,"CREATE TABLE IF NOT EXISTS personal_list_entries (id INTEGER PRIMARY KEY AUTOINCREMENT, listName TEXT, videoId TEXT);");
|
||||
Sqlite.Exec(db,"CREATE TABLE IF NOT EXISTS plugin_settings (id INTEGER PRIMARY KEY AUTOINCREMENT, extension TEXT, key TEXT, value TEXT, UNIQUE(extension,key) ON CONFLICT REPLACE);");
|
||||
Sqlite.Exec(db,"CREATE TABLE IF NOT EXISTS subscriptions (id INTEGER PRIMARY KEY AUTOINCREMENT, channelId TEXT UNIQUE ON CONFLICT REPLACE, bell TEXT);");
|
||||
Sqlite.Exec(db,"CREATE TABLE IF NOT EXISTS users (id INTEGER PRIMARY KEY AUTOINCREMENT, username TEXT UNIQUE, password_hash TEXT, password_salt TEXT, flags INTEGER);");
|
||||
Sqlite.Exec(db,"CREATE TABLE IF NOT EXISTS sessions (id INTEGER PRIMARY KEY AUTOINCREMENT, accountId INTEGER, key TEXT UNIQUE);");
|
||||
var config=Sqlite.Exec(db,"SELECT * FROM plugin_settings WHERE extension = '' AND key = 'settings';");
|
||||
if(TypeOf(config) == "List" && config.Length>0)
|
||||
{
|
||||
@@ -1264,6 +1307,10 @@ class TYTD.Downloader {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
this.Config.OobeState ??= "oobe";
|
||||
|
||||
|
||||
Sqlite.Close(db);
|
||||
this.Mutex.Unlock();
|
||||
|
||||
@@ -1718,11 +1765,219 @@ class TYTD.Downloader {
|
||||
this.lastRequest = curRequest;
|
||||
this.rlm.Unlock();
|
||||
}
|
||||
public GetSessionToken(ctx)
|
||||
{
|
||||
var cookie = ctx.RequestHeaders.TryGetFirst("Cookie");
|
||||
if(TypeOf(cookie) == "String")
|
||||
{
|
||||
each(var part : cookie.Split("; "))
|
||||
{
|
||||
if(part.Length > 0)
|
||||
{
|
||||
var cookieKV = part.Split("=",true,2);
|
||||
if(cookieKV.Length == 2 && cookieKV[0] == "Session")
|
||||
{
|
||||
return cookieKV[1];
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
var auth = ctx.RequestHeaders.TryGetFirst("Authorization");
|
||||
if(TypeOf(auth) == "String")
|
||||
{
|
||||
auth=auth.Split(" ",true,2);
|
||||
if(auth.Length < 2) return null;
|
||||
if(auth[0] != "Bearer") return null;
|
||||
return auth[1];
|
||||
}
|
||||
return null;
|
||||
}
|
||||
public Logout(ctx)
|
||||
{
|
||||
const token = GetSessionToken(ctx);
|
||||
if(TypeIsString(token))
|
||||
{
|
||||
|
||||
this.Mutex.Lock();
|
||||
const db = this.OpenDB();
|
||||
Sqlite.Exec(db, $"DELETE FROM sessions WHERE key = {Sqlite.Escape(token)};");
|
||||
Sqlite.Close(db);
|
||||
this.Mutex.Unlock();
|
||||
}
|
||||
}
|
||||
|
||||
public CreateAccount(ctx, username, password, flags)
|
||||
{
|
||||
var loggedIn = this.IsLoggedIn(ctx);
|
||||
if(loggedIn == 0xFFFFFFFE) flags = 0xFFFFFFFE;
|
||||
|
||||
if(UserFlags.CanCreateUsers(loggedIn))
|
||||
{
|
||||
this.Mutex.Lock();
|
||||
const db = this.OpenDB();
|
||||
|
||||
const salt = Crypto.RandomBytes(32, "TYTD2025");
|
||||
const hash = Crypto.PBKDF2(password, salt, UserFlags.ITTR,64,384);
|
||||
const resp = Sqlite.Exec(db, $"INSERT INTO users (username, password_salt, password_hash, flags) VALUES ({Sqlite.Escape(username)}, {Sqlite.Escape(Crypto.Base64Encode(salt))}, {Sqlite.Escape(Crypto.Base64Encode(hash))}, {flags});");
|
||||
|
||||
Sqlite.Close(db);
|
||||
this.Mutex.Unlock();
|
||||
|
||||
if(TypeIsString(resp)) return resp;
|
||||
}
|
||||
else {
|
||||
return "You are not authorized to create user accounts";
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public IsLoggedIn(ctx)
|
||||
{
|
||||
this.Mutex.Lock();
|
||||
const db = this.OpenDB();
|
||||
const res=Sqlite.Exec(db, "SELECT COUNT(*) FROM users;");
|
||||
var noAccounts=true;
|
||||
if(TypeOf(res) == "List" && res.Length != 0)
|
||||
{
|
||||
if(res[0].["COUNT(*)"] != "0")
|
||||
noAccounts=false;
|
||||
}
|
||||
if(noAccounts) {
|
||||
Sqlite.Close(db);
|
||||
this.Mutex.Unlock();
|
||||
return 0xFFFFFFFE;
|
||||
}
|
||||
const sessionToken = this.GetSessionToken(ctx);
|
||||
if(TypeIsString(sessionToken))
|
||||
{
|
||||
const res = Sqlite.Exec(db, $"SELECT * FROM sessions s INNER JOIN users u ON s.accountId = u.id WHERE key = {Sqlite.Escape(sessionToken)};");
|
||||
|
||||
if(TypeIsList(res))
|
||||
each(var item : res)
|
||||
{
|
||||
Sqlite.Close(db);
|
||||
this.Mutex.Unlock();
|
||||
return ParseLong(item.flags) | 1;
|
||||
}
|
||||
}
|
||||
|
||||
Sqlite.Close(db);
|
||||
this.Mutex.Unlock();
|
||||
return 0;
|
||||
}
|
||||
|
||||
public WhoAmI(ctx)
|
||||
{
|
||||
his.Mutex.Lock();
|
||||
const db = this.OpenDB();
|
||||
const res=Sqlite.Exec(db, "SELECT COUNT(*) FROM users;");
|
||||
var noAccounts=true;
|
||||
if(TypeOf(res) == "List" && res.Length != 0)
|
||||
{
|
||||
if(res[0].["COUNT(*)"] != "0")
|
||||
noAccounts=false;
|
||||
}
|
||||
if(noAccounts) {
|
||||
Sqlite.Close(db);
|
||||
this.Mutex.Unlock();
|
||||
return { flags = 0xFFFFFFFE, username = "N/A" };
|
||||
}
|
||||
const sessionToken = this.GetSessionToken(ctx);
|
||||
if(TypeIsString(sessionToken))
|
||||
{
|
||||
const res = Sqlite.Exec(db, $"SELECT * FROM sessions s INNER JOIN users u ON s.accountId = u.id WHERE key = {Sqlite.Escape(sessionToken)};");
|
||||
|
||||
if(TypeIsList(res))
|
||||
each(var item : res)
|
||||
{
|
||||
Sqlite.Close(db);
|
||||
this.Mutex.Unlock();
|
||||
item.flags = ParseLong(item.flags);
|
||||
return item;
|
||||
}
|
||||
}
|
||||
|
||||
Sqlite.Close(db);
|
||||
this.Mutex.Unlock();
|
||||
return { flags = 0, username = "N/A" };
|
||||
}
|
||||
public Passwd(ctx, oldPassword, newPassword, logout)
|
||||
{
|
||||
const whoami = this.WhoAmI(ctx);
|
||||
if(TypeIsDictionary(user) && TypeIsString(item.password_salt))
|
||||
{
|
||||
var salt = Crypto.Base64Decode(item.password_salt);
|
||||
var hash = Crypto.PBKDF2(password, salt, UserFlags.ITTR,64,384);
|
||||
var hashStr = Crypto.Base64Encode(hash);
|
||||
|
||||
if(item.password_hash == hashStr)
|
||||
{
|
||||
this.Mutex.Lock();
|
||||
const db = this.OpenDB();
|
||||
const res = Sqlite.Exec(db, $"UPDATE users SET password_hash = {Sqlite.Escape(Crypto.Base64Encode(hash))}, password_salt = {Sqlite.Escape(Crypto.Base64Encode(salt))} WHERE username = {Sqlite.Escape(item.username)};");
|
||||
if(TypeIsList(res))
|
||||
{
|
||||
if(logout)
|
||||
{
|
||||
Sqlite.Exec(db, $"DELETE FROM sessions WHERE accountId = {Sqlite.Escape(res.accountId)};");
|
||||
}
|
||||
Sqlite.Close(db);
|
||||
|
||||
this.Mutex.Unlock();
|
||||
return {success=true};
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
Sqlite.Close(db);
|
||||
this.Mutex.Unlock();
|
||||
return {success=false, reason = res};
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
public Login(username, password)
|
||||
{
|
||||
this.Mutex.Lock();
|
||||
const db = this.OpenDB();
|
||||
const user = Sqlite.Exec(db, $"SELECT * FROM users WHERE username = {Sqlite.Escape(username)};");
|
||||
if(TypeIsList(user))
|
||||
{
|
||||
each(var item : user)
|
||||
{
|
||||
this.Mutex.Unlock();
|
||||
|
||||
var salt = Crypto.Base64Decode(item.password_salt);
|
||||
var hash = Crypto.PBKDF2(password, salt, UserFlags.ITTR,64,384);
|
||||
var hashStr = Crypto.Base64Encode(hash);
|
||||
|
||||
if(item.password_hash == hashStr)
|
||||
{
|
||||
var rand = Net.Http.UrlEncode(Crypto.Base64Encode(Crypto.RandomBytes(32, "TYTD2025")));
|
||||
this.Mutex.Lock();
|
||||
const dbCon = this.OpenDB();
|
||||
Sqlite.Exec(dbCon, $"INSERT INTO sessions (accountId,key) VALUES ({item.id},{Sqlite.Escape(rand)});");
|
||||
Sqlite.Close(dbCon);
|
||||
this.Mutex.Unlock();
|
||||
return rand;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
this.Mutex.Unlock();
|
||||
return null;
|
||||
}
|
||||
/^
|
||||
Send the database as http response
|
||||
^/
|
||||
public SendDatabase(ctx)
|
||||
{
|
||||
if(UserFlags.CanDownloadDB(this.IsLoggedIn(ctx)))
|
||||
{
|
||||
this.Mutex.Lock();
|
||||
try {
|
||||
var strm = FS.Local.OpenFile(this.DatabaseDirectory/"tytd.db","rb");
|
||||
@@ -1732,5 +1987,11 @@ class TYTD.Downloader {
|
||||
Console.WriteLine($"ERROR: {ex}");
|
||||
}
|
||||
this.Mutex.Unlock();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user