alafafa
alafafa
  • 发布:2024-04-06 13:47
  • 更新:2024-04-10 15:22
  • 阅读:378

【报Bug】用了一次 dbJQL.setUser({ role: ['admin']})后,以后云对象里面的所有数据库操作都变成了超级管理员权限了

分类:uniCloud

产品分类: uniCloud/App

操作步骤:

复现步骤

预期结果:

复现步骤

实际结果:

实际结果

bug描述:

我在 /uniCloud-aliyun/cloudfunctions/common/uni-config-center/uni-id/hooks/index.js 的 beforeRegister 里用了一次 setUser,代码如下:

const dbJQL = uniCloud.databaseForJQL({clientInfo})  
dbJQL.setUser({   
    role: ['admin'],   
})

在这之后,不管是哪个函数里面的数据库查询都具备超级管理员权限了,不管是用 db = uniCloud.database() 还是用 dbJQL = uniCloud.databaseForJQL({clientInfo}) 都能拥有所有的数据库查询权限,哪怕当前的前端用户是匿名用户,请问 setUser 是用一次就销毁了呢,还是一直有效,有没有把这个临时提权的方法降回去的放呢?这一点官方文档里并没有专门的说明,请问我遇到的这问题是bug吗?感觉像是我打了一次 sudo -s 我就永远是 root 了,我把 HBuilderX重启了好几遍了,依然是这样。

2024-04-06 13:47 负责人:无 分享
已邀请:

最佳回复

DCloud_uniCloud_VK

DCloud_uniCloud_VK

官方文档有说明的,uniCloud.database 是传统mogondb语法,schema 无效,因为mogondb压根没有schema,也没有JQL,JQL是官方在mogondb语法上进行的封装,schema 就是专门给JQL使用的。

至于前端操作数据库时,现在应该写成 uniCloud.databaseForJQL,不应该写成 uniCloud.database,因为前端只能执行JQL语句,不能执行传统mogondb语句(前端操作数据库不安全,必须用schema来限制权限)

那么为什么前端还有一个 uniCloud.database 写法,且也是受到schema限制的呢?

这是历史原因,一开始设计JQL的时候,只考虑给前端使用,当时前端是没有 uniCloud.database API的,因此用了 uniCloud.database 来做前端JQL的声明,但是问题来了,开发者会困扰,同样是 uniCloud.database,为什么云函数里不受schema限制,因此前端又多了一个新的API uniCloud.databaseForJQL

所以前端 uniCloud.database 的效果 = uniCloud.databaseForJQL,而云函数中 uniCloud.database 就是传统的mogondb语法,schema 无效

  • alafafa (作者)

    感谢官方人员的耐心回复,这下我的困惑解决了,以后写前端查库代码的时候一律遵循新规范

    2024-04-10 17:39

alafafa

alafafa (作者)

我把电脑重新启动了一次,也还是这样,难道在一个云对象里使用了一次dbJQL.setUser({ role: ['admin']}),那么以后这个云对象里的多有方法中再查数据库的时候,权限都是admin了吗?

alafafa

alafafa (作者)

我之后又特意把 uni-id-user 表的 permission 设为 "read": false, 结果用 db = uniCloud.database() 查询 uni-id-user 表的任意记录都是畅通无阻的,包括能查到超级管理员的密码

alafafa

alafafa (作者)

运行在本地一直是这样,打印出来的数据显示只要用过一次 dbJQL.setUser({
role: ['admin'],
}) 之后,想查谁的数据就查谁的数据了,尽管schema里已经把所有权限都关闭了,用游客身份或者随意一个新注册的用户的身份都能查到不属于他的信息
运行在云端无法查看到相关日志,因为我这个代码是写在 /uniCloud-aliyun/cloudfunctions/common/uni-config-center/uni-id/custom-token.js 里的,在uniCloud web控制台无法看到相关打印信息,这些代码在执行的时候是运行在 uni-id-co 云对象里的,但是在云对象的相关日志了查不到输出,而公共模块里没有日志按钮,我的代码如下:

