Skip to content

Commit

Permalink
添加用户管理、修复漏洞
Browse files Browse the repository at this point in the history
  • Loading branch information
longjiahui committed Feb 18, 2023
1 parent fbe2af0 commit 7d995e2
Show file tree
Hide file tree
Showing 28 changed files with 683 additions and 231 deletions.
5 changes: 5 additions & 0 deletions server/app/controller/role.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
module.exports = app => class extends app.Controller {
async all(ctx){
ctx.body = this.service.ret.success(await ctx.model.Role.find())
}
}
152 changes: 144 additions & 8 deletions server/app/controller/user.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,31 +9,39 @@ module.exports = app=>class extends app.Controller{

async registerAdmin(ctx){
await ctx.v({
password: 'truthystring',
password: 'truthyString',
})
let { password } = ctx.request.body
let u = await ctx.model.User.findOne({ username: adminUserName })
if(u){
throw new app.Error(0, 'Admin account is registered !')
}else{
let adminRole = await ctx.model.Role.create({
username: 'admin',
key: 'admin',
name: '系统管理员',
description: '系统初始管理员,最大权限拥有者。',
isAdmin: true,
})
u = await ctx.model.User.create({
username: adminUserName,
password,
role: adminRole._id,
})
}
let token = app.userJWTSign(u.toObject())
let token = app.userJWTSign(await this.service.ret.user.getFullUserByID(u._id))
this.service.user.setUserTokenAsCookie(token)
ctx.body = this.service.ret.success(token)
}

async login(ctx){
await ctx.v({
username: 'truthystring',
password: 'truthystring',
username: 'truthyString',
password: 'truthyString',
})

let { username, password } = ctx.request.body
let u = await ctx.model.User.findOne({ username, password }, '-password')
let u = await this.service.user.getFullUser({ username, password })
if(!u){
throw new app.Error(0, '用户名或密码错误')
}
Expand All @@ -44,21 +52,149 @@ module.exports = app=>class extends app.Controller{

async myInfo(ctx){
let user = ctx.state.user
ctx.body = this.service.ret.success(await ctx.model.User.findOne({ _id: user._id }))
ctx.body = this.service.ret.success(await this.service.user.getFullUserByID(user._id))
}

async decode(ctx){
await ctx.v({
token: 'truthystring',
token: 'truthyString',
})
let { token } = ctx.request.body
ctx.body = this.service.ret.success(app.userJWTVerify(token))
let user = app.userJWTVerify(token)
if(!user?._id){
throw new app.Error(0, 'token内容不完整')
}
let newestUser = await this.service.ret.user.getFullUserByID(user._id)
if(!newestUser){
throw new app.Error(1, '用户不存在')
}
ctx.body = this.service.ret.success(newestUser)
}

async pageData(ctx){

await ctx.v({
keyword$: 'string$',
})

let { keyword } = ctx.request.body
let condition = {}
if(keyword){
if(!condition.$or){
condition.$or = []
}
let keywordRegExp = new RegExp(`.*${keyword}.*`)
;['name', 'username'].forEach(k=>{
condition.$or.push({[k]: keywordRegExp})
})
}
ctx.body = await this.service.ret.pageData({
model: ctx.model.User,
condition,
queryHandler: q=>q.populate('role').sort({ 'role.isAdmin': 1, createdAt: 1, }),
projection: '-password',
})
}

async save(ctx){
await ctx.v({
_id$: 'objectID',
username$: 'truthyString',
name$: 'string$',
password$: 'truthyString',
role$: 'objectID',
description$: 'truthyString$',
})

let { _id, username, name, password, role, description } = ctx.request.body

// check role exists
let r = await ctx.model.Role.findById(role)
if(!r){
throw new app.Error(0, '角色不存在')
}
let u = await this.service.basic.save({
model: ctx.model.User,
fields: '_id username name password role avatar description',
projection: '-password',
})
u.role = r
ctx.body = this.service.ret.success(u)
}

// 非管理员账号使用
async getChangingPasswordToken(ctx){
await ctx.v({
password: 'truthyString',
})
let me = ctx.state.user
let { password } = ctx.request.body
let myID = me?._id
if(!myID){
throw new app.Error(0, 'token信息不完整')
}
if(!await ctx.model.User.findOne({ _id: myID, password })){
throw new app.Error(1, '密码错误')
}
ctx.body = this.service.ret.success(app.passwordChangingJWTSign({ _id, password }))
}

// 非管理员账号使用
async changePasswordByToken(ctx){
await ctx.v({
token: 'truthyString',
})
let me = ctx.state.user
let myID = me?._id
if(!myID){
throw new app.Error(0, 'token信息不完整')
}
let { token } = ctx.request.body
let { _id, password: toPassword } = app.passwordChangingJWTVerify(token) || {}
if(myID !== _id){
throw new app.Error(0, '不是自己的token')
}
if(!_id || !toPassword){
throw new app.Error(1, 'token内容错误')
}
await ctx.model.User.updateOne({ _id }, { $set: { password: toPassword } })
ctx.body = this.service.ret.success()
}

async changePassword(ctx){
await ctx.v({
_id: 'objectID',
to: 'truthyString',
})
let { _id, to: toPassword } = ctx.request.body
let me = ctx.state.user
me = await this.service.user.getFullUserByID(me._id)
if(!me){
throw new app.Error(0, '我的信息不存在')
}
let user = await this.service.user.getFullUserByID(_id)
if(!user){
throw new app.Error(1, '用户不存在')
}
if(!me.role?.isAdmin){
throw new app.Error(2, '没有权限')
}
await ctx.model.User.updateOne({ _id }, { $set: { password: toPassword }})
ctx.body = this.service.ret.success()
}

async delete(ctx){
await ctx.v({
_id: 'objectID',
})

let { _id } = ctx.request.body

let me = ctx.state.user
if(_id === me?._id){
throw new app.Error(0, '不能删除自己')
}
await ctx.model.User.deleteOne({ _id })
ctx.body = this.service.ret.success()
}
}
21 changes: 0 additions & 21 deletions server/app/lib/validateSchema.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,25 +21,4 @@ module.exports = app=>({
email(val){
return this.v(val, ['string', val=>/^([a-zA-Z0-9]+[_|\_|\.]?)*[a-zA-Z0-9]+@([a-zA-Z0-9]+[_|\_|\.]?)*[a-zA-Z0-9]+\.[a-zA-Z]{2,3}$/.test(val)])
},
password(val){
return this.v(val, ['string', val=>val && val?.length >= 6])
},
username(val){
return this.v(val, ['string', val=>this.v(val?.length, '>=1 && <=32')])
},

//group
groupName(val){
return this.v(val, ['string', val=>this.v(val?.length, '>0 && <=64')])
},
dayLimit(val){
return this.v(val, ['number', '>=0 && <30'])
},

//groupmember
memberRole(val){
const consts = app.config.constValues
let roles = Object.keys(consts).filter(c=>/^GMROLE_/.test(c)).map(k=>consts[k])
return this.v(val, ['number', val=>roles.includes(val)])
}
})
23 changes: 23 additions & 0 deletions server/app/model/role.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
module.exports = app => {
const mongoose = app.mongoose
const Schema = mongoose.Schema

var RoleSchema = new Schema({
name: {
type: String,
unique: true,
},
// 业务后管系统使用的字段,可以不填
key: String,
description: String,
isAdmin: Boolean,
menus: [{
type: Schema.Types.ObjectId,
ref: 'Menu',
}]
}, {
timestamps: true,
})

return mongoose.model('Role', RoleSchema)
}
4 changes: 2 additions & 2 deletions server/app/model/user.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,12 @@ module.exports = app => {
type: String,
unique: true,
},
nickname: String,
name: String,
password: String,
avatar: String,
description: String,

// role: String,
role: { type: Schema.Types.ObjectId, ref: 'Role' },
}, {
timestamps: true,
})
Expand Down
43 changes: 32 additions & 11 deletions server/app/router.js
Original file line number Diff line number Diff line change
@@ -1,23 +1,44 @@
const compose = require('koa-compose')

