• 注册
    • 登录
    • 版块
    • 热门
    • Metabase官网
    • 社区汉化版
    • 达之云

    Metabase二次开发教程-05-行级权限

    Metabase二次开发教程
    1
    1
    919
    正在加载更多帖子
    • 从旧到新
    • 从新到旧
    • 最多赞同
    回复
    • 在新帖中回复
    登录后回复
    此主题已被删除。只有拥有主题管理权限的用户可以查看。
    • D
      dazdata 最后由 dazdata 编辑

      行级权限

      • metabase运行的middleware中包含4个enterprise edition的方法,其中 middleware.row_level_restrictions/apply-row-level-permissions 处理行级权限
      • 开启行级权限后,一次图表查询可能会触发多次DP流程,因为组装行级权限过程中会回调 query-processor/process-query
      • 注意,本文中的代码框引用的都是返回的参数示例,而不是源码,如需查看源码请移步gitHub

      apply-row-level-permissions

      1. 调用 query->table-id->gtap 方法,从元数据中查询 table-id->gtap;
        ;; 示例 table-id->gtap
        {2#metabase.models.group_table_access_policy.GroupTableAccessPolicyInstance{
          :id 1, 
          :group_id 4, 
          :table_id 2, 
          :card_id nil, 
          :attribute_remappings {user_id [:dimension [:field-id 3]]}
        }}
        
      2. 如果 table-id->gtap 为空,说明该表单不受权限控制,直接返回 query;否则,调用 gtapped-query 方法对 query 进行改写
      3. gtappend-query 方法本质上做两件事情:调用 apply-gtags 改写query,以及更新query中的 :gtap-perms字段

      apply-gtaps

      1. apply-gtaps 方法的核心是调用 mbql.util/replace方法,该方法可以替换掉一个map中符合其筛选条件的子map
      2. 第2个参数 (_ :guard (every-pred …)) 用于从 m 中筛选出符合条件的子map并存到 &match 中
        ;;示例 m
        {:database 1, 
        :query {:source-table 2, :filter [:= [:field-id 2] 3 6 7]}, 
        :type :query, 
        :middleware {:js-int-to-string? true, :add-default-userland-constraints? true}, 
        :info {:executed-by 2, :context :ad-hoc, :nested? false, :query-hash #object[[B 0x2f2dcce0 [B@2f2dcce0]}, 
        :constraints {:max-results 10000, :max-results-bare-rows 2000}}
        
        ;;示例 &match
        {:source-table 2, :filter [:= [:field-id 2] 3 6 7]}
        
      3. 第3个参数是用于替换 &match 的新值,本例中通过3步计算
      • 调用 apply-gtab 方法,计算 updated 值;
        ;; 示例 updated
        { :filter [:= [:field-id 2] 3 6 7], 
          :source-query {
            :source-table 2, 
            :filter [:= [:field-id 3] [:value 1 {:base_type :type/Integer, :special_type :type/FK, :database_type INTEGER, :name USER_ID}]], 
            :fields [[:field-id 9] [:field-id 3] [:field-id 5] [:field-id 6] [:field-id 8] [:field-id 7] [:field-id 1] [:datetime-field [:field-id 4] :default] [:field-id 2]]
          }, 
          :source-metadata [
            {:name ID, :id 9, :table_id 2, :display_name ID, :base_type :type/BigInteger, :special_type :type/PK, :fingerprint nil, :settings nil} 
            {:name USER_ID, :id 3, :table_id 2, :display_name USER_ID, :base_type :type/Integer, :special_type :type/FK, :fingerprint {:global {:distinct-count 929, :nil% 0.0}}, :settings nil} 
            {:name PRODUCT_ID, :id 5, :table_id 2, :display_name PRODUCT_ID, :base_type :type/Integer, :special_type :type/FK, :fingerprint {:global {:distinct-count 200, :nil% 0.0}}, :settings nil} 
            {:name SUBTOTAL, :id 6, :table_id 2, :display_name SUBTOTAL, :base_type :type/Float, :special_type nil, :fingerprint {:global {:distinct-count 340, :nil% 0.0}, :type {:type/Number {:min 15.691943673970439, :q1 49.74894519060184, :q3 105.42965746993103, :max 148.22900526552291, :sd 32.53705013056317, :avg 77.01295465356547}}}, :settings nil} 
            {:name TAX, :id 8, :table_id 2, :display_name TAX, :base_type :type/Float, :special_type nil, :fingerprint {:global {:distinct-count 797, :nil% 0.0}, :type {:type/Number {:min 0.0, :q1 2.273340386603857, :q3 5.337275338216307, :max 11.12, :sd 2.3206651358900316, :avg 3.8722100000000004}}}, :settings nil} 
            {:name TOTAL, :id 7, :table_id 2, :display_name TOTAL, :base_type :type/Float, :special_type nil, :fingerprint {:global {:distinct-count 10000, :nil% 0.0}, :type {:type/Number {:min 12.061602936923117, :q1 52.006147617878135, :q3 109.55803018499738, :max 238.32732001721533, :sd 38.35967664847571, :avg 82.96014815230805}}}, :settings nil} 
            {:name DISCOUNT, :id 1, :table_id 2, :display_name DISCOUNT, :base_type :type/Float, :special_type :type/Discount, :fingerprint {:global {:distinct-count 701, :nil% 0.898}, :type {:type/Number {:min 0.17088996672584322, :q1 2.9786226681458743, :q3 7.338187788658235, :max 61.69684269960571, :sd 3.053663125001991, :avg 5.161255547580326}}}, :settings nil} 
            {:table_id 2, :special_type :type/CreationTimestamp, :unit :default, :name CREATED_AT, :settings nil, :id 4, :display_name CREATED_AT, :fingerprint {:global {:distinct-count 9998, :nil% 0.0}, :type {:type/DateTime {:earliest 2016-04-30T18:56:13.352Z, :latest 2020-04-19T14:07:15.657Z}}}, :base_type :type/DateTime} 
            {:name QUANTITY, :id 2, :table_id 2, :display_name QUANTITY, :base_type :type/Integer, :special_type :type/Quantity, :fingerprint {:global {:distinct-count 62, :nil% 0.0}, :type {:type/Number {:min 0.0, :q1 1.755882607764982, :q3 4.882654507928044, :max 100.0, :sd 4.214258386403798, :avg 3.7015}}}, :settings nil}
          ]
        }
        
      • 在 updated 基础上,递归调用 apply-gtabs 计算 recursively-updated 值;
      • 在 recursively-updated 基础上,调用 mbql.util/replace 方法,为 :source-table 所在的map加上一个同级关键字 :gtap? true,即标记为已处理;

      apply-gtap

      • apply-gtap 是增加行级权限的核心方法,会结合gtap生成新的子map
        ;;入参 m
        {:source-table 2, :filter [:= [:field-id 2] 3 6 7]}
        
        ;;出参
        { :filter [:= [:field-id 2] 3 6 7],
          :source-query
          { :source-table 2,
            :filter [:= [:field-id 3] [:value 1 {:base_type :type/Integer, :special_type :type/FK, :database_type "INTEGER", :name "USER_ID"}]],
            :fields [[:field-id 9] [:field-id 3] [:field-id 5] [:field-id 6] [:field-id 8] [:field-id 7] [:field-id 1] [:datetime-field [:field-id 4] :default] [:field-id 2]]
          },
         :source-metadata
          [ {:name "ID", :id 9, :table_id 2, :display_name "ID", :base_type :type/BigInteger, :special_type :type/PK, :fingerprint nil, :settings nil}
            {:name "USER_ID", :id 3, :table_id 2, :display_name "USER_ID", :base_type :type/Integer, :special_type :type/FK, :fingerprint {:global {:distinct-count 929, :nil% 0.0}}, :settings nil}
            {:name "PRODUCT_ID", :id 5, :table_id 2, :display_name "PRODUCT_ID", :base_type :type/Integer, :special_type :type/FK, :fingerprint {:global {:distinct-count 200, :nil% 0.0}}, :settings nil}
            {:name "SUBTOTAL", :id 6, :table_id 2, :display_name "SUBTOTAL", :base_type :type/Float, :special_type nil, :fingerprint {:global {:distinct-count 340, :nil% 0.0}, :type {:type/Number {:min 15.691943673970439, :q1 49.74894519060184, :q3 105.42965746993103, :max 148.22900526552291, :sd 32.53705013056317, :avg 77.01295465356547}}}, :settings nil}
            {:name "TAX", :id 8, :table_id 2, :display_name "TAX", :base_type :type/Float, :special_type nil, :fingerprint {:global {:distinct-count 797, :nil% 0.0}, :type {:type/Number {:min 0.0, :q1 2.273340386603857, :q3 5.337275338216307, :max 11.12, :sd 2.3206651358900316, :avg 3.8722100000000004}}}, :settings nil}
            {:name "TOTAL", :id 7, :table_id 2, :display_name "TOTAL", :base_type :type/Float, :special_type nil, :fingerprint {:global {:distinct-count 10000, :nil% 0.0}, :type {:type/Number {:min 12.061602936923117, :q1 52.006147617878135, :q3 109.55803018499738, :max 238.32732001721533, :sd 38.35967664847571, :avg 82.96014815230805}}}, :settings nil}
            {:name "DISCOUNT", :id 1, :table_id 2, :display_name "DISCOUNT", :base_type :type/Float, :special_type :type/Discount, :fingerprint {:global {:distinct-count 701, :nil% 0.898}, :type {:type/Number {:min 0.17088996672584322, :q1 2.9786226681458743, :q3 7.338187788658235, :max 61.69684269960571, :sd 3.053663125001991, :avg 5.161255547580326}}}, :settings nil}
            {:table_id 2, :special_type :type/CreationTimestamp, :unit :default, :name "CREATED_AT", :settings nil, :id 4, :display_name "CREATED_AT", :fingerprint {:global {:distinct-count 9998, :nil% 0.0}, :type {:type/DateTime {:earliest "2016-04-30T18:56:13.352Z", :latest "2020-04-19T14:07:15.657Z"}}}, :base_type :type/DateTime}
            {:name "QUANTITY", :id 2, :table_id 2, :display_name "QUANTITY", :base_type :type/Integer, :special_type :type/Quantity, :fingerprint {:global {:distinct-count 62, :nil% 0.0}, :type {:type/Number {:min 0.0, :q1 1.755882607764982, :q3 4.882654507928044, :max 100.0, :sd 4.214258386403798, :avg 3.7015}}}, :settings nil}
          ]
        }
        
      • 其内部调用了metabase.util/prog1 方法,该方法本质上是一个装饰器:运行主函数(第一个参数)并返回其结果(保存在 <> 中),返回前运行其它函数(比如记录日志)
      • 主函数是一个merge,即将m中的 :source-table 和 :source-query 先排除,然后插入 gtap->source 方法的返回结果

      gtap->source

      • 该函数通过调用 preprocess-source-query ,生成包含 :source-query 和 :source-metadata 的map,示例参照上面 apply-gtap 的出参;
      • preprocess-source-query中执行顺序如下:
        • 调用 card-gtap->source 或 table-gtap->source 生成初版source-query,放到 query 中;
          ;; 示例:table-gtap->source生成的query
          { :database 1, 
            :type :query, 
            :query {
              :source-query {
                :source-table 2, 
                :parameters [{:type :category, :target [:dimension [:field-id 3]], :value 1}]
          }}}
          
        • 将 query 传入 metabase.query-processor/query->preprocessed 生成 preprocessed;
          ;; 示例: preprocessed
          { :database 1, 
            :type :query, 
            :query {
              :source-metadata [...], 
              :fields [...], 
              :limit 1048576, 
              :source-query {
                :source-table 2, 
                :filter [:= [:field-id 3] [:value 1 {:base_type :type/Integer, :special_type :type/FK, :database_type INTEGER, :name USER_ID}]], 
                :fields [[:field-id 9] [:field-id 3] [:field-id 5] [:field-id 6] [:field-id 8] [:field-id 7] [:field-id 1] [:datetime-field [:field-id 4] :default] [:field-id 2]]
          }}}
          
        • 从 preprocessed 中取出 :query 字段中的 :source-query 和 :source-metadata两个字段信息,返回给调用方

      gtap->parameters

      • 在gtap->source 中,根据是否包含card-id来选择调用 card-gtap->source 或 table-gtap->source,而这两个函数都会调用 gtap->parameters 来获取权限数据
      • 工作流程:
        • 从 gtap 中获取表定义的权限设置,保存在 attribute-remappings 中;
        • 从 current-user 中获取用户的权限设置,保存在 login_attributes 中;
        • 调用 attr-remapping->parameter
        ;; 示例 attribute-remappings
        {user_id [:dimension [:field-id 3]]}
        
        ;; 示例 login_attributes
        {user_id 1, quantity 2}
        

      转载自:https://blog.csdn.net/weixin_43821438/article/details/111594677

      1 条回复 最后回复 回复 引用 0
      • First post
        Last post

      Powered by MetabaseCN | 提供专业Metabase培训 | QQ群:799286435 微信:MetabaseCN

      鄂ICP备20010758号