//  /uniCloud-aliyun/cloudfunctions/common/uni-config-center/uni-id/custom-token.js  
module.exports = async (tokenObj) => {  
    console.log('看看这里执行不执行');  
    if(tokenObj.uid) {  
        // 去服务器取 last_login_tid  
        const db = uniCloud.database()  
        const user = await db.collection('uni-id-users').where({_id:'这里我填写了超级管理员的uid'}).get({getOne:true});  
        console.log('查询到的user数据:', user);  // 还有虽然上面加上了参数 {getOne:true} ,但得到的结果永远是数组  
        if(user.last_login_tid) {  
            tokenObj.tid = user.last_login_tid  
        } else {  
            tokenObj.tid = null  
        }  
        if(user.mobile) {  
            tokenObj.mobile = user.mobile  
        } else {  
            tokenObj.mobile = null  
        }  
    } else {  
        tokenObj.tid = null  
        tokenObj.mobile = null  
    }  

    return tokenObj // 注意务必返回修改后的token对象  
}

在出现这种现象之前,我在 /uniCloud-aliyun/cloudfunctions/common/uni-config-center/uni-id/hooks/index.js 里用过一次dbJQL.setUser 提权,之后就一直这样了,无法消除这个权限了,hooks 的代码如下:

//  /uniCloud-aliyun/cloudfunctions/common/uni-config-center/uni-id/hooks/index.js  
async function beforeRegister({  
  userRecord,  
  clientInfo  
} = {}) {  
  if(userRecord.mobile) {  
    // 如果该用户还没绑定租户,将租户id绑定  
    const dbJQL = uniCloud.databaseForJQL({clientInfo})  
    dbJQL.setUser({   
        role: ['admin'],   
    })  
    // 如果不提权是不会查询成功的,报错: "未能获取当前用户信息:当前用户为匿名身份"  
    // 因为用户还没有注册,当然没有身份,这个时候需要切换到管理员进行查询  
    const res = await dbJQL.collection('biz-users').where({mobile: userRecord.mobile}).get()  
    const data = res.data  
    if(data.length > 0) {  
      userRecord.last_login_tid = data[0]. tenant_id  
    } else {  
      userRecord.last_login_tid = null  
    }  
  }  
  return userRecord // 务必返回处理后的userRecord  
}  

module.exports = {  
  beforeRegister  
}
alafafa

alafafa (作者)

我在想要解决这个问题,是不是有每次连库之前,都要先手动把权限先清空一下,比如下面:

const dbJQL = uniCloud.databaseForJQL({clientInfo})  
dbJQL.setUser({   
    role: [],  // 把角色清空,或者取得当前用户的角色还原  
})

那么问题又来了,custom-token.js 里只能取得一个参数:tokenObj,无法获得 event, context,也无法使用 this.getClientInfo(),所以 uniCloud.databaseForJQL 在这里也就无法初始化,还没执行到 dbJQL.setUser 就报错了

关于 custom-token.js 官方除了下面的示例就再也找不到其他说明了

module.exports = async (tokenObj) => {  
  // tokenObj为原始token信息结构如下  
  // {  
  //   uid: 'abc', // 用户id  
  //   role: [], // 用户角色列表  
  //   permission: [] // 用户权限列表,admin角色的用户权限列表为空数组  
  // }  

  tokenObj.customField = 'hello custom token' // 自定义token字段  
  return tokenObj // 注意务必返回修改后的token对象  
}

那么这个函数除了tokenObj这一个参数之外还有没有其他参数呢,比如像 uni-id 的钩子函数那样提供一个 clientInfo

alafafa

alafafa (作者)

后来新建了个云空间新建了项目测试,未在任何地方调用过setUser,在custom-token.js 里查库,依然是能得到数据库里的所有,把 uni-id-co 里 access-control.js 里面提升了权限的所有方法的权限都给关闭了,但依然是无法约束写在 custom-token.js 查库代码拥有所有权限,猜测是官方把这个文件里的查库代码在框架里默认提升为超级管理员角色了,是这样吗官方的人来给回应一下,如果真的是这样,官方应该在关于custom-token的文档说明里给指出一下,这样可以警示开发者在写custom-token.js时有一定的安全防范

alafafa

alafafa (作者)

后来又经过多重测试证明并不是我上面猜测的样子,我随便建了一个云对象,在云对象里面用 db = uniCloud.database() 查库,就不会执行 schema 里面设置的任何拦截,可以查询所有的记录,而在云对象里面用 dbJQL = uniCloud.databaseForJQL({clientInfo = this.getClientInfo()}) 就会执行 schema 里面设置的拦截,无论是连接本地云函数,还是连接云端云函数都是一样的。

