You are viewing a plain text version of this content. The canonical link for it is here.
Posted to oak-dev@jackrabbit.apache.org by Jukka Zitting <ju...@gmail.com> on 2012/05/16 12:35:30 UTC

Login modules and the infamous loginAdministrative()

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;
    }

}

Re: Login modules and the infamous loginAdministrative()

Posted by Jukka Zitting <ju...@gmail.com>.
Hi,

On Wed, May 16, 2012 at 1:25 PM, Angela Schreiber <an...@adobe.com> wrote:
> sure... that's what the TODO is for. i prefer to think about the
> configuration setup using a concrete example.

I think the SlingRepository case, as described in my earlier post,
provides a fairly straightforward and concrete case for the kind of
configuration support we'll need. Basically we'll need something to
fill in to the blanks in the method below that'll associate the
constructed ContentRepository instance with a LoginModule like the one
I mentioned.

    SlingRepository createSlingRepository(MicroKernel mk) {
        ContentRepository cr = new ContentRepositoryImpl(mk, ...);
        ....;
        return new SlingRepositoryImpl(cr);
    }

Once we have that, it should be easy to use the same mechanism for
plugging in any kinds of login configurations.

> in general i would prefer having it slightly more mature before committing :)

Sure, no rush with this. I just wanted to bring this up since it was
fresh on my mind after looking at the OSGi side of the authentication
story in [1].

[1] http://blogs.adobe.com/jzitting/jaas-authentication-and-osgi/

BR,

Jukka Zitting

Re: Login modules and the infamous loginAdministrative()

Posted by Angela Schreiber <an...@adobe.com>.
hi jukka

> 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.

sure... that's what the TODO is for. i prefer to think about the
configuration setup using a concrete example. this is still at a
very early stage and i wouldn't have committed anything if the
code base would be a little bit more stable... not problem with
that, it just makes the auth setup moving target as well. in general
i would prefer having it slightly more mature before committing :)

kind regards
angela