module.exports = app=>{
const { router, middleware, controller } = app
let jwtQuiet = middleware.jwt('user', true)
let jwt = middleware.jwt('user')
let jwt = compose([middleware.jwt('user'), async (ctx, next)=>{
// 验证用户信息
await next()
}])

let jwtAdminRoleCheck = compose([jwt, async (ctx, next)=>{
let user = ctx.state.user
if(user.role?.isAdmin){
await next()
}else{
throw new app.ServiceError(100, '无权限')
}
}])

router.post('/api/user/registerAdmin', controller.user.registerAdmin)
router.post('/api/user/isAdminRegistered', controller.user.isAdminRegistered)
router.post('/api/user/login', controller.user.login)
router.post('/api/user/decode', controller.user.decode)
router.post('/api/user/myInfo', jwt, controller.user.myInfo)
router.post('/api/user/decode', jwt, controller.user.decode)
router.post('/api/user/pageData', jwt, controller.user.pageData)
router.post('/api/user/getChangingPasswordToken', jwt, controller.user.getChangingPasswordToken)
router.post('/api/user/changePasswordByToken', jwt, controller.user.changePasswordByToken)
router.post('/api/user/pageData', jwtAdminRoleCheck, controller.user.pageData)
router.post('/api/user/save', jwtAdminRoleCheck, controller.user.save)
router.post('/api/user/delete', jwtAdminRoleCheck, controller.user.delete)
router.post('/api/user/changePassword', jwtAdminRoleCheck, controller.user.changePassword)

router.post('/api/menu/pageData', jwt, controller.menu.pageData)
router.post('/api/menu/save', jwt, controller.menu.save)
router.post('/api/menu/delete', jwt, controller.menu.delete)
router.post('/api/menu/resetOrders', jwt, controller.menu.resetOrders)
router.post('/api/menu/save', jwtAdminRoleCheck, controller.menu.save)
router.post('/api/menu/delete', jwtAdminRoleCheck, controller.menu.delete)
router.post('/api/menu/resetOrders', jwtAdminRoleCheck, controller.menu.resetOrders)

router.post('/api/role/all', jwt, controller.role.all)

router.post('/api/admin/save', jwt, controller.admin.save)
router.post('/api/admin/get', jwt, controller.admin.get)
router.post('/api/admin/pageData', jwt, controller.admin.pageData)
router.post('/api/admin/delete', jwt, controller.admin.delete)
router.post('/api/admin/login', controller.user.login)
router.post('/api/admin/save', jwtAdminRoleCheck, controller.admin.save)
router.post('/api/admin/get', jwtAdminRoleCheck, controller.admin.get)
router.post('/api/admin/pageData', jwtAdminRoleCheck, controller.admin.pageData)
router.post('/api/admin/delete', jwtAdminRoleCheck, controller.admin.delete)
// router.post('/api/admin/login', controller.user.login)
}
16 changes: 16 additions & 0 deletions server/app/service/user.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,4 +9,20 @@ module.exports = app => class extends app.Service{
setUserTokenAsCookie(token){
return this.setTokenAsCookie('userToken', token)
}

getFullUsers(query, withPassword = false){
return this.ctx.model.User.find(query, withPassword ? undefined : '-password').populate('role').then(datas=>datas?.map?.(d=>d.toObject()))
}
getFullUserByID(user, ...rest){
return this.getFullUsers({ _id: user }, ...rest).then(users=>users?.[0])
}
getFullUserByIDWithPassword(user){
return this.getFullUserByID(user, true)
}
getFullUser(query, ...rest){
return this.getFullUsers(query, ...rest).then(users=>users?.[0])
}
getFullUserWithPassword(query){
return this.getFullUser(query, true)
}
}
3 changes: 2 additions & 1 deletion server/config/config.default.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@ module.exports = app=>({
keys: 'my-cookie-secret-key',
middleware: ['error', 'log'],
jwtSecret: {
user: 'test-haha'
user: 'test-haha',
passwordChanging: 'test-haha2',
},

static: {
Expand Down
Loading

0 comments on commit 7d995e2

Please sign in to comment.