checkpoint

This commit is contained in:
peter
2019-11-13 15:07:47 +01:00
parent a7a5a85033
commit 82bb96678c
351 changed files with 496 additions and 63 deletions
+227
View File
@@ -0,0 +1,227 @@
import { RPCServer } from "rpclibrary";
import { TableDefiniton, AnyRPCExporter, User, RPCPermission, _Rank, Token, Auth, FrontcraftFeatureIfc, Rank, LoginManagerFeatureIfc } from "../../Types/Types";
import { FrontworkAdmin } from "../../Admin/Admin";
import { PrivilegedRPCExporter } from "../../Types/PrivilegedRPCExporter";
import { FrontworkComponent } from "../../Types/FrontworkComponent";
import { getSpecTableData } from "../../Types/PlayerSpecs"
const uuid = require('uuid/v4')
export type LoginManagerIfc = {
Authenticator: {
login: (username:string, pwHash:string) => Promise<Token>
}
}
export class LoginManager
implements FrontworkComponent<LoginManagerFeatureIfc>{
name = "Authenticator" as "Authenticator";
admin:FrontworkAdmin
constructor(private exporters: PrivilegedRPCExporter[]){}
exportRPCs() {
return [
{
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() {
return [{
name: 'createUser' as 'createUser',
exportRPCs: () => [{
name: 'createUser' as 'createUser',
call: async (user:User) => {
return 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[] {
return [
{
name: 'users',
tableBuilder: (table) => {
table.increments("id").primary()
table.string("name").notNullable().unique()
table.string("pwhash").notNullable()
table.string("rank").notNullable()
table.integer("specid").notNullable()
table.foreign('specid').references('specs.id')
table.string("email").nullable().unique()
}
},{
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.increments('id').primary()
table.string('class')
table.string('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))
console.log(getSpecTableData())
await this.admin.knex('specs').insert(getSpecTableData()).catch(console.log)
}
async setPermission(permission: RPCPermission){
await this.admin.knex('rpcpermissions')
.where('rpcname', '=', permission.name)
.update(permission)
}
getPermissions = async () : Promise<RPCPermission[]> => {
return await this.admin.knex.select('*').from('rpcpermissions')
}
getPermission = async (feature: keyof FrontcraftFeatureIfc, rank:Rank) : Promise<boolean> => {
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<AnyRPCExporter[]> => {
return [
...this.exportRPCFeatures(),
...this.exporters.flatMap((exp) => exp.exportRPCFeatures())
].filter(async (feature) => await this.getPermission(<keyof FrontcraftFeatureIfc> feature.name, user.rank))
}
createUser = async(user:User): Promise<User> => {
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<Token> => {
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<Auth> => {
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<Token> => {
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]
}
}
}