Skip to content

Authorization framework internals

Vladimir Kotal edited this page Jul 24, 2019 · 3 revisions

Implementation

Each plugin must implement the IAuthorizationPlugin interface which contains four methods:

  1. void load ( void );

    This method is called whenever the framework loads the plugin into memory

    • watchdog service enabled and change was made (forced reload)
    • deploy of the webapp
    • configuration change (change of the plugin directory, ...)
  2. void unload ( void );

    This method is called whenever the framework destroys the plugin

    • watchdog service enabled and change was made (forced reload) (the order is first unload the old instance then load the new one)
    • undeploy of the webapp
  3. boolean isAllowed(HttpServletRequest request, Project project);

    This method gives a decision if the project should be allowed for the current request.

  4. boolean isAllowed(HttpServletRequest request, Group group);

    This analogically gives the same decision for the group.

Example plugin TruePlugin.java is included in the plugins directory in the root of the Git repository.

All those methods are also described in the code.

Each is expected to implement some sort of a cache for the decisions when the underlying read operations for the user rights is expensive.

HttpServletRequest object is the current request with all of its features like: session, attributes, getUser(), getPrincipal()

Sessions

If you decide that your plugin uses some sort of sessions then you might want to reload the session as well when the authorization framework reloads your plugin. The framework tracks a number of reloads which is available for you as RuntimeEnvironment.getInstance().getPluginVersion() and you can check this number and invalidate your session if your version (tracked perhaps in your session) is different than this.

Restrictions

Custom classloader restricts the plugin to load only this classes from org.opengrok.indexer package:

private final static String[] classWhitelist = new String[]{
    "org.opengrok.indexer.configuration.Group",
    "org.opengrok.indexer.configuration.Project",
    "org.opengrok.indexer.configuration.RuntimeEnvironment",
    "org.opengrok.indexer.authorization.IAuthorizationPlugin",
    "org.opengrok.indexer.util.*",
    "org.opengrok.indexer.logger.*",
};

And explicitly forbids these packages to be extended.

private final static String[] packageBlacklist = new String[]{
    "java",
    "javax",
    "org.w3c",
    "org.xml",
    "org.omg",
    "sun"
};

Also JVM can forbid you to extend some packages which are not meant to be extended.

Set up

The plugin class must be compiled to the .class file (and the it can be packaged into .jar file). The frameworks supports both .class and .jar files. For compiling you have to provide opengrok.jar and the servlet api (HttpServletRequest) in the classpath

Example (for the TruePlugin which is included in the repository):

$ javac -classpath dist/opengrok.jar -d . plugins/TruePlugin.java

Then you can just drop the compiled .class file into plugin directory and deploy the webapp. If the plugin is a part of a package, then you have to copy the full directory path which is made by the compiler relatively to the source file. For example, if the plugin directory configured in the web application is /var/opengrok/share/auth/plugins/ and the package of a plugin FooPlugin is opengrok.auth.plugin, then the plugin has to be copied to /var/opengrok/share/auth/plugins/opengrok/auth/plugin/FooPlugin.class.

If anything goes wrong, you should find information in the logs of the application server.

Running

The framework consists of three parts

  1. PluginFramework

    This is the plugin container

    • performs the authorization decision
    • cache the decisions so that for each request the particular plugin's isAllowed for particular project/group is called only once
  2. ProjectHelper

    UI facade for filtered (authorized) projects/groups/repositories which should be ONLY used for displaying filtered information anywhere

    • provides methods how to get filtered projects/groups/repositories
    • cache the filtered results for each request so that it does not have to call the framework unnecessarily
  3. AuthorizationFilter

    Standard javax.servlet.Filter which is used for all urls to decide if the current request has access to the url

    • restricts user to go to forbidden xref/history/download etc. with 403 HTTP error
    • reacts only when the url contains information about project (so it can decide per project)

Every HTTP 403 error is logged. But information about the user is missing because the decision is made by plugins and the filter does not know how to interpret the request to get the user from it.

Troubleshooting

Using IDE

When using IDE (e.g. IDEA) to build your plugin; you can face a problem when the framework does not load your .jar file with ClassFormatError resulting to ClassNotFoundException.

Possible solutions are:

  1. disable debugging symbols
  2. compile the .java files with javac manually and then package it into .jar manually (command above)
  3. compile the .java files with javac manually and use this directory structure (without packaging)

This should solve the problem. If it does not then try to change the code.