import path from 'path'
import fs from 'fs'
import get from 'lodash-es/get.js'
import each from 'lodash-es/each.js'
import map from 'lodash-es/map.js'
import keys from 'lodash-es/keys.js'
import size from 'lodash-es/size.js'
import join from 'lodash-es/join.js'
import iseobj from 'wsemi/src/iseobj.mjs'
import isestr from 'wsemi/src/isestr.mjs'
import ispint from 'wsemi/src/ispint.mjs'
import isearr from 'wsemi/src/isearr.mjs'
import isErr from 'wsemi/src/isErr.mjs'
import isfun from 'wsemi/src/isfun.mjs'
import ispm from 'wsemi/src/ispm.mjs'
import cint from 'wsemi/src/cint.mjs'
import j2o from 'wsemi/src/j2o.mjs'
import strleft from 'wsemi/src/strleft.mjs'
import strright from 'wsemi/src/strright.mjs'
import strdelright from 'wsemi/src/strdelright.mjs'
import pm2resolve from 'wsemi/src/pm2resolve.mjs'
import fsIsFolder from 'wsemi/src/fsIsFolder.mjs'
import fsIsFile from 'wsemi/src/fsIsFile.mjs'
import replace from 'wsemi/src/replace.mjs'
import strdelleft from 'wsemi/src/strdelleft.mjs'
import haskey from 'wsemi/src/haskey.mjs'
import arrHas from 'wsemi/src/arrHas.mjs'
import ltdtDiffByKey from 'wsemi/src/ltdtDiffByKey.mjs'
import ltdtmapping from 'wsemi/src/ltdtmapping.mjs'
import WServHapiServer from 'w-serv-hapi/src/WServHapiServer.mjs'
import WServOrm from 'w-serv-orm/src/WServOrm.mjs'
import ds from '../src/schema/index.mjs'
import { getUserRules } from '../src/plugins/mShare.mjs'
/**
* 權限伺服器
*
* @class
* @param {Function} WOrm 輸入資料庫ORM函數
* @param {String} url 輸入資料庫連線字串,例如w-orm-lmdb為'./db',或w-orm-mongodb為'mongodb://username:password@$127.0.0.1:27017'
* @param {String} db 輸入資料庫名稱字串
* @param {Function} getUserByToken 輸入處理函數,函數會傳入使用者token,通過此函數處理後並回傳使用者資訊物件,並至少須提供'id'、'email'、'name'、'isAdmin'欄位,且'isAdmin'限輸入'y'或'n',且輸入'y'時會複寫權限系統該使用者之'isAdmin'欄位值
* @param {Function} verifyClientUser 輸入驗證瀏覽使用者身份之處理函數,函數會傳入使用者資訊物件,通過此函數識別後回傳布林值,允許使用者回傳true,反之回傳false
* @param {Function} verifyAppUser 輸入驗證應用程序使用者身份之處理函數,函數會傳入使用者資訊物件,通過此函數識別後回傳布林值,允許使用者回傳true,反之回傳false
* @param {Object} [opt={}] 輸入設定物件,預設{}
* @param {Integer} [opt.serverPort=11006] 輸入伺服器通訊port,預設11006
* @param {Boolean} [opt.useCheckUser=false] 輸入是否檢查使用者資訊布林值,預設false
* @param {Function} [opt.getUserById=null] 輸入當useCheckUser=true時依照使用者ID取得使用者資訊物件函數,預設null
* @param {Boolean} [opt.useExcludeWhenNotAdmin=false] 輸入使用ORM的select方法時是否自動刪除數據內isActive欄位之布林值,預設false
* @param {Object} [opt.webName={}] 輸入站台名稱物件,至少包含語系eng與cht鍵的名稱,預設{}
* @param {Object} [opt.webDescription={}] 輸入站台描述物件,至少包含語系eng與cht鍵的名稱,預設{}
* @param {String} [opt.webLogo=''] 輸入站台logo字串,採base64格式,預設''
* @param {String} [opt.subfolder=''] 輸入站台所在子目錄字串,提供站台位於內網採反向代理進行服務時,故需支援位於子目錄情形,預設''
* @param {String} [opt.urlRedirect=''] 輸入錯誤時自動轉址字串,提供站台例如無法登入或驗證失敗時須自動轉址,預設''
* @param {String} [opt.mappingBy='email'] 輸入外部系統識別使用者token後所提供之資料物件,與權限系統之使用者資料物件,兩者間查找之對應欄位,可選'id'、'email'、'name',預設'email'
* @returns {Object} 回傳物件,其內server為hapi伺服器實體,wsrv為w-converhp的伺服器事件物件,wsds為w-serv-webdata的伺服器事件物件,可監聽error事件
* @example
*
* import WOrm from 'w-orm-lmdb/src/WOrmLmdb.mjs'
* import WWebPerm from './server/WWebPerm.mjs'
* import getSettings from './g.getSettings.mjs'
*
*
* //st
* let st = getSettings()
*
* let url = st.dbUrl
* let db = st.dbName
* let opt = {
*
* useCheckUser: false,
* getUserById: null,
* useExcludeWhenNotAdmin: false,
*
* serverPort: 11006,
* subfolder: '', //mperm
* urlRedirect: 'https://www.google.com/', //本機測試時得先編譯, 再瀏覽: http://localhost:11006/
*
* webName: {
* 'eng': 'Permission Service',
* 'cht': '權限管理系統',
* },
* webDescription: {
* 'eng': 'A web service package for user permissions and management targets.',
* 'cht': 'A web service package for user permissions and management targets.',
* },
* webLogo: 'data:image/svg+xml;base64,...',
*
* }
*
* let getUserByToken = async (token) => {
* // return {} //測試無法登入
* if (token === '{token-for-application}') { //提供外部應用系統作為存取使用者
* return {
* id: 'id-for-application',
* name: 'application',
* email: 'application@example.com',
* isAdmin: 'y',
* }
* }
* if (token === 'sys') { //開發階段w-ui-loginout自動給予browser使用者(且位於localhost)的token為sys
* return {
* id: 'id-for-admin',
* name: 'tester',
* email: 'admin@example.com', //mappingBy為email, 開發階段時會使用email找到所建置之使用者資料
* isAdmin: 'y',
* }
* }
* console.log('invalid token', token)
* console.log('於生產環境時得加入SSO等驗證token機制')
* return {}
* }
*
* let verifyClientUser = (user, from) => {
* console.log('verifyClientUser/user', user)
* // return false //測試無法登入
* console.log('於生產環境時得加入限制瀏覽器使用者身份機制')
* return user.isAdmin === 'y' //測試僅系統管理者使用
* }
*
* let verifyAppUser = (user, from) => {
* console.log('verifyAppUser/user', user)
* // return false //測試無法登入
* console.log('於生產環境時得加入限制應用程式使用者身份機制')
* return user.isAdmin === 'y' //測試僅系統管理者使用
* }
*
* //WWebPerm
* let instWWebPerm = WWebPerm(WOrm, url, db, getUserByToken, verifyClientUser, verifyAppUser, opt)
*
* instWWebPerm.on('error', (err) => {
* console.log(err)
* })
*
*/
function WWebPerm(WOrm, url, db, getUserByToken, verifyClientUser, verifyAppUser, opt = {}) {
let instWServHapiServer = null
//check WOrm
if (!isfun(WOrm)) {
console.log('invalid WOrm', WOrm)
throw new Error('invalid WOrm')
}
//check url
if (!isestr(url)) {
console.log('invalid url', url)
throw new Error('invalid url')
}
//check db
if (!isestr(db)) {
console.log('invalid db', db)
throw new Error('invalid db')
}
//check getUserByToken
if (!isfun(getUserByToken)) {
console.log('invalid getUserByToken', getUserByToken)
throw new Error('invalid getUserByToken')
}
//check verifyClientUser
if (!isfun(verifyClientUser)) {
console.log('invalid verifyClientUser', verifyClientUser)
throw new Error('invalid verifyClientUser')
}
//check verifyAppUser
if (!isfun(verifyAppUser)) {
console.log('invalid verifyAppUser', verifyAppUser)
throw new Error('invalid verifyAppUser')
}
//serverPort
let serverPort = get(opt, 'serverPort')
if (!ispint(serverPort)) {
serverPort = 11006
}
serverPort = cint(serverPort)
//useCheckUser
let useCheckUser = get(opt, 'useCheckUser', false)
//getUserById
let getUserById = get(opt, 'getUserById', null)
//useExcludeWhenNotAdmin
let useExcludeWhenNotAdmin = get(opt, 'useExcludeWhenNotAdmin', false)
//webName
let webName = get(opt, 'webName', {})
//webDescription
let webDescription = get(opt, 'webDescription', {})
//webLogo
let webLogo = get(opt, 'webLogo', '')
//subfolder
let subfolder = get(opt, 'subfolder', '')
if (isestr(subfolder)) {
if (strright(subfolder, 1) === '/') { //右邊不需要給「/」
subfolder = strdelright(subfolder, 1)
}
if (strleft(subfolder, 1) !== '/') { //左邊需要給「/」
subfolder = `/${subfolder}`
}
}
//urlRedirect
let urlRedirect = get(opt, 'urlRedirect', '')
// console.log('urlRedirect', urlRedirect)
//params
let showLanguage = get(opt, 'showLanguage', 'n')
let language = get(opt, 'language', 'eng')
let showModeEditTargets = get(opt, 'showModeEditTargets', 'n')
let showModeEditPemis = get(opt, 'showModeEditPemis', 'n')
let showModeEditGrups = get(opt, 'showModeEditGrups', 'n')
let showModeEditUsers = get(opt, 'showModeEditUsers', 'n')
let modeEditTargets = get(opt, 'modeEditTargets', 'n')
let modeEditPemis = get(opt, 'modeEditPemis', 'n')
let modeEditGrups = get(opt, 'modeEditGrups', 'n')
let modeEditUsers = get(opt, 'modeEditUsers', 'n')
let widthTargetId = get(opt, 'widthTargetId', '')
let widthTargetDescription = get(opt, 'widthTargetDescription', '')
let widthPemisName = get(opt, 'widthPemisName', '')
let widthPemisDescription = get(opt, 'widthPemisDescription', '')
let widthGrupsName = get(opt, 'widthGrupsName', '')
let widthGrupsDescription = get(opt, 'widthGrupsDescription', '')
let widthUsersName = get(opt, 'widthUsersName', '')
let widthUsersEmail = get(opt, 'widthUsersEmail', '')
let widthUsersDescription = get(opt, 'widthUsersDescription', '')
//mappingBy
let mappingBy = get(opt, 'mappingBy', '')
if (mappingBy !== 'id' && mappingBy !== 'email' && mappingBy !== 'name') {
mappingBy = 'id'
}
// console.log('mappingBy', mappingBy)
//kpLangExt
let kpLangExt = get(opt, 'kpLangExt', null)
//WServOrm
let optWServOrm = {
useCheckUser,
getUserById,
useExcludeWhenNotAdmin,
}
let wp = {}
try {
wp = WServOrm(ds, WOrm, url, db, optWServOrm)
}
catch (err) {
console.log(err)
}
let { woItems, procOrm } = wp
//getWebInfor
let getWebInfor = (userId) => {
return {
webName,
webDescription,
webLogo,
// urlRedirect, //登入失敗就需要轉址, 故須通過html模板取代提供, 無法用api提供
showLanguage,
language,
showModeEditTargets,
showModeEditPemis,
showModeEditGrups,
showModeEditUsers,
modeEditTargets,
modeEditPemis,
modeEditGrups,
modeEditUsers,
widthTargetId,
widthTargetDescription,
widthPemisName,
widthPemisDescription,
widthGrupsName,
widthGrupsDescription,
widthUsersName,
widthUsersEmail,
widthUsersDescription,
kpLangExt,
}
}
// //getTargetsList
// let getTargetsList = async (userId) => {
// let rs = await woItems.targets.select()
// return rs
// }
// //getPemisList
// let getPemisList = async (userId) => {
// let rs = await woItems.pemis.select()
// return rs
// }
// //getGrupsList
// let getGrupsList = async (userId) => {
// let rs = await woItems.grups.select()
// return rs
// }
// //getUsersList
// let getUsersList = async (userId) => {
// let rs = await woItems.users.select()
// return rs
// }
//updateTabItems
let updateTabItems = async (keyTable, userId, rows, keyDetect) => {
// console.log('updateTabItems', keyTable, userId, rows.length, keyDetect)
//ltdtmapping
rows = ltdtmapping(rows, ds[keyTable].keys)
// console.log('ltdtmapping rows', rows)
//重給order
rows = map(rows, (r, k) => {
r.order = k + 1
return r
})
//ckKey
let ckKey = (rows, key) => {
let err = null
//check
let kp = {}
each(rows, (v, k) => {
//value
let value = get(v, key, '')
//check
if (!isestr(value)) {
err = `rows[${k}].${key} is not an effective string`
return false //跳出
}
//check
if (haskey(kp, value)) {
err = `rows[${k}].${key}[${value}] is duplicate`
return false //跳出
}
//kp
kp[value] = true
})
return err
}
//偵測未給予或重複
let err = null
if (true) {
if (arrHas(keyTable, ['targets'])) {
err = ckKey(rows, 'id')
if (err !== null) {
return Promise.reject(err)
}
}
if (arrHas(keyTable, ['pemis', 'grups'])) { //users可重複name故不列入
err = ckKey(rows, 'name')
if (err !== null) {
return Promise.reject(err)
}
}
if (arrHas(keyTable, ['users'])) {
err = ckKey(rows, 'email')
if (err !== null) {
return Promise.reject(err)
}
}
}
//ltdtDiffByKey
let ltdtOld = await woItems[keyTable].select()
let ltdtNew = rows
let r = ltdtDiffByKey(ltdtOld, ltdtNew, keyDetect)
// console.log('ltdtDiffByKey r', r)
//del
if (size(r.del) > 0) {
await procOrm(userId, keyTable, 'del', r.del) //須使用procOrm才有辦法自動給予相關欄位
// .catch((err) => {
// console.log('woItems[keyTable].del err', err)
// })
}
//add
if (size(r.add) > 0) {
await procOrm(userId, keyTable, 'insert', r.add) //須使用procOrm才有辦法自動給予相關欄位
// .catch((err) => {
// console.log('woItems[keyTable].insert err', err)
// })
}
//diff
if (size(r.diff) > 0) {
await procOrm(userId, keyTable, 'save', r.diff) //須使用procOrm才有辦法自動給予相關欄位
// .catch((err) => {
// console.log('woItems[keyTable].save err', err)
// })
}
return ltdtNew
}
//updateTargets
let updateTargets = async (userId, rows) => {
rows = await updateTabItems('targets', userId, rows, 'id')
return rows
}
//updatePemis
let updatePemis = async (userId, rows) => {
rows = await updateTabItems('pemis', userId, rows, 'name')
return rows
}
//updateGrups
let updateGrups = async (userId, rows) => {
rows = await updateTabItems('grups', userId, rows, 'name')
return rows
}
//updateUsers
let updateUsers = async (userId, rows) => {
rows = await updateTabItems('users', userId, rows, 'id')
return rows
}
//checkUser
let checkUser = async(user) => {
let id = get(user, 'id', '')
if (!isestr(id)) {
console.log('user', user)
console.log('can not get the userId')
return Promise.reject(`can not get the userId`)
}
let email = get(user, 'email', '')
if (!isestr(email)) {
console.log('user', user)
console.log('can not get the email of user')
return Promise.reject(`can not get the email of user`)
}
let name = get(user, 'name', '')
if (!isestr(name)) {
console.log('user', user)
console.log('can not get userName')
return Promise.reject(`can not get userName`)
}
let isAdmin = get(user, 'isAdmin', '')
if (isAdmin !== 'y' && isAdmin !== 'n') {
console.log('user', user)
console.log('user.isAdmin is not y or n', user.isAdmin)
console.log('can not get the role of user')
return Promise.reject(`can not get the role of user`)
}
return true
}
//getTokenUser
let getTokenUser = async(token) => {
//基於token查找使用者, 會基於外部getUserByToken以及perm內部users進行查找並比對
//userSelf
let userSelf = null
if (isestr(token)) {
userSelf = getUserByToken(token)
if (ispm(userSelf)) {
userSelf = await userSelf
}
}
// console.log('userSelf', userSelf)
//check
if (!iseobj(userSelf)) {
console.log(`token`, token)
console.log(`can not find the user from token`)
return Promise.reject(`can not find the user from token`)
}
//check userSelf
await checkUser(userSelf)
//須反查perm內users, 提供正規化屬性
let vSelf = get(userSelf, mappingBy, '')
// console.log('vSelf', vSelf)
//check
if (!isestr(vSelf)) {
console.log('userSelf', userSelf)
console.log('mappingBy', mappingBy)
console.log('can not get the prop of user by mappingBy')
return Promise.reject(`can not get the prop of user by mappingBy`)
}
//userFind
let userFind = await woItems.users.select({ [mappingBy]: vSelf, isActive: 'y' })
userFind = get(userFind, 0, null)
// console.log('userFind', userFind)
//check
if (!iseobj(userFind)) {
console.log('userSelf', userSelf)
console.log('mappingBy', mappingBy)
console.log('can not get the user from perm')
return Promise.reject(`can not get the user from perm`)
}
//複寫isAdmin
let isAdminSrc = get(userSelf, 'isAdmin', '')
let isAdminSelf = get(userFind, 'isAdmin', '')
if (isAdminSrc !== isAdminSelf) {
userFind.isAdmin = isAdminSelf
}
// console.log('userFind(isAdmin)', userFind)
return userFind
}
//p
let p = {
checkToken: async(token) => {
//getUserByToken
let u = await getUserByToken(token)
// console.log('u', u)
//checkUser
await checkUser(u)
return true
},
}
//getAndVerifyClientUser
let getAndVerifyClientUser = async (token, from = '') => {
//基於token查找client使用者, 會基於外部getUserByToken以及perm內部users進行查找並比對
//getTokenUser
let user = await getTokenUser(token)
//verifyClientUser
let b = verifyClientUser(user, from)
if (ispm(b)) {
b = await b
}
//check
if (!b) {
console.log('user', user)
console.log(`user does not have permission`)
return Promise.reject(`user does not have permission`)
}
return user
}
//getAndVerifyAppUser
let getAndVerifyAppUser = async (token, from = '') => {
//基於token查找app使用者, 會基於外部getUserByToken進行查找, 故只要getUserByToken提供使用者即可, 可支援getUserByToken給予針對應用程式所產生之虛擬使用者
//user
let user = null
if (isestr(token)) {
user = getUserByToken(token)
if (ispm(user)) {
user = await user
}
}
// console.log('user', user)
//check
if (!iseobj(user)) {
console.log(`token`, token)
console.log(`can not find the user from token`)
return Promise.reject(`can not find the user from token`)
}
//verifyAppUser
let b = verifyAppUser(user, from)
if (ispm(b)) {
b = await b
}
//check
if (!b) {
console.log('user', user)
console.log(`user does not have permission`)
return Promise.reject(`user does not have permission`)
}
return user
}
//parsePayload
let parsePayload = async (req) => {
//payload
let payload = get(req, 'payload')
//to obj
if (isestr(payload)) {
payload = j2o(payload)
}
//check
if (!iseobj(payload)) {
console.log('payload', payload)
console.log('invalid payload in req')
return Promise.reject(`invalid payload in req`)
}
//from
let from = get(payload, 'from', '')
//check
if (!isestr(from)) {
console.log('payload', payload)
console.log('invalid from in payload')
return Promise.reject(`invalid from in payload`)
}
//rows
let rows = get(payload, 'rows', [])
//check
if (!isearr(rows)) {
console.log('payload', payload)
console.log(`invalid rows in payload`)
return Promise.reject(`invalid rows in payload`)
}
//inp
let inp = {
from,
rows,
}
return inp
}
//syncAndReplaceTabs
let syncAndReplaceTabs = async (userId, inp, keyTable) => {
//from
let from = get(inp, 'from', '')
// console.log('from', from)
//check
if (!isestr(from)) {
console.log('inp', inp)
console.log(`invalid from`)
return Promise.reject(`invalid from`)
}
//rows
let rows = get(inp, 'rows', [])
// console.log(keyTable, rows)
//check
if (!isearr(rows)) {
console.log('inp', inp)
console.log('keyTable', keyTable)
return Promise.reject(`invalid rows`)
}
//save from
rows = map(rows, (v, k) => {
// v.order = k + 1 //order大部分由外部給予, 不能複寫
v.from = from
return v
})
//ltdtmapping
rows = ltdtmapping(rows, ds[keyTable].keys)
// console.log('ltdtmapping rows', rows)
//delAll from
await woItems[keyTable].delAll({ from })
//insert
// let r = await woItems[keyTable].insert(rows)
let r = await procOrm(userId, keyTable, 'insert', rows) //須使用procOrm才有辦法自動給予相關欄位
// console.log('r', r)
return r
}
//getGenUserByKV
let getGenUserByKV = async(keyUser, valueUser) => {
//userFind
let userFind = await woItems.users.select({ [keyUser]: valueUser, isActive: 'y' })
userFind = get(userFind, 0, null)
//check
if (!iseobj(userFind)) {
return null
}
return userFind
}
//getGenUserByUserId
let getGenUserByUserId = async(userId) => {
//userFind
let userFind = await getGenUserByKV('id', userId)
//check
if (!iseobj(userFind)) {
return null
}
return userFind
}
//getGenUserAndRulesByUserId
let getGenUserAndRulesByUserId = async (userId) => {
//userFind
let userFind = await getGenUserByUserId(userId)
// console.log('userFind', userFind)
//grups
let grups = await woItems.grups.select()
// console.log('grups', grups)
//pemis
let pemis = await woItems.pemis.select()
// console.log('pemis', pemis)
//targets
let targets = await woItems.targets.select()
// console.log('targets', targets)
//getUserRules
let kur = getUserRules(userFind, grups, pemis, targets)
// console.log('getUserRules', kur)
//check
if (!iseobj(kur)) {
console.log('userId', userId)
console.log(`can not get rules of user`)
return Promise.reject(`can not get rules of user`)
}
//grupsNames
let grupsNames = map(get(kur, 'grups', []), 'name') //kur.grups僅會提供isActive='y', 故grupsNames為有效權限群組名稱
grupsNames = join(grupsNames, ';')
return {
user: {
...userFind,
grupsNames,
},
// grups: get(kur, 'grups', []),
rules: get(kur, 'rules', []),
}
}
//pathStaticFiles
let pathStaticFiles = 'dist'
let npmPathStaticFiles = './node_modules/w-web-perm/dist'
if (fsIsFolder(npmPathStaticFiles)) {
pathStaticFiles = npmPathStaticFiles
}
// console.log('pathStaticFiles', pathStaticFiles)
//subfolder
let fnEntryIn = 'index.tmp'
let fnEntryOut = 'index.html'
try {
let fpEntryIn = path.resolve(pathStaticFiles, fnEntryIn)
if (!fsIsFile(fpEntryIn)) {
fpEntryIn = path.resolve(pathStaticFiles, fnEntryOut) //本機開發另使用html替代tmp
}
if (!fsIsFile(fpEntryIn)) {
console.log('fpEntryIn', fpEntryIn)
throw new Error(`invalid fpEntryIn`)
}
let fpEntryOut = path.resolve(pathStaticFiles, fnEntryOut)
let c = fs.readFileSync(fpEntryIn, 'utf8')
c = replace(c, '/mperm/', '{sfd}/') //方法同genEntry
c = replace(c, '{sfd}', subfolder)
c = replace(c, '{urlRedirect}', urlRedirect)
c = replace(c, '{language}', language)
fs.writeFileSync(fpEntryOut, c, 'utf8')
}
catch (err) {
console.log(err)
console.log(`can not generate ${fnEntryOut}`)
}
//apis
let apis = [
{
method: 'GET',
path: '/api/getUserByToken', //未登入主頁時需先檢測token, getUserByToken為w-ui-loginout預設值, 若要更改兩邊須同時修改
handler: async function (req, res) {
// console.log('getUserByToken', req)
async function core() {
//提供對象為browser使用者, 須先檢測token是否有效, 確認後回傳該使用者之資訊物件
//token
let token = get(req, 'query.token', '')
// console.log('token', token)
//check
if (!isestr(token)) {
console.log('req.query', get(req, 'query'))
console.log('token', token)
console.log('[API]getUserByToken/check token: invalid token')
console.log(`token does not have permission`)
return Promise.reject(`token does not have permission`)
}
//getAndVerifyClientUser
let user = await getAndVerifyClientUser(token, 'getUserByToken')
// console.log('user', user)
//check
if (!iseobj(user)) {
console.log('token', token)
console.log('[API]getUserByToken/check user: invalid user')
console.log(`token does not have permission`)
return Promise.reject(`token does not have permission`)
}
return user
}
//pm2resolve core
let r = await pm2resolve(core)() //w-ui-loginout接收已預設格式用pm2resolve轉過, 須另提取state進行判斷
if (isErr(r.msg)) {
r.msg = r.msg.message
}
// console.log('getUserByToken', r)
return r
},
},
{
method: 'GET',
path: '/api/getPerm',
handler: async function (req, res) {
// console.log('getPerm', req)
async function core() {
//提供對象為node與browser端, 須為client使用者, 須先檢測token是否有效, 再依照token取得使用者資訊物件, 再由其userId查詢回傳該使用者之權限資訊物件
//token
let token = get(req, 'query.token', '')
// console.log('token', token)
//check
if (!isestr(token)) {
console.log('req.query', get(req, 'query'))
console.log('token', token)
console.log('[API]getPerm/check token: invalid token')
console.log(`token does not have permission`)
return Promise.reject(`token does not have permission`)
}
//getAndVerifyClientUser, 發起token的所屬使用者, 驗證僅對client使用者, 不得使用app使用者
let userSelf = await getAndVerifyClientUser(token, 'getPerm')
// console.log('userSelf', userSelf)
//check
if (!iseobj(userSelf)) {
console.log('token', token)
console.log('[API]getPerm/check userSelf: invalid userSelf')
console.log(`token does not have permission`)
return Promise.reject(`token does not have permission`)
}
//userIdSelf, client使用者的userId
let userIdSelf = get(userSelf, 'id', '')
// console.log('userIdSelf', userIdSelf)
//check
if (!isestr(userIdSelf)) {
console.log('token', token)
console.log('userSelf', userSelf)
console.log('[API]getPerm/check userIdSelf: invalid userIdSelf')
console.log(`token does not have permission`)
return Promise.reject(`token does not have permission`)
}
//getGenUserAndRulesByUserId, 查找到的client使用者與權限
let userWithRulesSelf = await getGenUserAndRulesByUserId(userIdSelf)
// console.log('getGenUserAndRulesByUserId', r)
//check
if (!iseobj(userWithRulesSelf)) {
console.log('token', token)
console.log('userSelf', userSelf)
console.log('userIdSelf', userIdSelf)
console.log('[API]getPerm/check userWithRulesSelf: invalid userWithRulesSelf')
console.log(`token does not have permission`)
return Promise.reject(`token does not have permission`)
}
return userWithRulesSelf
}
//pm2resolve core
let r = await pm2resolve(core)()
if (isErr(r.msg)) {
r.msg = r.msg.message
}
// console.log('getPerm r', r)
return r
},
},
{
method: 'GET',
path: '/api/getPermUserInfor',
handler: async function (req, res) {
// console.log('getPermUserInfor', req)
async function core() {
//提供對象為node端, 須為app使用者, 須先檢測token是否有效, 再由其userId查詢回傳該使用者權限資訊物件
//tokenSelf
let tokenSelf = get(req, 'query.token', '')
// console.log('token', token)
//check
if (!isestr(tokenSelf)) {
console.log('req.query', get(req, 'query'))
console.log('[API]getPermUserInfor/check tokenSelf: invalid tokenSelf')
console.log(`token does not have permission`)
return Promise.reject(`token does not have permission`)
}
//getAndVerifyAppUser, 發起token的所屬使用者, 驗證須為app使用者
let userSelf = await getAndVerifyAppUser(tokenSelf, 'getPermUserInfor')
// console.log('userSelf', userSelf)
//check
if (!iseobj(userSelf)) {
console.log('req.query', get(req, 'query'))
console.log('tokenSelf', tokenSelf)
console.log('[API]getPermUserInfor/check userSelf: invalid userSelf')
console.log(`token does not have permission`)
return Promise.reject(`token does not have permission`)
}
//userIdFind, 欲查找使用者的userId
let userIdFind = get(req, 'query.userId', '')
// console.log('userIdFind', userIdFind)
//check
if (!isestr(userIdFind)) {
console.log('req.query', get(req, 'query'))
console.log('tokenSelf', tokenSelf)
console.log('[API]getPermUserInfor/check userIdFind: invalid userIdFind')
console.log(`token does not have permission`)
return Promise.reject(`token does not have permission`)
}
//getGenUserAndRulesByUserId, 查找到的使用者與權限
let userWithRulesFind = await getGenUserAndRulesByUserId(userIdFind)
// console.log('getGenUserAndRulesByUserId', userWithRulesFind)
//check
if (!iseobj(userWithRulesFind)) {
console.log('userIdFind', userIdFind)
console.log('[API]getPermUserInfor/check userWithRulesFind: invalid userWithRulesFind')
console.log(`userId does not have permrules`)
return Promise.reject(`userId does not have permrules`)
}
return userWithRulesFind
}
//pm2resolve core
let r = await pm2resolve(core)() //w-ui-loginout接收已預設格式用pm2resolve轉過, 須另提取state進行判斷
if (isErr(r.msg)) {
r.msg = r.msg.message
}
// console.log('getPermUserInfor', r)
return r
},
},
{
method: 'POST',
path: '/syncAndReplaceTabs',
handler: async function (req, res) {
// console.log('syncAndReplaceTabs', req)
// console.log('syncAndReplaceTabs payload', req.payload)
async function core() {
//提供對象為node使用者, 須為系統管理者, 須先檢測token是否有效, 再進行數據變更
//token
let token = get(req, 'query.token', '')
//check
if (!isestr(token)) {
console.log('req.query', get(req, 'query'))
console.log('token', token)
console.log('[API]syncAndReplaceTabs/check token: invalid token')
console.log(`token does not have permission`)
return Promise.reject(`token does not have permission`)
}
//keyTable
let keyTable = get(req, 'query.keyTable', '')
//check
if (!isestr(keyTable)) {
console.log('req.query', get(req, 'query'))
console.log('keyTable', keyTable)
console.log('[API]syncAndReplaceTabs/check keyTable: invalid keyTable')
console.log(`token does not have permission`)
return Promise.reject(`token does not have permission`)
}
//check keyTable
let allowedTables = ['targets', 'pemis', 'grups', 'users']
if (!allowedTables.includes(keyTable)) {
console.log('[API]syncAndReplaceTabs/check keyTable: unexpected keyTable', keyTable)
return Promise.reject(`invalid keyTable: ${keyTable}`)
}
//getAndVerifyAppUser
let user = await getAndVerifyAppUser(token, 'syncAndReplaceTabs')
//check user
if (!iseobj(user)) {
console.log('token', token)
console.log('[API]syncAndReplaceTabs/check user: invalid user')
console.log(`token does not have permission`)
return Promise.reject(`token does not have permission`)
}
//userId
let userId = get(user, 'id', '')
//check userId
if (!isestr(userId)) {
console.log('token', token)
console.log('user', user)
console.log('[API]syncAndReplaceTabs/check userId: invalid userId')
console.log(`token does not have permission`)
return Promise.reject(`token does not have permission`)
}
//parsePayload
let inp = await parsePayload(req)
//syncAndReplaceTabs
let r = await syncAndReplaceTabs(userId, inp, keyTable)
return r
}
//pm2resolve core
let r = await pm2resolve(core)()
if (isErr(r.msg)) {
r.msg = r.msg.message
}
// console.log('syncAndReplaceTabs', r)
return r
},
},
]
//tableNamesExec, tableNamesSync
let tableNamesExec = keys(ds)
// let tableNamesSync = filter(tableNamesExec, (v) => {
// return strright(v, 5) !== 'Items'//不同步數據
// })
let tableNamesSync = tableNamesExec
//WServHapiServer
instWServHapiServer = new WServHapiServer({
port: opt.serverPort,
pathStaticFiles,
apiName: 'api',
apis,
tokenType: 'Bearer',
verifyConn: async ({ apiType, authorization, query, headers, req }) => {
// console.log('verifyConn', `apiType[${apiType}]`, `authorization[${authorization}]`)
//token
let token = strdelleft(authorization, 7) //刪除Bearer
// console.log('token', token)
//check
if (!isestr(token)) {
console.log('apiType', apiType)
console.log('authorization', authorization)
console.log('invalid token')
return false
}
//checkToken
let b = await p.checkToken(token)
// console.log('b', b)
return b
},
getUserIdByToken: async (token) => { //可使用async或sync函數
// console.log('getUserIdByToken', token)
let user = await getUserByToken(token)
let userId = get(user, 'id', '')
if (!isestr(userId)) {
console.log('token', token)
console.log('userId', userId)
return Promise.reject(`can not find user.id`)
}
return userId
},
corsOrigins: ['*'],
useDbOrm: true,
kpOrm: woItems,
operOrm: procOrm, //procOrm的輸入為: userId, tableName, methodName, input
tableNamesExec,
methodsExec: ['select', 'insert', 'save', 'del', 'delAll'], //mix需於procOrm內註冊以提供
tableNamesSync,
kpFunExt: { //接收參數第1個為userId, 之後才是前端給予參數
getWebInfor,
// getTargetsList,
// getPemisList,
// getGrupsList,
// getUsersList,
updateTargets,
updatePemis,
updateGrups,
updateUsers,
},
fnTableTags: 'tableTags-web-perm.json',
})
return instWServHapiServer
}
export default WWebPerm