jackrabbit-oak-dev mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From Jukka Zitting <jukka.zitt...@gmail.com>
Subject Login modules and the infamous loginAdministrative()
Date Wed, 16 May 2012 10:35:30 GMT
Hi,

I had a quick look at the current authentication configuration in
oak-core. Currently the ContentRepositoryImpl class simply creates a
LoginContextProviderImpl instance with a hardcoded configuration. As
said in the related TODO, we'll need to come up with an easy way to
specify custom authentication configurations.

One concrete case where such support will be needed is the infamous
SlingRepository.loginAdministrative() method. So far we've implemented
that method on top of Jackrabbit 2.x either by hardcoding admin
credentials or by explicitly exposing implementation details for that.
Neither solution is ideal.

The JAAS model provides a much cleaner approach for this and other
similar cases where elevated privileges are needed. Using a custom
LoginModule implementation like the one included at the end of this
message and a special AdministratorPrincipal class, a method like
loginAdministrative() can safely elevate it's privileges with code
like this:

    @Override
    public Session loginAdministrative(final String workspace)
            throws RepositoryException {
        try {
            return PrincipalLoginModule.doWithPrincipal(
                    new AdministratorPrincipal(),
                    new PrivilegedExceptionAction<Session>() {
                        @Override
                        public Session run() throws Exception {
                            return login(workspace);
                        }
                    });
        } catch (RepositoryException e) {
            throw e;
        } catch (Exception e) {
            throw new RepositoryException("Unexpected login failure", e);
        }
    }

The only requirement here is that the SlingRepository implementation
needs to be able to inject such a custom login module to the
authentication configuration of the repository. To do that in a way
that doesn't open up a backdoor to any repository client, the
injection needs to happen when the repository instance is being
constructed, before it is exposed to other clients. Once that's done,
we can easily control access to this privilege elevation mechanism
with standard Java visibility rules on either the doWithPrincipal()
method or the AdministratorPrincipal class.

Of course the loginAdministrative() method is still such a backdoor to
anyone with a reference to the SlingRepository instance, but that's a
design issue in Sling rather than in Jackrabbit or Oak.

BR,

Jukka Zitting


/**
 * Login module that injects an explicitly provided principal to the
 * {@link Subject} being authenticated. This login module passes
 * authentication if such a principal is provided, and fails it if not.
 */
public class PrincipalLoginModule implements LoginModule {

    /**
     * A thread-local variable that records the current explicitly
     * provided principal to be used in authentication.
     */
    private static final ThreadLocal<Principal> currentPrincipal =
            new ThreadLocal<Principal>();

    /**
     * Performs the given action with the specified principal being
     * made available for authentication.
     *
     * @param principal explicitly provided principal
     * @param action action to be performed
     * @return return value of the given action
     * @throws Exception exception thrown by the given action
     */
    public static <T> T doWithPrincipal(
            Principal principal, PrivilegedExceptionAction<T> action)
            throws Exception {
        // No need for synchronization since we're using a thread-local
        Principal before = currentPrincipal.get();
        currentPrincipal.set(principal);
        try {
            return action.run();
        } finally {
            if (before != null) {
                currentPrincipal.set(before);
            } else {
                currentPrincipal.remove();
            }
        }
    }

    private Subject subject;

    private Principal principal;

    //-------------------------------------------------------< LoginModule >--

    @Override
    public void initialize(
            Subject subject, CallbackHandler callbackHandler,
            Map<String, ?> sharedState, Map<String, ?> options) {
        this.subject = subject;
        this.principal = null;
    }

    @Override
    public boolean login() {
        principal = currentPrincipal.get();
        return principal != null;
    }

    @Override
    public boolean commit() {
        if (principal != null) {
            subject.getPrincipals().add(principal);
        }
        return true;
    }

    @Override
    public boolean abort() {
        principal = null;
        return true;
    }

    @Override
    public boolean logout() {
        if (principal != null) {
            subject.getPrincipals().remove(principal);
        }
        return true;
    }

}

Mime
View raw message