一个 Metabase 之谜
公共链接、公共嵌入和签名嵌入之间有什么区别?
假设您正在 Metabase 中进行一项有趣的分析,您需要分享您的工作——首先是为了获得反馈,然后作为最终产品发布。
公共链接和嵌入,以及静态嵌入,都是免费的开源方式,可以用来与没有 Metabase 帐户或没有权限访问您的原始数据的人分享 Metabase 作品(例如图表和仪表板,但不要限制自己)。
在选择这些数据共享选项时,您需要考虑
- 您需要多快分享结果
- 您希望添加多少上下文
- 您数据周围的安全级别
您能帮我们找到隐藏的信息吗?
在本教程中,我们将从 Metabase 数据分析师的角度探讨一系列数据共享用例。我们的工作是调查一位队友留下的一个谜团,这位队友在即将解决 Metabase 最古老的 bug 时消失了。
我们收到匿名举报,称我们队友的 Git 提交信息中可能隐藏着一条信息,因此我们想分析数据,寻找任何异常或可疑之处。当然,我们也希望与社区分享调查结果(毕竟我们是开源的)。
如果您想跳到有趣的部分
仅限内部使用
在公开分享我们正在进行的调查结果之前,我们希望获得团队的一些反馈,并确保我们没有暴露任何个人身份信息 (PII)。初步分析(图 1)保存在我们的内部 Metabase 实例中——此版本将仅与我们的团队共享。
共享 Metabase 仪表板的 URL
创建仪表板的公共链接。
您可以将实时调查仪表板的公共链接添加到书签。我们决定以聚合方式显示提交数据,以便更轻松地阅读分析结果。我们还设置了自定义目标,以便人们可以查看与仪表板上显示的每个统计数据相关的完整提交消息。随着调查的继续,我们可以发布更改以在公共链接处显示最新分析结果。
线索 1
在我们的探索性数据分析中,我们查看了 Git 提交消息长度的分布。其中一个异常值(最长的消息)包含以下消息
update the database sync event publication so that we can actually
track start & END as separate events but part of the same activity.
to do so we add a :tracking-hash to the events when they are published
which is meant to remain consistent throughout the sync, then we
implement our activity tracking code so that we can lookup an existing
activity item and update it based on the custom hash.
我们不确定为什么这里的 END 是大写的。这可能没有任何意义,但我们会继续寻找……
接下来:线索 2。
将 Metabase 仪表板添加到网站
接下来,我们希望将实时调查仪表板与用于评论的网页表单一起显示,以便我们的社区可以前往一个地方为调查做出贡献。为此,我们将设置一个新网站,并将我们的仪表板和网页表单添加为嵌入式 iframe。
设置网站最简单的方法是使用免费的网站构建器。在这里,我们使用 Google Sites。如果您不使用 Google Workspace,可以尝试使用 Squarespace 或 Webflow 等工具。
接下来,我们将返回到 Metabase 仪表板上的共享选项,并复制公共嵌入下的代码。我们将在 Google 站点中添加一个嵌入元素,并将代码片段粘贴到那里
然后,我们将创建一个公共 Google 表单并将其保存在 Google 云端硬盘中。要将表单添加到我们的 Google 站点,我们可以向下滚动“插入”菜单(如上图 2 所示)并选择“表单”。
现在,我们有一个实时调查网站,显示了我们的 Metabase 仪表板以及一个评论表单。我们的社区将能够
- 通过我们 Metabase 仪表板的公共嵌入访问最新数据和线索。
- 使用嵌入式 Google 表单为搜索做出贡献。
线索 2
由于我们查看了最大异常值,因此查看消息长度最短的提交也很有意义。最短的消息只有三个字符长,而且再次是大写字母
LAG
接下来:线索 3。
限制对嵌入式 Metabase 仪表板的访问
今天早上,有人用数千个对不存在的查询参数 filter=some_spooky_nonsense
的请求攻击了我们的 Metabase 公共嵌入。为了保护线索不落入坏人之手,我们希望确保只有经过验证的用户才能访问包含线索的行。
我们可以通过将公共嵌入更改为静态嵌入来添加这一层额外的安全性。我们希望仪表板的静态嵌入显示
- 未登录用户的无数据。
- 已登录用户的受限数据(除了线索之外的所有数据)。
- 已登录并使用秘密密码用户的秘密数据(线索)。
静态嵌入不适用于 Google Sites 等站点生成器,因为我们需要运行自己的 Web 服务器才能生成保护我们数据的签名令牌。如果您想继续阅读其余的静态嵌入教程,您需要
- 您自己的 Metabase 实例(以及相关的嵌入密钥)。
- 嵌入参考应用程序存储库的克隆(我们将修改 Node 示例)。
metabase-mystery
数据的副本。
设置登录页面
首先,我们需要某种方法来验证尝试访问我们实时调查网站的人的身份。通常,这是通过由身份验证服务(如 Google 登录)提供支持的登录页面完成的,这样
- 成功登录后,我们的 Web 服务器将发送一个查询参数,如
user=verified
到 Metabase 仪表板上的过滤器,该过滤器会显示线索。 - 登录失败时,Metabase 仪表板过滤器不会从 Web 服务器接收到正确的参数,从而隐藏线索并显示其余数据。
为了演示根据登录显示和隐藏数据,我们将从 login.pug
中定义的简单身份验证流程开始(这绝对证明您既真实又无可疑)。
form(method="POST" action="/login")
input(type="text" name="username" value="metabot")
input(type="password" name="password" value="unverified")
input(type="hidden" name="redirect" value=redirectUrl)
p Can you be trusted?
button(type="submit" class="primary") Yes
根据登录情况将人员重定向到不同的 URL
为了跟踪用户的登录信息,我们将修改 index.js
中的服务器代码,添加一个名为 messageType
的变量
function checkAuth(req, res, next) {
const messageType = req.session.messageType;
if (messageType) {
return next();
}
req.session.redirectTo = req.path;
return res.redirect("/login");
}
然后,我们将设置一些逻辑来处理不同类型的登录。messageType
中存储的值将用于过滤数据,这些数据将显示在静态嵌入 URL 中。
if (username === "metabot" && password === "regularPassword") {
// require a login to view the commit messages
req.session.messageType = "not secret";
res.redirect(req.session.redirectTo);
} else if (username === "metabot" && password === "secretPassword") {
// require a login with the secret password to view the clues
req.session.messageType = "secret";
res.redirect(req.session.redirectTo);
} else {
// allow people without logins to submit comments
res.redirect("/");
}
生成静态嵌入 URL
现在,我们可以更新 index.js
中的服务器代码,为每个登录用户动态生成静态嵌入的 URL。这些 URL 是安全的,因为它们使用我们的嵌入密钥进行签名。服务器还将传递 messageType
的值(“秘密”或“非秘密”)到 Metabase 仪表板上的过滤器,以便在数据中隐藏或显示线索。我们将在下一步中设置此过滤器。
app.get("/signed_dashboard/:id", checkAuth, (req, res) => {
const messageType = req.session.messageType;
const unsignedToken = {
resource: { dashboard: DASHBOARD_ID },
params: { keep_secret: messageType },
exp: Math.round(Date.now() / 1000) + 10 * 60, // 10 minute expiration
};
// sign the JWT token with our secret key
const signedToken = jwt.sign(unsignedToken, MB_EMBEDDING_SECRET_KEY);
// construct the URL of the iframe to be displayed
const iframeUrl = `${MB_SITE_URL}/embed/dashboard/${signedToken}`;
res.render("dashboard", { messageType: req.params.id, iframeUrl: iframeUrl });
});
使用锁定参数限制数据
为了指定根据用户的登录情况应显示的行数据,我们将返回到我们的 git 提交数据,并添加一个名为“消息类型”的自定义列,该列使用case 表达式将包含线索的行设置为“secret”,其余数据设置为“not secret”。
然后,为了确保我们可以限制实时调查仪表板上的所有数据,我们需要为仪表板上的每个问题创建一个“消息类型”列。
数据设置完成后,我们将在实时调查仪表板上添加一个过滤器,并将该过滤器链接到每个问题中的所有“消息类型”列。我们还将“非秘密”设置为“默认值”,以便默认情况下仅显示非线索行。
现在,我们可以启用仪表板上的静态嵌入并设置锁定参数。锁定参数将在登录流程中从 Web 服务器接收“secret”或“not secret”的 messageType
值(取决于所使用的密码)。此锁定参数将在结果显示在静态嵌入 URL 之前,将“secret”或“not secret”值应用于我们原始仪表板上的过滤器。
查看具有不同访问级别的静态嵌入
一旦我们使用静态嵌入的前端代码更新了layout.pug
,我们就可以运行服务器代码来构建一个本地站点,其中包含指向我们的实时调查仪表板的链接和用于评论的嵌入式 Google 表单
点击链接将提示人们登录以查看数据。我们目前找到的线索默认是隐藏的
要访问包含线索的行,人们必须提供秘密密码
如果您没有有效的登录信息,可以通过表单请求访问(或者只是作为狂热的评论者参与这个谜团)
线索 3
在分布中隐藏的另一个线索——这次是在消息长度的中位数处
fixes mobile password reSET and confirmation #869
游戏开始
我们需要更多来自数据朋友的帮助。到目前为止,我们已经找到了线索 END、LAG 和 SET。我们的队友想告诉我们什么?获取数据并尝试一下,看看您是否能解开这个谜团。
点击此处获取提示。
尝试使用提交消息长度计算其他摘要统计信息。解决方案并非由 Metabase 功能付费墙限制(那样会让这个谜团有点过于诡异),但如果您想使用 Metabase 探索数据集,您可以
- 将 CSV 文件上传到 Google 表格并将 Metabase 连接到 Google 云端硬盘,或者
- 将 CSV 直接同步到连接到 Metabase 的数据库或数据仓库。