是这套框架本来就这么设计的么?还是我的开发环境被搞乱了?难道 官方默认的就是在云函数里执行 uniCloud.database() 就拥有数据库的所有权限,在云函数里执行 uniCloud.databaseForJQL 就必须带上客户端参数或指定user来进行权限隔离,可是这些我在查阅官方文档的时候都没有这些说明啊?

还有不理解的就是在 custom-token.js 执行db = uniCloud.database() 查库的时候就不执行 schema 里面设置的拦截,而在 hooks/index.js 执行db = uniCloud.database() 查库的时候就会执行 schema 里面设置的拦截,这两个 js 应该都是云函数范畴的啊怎么执行的隔离政策不一样呢?所以我不得不在 hooks/index.js 使用了 uniCloud.databaseForJQL({clientInfo = this.getClientInfo()}) 然后 dbJQL.setUser({ role: ['admin'] }) 来提升权限,这一切整的我好迷茫,期盼官方的工作人员能给解释一下,或者有熟悉这块的大神帮忙给解答一下,感激不尽!

alafafa

alafafa (作者)

我在DB Schema概述翻到这样一句话:

所以注意,在云函数中使用传统MongoDB API操作数据库时DB Schema不生效。不管在客户端还是云端,都必须使用JQL操作数据库。

难道 db = uniCloud.database() 就是使用传统MongoDB API操作数据库吗?而 uniCloud.databaseForJQL({clientInfo = this.getClientInfo()}) 就是使用JQL操作数据库?可是我在官方出品的 uni-admin 里和 uni-starter 里看到的无论是在 客户端还是云端 都是大量使用的 db = uniCloud.database() 这种方式啊,很少有见到使用 uniCloud.databaseForJQL({clientInfo = this.getClientInfo()}) 这种方式,非常的疑惑,哪位大侠能来给说一下用 db = uniCloud.database() 这种方式来操作数据库到底属于用传统MongoDB API操作数据库还是用JQL操作数据库?

alafafa

alafafa (作者)

为了寻找答案,我又弄了个全新的空间安装了 hello uniCloud 这个模板,发现在云对象里执行 uniCloud.database() 操作数据库就是不需要在 Schema 开通任何权限的,而在 vue 里执行 uniCloud.database() 就需要在 在 Schema 开通相应权限,难道这两个 uniCloud.database() 不是同一个东西?

uniCloud.database() 在云函数里执行相当于执行传统的查询,在 vue 里执行就是 clientDB 相当于 uniCloud.databaseForJQL 是吗?

uniCloud.database() 在云函数里执行的时候到底是传统查询还是JQL查询,我看官网的很多例子里,uniCloud.database() 的where 写法既有传统的对象式传参的写法 ({_id: '123'}) 又有 JQL 方式的传参写法 ("name == 'abc'") , 是不是 uniCloud.database() 既可以使用传统MongoDB API操作数据库又可以使用JQL操作数据库呢?当使用传统MongoDB API操作数据库他的权限就不受 Schema 的约束,当使用JQL写法时就受 Schema 的约束 ?

另外在 custom-token.js 操作数据库不受 Schema 的约束,而在 hooks/index.js 操作数据库就受 Schema 的约束必须提升权限才能操作,我猜是 hooks/index.js 虽然是写在 uniCloud 目录的,但是它的目的是拦截合并前端提交的数据,实际上还是相当于前端操作相当于 clientDB 所以受 Schema 的约束,可是不理解的是既然他相当于前端查询了,有怎么可以自己给自己提升权限呢,难道写在 vue 里的数据操作代码也可以通过 setUser 来给自己提升权限吗?

然而上面这些都是我猜测的,我在官网的文档里并没有找到具体的说明 uniCloud.database() 到底是传统的操作数据库还是JQL操作数据库还是二者兼备,也没有找到 uniCloud.database 和 uniCloud.databaseForJQL 的区别和对比,这些概念混淆不清很难区分,猜来猜去一整天也没搞明白,希望有明白的大神或官方的人员来给答疑一下

alafafa

alafafa (作者)

今天早上又做了一次测试,在 hooks/index.js 用 uniCloud.database() 操作数据库又不报 "未能获取当前用户信息:当前用户为匿名身份" 这样的错误了,而昨天是有这条报错的,所以不得已才用了 uniCloud.databaseForJQL 和 dbJQL.setUser 去提权,难道 hooks/index.js 目前是处于试验阶段的吗?昨天和今天用的同样的代码反复测试了很多次,得到的结果是不一样的

要回复问题请先登录注册