diff --git a/package-lock.json b/package-lock.json index 2669275..51f3a68 100644 --- a/package-lock.json +++ b/package-lock.json @@ -4961,9 +4961,9 @@ } }, "rpclibrary": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/rpclibrary/-/rpclibrary-1.4.1.tgz", - "integrity": "sha512-Tv9zOBVT8JFMZfTJgmnX9vvUvtALCDtj/cnlYw2SxsFnj1MuH9VgPniSfThUgdwZZknjFuYVfhLaYb9Yl+eRxQ==", + "version": "1.5.2", + "resolved": "https://registry.npmjs.org/rpclibrary/-/rpclibrary-1.5.2.tgz", + "integrity": "sha512-BI08DxIcndBf25Rd1k957cTfxhNO9Mr1Y3YBSEuYG2dzI5x2/+T83N3swTn68esjLlzvbcmqMHncd5sCsVmN8w==", "requires": { "bsock": "^0.1.9", "http": "0.0.0", diff --git a/package.json b/package.json index c03f31b..4bc7ebe 100644 --- a/package.json +++ b/package.json @@ -37,7 +37,7 @@ "node-fetch": "^2.6.0", "path": "^0.12.7", "rimraf": "^3.0.0", - "rpclibrary": "^1.4.1", + "rpclibrary": "^1.5.2", "simple-git": "^1.124.0", "spawn-sync": "^2.0.0", "sqlite3": "^4.1.0", diff --git a/src/backend/Admin/Admin.ts b/src/backend/Admin/Admin.ts index d9b5e84..d15aeef 100644 --- a/src/backend/Admin/Admin.ts +++ b/src/backend/Admin/Admin.ts @@ -18,6 +18,7 @@ export class FrontworkAdmin implements TableDefinitionExporter { knex:Knex config: RPCConfigLoader + rpcServer: RPCServer private express private httpServer @@ -75,7 +76,7 @@ implements TableDefinitionExporter { } private startWebsocket(){ - new RPCServer(20000, [ + this.rpcServer = new RPCServer(20000, [ ...this.components, ]) } diff --git a/src/backend/Components/Eventbus.ts b/src/backend/Components/Eventbus.ts index d5d55d6..6d33315 100644 --- a/src/backend/Components/Eventbus.ts +++ b/src/backend/Components/Eventbus.ts @@ -73,15 +73,13 @@ implements RPCExporter, TableDefinitionExporter { exportRPCs(){ return [{ name: 'getNotificationLog' as 'getNotificationLog', - call: async () => await this.getNotificationLog() + call: this.getNotificationLog },{ name: 'pushNotification' as 'pushNotification', - call: async (notification: Notification) => { return await this.pushNotification(notification) } + call: this.pushNotification },{ name: 'subscribeNotificaitons' as 'subscribeNotifications', - hook: async (callback: Function) => { - return await this.subscribeNotifications(callback) - } + hook: async (callback: Function) => await this.subscribeNotifications(callback) }] } diff --git a/src/backend/Components/Guild/GuildManager.ts b/src/backend/Components/Guild/GuildManager.ts index 14bed6c..46f8e1b 100644 --- a/src/backend/Components/Guild/GuildManager.ts +++ b/src/backend/Components/Guild/GuildManager.ts @@ -33,7 +33,7 @@ implements FrontworkComponent{ exportRPCs = () => [{ name: 'getHeadCount' as 'getHeadCount', - call: async () => await this.headCount() + call: this.headCount },{ name: 'getGuildInfo' as 'getGuildInfo', call: async () => this.guild.getConfig() diff --git a/src/backend/Components/Item/ItemManager.ts b/src/backend/Components/Item/ItemManager.ts index 1318f4f..a9143b4 100644 --- a/src/backend/Components/Item/ItemManager.ts +++ b/src/backend/Components/Item/ItemManager.ts @@ -29,10 +29,10 @@ implements FrontworkComponent, TableDefin exportRPCs(): RPC[]{ return [{ name: 'getItems', - call: async () => await this.getItems() + call: this.getItems },{ name: 'getItem', - call: async (name:string) => await this.getItem(name) + call: this.getItem }] } diff --git a/src/backend/Components/PluginLoader.ts b/src/backend/Components/PluginLoader.ts index 81ee23f..f6c878f 100644 --- a/src/backend/Components/PluginLoader.ts +++ b/src/backend/Components/PluginLoader.ts @@ -146,22 +146,22 @@ implements RPCExporter{ exportRPCs(){ return [{ name: "installPlugin" as "installPlugin", - call: async (name:string, force = false) => {return await this.installPlugin(name, force)}, + call: this.installPlugin, },{ name: "startPlugin" as "startPlugin", - call: async (name:string) => {return await this.startPlugin(name)}, + call: this.startPlugin, },{ name: "updatePlugin" as "updatePlugin", - call: async (name:string) => {return await this.updatePlugin(name)}, + call: this.updatePlugin, },{ name: "setPluginVersion" as "setPluginVersion", - call: async (name:string, tag:string) => {return await this.setPluginVersion(name, tag)}, + call: this.setPluginVersion, },{ name: "getLoadedPluginNames" as "getLoadedPluginNames", - call: async () => {return this.getPlugins().map(p => p.name)}, + call: async () => this.getPlugins().map(p => p.name), },{ name: "selfUpdate" as "selfUpdate", - call: async (force: boolean) => {return await this.selfUpdate(force)}, + call: this.selfUpdate, }] } } \ No newline at end of file diff --git a/src/backend/Components/RPCConfigLoader.ts b/src/backend/Components/RPCConfigLoader.ts index 94cd732..23e959a 100644 --- a/src/backend/Components/RPCConfigLoader.ts +++ b/src/backend/Components/RPCConfigLoader.ts @@ -21,22 +21,22 @@ implements RPCExporter, "Config">{ exportRPCs() { return [{ name: "getConfig" as "getConfig", - call: () => { return this.getConfig() } + call: this.getConfig },{ name: "resetConfig" as "resetConfig", - call: () => { return this.resetConfig() } + call: this.resetConfig },{ name: "setConfig" as "setConfig", - call: (conf:ConfT) => { return this.setConfig(conf) } + call: this.setConfig },{ name: "setConfigKey" as "setConfigKey", - call: (key:string, value:any) => { return this.setConfigKey(key, value) } + call: this.setConfigKey },{ name: "deleteConfigKey" as "deleteConfigKey", - call: (key:string) => { return this.deleteConfigKey(key) } + call: this.deleteConfigKey },{ name: "getConfigKey" as "getConfigKey", - call: (key:string) => { return this.getConfigKey(key) } + call: this.getConfigKey }] } } \ No newline at end of file diff --git a/src/backend/Components/Raid/RaidManager.ts b/src/backend/Components/Raid/RaidManager.ts index 579ec0e..dc3e91d 100644 --- a/src/backend/Components/Raid/RaidManager.ts +++ b/src/backend/Components/Raid/RaidManager.ts @@ -15,25 +15,25 @@ implements FrontworkComponent{ name: 'manageRaid' as 'manageRaid', exportRPCs: () => [{ name: 'createRaid' as 'createRaid', - call: async (raid:Raid) => await this.createRaid(raid) + call: this.createRaid },{ name: 'addSignup' as 'addSignup', - call: async(signup: Signup) => await this.addSignup(signup) + call: this.addSignup },{ name: 'removeSignup' as 'removeSignup', - call: async(signup: Signup) => await this.removeSignup(signup) + call: this.removeSignup }] },{ name: 'signup' as 'signup', exportRPCs: () => [{ name: 'getRaids' as 'getRaids', - call: async () => await this.getRaids() + call: this.getRaids },{ name: 'getSingups' as 'getSingups', - call: async(raid: Raid) => await this.getSignups(raid) + call: this.getSignups },{ name: 'sign' as 'sign', - call: async(user:User, raid:Raid) => await this.sign(user, raid) + call: this.sign }] },] } diff --git a/src/backend/Components/User/LoginManager.ts b/src/backend/Components/User/LoginManager.ts new file mode 100644 index 0000000..8784b19 --- /dev/null +++ b/src/backend/Components/User/LoginManager.ts @@ -0,0 +1,291 @@ +import { RPCServer, Socket } from "rpclibrary"; +import { TableDefiniton, AnyRPCExporter, User, RPCPermission, Token, Auth, Rank, FrontcraftFeatureIfc, _Rank } from "../../Types/Types"; +import { FrontworkAdmin } from "../../Admin/Admin"; +import { PrivilegedRPCExporter } from "../../Types/PrivilegedRPCExporter"; +import { FrontworkComponent } from "../../Types/FrontworkComponent"; +import { getSpecTableData } from "../../Types/PlayerSpecs" +import { LoginManagerIfc, LoginManagerFeatureIfc } from "./RPCInterface"; +const uuid = require('uuid/v4') + +const ONE_WEEK = 604800000 + +type Serverstate = { + server: RPCServer, + port : number, + allowed: string[] +} + +export class LoginManager +implements FrontworkComponent{ + name = "Authenticator" as "Authenticator"; + admin:FrontworkAdmin + + rankServers : {[rank in Rank] : Serverstate} + userLogins : {[username in string] : { + connections: {[port in number]: Socket} + auth: Auth + }} = {} + + constructor( + private exporters: PrivilegedRPCExporter[] + ){} + + exportRPCs = () => [ + { + name: 'login' as 'login', + call: this.login + },{ + name: 'logout' as 'logout', + call: this.logout + },{ + name: 'getAuth' as 'getAuth', + call: this.getAuth + },{ + name: 'checkToken' as 'checkToken', + call: async (tokenValue : string, rank: Rank) => this.checkToken(tokenValue, rank) + } + ] + + onSetAdmin(admin:FrontworkAdmin){ + this.exporters.forEach(e => e['admin'] = admin) + } + + exportRPCFeatures = () => [ + { + name: 'createUser' as 'createUser', + exportRPCs: () => [{ + name: 'createUser' as 'createUser', + call: this.createUser + }] + },{ + name: 'modifyPermissions' as 'modifyPermissions', + exportRPCs: () => [{ + name: 'getPermissions' as 'getPermissions', + call: this.getPermissions + },{ + name: 'setPermission' as 'setPermission', + call: this.setPermission + }] + } + ] + + + getTableDefinitions = (): TableDefiniton[] => [ + { + name: 'users', + tableBuilder: (table) => { + table.increments("id").primary() + table.string("name").notNullable().unique() + table.string("pwhash").notNullable() + table.string("rank").notNullable() + table.string("email").nullable().unique() + } + },{ + name: 'characters', + tableBuilder: (table) => { + table.increments("id").primary() + table.string("name").notNullable().unique() + table.integer("specid").notNullable() + table.foreign("specid").references("specs.id") + table.integer("userid").notNullable() + table.foreign("userid").references("users.id") + } + },{ + name: 'rpcpermissions', + tableBuilder: (table) => { + table.string("name").primary().notNullable() + table.boolean("ADMIN").defaultTo(true).notNullable() + _Rank.forEach(r => table.boolean(r).defaultTo(false).notNullable()) + } + },{ + name: 'specs', + tableBuilder: (table) => { + table.increments("id") + table.string('class') + table.string('name') + } + } + ,...this.exporters.flatMap(exp => exp['getTableDefinitions']?exp['getTableDefinitions']():undefined) + ] + + initialize = async () => { + //set up permissions + await Promise.all( + [this, ...this.exporters].flatMap(exp => exp.exportRPCFeatures().map(async (feature) => { + try{ + await this.admin.knex.insert({ name: feature.name }).into('rpcpermissions') + }catch(e){} + }))) + + //initialize managed exporters + await Promise.all(this.exporters.map(ex => ex['initialize']?ex['initialize']():undefined)) + + //initialize spec table + await this.admin.knex('specs').insert(getSpecTableData()).catch(e => { console.log("skipping spec insertion") }) + + //start rankServers + const rankServers : any = {} + + for(let i = 0; i < _Rank.length; i++){ + const rank:Rank = _Rank[i] + const port = 20001 + i + const rankServer = await this.startRankServer(rank, port) + rankServers[rank] = { + server: rankServer, + port: port, + allowed: [] + } + } + this.rankServers = rankServers + + setInterval(this.checkExpiredSessions, 600_000) + } + + checkExpiredSessions = () => { + Object.values(this.userLogins).map(userLogin => { + const auth = userLogin.auth + if(!this.checkToken(auth.token.value, auth.user.rank)){ + this.logout(auth.user.name, auth.token.value) + } + }) + } + + checkConnection = async (socket: Socket) => { + let data : Auth | false = false + while(!data){ + data = await Promise.race([socket.call('getUserData'), new Promise((res, rej) => { setTimeout(res, 250);})]) + } + this.userLogins[data.user.name].connections[socket.port] = socket + await socket.call('navigate', ["/frontcraft/dashboard"]) + return true + } + + setPermission = async (permission: RPCPermission) => { + await this.admin.knex('rpcpermissions') + .where('rpcname', '=', permission.name) + .update(permission) + } + + getPermissions = async () : Promise => { + return await this.admin.knex.select('*').from('rpcpermissions') + } + + getPermission = async (feature: keyof FrontcraftFeatureIfc, rank:Rank) : Promise => { + const perm : RPCPermission[] = await this.admin.knex + .select(rank) + .from('rpcpermissions') + .where('name', '=', feature) + + if(perm.length === 0) return false + return perm[0][rank] + } + + getRPCForRank = async (rank: Rank): Promise => { + return [ + ...this.exportRPCFeatures(), + ...this.exporters.flatMap((exp) => exp.exportRPCFeatures()) + ].filter(async (feature) => await this.getPermission( feature.name, rank)) + } + + createUser = async(user:User): Promise => { + await this.admin.knex('users') + .insert(user) + + const users = await this.admin.knex + .select("*") + .from('users') + .where(user) + return users[0] + } + + logout = async (username:string, tokenValue : string) : Promise => { + try{ + + await Promise.all (Object.values(this.userLogins[username].connections).map(async (sock) => { + await sock.call('navigate', '/auth/login') + })) + + Object.values(this.rankServers) + .forEach(state => { + state.allowed = state.allowed.filter(allowed => allowed !== tokenValue) + }) + + delete this.userLogins[username] + }catch(e){ + console.log(e) + } + } + + login = async(username:string, pwHash:string) : Promise => { + const res:User[] = await this.admin.knex + .select('*') + .from('users') + .where({ name: username }) + + if(res.length > 0 && pwHash === res[0].pwhash){ + const user:User = res[0] + delete user.pwhash + + //return existing auth + if(this.userLogins[username] != null){ + return this.userLogins[username].auth + } + + const token = this.createToken(user) + const userAuth : Auth = { + token: token, + user: user, + port: this.rankServers[user.rank].port + } + + this.userLogins[user.name] = {connections: {}, auth: userAuth} + this.rankServers[user.rank].allowed.push(token.value) + + return userAuth + } + + throw new Error('login failed') + } + + getAuth = async (tokenValue:string) : Promise => { + const maybeAuth = Object.values(this.userLogins).find(login => login.auth.token.value === tokenValue) + if(maybeAuth) + return maybeAuth.auth + + throw new Error("Bad token") + } + + startRankServer = async (rank : Rank, port: number) : Promise => { + const allowedRPCs = await this.getRPCForRank(rank) + let rpcServer : RPCServer = new RPCServer(port, allowedRPCs, { + closeHandler: (socket) => { + Object.values(this.userLogins) + .forEach(login => delete login.connections[socket.port]) + + }, + connectionHandler: (socket) => { + this.checkConnection(socket).catch(() => {}) //sometimes times out if you go too fast + }, + sesame: (sesame) => this.checkToken(sesame, rank) + }) + return rpcServer + } + + checkToken = (token: string, rank: Rank) : boolean => this.rankServers[rank].allowed.includes(token) + && Object.values(this.userLogins).find(login => login.auth.token.value === token)!.auth.token.created > Date.now() - ONE_WEEK + + createToken = (user:User): Token => { + + if(this.userLogins[user.name]){ + return this.userLogins[user.name].auth.token + } + + const token:Token = { + value: uuid(), + user_id: user.id!, + created: Date.now() + } + + return token + } +} \ No newline at end of file diff --git a/src/backend/Components/User/RPCInterface.ts b/src/backend/Components/User/RPCInterface.ts index 9563a3f..ac381f7 100644 --- a/src/backend/Components/User/RPCInterface.ts +++ b/src/backend/Components/User/RPCInterface.ts @@ -1,13 +1,15 @@ -import { Token, Auth, User, RPCPermission } from "../../Types/Types" +import { Token, Auth, User, RPCPermission, Rank } from "../../Types/Types" -export type UserManagerIfc = { +export type LoginManagerIfc = { Authenticator: { - login: (username:string, pwHash:string) => Promise - authenticate: (token:string | Token) => Promise + login: (username:string, pwHash:string) => Promise + logout: (username: string, tokenValue :string) => Promise + getAuth: (tokenValue: string) => Promise + checkToken: (token: string, rank: Rank) => Promise } } -export type UserManagerFeatureIfc = { +export type LoginManagerFeatureIfc = { createUser: { createUser: (user:User) => Promise } diff --git a/src/backend/Components/User/UserManager.ts b/src/backend/Components/User/UserManager.ts deleted file mode 100644 index 8d5ec03..0000000 --- a/src/backend/Components/User/UserManager.ts +++ /dev/null @@ -1,227 +0,0 @@ -import { RPCServer } from "rpclibrary"; -import { TableDefiniton, AnyRPCExporter, User, RPCPermission, _Rank, Token, Auth, Rank, FrontcraftFeatureIfc } from "../../Types/Types"; -import { FrontworkAdmin } from "../../Admin/Admin"; -import { PrivilegedRPCExporter } from "../../Types/PrivilegedRPCExporter"; -import { FrontworkComponent } from "../../Types/FrontworkComponent"; -import { getSpecTableData } from "../../Types/PlayerSpecs" -import { UserManagerIfc, UserManagerFeatureIfc } from "./RPCInterface"; -const uuid = require('uuid/v4') - -export class LoginManager -implements FrontworkComponent{ - name = "Authenticator" as "Authenticator"; - admin:FrontworkAdmin - - constructor( - private exporters: PrivilegedRPCExporter[] - ){} - - exportRPCs = () => [ - { - name: 'login' as 'login', - call: async (username:string, pwHash:string) => await this.login(username, pwHash) - },{ - name: 'authenticate' as 'authenticate', - call: async (tokenValue:string | Token) => await this.authenticate(tokenValue) - } - ] - - onSetAdmin(admin:FrontworkAdmin){ - this.exporters.forEach(e => e['admin'] = admin) - } - - exportRPCFeatures = () => [ - { - name: 'createUser' as 'createUser', - exportRPCs: () => [{ - name: 'createUser' as 'createUser', - call: async (user:User) => await this.createUser(user) - }] - },{ - name: 'modifyPermissions' as 'modifyPermissions', - exportRPCs: () => [{ - name: 'getPermissions' as 'getPermissions', - call: async () => await this.getPermissions() - },{ - name: 'setPermission' as 'setPermission', - call: async (perm: RPCPermission) => await this.setPermission(perm) - }] - } - ] - - - getTableDefinitions = (): TableDefiniton[] => [ - { - name: 'users', - tableBuilder: (table) => { - table.increments("id").primary() - table.string("name").notNullable().unique() - table.string("pwhash").notNullable() - table.string("rank").notNullable() - table.string("email").nullable().unique() - } - },{ - name: 'characters', - tableBuilder: (table) => { - table.increments("id").primary() - table.string("name").notNullable().unique() - table.integer("specid").notNullable() - table.foreign("specid").references("specs.id") - table.integer("userid").notNullable() - table.foreign("userid").references("users.id") - } - },{ - name: 'rpcpermissions', - tableBuilder: (table) => { - table.string("name").primary().notNullable() - table.boolean("ADMIN").defaultTo(true).notNullable() - _Rank.forEach(r => table.boolean(r).defaultTo(false).notNullable()) - } - },{ - name: 'tokens', - tableBuilder: (table) => { - table.string('value').primary() - table.integer('user_id').notNullable() - table.foreign('user_id').references('users') - table.dateTime('created').defaultTo(this.admin.knex.fn.now()) - } - },{ - name: 'specs', - tableBuilder: (table) => { - table.string('class') - table.string('name') - table.primary(['class', 'name']) - } - } - ,...this.exporters.flatMap(exp => exp['getTableDefinitions']?exp['getTableDefinitions']():undefined) - ] - - - async initialize(){ - await Promise.all( - [this, ...this.exporters].flatMap(exp => exp.exportRPCFeatures().map(async (feature) => { - try{ - await this.admin.knex.insert({ name: feature.name }).into('rpcpermissions') - }catch(e){} - }))) - - await Promise.all(this.exporters.map(ex => ex['initialize']?ex['initialize']():undefined)) - - await this.admin.knex('specs').insert(getSpecTableData()).catch(e => { console.log("skipping spec insertion") }) - } - - async setPermission(permission: RPCPermission){ - await this.admin.knex('rpcpermissions') - .where('rpcname', '=', permission.name) - .update(permission) - } - - getPermissions = async () : Promise => { - return await this.admin.knex.select('*').from('rpcpermissions') - } - - getPermission = async (feature: keyof FrontcraftFeatureIfc, rank:Rank) : Promise => { - const perm : RPCPermission[] = await this.admin.knex - .select(rank) - .from('rpcpermissions') - .where('name', '=', feature) - - if(perm.length === 0) return false - return perm[0][rank] - } - - getRPCForUser = async (user:User): Promise => { - return [ - ...this.exportRPCFeatures(), - ...this.exporters.flatMap((exp) => exp.exportRPCFeatures()) - ].filter(async (feature) => await this.getPermission( feature.name, user.rank)) - } - - createUser = async(user:User): Promise => { - await this.admin.knex('users') - .insert(user) - - const users = await this.admin.knex - .select("*") - .from('users') - .where(user) - return users[0] - } - - login = async(username:string, pwHash:string) : Promise => { - const res:User[] = await this.admin.knex - .select("*") - .from('users') - .where({ name: username }) - - if(res.length > 0 && pwHash === res[0].pwhash){ - return await this.createToken(res[0]) - } - - throw new Error('login failed') - } - - authenticate = async(tokenValue: string | Token) : Promise => { - if(typeof tokenValue !== 'string') tokenValue = tokenValue.value - - const res : User[] = await this.admin.knex - .select('users.id', 'name', 'specid', 'rank', 'email') - .from('tokens') - .join('users', function(){ - this.on('users.id', '=', 'tokens.user_id') - }) - .where({ value: tokenValue}) - - if(res.length === 0) - throw new Error('authentication failed') - - const allowedRPCs = await this.getRPCForUser(res[0]) - const randomPort = 20000 + Math.floor(Math.random() * 10000) - while(true){ - try{ - let commSock = new RPCServer(randomPort, allowedRPCs, { - closeHandler: () => { - console.log(res[0].name, 'disconnected') - commSock.destroy() - }, - connectionHandler: () => { - console.log(res[0].name, 'connected') - cancelTimeout() - }, - sesame: tokenValue - }) - const timeout = setTimeout(() => { - console.log(res[0].name, 'timeout') - commSock.destroy() - }, 10000) - const cancelTimeout = () => { clearTimeout(timeout) } - break; - }catch(e){ - //retry - } - } - - return { - port: randomPort, - user: res[0], - token: { - value: tokenValue, - user_id: res[0].id! - } - } - } - - createToken = async(user:User): Promise => { - const old = await this.admin.knex.select('*').from('tokens').where({user_id: user.id}) - if(old.length === 0){ - const token:Token = { - value: uuid(), - user_id: user.id! - } - await this.admin.knex('tokens').insert(token) - return token - }else{ - return old[0] - } - } -} \ No newline at end of file diff --git a/src/backend/Launcher.ts b/src/backend/Launcher.ts index de752b9..3bee40d 100644 --- a/src/backend/Launcher.ts +++ b/src/backend/Launcher.ts @@ -1,7 +1,7 @@ import { FrontworkAdmin } from './Admin/Admin' import { RaidManager } from "./Components/Raid/RaidManager"; import { ItemManager } from "./Components/Item/ItemManager"; -import { LoginManager } from "./Components/User/UserManager"; +import { LoginManager } from "./Components/User/LoginManager"; import { Debugger } from './Components/Debugger/Debugger'; import { FrontworkComponent } from './Types/FrontworkComponent'; import { GuildManager } from './Components/Guild/GuildManager'; @@ -10,13 +10,13 @@ require('events').EventEmitter.defaultMaxListeners = 0; let raidManager = new RaidManager() let itemManager = new ItemManager() let guildManager = new GuildManager() -let userManager = new LoginManager([ +let loginManager = new LoginManager([ raidManager, itemManager, guildManager ]) -let components:FrontworkComponent[] = [ guildManager, raidManager, itemManager, userManager ] +let components:FrontworkComponent[] = [ guildManager, raidManager, itemManager, loginManager ] let dbg = new Debugger(components) diff --git a/src/backend/Types/Types.ts b/src/backend/Types/Types.ts index 0b084df..56dd5f9 100644 --- a/src/backend/Types/Types.ts +++ b/src/backend/Types/Types.ts @@ -1,8 +1,7 @@ import * as Knex from "knex" import { RPCExporter } from "rpclibrary"; import { RaidManagerFeatureIfc } from "../Components/Raid/RPCInterface"; -import { ItemManagerIfc, ItemManagerFeatureIfc } from "../Components/Item/RPCInterface"; -import { UserManagerIfc, UserManagerFeatureIfc } from "../Components/User/RPCInterface"; +import { LoginManagerIfc, LoginManagerFeatureIfc } from "../Components/User/RPCInterface"; export declare type NotificationSeverity = 'Info' | 'Important' | 'Error'; @@ -19,7 +18,7 @@ export type TableDefiniton = { } export type Rank = "ADMIN" | "Guildmaster" | "Officer" | "Classleader" | "Raider" | "Trial" | "Social" | "Guest" -export const _Rank : Rank[] = ["Guildmaster" , "Officer" , "Classleader" , "Raider" , "Trial" , "Social" , "Guest"] +export const _Rank : Rank[] = ["ADMIN" , "Guildmaster" , "Officer" , "Classleader" , "Raider" , "Trial" , "Social" , "Guest"] export type Class = "Warrior" | "Rogue" | "Hunter" | "Mage" | "Warlock" | "Priest" | "Shaman" | "Paladin" | "Druid" export const _Class : Class[] = ["Warrior" , "Rogue" , "Hunter" , "Mage" , "Warlock" , "Priest" , "Shaman" , "Paladin" , "Druid"] @@ -56,17 +55,17 @@ export type Signup = { export type Token = { value: string user_id: number - created?: number + created: number } export type Auth = {port: number, user: User, token: Token} -export type FrontcraftIfc = UserManagerIfc - & ItemManagerIfc +export type FrontcraftIfc = LoginManagerIfc + //& ItemManagerIfc export type FrontcraftFeatureIfc = RaidManagerFeatureIfc - & UserManagerFeatureIfc - & ItemManagerFeatureIfc + & LoginManagerFeatureIfc + //& ItemManagerFeatureIfc export type Spec = { id?: number, diff --git a/src/frontend/package-lock.json b/src/frontend/package-lock.json index 8daeb97..e8138fd 100644 --- a/src/frontend/package-lock.json +++ b/src/frontend/package-lock.json @@ -14269,9 +14269,9 @@ "integrity": "sha512-ZYzRkETgBrdEGzL5JSKimvjI2CX7ioyZCkX2BpcfyjqI+079W0wHAyj5W4rIZMcDSOHgLZtgz1IdDi/vU77KEQ==" }, "rpclibrary": { - "version": "1.4.2", - "resolved": "https://registry.npmjs.org/rpclibrary/-/rpclibrary-1.4.2.tgz", - "integrity": "sha512-IFigD+65a9MM+1AgEH5nurUzCyMg9hawqtnOA67/PtTGP5GsnWxaFESKl03k8L/PQ8ptZkWcub/63bfxkrwyMw==", + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/rpclibrary/-/rpclibrary-1.5.1.tgz", + "integrity": "sha512-EN6wlifkFmEQrHhtbalS0i90ZBNbLNFcrQ8hTrcJLBuNMFab49R8i5fruoDLDJM88iPcx6eWccI6zLHK7Qkl2A==", "requires": { "bsock": "^0.1.9", "http": "0.0.0", @@ -14279,9 +14279,9 @@ }, "dependencies": { "uuid": { - "version": "3.3.3", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.3.3.tgz", - "integrity": "sha512-pW0No1RGHgzlpHJO1nsVrHKpOEIxkGg1xB+v0ZmdNH5OAeAwzAVrCnI2/6Mtx+Uys6iaylxa+D3g4j63IKKjSQ==" + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", + "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==" } } }, diff --git a/src/frontend/package.json b/src/frontend/package.json index db5badc..ef16d74 100644 --- a/src/frontend/package.json +++ b/src/frontend/package.json @@ -69,7 +69,7 @@ "normalize.css": "6.0.0", "pace-js": "1.0.2", "roboto-fontface": "0.8.0", - "rpclibrary": "^1.4.2", + "rpclibrary": "^1.5.1", "rxjs": "6.5.2", "rxjs-compat": "6.3.0", "socicon": "3.0.5", diff --git a/src/frontend/src/app/@theme/components/footer/footer.component.ts b/src/frontend/src/app/@theme/components/footer/footer.component.ts index aae8b8a..a20785f 100644 --- a/src/frontend/src/app/@theme/components/footer/footer.component.ts +++ b/src/frontend/src/app/@theme/components/footer/footer.component.ts @@ -4,9 +4,9 @@ import { Component } from '@angular/core'; selector: 'ngx-footer', styleUrls: ['./footer.component.scss'], template: ` - Created with by FrontCraft + Created with by nitowa.xyz
- +
`, }) diff --git a/src/frontend/src/app/@theme/components/header/header.component.html b/src/frontend/src/app/@theme/components/header/header.component.html index 9a1f72d..c3d93df 100644 --- a/src/frontend/src/app/@theme/components/header/header.component.html +++ b/src/frontend/src/app/@theme/components/header/header.component.html @@ -10,10 +10,7 @@
- - - - + diff --git a/src/frontend/src/app/@theme/components/header/header.component.ts b/src/frontend/src/app/@theme/components/header/header.component.ts index cd7797b..354d71b 100644 --- a/src/frontend/src/app/@theme/components/header/header.component.ts +++ b/src/frontend/src/app/@theme/components/header/header.component.ts @@ -1,5 +1,5 @@ import { Component, OnDestroy, OnInit } from '@angular/core'; -import { NbMediaBreakpointsService, NbMenuService, NbSidebarService, NbThemeService } from '@nebular/theme'; +import { NbMediaBreakpointsService, NbMenuService, NbSidebarService, NbThemeService, NbContextMenuComponent, NbMenuItem } from '@nebular/theme'; import { LayoutService } from '../../../@core/utils'; import { map, takeUntil } from 'rxjs/operators'; @@ -39,14 +39,14 @@ export class HeaderComponent implements OnInit, OnDestroy { currentTheme = 'dark'; - userMenu = [ { title: 'Profile' }, { title: 'Log out' } ]; + userMenu : NbMenuItem[] = [ { title: 'Profile' }, { title: 'Log out', url: '/auth/logout' } ]; constructor(private sidebarService: NbSidebarService, private menuService: NbMenuService, private themeService: NbThemeService, private layoutService: LayoutService, private breakpointService: NbMediaBreakpointsService, - private loginService: LoginApiService + private loginService: LoginApiService, ) {} ngOnInit() { @@ -92,6 +92,10 @@ export class HeaderComponent implements OnInit, OnDestroy { return false; } + logout() { + this.loginService.logout() + } + navigateHome() { this.menuService.navigateHome(); return false; diff --git a/src/frontend/src/app/@theme/layouts/index.ts b/src/frontend/src/app/@theme/layouts/index.ts index 6d28d15..f7e7175 100644 --- a/src/frontend/src/app/@theme/layouts/index.ts +++ b/src/frontend/src/app/@theme/layouts/index.ts @@ -1,3 +1,4 @@ export * from './one-column/one-column.layout'; export * from './two-columns/two-columns.layout'; export * from './three-columns/three-columns.layout'; +export * from './one-column-no-sidebar/one-column.layout'; diff --git a/src/frontend/src/app/@theme/layouts/one-column-no-sidebar/one-column.layout.scss b/src/frontend/src/app/@theme/layouts/one-column-no-sidebar/one-column.layout.scss new file mode 100644 index 0000000..448cb73 --- /dev/null +++ b/src/frontend/src/app/@theme/layouts/one-column-no-sidebar/one-column.layout.scss @@ -0,0 +1,9 @@ +@import '../../styles/themes'; +@import '~bootstrap/scss/mixins/breakpoints'; +@import '~@nebular/theme/styles/global/breakpoints'; + +@include nb-install-component() { + .menu-sidebar ::ng-deep .scrollable { + padding-top: nb-theme(layout-padding-top); + } +} diff --git a/src/frontend/src/app/@theme/layouts/one-column-no-sidebar/one-column.layout.ts b/src/frontend/src/app/@theme/layouts/one-column-no-sidebar/one-column.layout.ts new file mode 100644 index 0000000..7c817a0 --- /dev/null +++ b/src/frontend/src/app/@theme/layouts/one-column-no-sidebar/one-column.layout.ts @@ -0,0 +1,19 @@ +import { Component } from '@angular/core'; + +@Component({ + selector: 'ngx-one-column-no-sidebar-layout', + styleUrls: ['./one-column.layout.scss'], + template: ` + + + + + + + + + + + `, +}) +export class OneColumnNoSidebarLayoutComponent {} diff --git a/src/frontend/src/app/@theme/theme.module.ts b/src/frontend/src/app/@theme/theme.module.ts index 2a8a700..4aacc66 100644 --- a/src/frontend/src/app/@theme/theme.module.ts +++ b/src/frontend/src/app/@theme/theme.module.ts @@ -33,6 +33,7 @@ import { OneColumnLayoutComponent, ThreeColumnsLayoutComponent, TwoColumnsLayoutComponent, + OneColumnNoSidebarLayoutComponent, } from './layouts'; import { DEFAULT_THEME } from './styles/theme.default'; import { COSMIC_THEME } from './styles/theme.cosmic'; @@ -61,6 +62,7 @@ const COMPONENTS = [ OneColumnLayoutComponent, ThreeColumnsLayoutComponent, TwoColumnsLayoutComponent, + OneColumnNoSidebarLayoutComponent ]; const PIPES = [ CapitalizePipe, diff --git a/src/frontend/src/app/app-routing.module.ts b/src/frontend/src/app/app-routing.module.ts index 94eb529..08349a1 100644 --- a/src/frontend/src/app/app-routing.module.ts +++ b/src/frontend/src/app/app-routing.module.ts @@ -2,11 +2,6 @@ import { ExtraOptions, RouterModule, Routes } from '@angular/router'; import { NgModule } from '@angular/core'; const routes: Routes = [ - { - path: 'pages', - loadChildren: () => import('./demo_pages/pages.module') - .then(m => m.PagesModule), - }, { path: 'auth', loadChildren: () => import('./frontcraft/auth/auth.module') diff --git a/src/frontend/src/app/app.module.ts b/src/frontend/src/app/app.module.ts index 0616603..24633f7 100644 --- a/src/frontend/src/app/app.module.ts +++ b/src/frontend/src/app/app.module.ts @@ -20,6 +20,7 @@ import { NbToastrModule, NbWindowModule, } from '@nebular/theme'; +import { LoginApiService } from './frontcraft/services/login-api'; @NgModule({ declarations: [AppComponent], @@ -28,9 +29,7 @@ import { BrowserAnimationsModule, HttpClientModule, AppRoutingModule, - ThemeModule.forRoot(), - NbSidebarModule.forRoot(), NbMenuModule.forRoot(), NbDatepickerModule.forRoot(), @@ -43,6 +42,9 @@ import { CoreModule.forRoot(), ], bootstrap: [AppComponent], + providers: [ + + ] }) export class AppModule { } diff --git a/src/frontend/src/app/demo_pages/pages-menu.ts b/src/frontend/src/app/demo_pages/pages-menu.ts index 5ffcb34..facc73f 100644 --- a/src/frontend/src/app/demo_pages/pages-menu.ts +++ b/src/frontend/src/app/demo_pages/pages-menu.ts @@ -1,4 +1,3 @@ import { NbMenuItem } from '@nebular/theme'; -export const MENU_ITEMS: NbMenuItem[] = [ -]; +export const MENU_ITEMS: NbMenuItem[] = []; diff --git a/src/frontend/src/app/demo_pages/pages-routing.module.ts b/src/frontend/src/app/demo_pages/pages-routing.module.ts index 96f2168..da70147 100644 --- a/src/frontend/src/app/demo_pages/pages-routing.module.ts +++ b/src/frontend/src/app/demo_pages/pages-routing.module.ts @@ -3,6 +3,7 @@ import { NgModule } from '@angular/core'; import { PagesComponent } from './pages.component'; import { NotFoundComponent } from './miscellaneous/not-found/not-found.component'; +import { AuthComponent } from '../frontcraft/auth/auth-layout.component'; const routes: Routes = [{ path: '', diff --git a/src/frontend/src/app/frontcraft/auth/auth-layout.component.ts b/src/frontend/src/app/frontcraft/auth/auth-layout.component.ts index cf53ea4..a08eb3b 100644 --- a/src/frontend/src/app/frontcraft/auth/auth-layout.component.ts +++ b/src/frontend/src/app/frontcraft/auth/auth-layout.component.ts @@ -1,13 +1,22 @@ -import { Component } from '@angular/core'; +import { Component, OnInit } from '@angular/core'; +import { LoginApiService } from '../services/login-api'; +import { Router } from '@angular/router'; @Component({ selector: 'auth-layout', template: ` - - - + + + `, }) -export class AuthComponent { - + +export class AuthComponent implements OnInit{ + constructor( + private loginSvc : LoginApiService, + private router: Router + ){} + + ngOnInit(){ + } } diff --git a/src/frontend/src/app/frontcraft/auth/auth-routing.module.ts b/src/frontend/src/app/frontcraft/auth/auth-routing.module.ts index 6d83a27..6cdfb1b 100644 --- a/src/frontend/src/app/frontcraft/auth/auth-routing.module.ts +++ b/src/frontend/src/app/frontcraft/auth/auth-routing.module.ts @@ -2,12 +2,22 @@ import { NgModule } from '@angular/core'; import { RouterModule, Routes } from '@angular/router'; import { MyLoginComponent } from './login/login.component'; import { AuthComponent } from './auth-layout.component'; +import { LogoutComponent } from './logout/logout.component'; +import { RegisterComponent } from './register/register.component'; export const routes: Routes = [ { path: '', component: AuthComponent, children: [ + { + path: 'register', + component: RegisterComponent + }, + { + path: 'logout', + component: LogoutComponent, + }, { path: '**', component: MyLoginComponent, diff --git a/src/frontend/src/app/frontcraft/auth/auth.module.ts b/src/frontend/src/app/frontcraft/auth/auth.module.ts index 95c96c0..4fbc7b8 100644 --- a/src/frontend/src/app/frontcraft/auth/auth.module.ts +++ b/src/frontend/src/app/frontcraft/auth/auth.module.ts @@ -8,7 +8,9 @@ import { NbButtonModule, NbCheckboxModule, NbInputModule, - NbMenuModule + NbMenuModule, + NbCardModule, + NbSelectModule } from '@nebular/theme'; import { MyAuthRoutingModule } from './auth-routing.module'; import { MyLoginComponent } from './login/login.component'; @@ -17,6 +19,8 @@ import { ThemeModule } from '../../@theme/theme.module'; import { DashboardModule } from '../../demo_pages/dashboard/dashboard.module'; import { ECommerceModule } from '../../demo_pages/e-commerce/e-commerce.module'; import { MiscellaneousModule } from '../../demo_pages/miscellaneous/miscellaneous.module'; +import { LogoutComponent } from './logout/logout.component'; +import { RegisterComponent } from './register/register.component'; @NgModule({ @@ -34,9 +38,12 @@ import { MiscellaneousModule } from '../../demo_pages/miscellaneous/miscellaneou DashboardModule, ECommerceModule, MiscellaneousModule, - + NbCardModule, + NbSelectModule ], declarations: [ + RegisterComponent, + LogoutComponent, AuthComponent, MyLoginComponent ], diff --git a/src/frontend/src/app/frontcraft/auth/login/login.component.html b/src/frontend/src/app/frontcraft/auth/login/login.component.html index 836c2a1..4bb42b4 100644 --- a/src/frontend/src/app/frontcraft/auth/login/login.component.html +++ b/src/frontend/src/app/frontcraft/auth/login/login.component.html @@ -1,19 +1,4 @@

Login

-

Hello! Log in with your email.

- - -

Oh snap!

-
    -
  • {{ error }}
  • -
-
- - -

Hooray!

-
    -
  • {{ message }}
  • -
-
@@ -21,25 +6,16 @@ - -

- Email is required! -

-

- Email should be the real one! -

-
+ [required]="true"> +
@@ -55,20 +31,9 @@ fieldSize="giant" [status]="password.dirty ? (password.invalid ? 'danger' : 'success') : ''" [required]="true" - [minlength]="6" + [minlength]="1" [maxlength]="15" [attr.aria-invalid]="password.invalid && password.touched ? true : null"> - -

- Password is required! -

-

- Password should contains - from {{ 6 }} - to {{ 15 }} - characters -

-
@@ -88,5 +53,5 @@
- Don't have an account? Register -
\ No newline at end of file + Don't have an account? Register + diff --git a/src/frontend/src/app/frontcraft/auth/login/login.component.ts b/src/frontend/src/app/frontcraft/auth/login/login.component.ts index 16a91ab..cc5efe3 100644 --- a/src/frontend/src/app/frontcraft/auth/login/login.component.ts +++ b/src/frontend/src/app/frontcraft/auth/login/login.component.ts @@ -25,7 +25,14 @@ export class MyLoginComponent implements OnInit{ ){} ngOnInit(){ - console.log(this.router) + this.loginApi.checkLogin().then(loggedin => { + if(loggedin){ + this.router.navigateByUrl("/") + } + }); } + login(){ + this.loginApi.login(this.user.name, this.user.password) + } } \ No newline at end of file diff --git a/src/frontend/src/app/frontcraft/auth/logout/logout.component.html b/src/frontend/src/app/frontcraft/auth/logout/logout.component.html new file mode 100644 index 0000000..7f6410f --- /dev/null +++ b/src/frontend/src/app/frontcraft/auth/logout/logout.component.html @@ -0,0 +1 @@ +You will be redirected shortly. If not click here click \ No newline at end of file diff --git a/src/frontend/src/app/frontcraft/auth/logout/logout.component.ts b/src/frontend/src/app/frontcraft/auth/logout/logout.component.ts new file mode 100644 index 0000000..f1c91e3 --- /dev/null +++ b/src/frontend/src/app/frontcraft/auth/logout/logout.component.ts @@ -0,0 +1,19 @@ +import { Component, OnInit } from '@angular/core'; +import { LoginApiService } from '../../services/login-api'; +import { Router } from '@angular/router'; + +@Component({ + selector: 'mylogout', + templateUrl: './logout.component.html', +}) +export class LogoutComponent implements OnInit{ + + constructor( + private loginApi : LoginApiService + ){} + + ngOnInit(){ + this.loginApi.logout() + } + +} \ No newline at end of file diff --git a/src/frontend/src/app/frontcraft/auth/register/register.component.html b/src/frontend/src/app/frontcraft/auth/register/register.component.html new file mode 100644 index 0000000..4ebe515 --- /dev/null +++ b/src/frontend/src/app/frontcraft/auth/register/register.component.html @@ -0,0 +1,89 @@ +

Account application

+ + + +
+ + +
+ +
+ + +
+ +
+ + +
+ +
+ Remember me +
+ + + + I am already a member +
+ + And my main is   with rank + {{rank}} + + + + Hello my name is   + I am playing a level 60 and I would like to join tranquil because + + + + +
+
+ + + + + +
+ Back to Login +
diff --git a/src/frontend/src/app/frontcraft/auth/register/register.component.ts b/src/frontend/src/app/frontcraft/auth/register/register.component.ts new file mode 100644 index 0000000..8f223e2 --- /dev/null +++ b/src/frontend/src/app/frontcraft/auth/register/register.component.ts @@ -0,0 +1,32 @@ +import { Component, OnInit } from '@angular/core'; +import { LoginApiService } from '../../services/login-api'; +import { Router } from '@angular/router'; +import { _Rank } from '../../../../../../backend/Types/Types' + +@Component({ + selector: 'register', + templateUrl: './register.component.html', +}) +export class RegisterComponent implements OnInit{ + + user: any = {}; + showApplication = false + ranks = _Rank + + constructor( + private router : Router, + private loginApi : LoginApiService + ){} + + ngOnInit(){ + this.loginApi.checkLogin().then(loggedin => { + if(loggedin){ + this.router.navigateByUrl("/") + } + }); + } + + login(){ + this.loginApi.login(this.user.name, this.user.password) + } +} \ No newline at end of file diff --git a/src/frontend/src/app/frontcraft/pages/pages-layout.component.ts b/src/frontend/src/app/frontcraft/pages/pages-layout.component.ts index 7f68e66..48a7e2a 100644 --- a/src/frontend/src/app/frontcraft/pages/pages-layout.component.ts +++ b/src/frontend/src/app/frontcraft/pages/pages-layout.component.ts @@ -1,8 +1,10 @@ -import { Component } from '@angular/core'; +import { Component, AfterContentChecked, OnInit } from '@angular/core'; import { NbMenuItem } from '@nebular/theme'; +import { LoginApiService } from '../services/login-api'; +import { Router } from '@angular/router'; @Component({ - selector: 'auth-layout', + selector: 'page-layout', template: ` @@ -12,12 +14,25 @@ import { NbMenuItem } from '@nebular/theme'; `, }) -export class PagesLayoutComponent { - menu:NbMenuItem[] = [{ - icon: 'cube-outline', - title: 'test' - },{ - icon: 'globe-2-outline', - title: 'test2' - }] +export class PagesLayoutComponent implements OnInit{ + menu:NbMenuItem[] = [{ + icon: 'people-outline', + title: 'People' + },{ + icon: 'clock-outline', + title: 'Raids' + }] + + constructor( + private loginSvc : LoginApiService, + private router : Router + ){} + + ngOnInit() : void { + this.loginSvc.checkLogin().then(loggedin => { + if(!loggedin){ + this.router.navigateByUrl("/auth") + } + }); + } } \ No newline at end of file diff --git a/src/frontend/src/app/frontcraft/pages/pages.module.ts b/src/frontend/src/app/frontcraft/pages/pages.module.ts index eba5ff3..c61e728 100644 --- a/src/frontend/src/app/frontcraft/pages/pages.module.ts +++ b/src/frontend/src/app/frontcraft/pages/pages.module.ts @@ -8,7 +8,8 @@ import { NbButtonModule, NbCheckboxModule, NbInputModule, - NbMenuModule + NbMenuModule, + NbCardModule } from '@nebular/theme'; import { MyAuthRoutingModule } from './pages-routing.module'; import { FrontcraftDashboardComponent } from './dashboard/dashboard.component'; @@ -34,7 +35,8 @@ import { MiscellaneousModule } from '../../demo_pages/miscellaneous/miscellaneou DashboardModule, ECommerceModule, MiscellaneousModule, - + NbCardModule, + ], declarations: [ PagesLayoutComponent, diff --git a/src/frontend/src/app/frontcraft/services/login-api.ts b/src/frontend/src/app/frontcraft/services/login-api.ts index 2bab7f4..185cdaa 100644 --- a/src/frontend/src/app/frontcraft/services/login-api.ts +++ b/src/frontend/src/app/frontcraft/services/login-api.ts @@ -1,8 +1,9 @@ -import { Injectable } from "@angular/core"; +import { Injectable, Injector, NgZone } from "@angular/core"; import {RPCSocket} from 'rpclibrary/js/src/Frontend' import { Token, Auth, User, _Class, FrontcraftFeatureIfc, SomeOf, FrontcraftIfc, } from '../../../../../backend/Types/Types' import { CookieService } from 'ngx-cookie-service'; +import { Router } from '@angular/router'; @Injectable() export class LoginApiService{ @@ -11,93 +12,112 @@ export class LoginApiService{ private privSocket: RPCSocket & SomeOf constructor( - private cookieSvc : CookieService + private injector: Injector, + private cookieSvc : CookieService, + private ngZone : NgZone ){} getUnprivilegedSocket = () : RPCSocket & FrontcraftIfc => this.socket private getPrivilegedSocket = async (auth:Auth) : Promise> => { if(this.privSocket) return this.privSocket - if(auth.user.rank === 'Guest'){ - return await RPCSocket.makeSocket>( - 20001, + if(auth == null) throw new Error("Bad Auth") + + try{ + const sock = new RPCSocket( + auth.port, window.location.hostname ) - } - try{ - const sock = await RPCSocket.makeSocket>( - auth.port, - window.location.hostname, - auth.token.value - ) + + const authSock = await sock.connect>(auth.token.value) + + sock.hook('kick', () => this.logout()) + sock.hook('getUserData', () => auth) + sock.hook('navigate', (where:string) => { + this.ngZone.run(() => { + this.injector.get(Router).navigateByUrl(where); + }) + }) + sock.on('error', (e) => { + console.log('Socket error', e) + this.logout() + }) - //login success this.auth = auth - this.privSocket = sock - this.setCookie(auth.token) - return sock + this.privSocket = authSock + this.cookieSvc.set('token', JSON.stringify(auth)) + return authSock }catch(e){ //login failed - throw new Error('login failed') + throw new Error(e) } } getFeature = async (feature : K) : Promise => { - const sock = await this.getPrivilegedSocket(this.auth) - if(sock[feature]) return sock[feature] + try{ + const sock = await this.getPrivilegedSocket(this.auth) + if(sock[feature]) return sock[feature] + }catch(e){ + return + } } - getCurrentUser = () : User => this.auth - ? this.auth.user - : { - name: 'Guest', - specid: 1, - pwhash: '', - rank: 'Guest' - } + getCurrentUser = () : User | undefined => this.auth?this.auth.user:undefined - authenticate = async (token : Token | string) : Promise> => { - const auth = await this.socket.Authenticator.authenticate(token) - return await this.getPrivilegedSocket(auth) - } - - login = async (username: string, password: string) => { + login = async (username: string, password: string) : Promise> => { const buf = str2arraybuf(password) const pwHash = await crypto.subtle.digest('SHA-256', buf); - const token = await this.socket.Authenticator.login(username, buf2hex(pwHash)) - return await this.authenticate(token) + + const auth = await this.socket.Authenticator.login(username, buf2hex(pwHash)) + + + if(!auth){ + await this.logout() + throw new Error("Login failed") + } + const sock = await this.getPrivilegedSocket(auth) + return sock } - logout = () => { - this.cookieSvc.deleteAll() - this.auth = null + logout = async () => { + this.cookieSvc.set('token', undefined) + if(this.auth) await this.socket.Authenticator.logout(this.auth.user.name, this.auth.token.value) if(this.privSocket) this.privSocket.destroy() this.privSocket = null + this.auth = null + this.ngZone.run(() => { + this.injector.get(Router).navigate(['/auth']); + }) } - private setCookie = (token: string | Token) => { - token = 'string' === typeof token? token : token.value - this.cookieSvc.set('token', token) - } - private getCookie = () : string | Token | undefined => { - return this.cookieSvc.get('token') - } - - initialize = async () : Promise => { + initialize = async () : Promise => { const sock = await RPCSocket.makeSocket(20000, window.location.hostname) this.socket = sock - - const cookie = this.getCookie() - if(cookie) { - try{ - return await this.authenticate(cookie) - }catch(e){ - this.logout() + try{ + const cookie = JSON.parse(this.cookieSvc.get('token')) + if(cookie != null) { + try{ + const auth = await sock.Authenticator.getAuth(cookie.token.value) + if(!auth) return sock + return await this.getPrivilegedSocket(auth) + }catch(e){ + await this.logout() + return + } } + }catch(e){ } + } - return sock + getAuth = () => this.auth + + async checkLogin() : Promise{ + if(!this.auth) return false + const valid = await this.socket.Authenticator.checkToken(this.auth.token.value, this.auth.user.rank) + if(valid) return true + await this.logout() + return false } }