Skip to content

Authorization

Vladimir Kotal edited this page Oct 12, 2021 · 28 revisions

Using OpenGrok authorization

If OpenGrok instance is deployed in an environment where indexed data (usually source code) needs to be protected for some reason, the built in authorization framework can be used to provide access controls with high granularity.

In order to perform authorization, authentication needs to be performed first. By itself, OpenGrok does not perform any authentication, this is to be performed by the application server (e.g. Tomcat) or by a reverse proxy passing data to and from the application server. This is deliberate design decision that allows for very flexible configurations.

The OpenGrok authorization framework resembles the PAM framework, i.e. the configuration specifies a stack of authorization plugins where each plugin instance is paired with a flag saying how to interpret the decision of the plugin. The framework covers all aspects of the web application, i.e. both the web interfaces as well as the RESTful API endpoints.

The general idea is that users are able to see/query the OpenGrok service but will only get the data they are authorized to view.

OpenGrok distribution contains set of various plugins that can be used for various scenarios. You can find the set of plugins in a single JAR file stored in share/lib/plugins.jar inside the OpenGrok distribution archive. If that is not enough, custom plugins can be implemented using the documented programmatic API. For details about the plugin implementation (handy when implementing custom plugin) see https://github.com/oracle/opengrok/wiki/Authorization-framework-internals

Fully working example on how to use the authorization framework combined with HTTP Basic authentication provided by Tomcat is here.

The documentation of built-in plugins is on https://github.com/oracle/opengrok/wiki/Authorization-plugins

Configuration

You can configure:

  1. Plugin directory

    The plugin directory is set in read-only configuration file using the pluginDirectory property with a String parameter with absolute path to the directory with plugins.

    By default the directory points to the plugins subdirectory under the data root directory. If no plugin is available/the directory does not exist then the framework allows every request.

    OpenGrok authorization framework expects plugin files (in the form of .class files or .jar archives) in that directory, loads those files as plugins and then uses them to perform the authorization for each request.

  2. Plugin watchdog service

    Similar for the watchdog service there is a dedicated option in the configuration which you can set as described above. The name of it is authorizationWatchdogEnabled and the value is a boolean type.

    This service watches for changes (even recursive) in the plugin directory and if such change was done (modifing, deleting); it reloads the plugins. Old plugins in memory are discarded and only the plugins contained in plugin directory are loaded. This is done in real time.

    This option is useful for development.

    NOTE: Modification of every file in plugin directory is propagated as an event. This means that modifying bunch of files in the plugin directory leads to several reload events

    By default the value is false.

  3. Authorization stack

    Discussed below.

A stack of plugins

The plugins form a linear structure of a list (called stack). The order of invocation of the plugins methods isAllowed is determined by their position in the stack. Moreover you can configure a different flag for each of your plugins which is respected when performing the authorization.

These flags are supported:

  • REQUIRED: Failure of such a plugin will ultimately lead to the authorization framework returning failure but only after the remaining plugins have been invoked.
  • REQUISITE: Like required, however, in the case that such a plugin returns a failure, control is directly returned to the application. The return value is that associated with the first required or requisite plugin to fail.
  • SUFFICIENT: If such a plugin succeeds and no prior required plugin has failed the authorization framework returns success to the application immediately without calling any further plugins in the stack. A failure of a sufficient plugin is ignored and processing of the plugin list continues unaffected.
  • OPTIONAL: failure or success of this plugin does not matter unless it is the only plugin in the stack in which case the failure leads to overall failure.

These are inspired by the PAM Authorization framework and the definition is taken directly from the man pages of the PAM configuration. However OpenGrok does not implement all PAM flags.

Backwards compatibility

You can use the authorization framework without providing such an advanced configuration because all loaded plugins which do not occur in this advanced configuration are appended to the list with REQUIRED flag. However, as of the nature of the class discovery this means that the order of invocation of these plugins is rather random.

Example

This is an example snippet that can be inserted into the read-only configuration file. This defines three plugins, each of them has a different role and so affect the stack processing in different way. Use the class name to specify the targeted plugin and one of the flags as the authorization role. This stack is then used for every request and is processed from the top to the bottom (as it is a list) evaluating the flags with the particular plugin decisions.

<void property="pluginStack">
  <void method="add">
    <object class="org.opengrok.indexer.authorization.AuthorizationPlugin">
      <void property="flag">
        <string>REQUISITE</string>
      </void>
      <void property="name">
        <string>some.my.package.RequisitePlugin</string>
      </void>
    </object>
  </void> 
  <void method="add">
    <object class="org.opengrok.indexer.authorization.AuthorizationPlugin">
      <void property="flag">
        <string>SUFFICIENT</string>
      </void>
      <void property="name">
        <string>Plugin</string>
      </void>
    </object>
  </void>
  <void method="add">
    <object class="org.opengrok.indexer.authorization.AuthorizationPlugin">
      <void property="flag">
        <string>REQUIRED</string>
      </void>
      <void property="name">
        <string>ExamplePlugin</string>
      </void>
    </object>
  </void>   
</void>

A typical use of this configuration would be:

RequisitePlugin

  1. Determine a user identity in the requisite plugin and store it in the request
  2. Return true or false if such action was successful

Plugin

  1. Use the user identity determined in the requisite plugin which is stored in the request
  2. Perform an authorization check which can lead to immediately return from the stack with a success value
  3. If the check is negative, the stack continues to the third plugin

ExamplePlugin

  1. Use the user identity determined in the requisite plugin which is stored in the request
  2. Filter the rest of which was not enabled by the previous Plugin implementation

Stack of stacks

For more complex deployments, one can configure stack of stacks of plugins. This is handy if the sub-stacks need to share a pre-requisite plugin but have different needs to perform authorization decisions.

Debugging

Chatty page

For basic overview of how the authorization stack looks like set the chattyStatusPage option to true (see https://github.com/oracle/opengrok/wiki/Webapp-configuration#configuration-tunables) and display the status page (e.g. http://YOURHOST:PORT/source/status.jsp).

Tomcat logging

In general, it should be possible to increase log level in Tomcat's logging.properties file to get more verbose logging, for example:

org.opengrok.level = FINEST

opengrok.auth.level = FINEST