add password reset
This commit is contained in:
parent
41c0c39620
commit
14401b2784
26
app.ts
26
app.ts
@ -1,8 +1,20 @@
|
||||
import { app, BrowserWindow, session, dialog } from "electron";
|
||||
import { get_setting } from "./src/SettingConfig";
|
||||
import { create_server, start_server } from "./src/server";
|
||||
import { create_server } from "./src/server";
|
||||
import { getAdminAccessTokenValue,getAdminRefreshTokenValue, accessTokenName, refreshTokenName } from "./src/login";
|
||||
|
||||
import { join } from "path";
|
||||
import { ipcMain } from 'electron';
|
||||
import { UserAccessor } from "./src/model/mod";
|
||||
function registerChannel(cntr: UserAccessor){
|
||||
ipcMain.handle('reset_password', async(event,username:string,password:string)=>{
|
||||
const user = await cntr.findUser(username);
|
||||
if(user === undefined){
|
||||
return false;
|
||||
}
|
||||
user.reset_password(password);
|
||||
return true;
|
||||
});
|
||||
}
|
||||
const setting = get_setting();
|
||||
if (!setting.cli) {
|
||||
let wnd: BrowserWindow | null = null;
|
||||
@ -13,9 +25,14 @@ if (!setting.cli) {
|
||||
height: 600,
|
||||
center: true,
|
||||
useContentSize: true,
|
||||
webPreferences:{
|
||||
preload:join(__dirname,'preload.js'),
|
||||
contextIsolation:true,
|
||||
}
|
||||
});
|
||||
await wnd.loadURL(`data:text/html;base64,`+Buffer.from(loading_html).toString('base64'));
|
||||
//await wnd.loadURL('../loading.html');
|
||||
//set admin cookies.
|
||||
await session.defaultSession.cookies.set({
|
||||
url:`http://localhost:${setting.port}`,
|
||||
name:accessTokenName,
|
||||
@ -34,7 +51,8 @@ if (!setting.cli) {
|
||||
});
|
||||
try{
|
||||
const server = await create_server();
|
||||
start_server(server);
|
||||
server.start_server();
|
||||
registerChannel(server.userController);
|
||||
await wnd.loadURL(`http://localhost:${setting.port}`);
|
||||
}
|
||||
catch(e){
|
||||
@ -85,7 +103,7 @@ if (!setting.cli) {
|
||||
} else {
|
||||
(async () => {
|
||||
const server = await create_server();
|
||||
start_server(server);
|
||||
server.start_server();
|
||||
})();
|
||||
}
|
||||
const loading_html = `<!DOCTYPE html>
|
||||
|
@ -1 +1,7 @@
|
||||
import {} from 'electron';
|
||||
import {ipcRenderer, contextBridge} from 'electron';
|
||||
|
||||
contextBridge.exposeInMainWorld('electron',{
|
||||
passwordReset:async (username:string,toPw:string)=>{
|
||||
return await ipcRenderer.invoke('reset_password',username,toPw);
|
||||
}
|
||||
});
|
@ -1,11 +1,24 @@
|
||||
import React, { useContext, useEffect, useState } from 'react';
|
||||
import { CommonMenuList, Headline } from "../component/mod";
|
||||
import { UserContext } from "../state";
|
||||
import { Grid, Paper, Typography } from "@material-ui/core";
|
||||
import { Box, Grid, Paper, Typography,Button, makeStyles, Theme } from "@material-ui/core";
|
||||
|
||||
const useStyles = makeStyles((theme:Theme)=>({
|
||||
paper:{
|
||||
padding: theme.spacing(2),
|
||||
},
|
||||
commitable:{
|
||||
display:'grid',
|
||||
gridTemplateColumns: `100px auto`,
|
||||
},
|
||||
contentTitle:{
|
||||
marginLeft: theme.spacing(2)
|
||||
}
|
||||
}));
|
||||
|
||||
export function DifferencePage(){
|
||||
const ctx = useContext(UserContext);
|
||||
const classes = useStyles();
|
||||
const [diffList,setDiffList] = useState<
|
||||
{type:string,value:{path:string,type:string}[]}[]
|
||||
>([]);
|
||||
@ -44,9 +57,15 @@ export function DifferencePage(){
|
||||
const menu = CommonMenuList();
|
||||
(ctx.username == "admin")
|
||||
return (<Headline menu={menu}>
|
||||
{(ctx.username == "admin") ? (diffList.map(x=><Paper key={x.type}>
|
||||
<Typography variant='h3'>{x.type}</Typography>
|
||||
{x.value.map(y=>(<Typography key={y.path} variant='h5' onClick={()=>Commit(y)}>{y.path}</Typography>))}
|
||||
{(ctx.username == "admin") ? (diffList.map(x=><Paper key={x.type} className={classes.paper}>
|
||||
<Typography variant='h3' className={classes.contentTitle}>{x.type}</Typography>
|
||||
<Box className={classes.commitable}>
|
||||
{x.value.map(y=>(
|
||||
<>
|
||||
<Button key={`button_${y.path}`} onClick={()=>Commit(y)}>Commit</Button>
|
||||
<Typography key={`typography_${y.path}`} variant='h5'>{y.path}</Typography>
|
||||
</>))}
|
||||
</Box>
|
||||
</Paper>)):(<Typography variant='h2'>Not Allowed : please login as an admin</Typography>)
|
||||
}
|
||||
|
||||
|
@ -9,7 +9,7 @@ export const LoginPage = ()=>{
|
||||
const theme = useTheme();
|
||||
const [userLoginInfo,setUserLoginInfo]= useState({username:"",password:""});
|
||||
const [openDialog,setOpenDialog] = useState({open:false,message:""});
|
||||
const {username,setUsername,permission,setPermission} = useContext(UserContext);
|
||||
const {setUsername,setPermission} = useContext(UserContext);
|
||||
const history = useHistory();
|
||||
const handleDialogClose = ()=>{
|
||||
setOpenDialog({...openDialog,open:false});
|
||||
|
@ -1,18 +1,98 @@
|
||||
import { CommonMenuList, Headline } from "../component/mod";
|
||||
import React, { useContext } from 'react';
|
||||
import React, { useContext, useState } from 'react';
|
||||
import { UserContext } from "../state";
|
||||
import { Grid, Paper, Typography } from "@material-ui/core";
|
||||
import { Chip, Grid, makeStyles, Paper, Theme, Typography, Divider, Button,
|
||||
Dialog, DialogTitle, DialogContentText, DialogContent, TextField, DialogActions } from "@material-ui/core";
|
||||
|
||||
const useStyles = makeStyles((theme:Theme)=>({
|
||||
paper:{
|
||||
alignSelf:"center",
|
||||
padding:theme.spacing(2),
|
||||
},
|
||||
formfield:{
|
||||
display:'flex',
|
||||
flexFlow:'column',
|
||||
}
|
||||
}));
|
||||
|
||||
export function ProfilePage(){
|
||||
const userctx = useContext(UserContext);
|
||||
const classes = useStyles();
|
||||
const menu = CommonMenuList();
|
||||
const [pw_open,set_pw_open] = useState(false);
|
||||
const [oldpw,setOldpw] = useState("");
|
||||
const [newpw,setNewpw] = useState("");
|
||||
const [newpwch,setNewpwch] = useState("");
|
||||
const [msg_dialog,set_msg_dialog] = useState({opened:false,msg:""});
|
||||
const permission_list =userctx.permission.map(p=>(
|
||||
<Chip key={p} label={p}></Chip>
|
||||
));
|
||||
const isElectronContent = window['electron'] !== undefined;
|
||||
const handle_open = ()=>set_pw_open(true);
|
||||
const handle_close = ()=>{
|
||||
set_pw_open(false);
|
||||
setNewpw("");
|
||||
setNewpwch("");
|
||||
};
|
||||
const handle_ok= ()=>{
|
||||
if(isElectronContent){
|
||||
const elec = window['electron'];
|
||||
if(newpw == newpwch){
|
||||
const success = elec.passwordReset(userctx.username,newpw);
|
||||
if(!success){
|
||||
set_msg_dialog({opened:true,msg:"user not exist."});
|
||||
}
|
||||
}
|
||||
else{
|
||||
set_msg_dialog({opened:true,msg:"password and password check is not equal."});
|
||||
}
|
||||
handle_close();
|
||||
}
|
||||
}
|
||||
return (<Headline menu={menu}>
|
||||
<Paper style={{alignSelf : 'center'}}>
|
||||
<Grid container>
|
||||
<Paper className={classes.paper}>
|
||||
<Grid container direction="column" alignItems="center">
|
||||
<Grid item>
|
||||
<Typography>{userctx.username}</Typography>
|
||||
<Typography variant='h4'>{userctx.username}</Typography>
|
||||
</Grid>
|
||||
<Divider></Divider>
|
||||
<Grid item>
|
||||
Permission
|
||||
</Grid>
|
||||
<Grid item>
|
||||
{permission_list.length == 0 ? "-" : permission_list}
|
||||
</Grid>
|
||||
<Grid item>
|
||||
<Button onClick={handle_open}>Password Reset</Button>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Paper>
|
||||
<Dialog open={pw_open} onClose={handle_close}>
|
||||
<DialogTitle>Password Reset</DialogTitle>
|
||||
<DialogContent>
|
||||
<Typography>type the old and new password</Typography>
|
||||
<div className={classes.formfield}>
|
||||
{(!isElectronContent) && (<TextField autoFocus margin='dense' type="password" label="old password"
|
||||
value={oldpw} onChange={(e)=>setOldpw(e.target.value)}></TextField>)}
|
||||
<TextField margin='dense' type="password" label="new password"
|
||||
value={newpw} onChange={e=>setNewpw(e.target.value)}></TextField>
|
||||
<TextField margin='dense' type="password" label="new password check"
|
||||
value={newpwch} onChange={e=>setNewpwch(e.target.value)}></TextField>
|
||||
</div>
|
||||
</DialogContent>
|
||||
<DialogActions>
|
||||
<Button onClick={handle_close} color="primary">Cancel</Button>
|
||||
<Button onClick={handle_ok} color="primary">Ok</Button>
|
||||
</DialogActions>
|
||||
</Dialog>
|
||||
<Dialog open={msg_dialog.opened} onClose={()=>set_msg_dialog({opened:false,msg:""})}>
|
||||
<DialogTitle>Alert!</DialogTitle>
|
||||
<DialogContent>
|
||||
<DialogContentText>{msg_dialog.msg}</DialogContentText>
|
||||
</DialogContent>
|
||||
<DialogActions>
|
||||
<Button onClick={()=>set_msg_dialog({opened:false,msg:""})} color="primary">Close</Button>
|
||||
</DialogActions>
|
||||
</Dialog>
|
||||
</Headline>)
|
||||
}
|
52
src/login.ts
52
src/login.ts
@ -87,9 +87,8 @@ const setToken = (
|
||||
expires: new Date(Date.now() + expiredtime),
|
||||
});
|
||||
};
|
||||
export const createLoginMiddleware = (knex: Knex) => {
|
||||
const userController = createKnexUserController(knex);
|
||||
return async (ctx: Koa.Context, next: Koa.Next) => {
|
||||
export const createLoginMiddleware = (userController: UserAccessor) =>
|
||||
async (ctx: Koa.Context, next: Koa.Next) => {
|
||||
const setting = get_setting();
|
||||
const secretKey = setting.jwt_secretkey;
|
||||
const body = ctx.request.body;
|
||||
@ -143,16 +142,15 @@ export const createLoginMiddleware = (knex: Knex) => {
|
||||
console.log(`${username} logined`);
|
||||
return;
|
||||
};
|
||||
};
|
||||
|
||||
export const LogoutMiddleware = (ctx: Koa.Context, next: Koa.Next) => {
|
||||
ctx.cookies.set(accessTokenName, null);
|
||||
ctx.cookies.set(refreshTokenName, null);
|
||||
ctx.body = { ok: true };
|
||||
return;
|
||||
};
|
||||
export const createUserMiddleWare = (knex: Knex) =>
|
||||
export const createUserMiddleWare = (userController: UserAccessor) =>
|
||||
async (ctx: Koa.ParameterizedContext<UserState>, next: Koa.Next) => {
|
||||
const userController = createKnexUserController(knex);
|
||||
const refreshToken = refreshTokenHandler(userController);
|
||||
const setting = get_setting();
|
||||
const setGuest = async () => {
|
||||
@ -163,7 +161,8 @@ async (ctx: Koa.ParameterizedContext<UserState>, next: Koa.Next) => {
|
||||
};
|
||||
return await refreshToken(ctx, setGuest, next);
|
||||
};
|
||||
const refreshTokenHandler = (cntr:UserAccessor) => async (ctx:Koa.Context,fail: Koa.Next,next: Koa.Next)=>{
|
||||
const refreshTokenHandler = (cntr: UserAccessor) =>
|
||||
async (ctx: Koa.Context, fail: Koa.Next, next: Koa.Next) => {
|
||||
const payload = ctx.cookies.get(accessTokenName);
|
||||
const setting = get_setting();
|
||||
const secretKey = setting.jwt_secretkey;
|
||||
@ -215,17 +214,17 @@ const refreshTokenHandler = (cntr:UserAccessor) => async (ctx:Koa.Context,fail:
|
||||
return await checkRefreshAndUpdate();
|
||||
} else throw e;
|
||||
}
|
||||
}
|
||||
export const createRefreshTokenMiddleware = (knex:Knex)=> async (ctx:Koa.Context,next:Koa.Next)=>{
|
||||
const cntr= createKnexUserController(knex);
|
||||
};
|
||||
export const createRefreshTokenMiddleware = (cntr: UserAccessor) =>
|
||||
async (ctx: Koa.Context, next: Koa.Next) => {
|
||||
const handler = refreshTokenHandler(cntr);
|
||||
const fail = async () => {
|
||||
const user = ctx.state.user as PayloadInfo;
|
||||
ctx.body = {
|
||||
refresh: false,
|
||||
...user
|
||||
}
|
||||
ctx.type = 'json';
|
||||
...user,
|
||||
};
|
||||
ctx.type = "json";
|
||||
};
|
||||
const success = async () => {
|
||||
const user = ctx.state.user as PayloadInfo;
|
||||
@ -233,13 +232,32 @@ export const createRefreshTokenMiddleware = (knex:Knex)=> async (ctx:Koa.Context
|
||||
...user,
|
||||
refresh: true,
|
||||
refreshExpired: Math.floor(Date.now() / 1000 + accessExpiredTime),
|
||||
};
|
||||
ctx.type = "json";
|
||||
};
|
||||
await handler(ctx, fail, success);
|
||||
};
|
||||
export const resetPasswordMiddleware = (cntr: UserAccessor) =>
|
||||
async (ctx:Koa.Context, next: Koa.Next) => {
|
||||
const body = ctx.request.body;
|
||||
if(typeof body !== "object" || !('username' in body)||!('oldpassword' in body) || !('newpassword' in body)){
|
||||
return sendError(400,"request body is invalid format");
|
||||
}
|
||||
const username = body['username'];
|
||||
const oldpw = body['oldpassword'];
|
||||
const newpw = body['newpassword'];
|
||||
const user = await cntr.findUser(username);
|
||||
if(user === undefined){
|
||||
return sendError(403,"not authorized");
|
||||
}
|
||||
if(!user.password.check_password(oldpw)){
|
||||
return sendError(403,"not authorized");
|
||||
}
|
||||
user.reset_password(newpw);
|
||||
ctx.body = {ok:true}
|
||||
ctx.type = 'json';
|
||||
}
|
||||
await handler(ctx,fail,success);
|
||||
}
|
||||
export const getAdmin = async (knex: Knex) => {
|
||||
const cntr = createKnexUserController(knex);
|
||||
export const getAdmin = async (cntr: UserAccessor) => {
|
||||
const admin = await cntr.findUser("admin");
|
||||
if (admin === undefined) {
|
||||
throw new Error("initial process failed!"); //???
|
||||
|
111
src/server.ts
111
src/server.ts
@ -1,32 +1,39 @@
|
||||
import Koa from 'koa';
|
||||
import Router from 'koa-router';
|
||||
|
||||
import {get_setting} from './SettingConfig';
|
||||
import {get_setting, SettingConfig} from './SettingConfig';
|
||||
import {connectDB} from './database';
|
||||
import {DiffManager, createDiffRouter} from './diff/mod';
|
||||
|
||||
import { createReadStream, readFileSync } from 'fs';
|
||||
import getContentRouter from './route/contents';
|
||||
import { createKnexDocumentAccessor } from './db/mod';
|
||||
import { createKnexDocumentAccessor, createKnexUserController } from './db/mod';
|
||||
import bodyparser from 'koa-bodyparser';
|
||||
import {error_handler} from './route/error_handler';
|
||||
import {createUserMiddleWare,createLoginMiddleware, isAdminFirst, getAdmin, LogoutMiddleware, createRefreshTokenMiddleware} from './login';
|
||||
|
||||
import {createInterface as createReadlineInterface} from 'readline';
|
||||
import { DocumentAccessor, UserAccessor } from './model/mod';
|
||||
|
||||
|
||||
//let Koa = require("koa");
|
||||
|
||||
export async function create_server(){
|
||||
class ServerApplication{
|
||||
readonly userController: UserAccessor;
|
||||
readonly documentController: DocumentAccessor;
|
||||
readonly diffManger;
|
||||
readonly app: Koa;
|
||||
private index_html:Buffer;
|
||||
constructor(userController: UserAccessor,documentController:DocumentAccessor){
|
||||
this.userController = userController;
|
||||
this.documentController = documentController;
|
||||
this.diffManger = new DiffManager(documentController);
|
||||
this.app = new Koa();
|
||||
this.index_html = readFileSync("index.html");
|
||||
}
|
||||
private async setup(){
|
||||
const setting = get_setting();
|
||||
let db = await connectDB();
|
||||
|
||||
let diffmgr = new DiffManager(createKnexDocumentAccessor(db));
|
||||
let diff_router = createDiffRouter(diffmgr);
|
||||
diffmgr.register("manga","testdata");
|
||||
const app = this.app;
|
||||
|
||||
if(setting.cli){
|
||||
const userAdmin = await getAdmin(db);
|
||||
const userAdmin = await getAdmin(this.userController);
|
||||
if(await isAdminFirst(userAdmin)){
|
||||
const rl = createReadlineInterface({input:process.stdin,output:process.stdout});
|
||||
rl.setPrompt("put admin password : ");
|
||||
@ -37,47 +44,28 @@ export async function create_server(){
|
||||
userAdmin.reset_password(pw);
|
||||
}
|
||||
}
|
||||
let app = new Koa();
|
||||
app.use(bodyparser());
|
||||
app.use(error_handler);
|
||||
app.use(createUserMiddleWare(db));
|
||||
//app.use(ctx=>{ctx.state['setting'] = settings});
|
||||
|
||||
app.use(createUserMiddleWare(this.userController));
|
||||
|
||||
let diff_router = createDiffRouter(this.diffManger);
|
||||
this.diffManger.register("manga","testdata");
|
||||
const index_html = readFileSync("index.html");
|
||||
let router = new Router();
|
||||
|
||||
router.use('/api/diff',diff_router.routes());
|
||||
router.use('/api/diff',diff_router.allowedMethods());
|
||||
|
||||
//let watcher = new Watcher(setting.path[0]);
|
||||
//await watcher.setup([]);
|
||||
const serveindex = (url:string)=>{
|
||||
router.get(url, (ctx)=>{ctx.type = 'html'; ctx.body = index_html;})
|
||||
}
|
||||
serveindex('/');
|
||||
serveindex('/doc/:rest(.*)');
|
||||
serveindex('/search');
|
||||
serveindex('/login');
|
||||
serveindex('/profile');
|
||||
serveindex('/difference');
|
||||
this.serve_index(router);
|
||||
this.serve_static_file(router);
|
||||
|
||||
const static_file_server = (path:string,type:string) => {
|
||||
router.get('/'+path,async (ctx,next)=>{
|
||||
ctx.type = type; ctx.body = createReadStream(path);
|
||||
})}
|
||||
static_file_server('dist/css/style.css','css');
|
||||
static_file_server('dist/js/bundle.js','js');
|
||||
if(setting.mode === "development")
|
||||
static_file_server('dist/js/bundle.js.map','text');
|
||||
|
||||
const content_router = getContentRouter(createKnexDocumentAccessor(db));
|
||||
const content_router = getContentRouter(this.documentController);
|
||||
router.use('/api/doc',content_router.routes());
|
||||
router.use('/api/doc',content_router.allowedMethods());
|
||||
|
||||
router.post('/user/login',createLoginMiddleware(db));
|
||||
router.post('/user/login',createLoginMiddleware(this.userController));
|
||||
router.post('/user/logout',LogoutMiddleware);
|
||||
router.post('/user/refresh',createRefreshTokenMiddleware(db));
|
||||
router.post('/user/refresh',createRefreshTokenMiddleware(this.userController));
|
||||
|
||||
if(setting.mode == "development"){
|
||||
let mm_count = 0;
|
||||
@ -90,12 +78,49 @@ export async function create_server(){
|
||||
});}
|
||||
app.use(router.routes());
|
||||
app.use(router.allowedMethods());
|
||||
return app;
|
||||
}
|
||||
export const start_server = (server: Koa)=>{
|
||||
private serve_index(router:Router){
|
||||
const serveindex = (url:string)=>{
|
||||
router.get(url, (ctx)=>{ctx.type = 'html'; ctx.body = this.index_html;})
|
||||
}
|
||||
serveindex('/');
|
||||
serveindex('/doc/:rest(.*)');
|
||||
serveindex('/search');
|
||||
serveindex('/login');
|
||||
serveindex('/profile');
|
||||
serveindex('/difference');
|
||||
}
|
||||
private serve_static_file(router: Router){
|
||||
const setting = get_setting();
|
||||
const static_file_server = (path:string,type:string) => {
|
||||
router.get('/'+path,async (ctx,next)=>{
|
||||
ctx.type = type; ctx.body = createReadStream(path);
|
||||
})}
|
||||
static_file_server('dist/css/style.css','css');
|
||||
static_file_server('dist/js/bundle.js','js');
|
||||
if(setting.mode === "development")
|
||||
static_file_server('dist/js/bundle.js.map','text');
|
||||
}
|
||||
start_server(){
|
||||
let setting = get_setting();
|
||||
console.log("start server");
|
||||
//todo : support https
|
||||
return server.listen(setting.port,setting.localmode ? "127.0.0.1" : "0.0.0.0");
|
||||
return this.app.listen(setting.port,setting.localmode ? "127.0.0.1" : "0.0.0.0");
|
||||
}
|
||||
export default {create_server, start_server};
|
||||
static async createServer(){
|
||||
const setting = get_setting();
|
||||
let db = await connectDB();
|
||||
|
||||
const app = new ServerApplication(createKnexUserController(db)
|
||||
,createKnexDocumentAccessor(db));
|
||||
await app.setup();
|
||||
return app;
|
||||
}
|
||||
}
|
||||
//let Koa = require("koa");
|
||||
|
||||
export async function create_server(){
|
||||
return await ServerApplication.createServer();
|
||||
}
|
||||
|
||||
export default {create_server};
|
Loading…
Reference in New Issue
Block a user