• Publicity: Public Only All

kibana-procs.tcl

This is an experimental implementation of an integration of kibana panels via a reverse proxy

Location:
packages/xowiki/tcl/kibana-procs.tcl
Author:
Gustaf Neumann

Procedures in this file

Detailed information

xowiki::kibana_backend_reply (public)

 xowiki::kibana_backend_reply [ -url url ] \
    [ -replyHeaders replyHeaders ] [ -status status ]

Rewrite backend reply

Switches:
-url (optional)
-replyHeaders (optional)
-status (optional)

Partial Call Graph (max 5 caller/called nodes):
%3

Testcases:
No testcase defined.

xowiki::kibana_url_rewrite (public)

 xowiki::kibana_url_rewrite [ -target target ] [ -url url ] \
    [ -query query ]

Rewrite default url_rewrite_callback

Switches:
-target (optional)
-url (optional)
-query (optional)

Partial Call Graph (max 5 caller/called nodes):
%3

Testcases:
No testcase defined.
[ hide source ] | [ make this the default ]

Content File Source

::xo::library doc {

  This is an experimental implementation of an integration of kibana
  panels via a reverse proxy

  @author Gustaf Neumann
}

::xo::library require xowiki-procs
::xo::library require includelet-procs

namespace eval ::xowiki::includelet {

