具有自定义声明的Firebase存储规则

时间:2019-02-27 08:44:25

标签: python firebase firebase-authentication firebase-storage firebase-security-rules

我无法使Firebase Storage使用自定义规则和自定义声明。

在“ Python管理”面板中,我执行以下操作来创建用户并分配声明client_id:

# Standard Auth
import firebase_admin
from firebase_admin import db, storage, auth
cred   = firebase_admin.credentials.Certificate('path_to_cert_json')
app    = firebase_admin.initialize_app(cred, 'config')
bucket = storage.bucket(app=app)

# Create User
auth.create_user(email=email) 

# Create custom claims
auth.set_custom_user_claims(uid, {'client_id': client_id})

然后针对Firebase规则,当文件位于具有client_id的子文件夹中时,我尝试允许用户仅读取(或下载)文件:

存储中的文件结构:

/{environment}/{client_id}/other_folders_and_files

我设置了以下存储规则:

service firebase.storage {
  match /b/{bucket}/o {
    match /{environment}/{client_id}/{allPaths=**} {
      allow read: if request.auth.token.client_id == client_id
    }
  }
}

但是,这给了我一个错误,即权限被拒绝。

我在做什么错了?

注意:

  • client_id正确且文件夹结构正确,已检查了一百万遍。

3 个答案:

答案 0 :(得分:2)

如果我没记错,那么您使用的是这个错误。应该是:

service firebase.storage {
  match /b/{bucket}/o {
    match /{environment}/{client_id}/{allPaths=**} {
      allow read: if request.auth.uid == client_id
    }
  }
}

令牌返回其他对象,例如:

  • 电子邮件
  • email_verified
  • 电话号码
  • 名称
  • sub

因此,为了能够比较用户ID,必须使用request.auth.uid。这种方式将比较cliente客户ID。如果您想看看docsrequest.auth上的一切都来了。

编辑

如果您想要自己的自定义令牌,例如:request.auth.token.client_id,则需要使用Python中的以下代码来实现:

uid = 'some-uid'
additional_claims = {
    'client_id': your_custom_client_id
}

custom_token = auth.create_custom_token(uid, additional_claims)

然后您可以在存储规则中使用

service firebase.storage {
  match /b/{bucket}/o {
    match /{environment}/{client_id}/{allPaths=**} {
      allow read: if request.auth.token.client_id == client_id
    }
  }
}

请参见docs

答案 1 :(得分:0)

自定义声明是目前执行此操作的唯一方法。 规则应如下所示:

service firebase.storage {
  match /b/{bucket}/o {
    function isAuth() {
      return request.auth != null && request.auth.uid != null
    }
    function isAdmin() {
      return isAuth() &&
      request.auth.token.admin == true;
    }
    function clientMatch(clientId) { // expects user's "client" field to be ID of client
      return isAuth() &&
      clientId == request.auth.token.clientId;
    }
    match /storage/path/{clientId}/{allPaths=**} {
        allow read, write: if isAdmin() || clientMatch(clientId)
    }

我们在auth令牌上使用两个自定义字段:adminclientId。 与数据库同步的云功能看起来像这样:

exports.updateUser = functions.firestore
  .document('users/{userId}')
  .onWrite( async (change, context) => {
    // change.before and change.after are DocumentSnapshot objects
    const userid=context.params.userId // (from {userId} above)
    const isDeleted = !change.after.exists
    const isNew = !change.before.exists
    let customClaims = {}
    if (!isDeleted) {
      let newData = change.after.data()
      let oldData = change.before.data()
      // do we need to update anything?
      if (isNew ||
          newData.admin !== oldData.admin ||
          newData.client !== oldData.client) {
        customClaims.admin = Boolean(newData.admin)
        customClaims.clientId = newData.client
      }
    }
    else {
      let oldData = change.before.data()
      customClaims.admin = false
      customClaims.clientId = null
    }
    // now do the update if needed
    if (customClaims !== {}) {
      // See https://firebase.google.com/docs/reference/admin/node/admin.auth.Auth
      await admin.auth().setCustomUserClaims(userid, customClaims)
      console.log(`Updating client for ${isNew?"new":"existing"} user ${userid}: ` +
                  `${JSON.stringify(customClaims)}`)
    }
  })

可以在对用户文档进行任何更改时运行,并将其同步到auth的自定义声明。

答案 2 :(得分:0)

出于完整性考虑,需要在为用户设置令牌之前创建一个令牌。

因此,完整代码:

uid = 'some-uid'
additional_claims = {
    'client_id': your_custom_client_id
}

# First time, only once needed
custom_token = auth.create_custom_token(uid, additional_claims)

# Then
auth.set_custom_user_claims(uid, additional_claims )