  #
  # Configuration
  #
  # The location of the Kibana server can be tailored via the NaviServer
  # config file:
  #
  # ns_section ns/server/${server}/acs/xowiki
  #        ns_param KibanaLocation   http://localhost:5601
  #
  set ::xowiki::includelet::kibana_location \
      [ns_config ns/server/[ns_info server]/acs/xowiki KibanaLocation http://localhost:5601]


  ###########################################################
  #
  # ::xowiki::includelet::kibana
  #
  ###########################################################
  ::xowiki::IncludeletClass create kibana \
      -superclass ::xowiki::Includelet \
      -cacheable false \
      -parameter {
        {__decoration plain}
        {parameter_declaration {
          {-chart openacs-status-codes}
          {-from now-24h}
          {-to now}
          {-hash ""}
          {-width:integer 800}
          {-height:integer 400}
        }}
        {id "[xowiki::Includelet js_name [self]]"}
      } -ad_doc {

        Include a Kibana chart identified by the provided hash

        @param from start of time window (default now-24h)
        @param to end of time window (default now-)
        @param hash hash of the included visualization
        @param width width of included content
        @param height height of included content

      }

  kibana proc url {path} {
    set url $::xowiki::includelet::kibana_location$path
    #ns_log notice "xowiki KIBANA request: $url"
    return $url
  }

  kibana proc grant_permission {object_id hash url} {
    nsv_set kibana_permissions [::xo::cc user_id] $object_id
    nsv_set kibana_hashes [::xo::cc user_id]-$hash 1
    ns_log notice "kibana GRANT ACCESS for <$url> to [::xo::cc user_id]"
    ad_set_signed_cookie -discard t -scriptable f ad_kibana $object_id
  }

  kibana proc check_permission {url} {
    #set grantAlways {^/kibana/(api|built_assets|bundles|node_modules|translations|ui)/}
    set grantAlways {^/kibana/(built_assets|bundles|node_modules|translations|ui)/}
    set grantNever {^/kibana/(elasticsearch)}
    set restricted {
      /kibana
      /kibana/app/canvas
      /kibana/app/maps
      /kibana/app/ml
      /kibana/app/infra
      /kibana/app/apm
      /kibana/app/uptime
      /kibana/app/siem
      /kibana/app/monitoring
    }
    set strippedUrl [string trimright $url /]

    #
    # Grant some access always and some access never.
    #
    #ns_log notice "kibana CHECK PERMISSIONS for <$strippedUrl> in restricted --> [expr {$strippedUrl in $restricted}]"
    if {[regexp $grantAlways $url]} {
      set when always
      set granted 1

    } elseif {$strippedUrl in $restricted || [regexp $grantNever $url]} {
      set when never
      set granted 0

    } else {
      #
      # Go into the details.
      #
      ::xo::ConnectionContext require
      set when sometimes
      set user_id [::xo::cc user_id]
      set method  [ns_conn method]
      set granted [nsv_exists kibana_permissions $user_id]
      try {
        ad_get_signed_cookie ad_kibana

      } trap {AD_EXCEPTION NO_COOKIE} {errorMsg} {
        set cookie ""
        append when -nocookie

      } trap {AD_EXCEPTION INVALID_COOKIE} {errorMsg} {
        set cookie ""
        append when -invalidcookie

      } on ok {cookie} {
      }

      if {$granted} {
        #
        # Just allow one access to the main app. This is the first
        # call inside the iframe (should be actually
        # "/kibana/app/kibana")
        #
        nsv_unset kibana_permissions $user_id
        append when -first

      } elseif {$cookie ne ""} {
        #
        # The cookie is the object_id. Therefore, we can check whether
        # the current user_id has permissions on this object.
        #
        set granted [permission::permission_p \
                         -party_id $user_id \
                         -object_id $cookie \
                         -privilege read]
        append when -$granted

        if {$granted} {
          #
          # Check as well whether the user has rights on the hash. We
          # can this do just in limited cases. Since this hash is just
          # in a few queries.
          #
          if {$method eq "POST" && $url eq "/kibana/api/saved_objects/_bulk_get"} {
            set payload [ns_conn content]
            if {[regexp {"id":"([^\"]+)"} $payload . hash]} {
              #
              # We have the hash_code, the user_id and the object_id
              #
              set granted [nsv_exists kibana_hashes $user_id-$hash]
              ns_log notice "kibana CHECK PERMISSIONS $url hash $hash -> $granted"
              append when -payload=$granted
            }
          } else {
            #
            # For the time being, also allow the following cases, when the cookie is ok.
            #
            ns_log notice "kibana CHECK PERMISSIONS for $user_id [ns_conn request] // [ns_set array [ns_conn headers]]"
            if {$method eq "POST"} {
              ns_log notice "kibana CHECK PERMISSIONS payload [ns_conn content]"
            }
          }
        }
      }
    }

    if {$when ne "always"} {
      ns_log notice "kibana CHECK PERMISSIONS <$url$when --> $granted"
    }
    return $granted
  }

  kibana instproc include_head_entries {} {
    next
  }

  kibana instproc render {} {
    :get_parameters
    #set width 800
    #set height 400
    #set from now-24h
    #set to now
    #set hash [kibana get visitors-map-openacs]
    set refreshInterval {(pause:!t,value:0)}
    set time [subst {from:$from,mode:quick,to:$to}]
    set _g (refreshInterval:$refreshInterval,time:($time))
    set url /kibana/app/kibana#/visualize/edit/$hash?embed=true&_g=[ns_urlencode $_g]
    #ns_log notice "kibana iframe src: $url"
    [self class] grant_permission [${:__including_page} item_id] $hash $url
    set HTML [subst {<iframe src="$url" width="$width" height="$height" frameborder="0"></iframe>}]
    return $HTML
  }
}


# http://localhost:3000/dashboard/snapshot/07UV7rL9tzUNf7PuSS4Y3XlBWQ1yget0
d_proc ::xowiki::kibana_url_rewrite {
  -target
  -url
  -query
} {
  Rewrite default url_rewrite_callback
} {
  #ns_log notice "=== kibana_url_rewrite; target $target query <$query>"

  set headers [ns_conn headers]
  # foreach {key value} [ns_set array $headers] {ns_log notice "====== $key: $value"}
  ns_set update $headers Host localhost:5602
  ns_set idelkey $headers Sec-Fetch-Site

  if {$query ne ""} {
    append target ?$query
    append url ?$query
  }
  set granted [::xowiki::includelet::kibana check_permission $url]
  if {!$granted} {
    ns_returnforbidden
    set target ""
  }

  #ns_log notice "=== kibana_url_rewrite; final $target"
  return $target
}

d_proc ::xowiki::kibana_backend_reply {
  -url
  -replyHeaders
  -status
} {
  Rewrite backend reply
} {
  ns_log notice "=== kibana_backend_reply; url <$url> replyHeaders $replyHeaders status $status"
  #foreach {key value} [ns_set array $replyHeaders] {
  #  ns_log notice "=== $key: $value"
  #}
}

ns_register_proc GET /kibana/* {
  ::revproxy::upstream {} \
      -target [::xowiki::includelet::kibana url [ns_conn url]] \
      -url_rewrite_callback   ::xowiki::kibana_url_rewrite \
      -backend_reply_callback ::xowiki::kibana_backend_reply \
      -timeout 10.0 \
      -sendtimeout 0.0 \
      -receivetimeout 10.0
}

ns_register_proc POST /kibana/* {
  ::revproxy::upstream {} \
      -target [::xowiki::includelet::kibana url [ns_conn url]] \
      -url_rewrite_callback   ::xowiki::kibana_url_rewrite \
      -backend_reply_callback ::xowiki::kibana_backend_reply \
      -timeout 10.0 \
      -sendtimeout 0.0 \
      -receivetimeout 10.0
}

::xo::library source_dependent

#
# Local variables:
#    mode: tcl
#    tcl-indent-level: 2
#    indent-tabs-mode: nil
# End: