jackrabbit-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From fmesc...@apache.org
Subject svn commit: r902734 - in /jackrabbit/sandbox/jackrabbit2-bundle/jackrabbit-core/src/main/java/org/apache/jackrabbit/core: api/ api/security/ osgi/ osgi/security/
Date Mon, 25 Jan 2010 08:56:14 GMT
Author: fmeschbe
Date: Mon Jan 25 08:56:14 2010
New Revision: 902734

URL: http://svn.apache.org/viewvc?rev=902734&view=rev
Log:
Add PluggableLoginModule and plugin API

Added:
    jackrabbit/sandbox/jackrabbit2-bundle/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/api/
    jackrabbit/sandbox/jackrabbit2-bundle/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/api/security/
    jackrabbit/sandbox/jackrabbit2-bundle/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/api/security/AbstractLoginModulePlugin.java
  (with props)
    jackrabbit/sandbox/jackrabbit2-bundle/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/api/security/LoginModulePlugin.java
  (with props)
    jackrabbit/sandbox/jackrabbit2-bundle/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/osgi/
    jackrabbit/sandbox/jackrabbit2-bundle/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/osgi/Activator.java
  (with props)
    jackrabbit/sandbox/jackrabbit2-bundle/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/osgi/RepositoryConfigurer.java
  (with props)
    jackrabbit/sandbox/jackrabbit2-bundle/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/osgi/security/
    jackrabbit/sandbox/jackrabbit2-bundle/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/osgi/security/PluggableDefaultLoginModule.java
  (with props)

Added: jackrabbit/sandbox/jackrabbit2-bundle/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/api/security/AbstractLoginModulePlugin.java
URL: http://svn.apache.org/viewvc/jackrabbit/sandbox/jackrabbit2-bundle/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/api/security/AbstractLoginModulePlugin.java?rev=902734&view=auto
==============================================================================
--- jackrabbit/sandbox/jackrabbit2-bundle/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/api/security/AbstractLoginModulePlugin.java
(added)
+++ jackrabbit/sandbox/jackrabbit2-bundle/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/api/security/AbstractLoginModulePlugin.java
Mon Jan 25 08:56:14 2010
@@ -0,0 +1,69 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.jackrabbit.core.api.security;
+
+import java.security.Principal;
+import java.util.Set;
+
+import javax.jcr.Credentials;
+import javax.jcr.RepositoryException;
+import javax.security.auth.login.FailedLoginException;
+
+/**
+ * The <code>AbstractLoginModulePlugin</code> class is a default implementation
+ * of the {@link LoginModulePlugin} interface providing a some default
+ * implementations. Extensions of this class still have to implement the core
+ * methods of the interface, namely
+ * {@link LoginModulePlugin#canHandle(Credentials)} and
+ * {@link LoginModulePlugin#authenticate(Principal, Credentials)}.
+ *
+ * @see LoginModulePlugin
+ */
+public abstract class AbstractLoginModulePlugin implements LoginModulePlugin {
+
+    /**
+     * The default implementation does not provide the user ID and relies on the
+     * <code>DefaultLoginModule</code> to extract the user ID from the
+     * credentials.
+     */
+    public String getUserId(Credentials credentials) {
+        return null;
+    }
+
+    /**
+     * The default implementation does not provide a principal and relies on the
+     * <code>DefaultLoginModule</code> to provide one for the credentials.
+     */
+    public Principal getPrincipal(Credentials credentials) {
+        return null;
+    }
+
+    /**
+     * Adds no principals to the set
+     */
+    public void addPrincipals(Set<Principal> principals) {
+    }
+
+    @SuppressWarnings("unused")
+    public int impersonate(Principal principal, Credentials credentials)
+            throws RepositoryException, FailedLoginException {
+        return IMPERSONATION_DEFAULT;
+    }
+
+}

Propchange: jackrabbit/sandbox/jackrabbit2-bundle/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/api/security/AbstractLoginModulePlugin.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: jackrabbit/sandbox/jackrabbit2-bundle/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/api/security/AbstractLoginModulePlugin.java
------------------------------------------------------------------------------
    svn:keywords = Author Date Id Revision Rev Url

Added: jackrabbit/sandbox/jackrabbit2-bundle/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/api/security/LoginModulePlugin.java
URL: http://svn.apache.org/viewvc/jackrabbit/sandbox/jackrabbit2-bundle/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/api/security/LoginModulePlugin.java?rev=902734&view=auto
==============================================================================
--- jackrabbit/sandbox/jackrabbit2-bundle/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/api/security/LoginModulePlugin.java
(added)
+++ jackrabbit/sandbox/jackrabbit2-bundle/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/api/security/LoginModulePlugin.java
Mon Jan 25 08:56:14 2010
@@ -0,0 +1,139 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.jackrabbit.core.api.security;
+
+import java.security.Principal;
+import java.util.Set;
+
+import javax.jcr.Credentials;
+import javax.jcr.RepositoryException;
+import javax.security.auth.login.FailedLoginException;
+
+/**
+ * The <code>LoginModulePlugin</code> interface defines the OSGi service API
for
+ * dynamic login module providers.
+ * <p>
+ * To use this plugin functionality the
+ * <code>org.apache.jackrabbit.core.security.pluggable.PluggableDefaultLoginModule</code>
+ * must be configued in the repository. Whenever a login process is initiated
+ * the <code>PluggableDefaultLoginModule</code> calls the
+ * {@link #canHandle(Credentials)} method of each registered
+ * <code>LoginModulePlugin</code>. The first plugin returning <code>true</code>
+ * is then used to handle all login related questions.
+ *
+ * @see AbstractLoginModulePlugin
+ */
+public interface LoginModulePlugin {
+
+    /**
+     * Returned by the {@link #impersonate(Principal, Credentials)} method if
+     * impersonation should be handled by the <code>DefaultLoginModule</code>.
+     */
+    static final int IMPERSONATION_DEFAULT = 0;
+
+    /**
+     * Returned by the {@link #impersonate(Principal, Credentials)} method if
+     * impersonation was handled by the login plugin and succeeded.
+     */
+    static final int IMPERSONATION_SUCCESS = 1;
+
+    /**
+     * Returned by the {@link #impersonate(Principal, Credentials)} method if
+     * impersonation was handled by the login plugin and failed. The
+     * <code>DefaultLoginModule</code> is not asked for impersonation in this
+     * case.
+     */
+    static final int IMPERSONATION_FAILED = 2;
+
+    /**
+     * Returns <code>true</code> if this plugin can process the given
+     * <code>credentials</code> object.
+     * <p>
+     * If no <code>LoginModulePlugin</code> can handle the credentials, the
+     * <code>DefaultLoginModule</code> is handling the complete authentication
+     * process and no plugin will be asked any more.
+     */
+    abstract boolean canHandle(Credentials credentials);
+
+    /**
+     * Returns the user ID encoded in the given credentials or <code>null</code>
+     * if this plugin does not support these Credentials.
+     * <p>
+     * This method is generally called by the <code>DefaultLoginModule</code>
if
+     * the {@link #getPrincipal(Credentials)} method returns <code>null</code>.
+     * <p>
+     * It is expected that this method only returns a non-<code>null</code>
+     * value if the {@link #canHandle(Credentials)} method returns
+     * <code>true</code>.
+     * <p>
+     * If <code>null</code> is returned the user ID is expected to be provided
+     * by the <code>AbstractLoginModule.getUserId(Credentials)</code> method.
+     */
+    String getUserId(Credentials credentials);
+
+    /**
+     * Return a Principal object or <code>null</code>.
+     * <p>
+     * If <code>null</code> is returned the Principal will be provided by the
+     * <code>DefaultLoginModule.getPrincipal</code> method.
+     *
+     * @return an instance of the Principal associated with these Credentials or
+     *         <code>null</code>
+     */
+    Principal getPrincipal(Credentials credentials);
+
+    /**
+     * Allows for adding additional {@link Principal} objects, such as groups or
+     * roles, to the {@link Subject}.
+     *
+     * @param principals original collection of principals
+     */
+    void addPrincipals(Set<Principal> principals);
+
+    /**
+     * Validate the credentials to identify the given principal. If the
+     * credentials identify the principal this method simply returns. If the
+     * credentials don't identify the principal the method throws a
+     * {@link FailedLoginException}.
+     * <p>
+     * This method is only called if the {@link #canHandle(Credentials)} returns
+     * <code>true</code>.
+     *
+     * @throws FailedLoginException If the credentials to not identify the
+     *             principal.
+     */
+    void authenticate(Principal principal, Credentials credentials)
+            throws FailedLoginException;
+
+    /**
+     * Returns a code indicating either the status of the impersonation attempt,
+     * or {@link #IMPERSONATION_DEFAULT} if the impersonation should be handled
+     * by
+     * {@link org.apache.jackrabbit.core.security.authentication.DefaultLoginModule#impersonate}
+     * .
+     * <p>
+     * This method is only called if the {@link #canHandle(Credentials)} returns
+     * <code>true</code>.
+     *
+     * @see org.apache.jackrabbit.core.security.authentication.DefaultLoginModule#impersonate
+     * @return one of {@link #IMPERSONATION_DEFAULT},
+     *         {@link #IMPERSONATION_SUCCESS} or {@link #IMPERSONATION_FAILED}
+     */
+    int impersonate(Principal principal, Credentials credentials)
+            throws RepositoryException, FailedLoginException;
+
+}

Propchange: jackrabbit/sandbox/jackrabbit2-bundle/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/api/security/LoginModulePlugin.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: jackrabbit/sandbox/jackrabbit2-bundle/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/api/security/LoginModulePlugin.java
------------------------------------------------------------------------------
    svn:keywords = Author Date Id Revision Rev Url

Added: jackrabbit/sandbox/jackrabbit2-bundle/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/osgi/Activator.java
URL: http://svn.apache.org/viewvc/jackrabbit/sandbox/jackrabbit2-bundle/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/osgi/Activator.java?rev=902734&view=auto
==============================================================================
--- jackrabbit/sandbox/jackrabbit2-bundle/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/osgi/Activator.java
(added)
+++ jackrabbit/sandbox/jackrabbit2-bundle/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/osgi/Activator.java
Mon Jan 25 08:56:14 2010
@@ -0,0 +1,307 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.jackrabbit.core.osgi;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.Hashtable;
+
+import org.apache.jackrabbit.core.api.security.LoginModulePlugin;
+import org.osgi.framework.BundleActivator;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.Constants;
+import org.osgi.framework.InvalidSyntaxException;
+import org.osgi.framework.ServiceEvent;
+import org.osgi.framework.ServiceListener;
+import org.osgi.framework.ServiceReference;
+import org.osgi.service.cm.Configuration;
+import org.osgi.service.cm.ConfigurationAdmin;
+import org.osgi.util.tracker.ServiceTracker;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * The <code>Activator</code> TODO
+ */
+public class Activator implements BundleActivator, ServiceListener {
+
+    /** default log */
+    private static final Logger log = LoggerFactory.getLogger(Activator.class);
+
+    public static final String SERVER_REPOSITORY_FACTORY_PID = "org.apache.sling.jcr.jackrabbit.server.SlingServerRepository";
+
+    /**
+     * The name of the configuration property naming the Sling Context for which
+     * a factory configuration has been created.
+     */
+    public static final String SLING_CONTEXT = "sling.context";
+
+    /**
+     * The name of the framework property containing the default sling context
+     * name.
+     */
+    public static final String SLING_CONTEXT_DEFAULT = "sling.context.default";
+
+    // The name of the Configuration Admin Service
+    private static final String CONFIG_ADMIN_NAME = ConfigurationAdmin.class.getName();
+
+    // this bundle's context, used by verifyConfiguration
+    private static BundleContext bundleContext;
+
+    // the service tracker used by the PluggableDefaultLoginModule
+    // this field is only set on the first call to getLoginModules()
+    private static ServiceTracker loginModuleTracker;
+
+    // the tracking count when the moduleCache has been filled
+    private static int lastTrackingCount = -1;
+
+    // the cache of login module services
+    private static LoginModulePlugin[] moduleCache;
+
+    // empty list of login modules if there are none registered
+    private static LoginModulePlugin[] EMPTY = new LoginModulePlugin[0];
+
+    // the name of the default sling context
+    private String slingContext;
+
+    private static AccessManagerFactoryTracker accessManagerFactoryTracker;
+
+    public void start(BundleContext context) {
+
+        bundleContext = context;
+
+        // ensure the module cache is not set right now, this may
+        // (theoretically) be non-null after the last bundle stop
+        moduleCache = null;
+
+        // check the name of the default context, nothing to do if none
+        slingContext = context.getProperty(SLING_CONTEXT_DEFAULT);
+        if (slingContext == null) {
+            slingContext = "default";
+        }
+
+        ServiceReference sr = context.getServiceReference(CONFIG_ADMIN_NAME);
+        if (sr != null) {
+
+            // immediately verify the configuration as the service is here
+            verifyConfiguration(sr);
+
+        } else {
+
+            // register as service listener for Configuration Admin to verify
+            // the configuration when the service is registered
+            try {
+                bundleContext.addServiceListener(this, "("
+                    + Constants.OBJECTCLASS + "=" + CONFIG_ADMIN_NAME + ")");
+            } catch (InvalidSyntaxException ise) {
+                log.error(
+                    "start: Failed to register for Configuration Admin Service, will not
verify configuration",
+                    ise);
+            }
+        }
+        if (accessManagerFactoryTracker == null) {
+            accessManagerFactoryTracker = new AccessManagerFactoryTracker(
+                bundleContext);
+        }
+        accessManagerFactoryTracker.open();
+    }
+
+    public void stop(BundleContext arg0) {
+
+        // drop module cache
+        moduleCache = null;
+
+        // close the loginModuleTracker
+        if (loginModuleTracker != null) {
+            loginModuleTracker.close();
+            loginModuleTracker = null;
+        }
+
+        if (accessManagerFactoryTracker != null) {
+            accessManagerFactoryTracker.close();
+            accessManagerFactoryTracker = null;
+        }
+
+        // clear the bundle context field
+        bundleContext = null;
+    }
+
+    // ---------- ServiceListener ----------------------------------------------
+
+    public void serviceChanged(ServiceEvent event) {
+        if (event.getType() == ServiceEvent.REGISTERED) {
+
+            // verify the configuration with the newly registered service
+            verifyConfiguration(event.getServiceReference());
+
+            // don't care for any more service state changes
+            bundleContext.removeServiceListener(this);
+        }
+    }
+
+    // ---------- LoginModule tracker for PluggableDefaultLoginModule
+
+    private static BundleContext getBundleContext() {
+        return bundleContext;
+    }
+
+    /**
+     * Returns the registered {@link LoginModulePlugin} services. If there are
+     * no {@link LoginModulePlugin} services registered, this method returns an
+     * empty array. <code>null</code> is never returned from this method.
+     */
+    public static LoginModulePlugin[] getLoginModules() {
+        // fast track cache (cache first, since loginModuleTracker is only
+        // non-null if moduleCache is non-null)
+        if (moduleCache != null
+            && lastTrackingCount == loginModuleTracker.getTrackingCount()) {
+            return moduleCache;
+        }
+        // invariant: moduleCache is null or modules have changed
+
+        // tracker may be null if moduleCache is null
+        if (loginModuleTracker == null) {
+            loginModuleTracker = new ServiceTracker(getBundleContext(),
+                LoginModulePlugin.class.getName(), null);
+            loginModuleTracker.open();
+        }
+
+        if (moduleCache == null
+            || lastTrackingCount < loginModuleTracker.getTrackingCount()) {
+            Object[] services = loginModuleTracker.getServices();
+            if (services == null || services.length == 0) {
+                moduleCache = EMPTY;
+            } else {
+                moduleCache = new LoginModulePlugin[services.length];
+                System.arraycopy(services, 0, moduleCache, 0, services.length);
+            }
+            lastTrackingCount = loginModuleTracker.getTrackingCount();
+        }
+
+        // the module cache is now up to date
+        return moduleCache;
+    }
+
+    public static AccessManagerFactoryTracker getAccessManagerFactoryTracker() {
+        return accessManagerFactoryTracker;
+    }
+
+    // ---------- internal -----------------------------------------------------
+
+    private String getRepositoryName() {
+        String repoName = bundleContext.getProperty("sling.repository.name");
+        if (repoName != null) {
+            return repoName; // the repository name is set
+        }
+
+        return "jackrabbit";
+    }
+
+    private void verifyConfiguration(ServiceReference ref) {
+        ConfigurationAdmin ca = (ConfigurationAdmin) bundleContext.getService(ref);
+        if (ca == null) {
+            log.error("verifyConfiguration: Failed to get Configuration Admin Service from
Service Reference");
+            return;
+        }
+
+        try {
+            // find a configuration for theses properties...
+            Configuration[] cfgs = ca.listConfigurations("("
+                + ConfigurationAdmin.SERVICE_FACTORYPID + "="
+                + SERVER_REPOSITORY_FACTORY_PID + ")");
+            if (cfgs != null && cfgs.length > 0) {
+                log.info(
+                    "verifyConfiguration: {} Configurations available for {}, nothing to
do",
+                    new Object[] { new Integer(cfgs.length),
+                        SERVER_REPOSITORY_FACTORY_PID });
+                return;
+            }
+
+            // No config, create a default one.
+            Hashtable<String, String> defaultConfig = new Hashtable<String, String>();
+            initDefaultConfig(defaultConfig, bundleContext);
+
+            // create the factory and set the properties
+            Configuration config = ca.createFactoryConfiguration(SERVER_REPOSITORY_FACTORY_PID);
+            config.update(defaultConfig);
+
+            log.info("verifyConfiguration: Created configuration {} for {}",
+                config.getPid(), config.getFactoryPid());
+
+        } catch (Throwable t) {
+            log.error(
+                "verifyConfiguration: Cannot check or define configuration", t);
+        } finally {
+            bundleContext.ungetService(ref);
+        }
+    }
+
+    private void initDefaultConfig(Hashtable<String, String> props,
+            BundleContext bundleContext) throws IOException {
+        File homeDir = getHomeDir(bundleContext);
+        if (homeDir == null) return;
+
+        // default config values
+        props.put(SLING_CONTEXT, slingContext);
+        props.put(RepositoryConfigurer.REPOSITORY_CONFIG_URL,
+            getConfig(bundleContext, homeDir));
+        props.put(RepositoryConfigurer.REPOSITORY_HOME_DIR, homeDir.getPath());
+        props.put(RepositoryConfigurer.REPOSITORY_REGISTRATION_NAME,
+            this.getRepositoryName());
+    }
+
+    private File getHomeDir(BundleContext bundleContext) {
+        File homeDir;
+
+        String repoHomePath = bundleContext.getProperty("sling.repository.home");
+        String slingHomePath = bundleContext.getProperty("sling.home");
+
+        if (repoHomePath != null) {
+            homeDir = new File(repoHomePath, getRepositoryName());
+        } else if (slingHomePath != null) {
+            homeDir = new File(slingHomePath, getRepositoryName());
+        } else {
+            homeDir = new File(getRepositoryName());
+        }
+
+        // make sure jackrabbit home exists
+        log.info("Creating default config for Jackrabbit in " + homeDir);
+        if (!homeDir.isDirectory()) {
+            if (!homeDir.mkdirs()) {
+                log.info("verifyConfiguration: Cannot create Jackrabbit home "
+                    + homeDir + ", failed creating default configuration");
+                return null;
+            }
+        }
+
+        return homeDir;
+    }
+
+    private static String getConfig(BundleContext bundleContext, File homeDir) {
+        File configFile;
+
+        String repoConfigFileUrl = bundleContext.getProperty("sling.repository.config.file.url");
+        if (repoConfigFileUrl != null) {
+            return repoConfigFileUrl;
+        }
+
+        // ensure the configuration file (inside the home Dir !)
+        configFile = new File(homeDir, "repository.xml");
+        return configFile.getAbsolutePath();
+    }
+
+}

Propchange: jackrabbit/sandbox/jackrabbit2-bundle/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/osgi/Activator.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: jackrabbit/sandbox/jackrabbit2-bundle/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/osgi/Activator.java
------------------------------------------------------------------------------
    svn:keywords = Author Date Id Revision Rev Url

Added: jackrabbit/sandbox/jackrabbit2-bundle/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/osgi/RepositoryConfigurer.java
URL: http://svn.apache.org/viewvc/jackrabbit/sandbox/jackrabbit2-bundle/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/osgi/RepositoryConfigurer.java?rev=902734&view=auto
==============================================================================
--- jackrabbit/sandbox/jackrabbit2-bundle/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/osgi/RepositoryConfigurer.java
(added)
+++ jackrabbit/sandbox/jackrabbit2-bundle/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/osgi/RepositoryConfigurer.java
Mon Jan 25 08:56:14 2010
@@ -0,0 +1,346 @@
+/*
+ * Copyright 1997-2010 Day Management AG
+ * Barfuesserplatz 6, 4001 Basel, Switzerland
+ * All Rights Reserved.
+ *
+ * This software is the confidential and proprietary information of
+ * Day Management AG, ("Confidential Information"). You shall not
+ * disclose such Confidential Information and shall use it only in
+ * accordance with the terms of the license agreement you entered into
+ * with Day.
+ */
+package org.apache.jackrabbit.core.osgi;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.util.Dictionary;
+import java.util.Enumeration;
+import java.util.Hashtable;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+
+import javax.jcr.RepositoryException;
+
+import org.apache.commons.io.IOUtils;
+import org.apache.jackrabbit.api.JackrabbitRepository;
+import org.apache.jackrabbit.core.RepositoryImpl;
+import org.apache.jackrabbit.core.config.RepositoryConfig;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.Constants;
+import org.osgi.framework.ServiceRegistration;
+import org.osgi.service.cm.ConfigurationException;
+import org.osgi.service.cm.ManagedServiceFactory;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class RepositoryConfigurer implements ManagedServiceFactory {
+
+    /**
+     * The name of the configuration property defining the URL to the repository
+     * configuration file (value is "config").
+     * <p>
+     * If the configuration file is located in the local file system, the
+     * "file:" scheme must still be specified.
+     * <p>
+     * This parameter is mandatory for this activator to start the repository.
+     *
+     * @scr.property value=""
+     */
+    public static final String REPOSITORY_CONFIG_URL = "config";
+
+    /**
+     * The name of the configuration property defining the file system directory
+     * where the repository files are located (value is "home").
+     * <p>
+     * This parameter is mandatory for this activator to start the repository.
+     *
+     * @scr.property value=""
+     */
+    public static final String REPOSITORY_HOME_DIR = "home";
+
+    /**
+     * @scr.property value=""
+     */
+    public static final String REPOSITORY_REGISTRATION_NAME = "name";
+
+    /** default log */
+    private final Logger log = LoggerFactory.getLogger(getClass());
+
+    private final Map<String, RepositoryHolder> repositories = new ConcurrentHashMap<String,
RepositoryHolder>();
+
+    private final BundleContext bundleContext;
+
+    private final ServiceRegistration configurerService;
+
+    public RepositoryConfigurer(final BundleContext bundleContext) {
+        this.bundleContext = bundleContext;
+
+        final Hashtable<String, Object> cfgServiceProps = new Hashtable<String,
Object>();
+        cfgServiceProps.put(Constants.SERVICE_DESCRIPTION,
+            getConfigurerServiceDescription());
+        cfgServiceProps.put(Constants.SERVICE_VENDOR, getVendor());
+
+        configurerService = bundleContext.registerService(
+            "org.osgi.service.cm.ManagedServiceFactory", this, cfgServiceProps);
+    }
+
+    protected void shutdown() {
+        if (configurerService != null) {
+            configurerService.unregister();
+        }
+
+        String[] pids = repositories.keySet().toArray(
+            new String[repositories.size()]);
+        for (String pid : pids) {
+            stopRepository(pid);
+        }
+    }
+
+    // ---------- Overwritable support methods
+
+    protected String getVendor() {
+        return "The Apache Software Foundation";
+    }
+
+    protected String getConfigurerServiceDescription() {
+        return "Apache Jackrabbit Repository Configurer";
+    }
+
+    protected String[] getRepositoryServiceNames() {
+        return new String[] { "javax.jcr.Repository",
+            "org.apache.jackrabbit.api.JackrabbitRepository" };
+    }
+
+    protected String getRepositoryServiceDescription() {
+        return "Embedded Jackrabbit Repository";
+    }
+
+    protected String getDefaultRepositoryUrl() {
+        return "repository.xml";
+    }
+
+    protected String getDefaultRepositoryHome() {
+        return "repository";
+    }
+
+    // ---------- ManagedServiceFactory interface
+
+    public final String getName() {
+        return getConfigurerServiceDescription();
+    }
+
+    @SuppressWarnings("unchecked")
+    public final void updated(final String pid, final Dictionary properties)
+            throws ConfigurationException {
+
+        // stop repository, if this is an update to existing configuration
+        if (repositories.containsKey(pid)) {
+            log.info("updated: Reconfiguring repository {}", pid);
+            stopRepository(pid);
+        } else {
+            log.info("updated: Starting repository {}", pid);
+        }
+
+        // start new repository with updated/new configuration
+        try {
+            startRepository(pid, properties);
+        } catch (RepositoryException re) {
+            throw new ConfigurationException(
+                null,
+                "Failed starting repository " + pid + " from new configuration",
+                re);
+        }
+    }
+
+    public final void deleted(final String pid) {
+        stopRepository(pid);
+    }
+
+    // ---------- Repository startup and shutdown
+
+    private void startRepository(final String pid,
+            final Dictionary<String, ?> properties) throws ConfigurationException,
RepositoryException {
+
+        final String config = getProperty(properties, REPOSITORY_CONFIG_URL,
+            getDefaultRepositoryUrl());
+        final String home = getProperty(properties, REPOSITORY_HOME_DIR,
+            getDefaultRepositoryHome());
+
+        InputStream ins = null;
+        try {
+
+            // check whether the URL is a file path
+            final File configFile = new File(config);
+            if (configFile.canRead()) {
+
+                ins = new FileInputStream(configFile);
+                log.info("startRepository: Using configuration file {}",
+                    configFile.getAbsolutePath());
+
+            } else {
+
+                try {
+
+                    URL configURL = new URL(config);
+                    ins = configURL.openStream();
+                    log.info("startRepository: Using configuration URL ",
+                        configURL);
+
+                } catch (MalformedURLException mue) {
+
+                    // last ressort: URL is a file, which we have to prepare yet
+                    copyFile(configFile);
+                    ins = new FileInputStream(configFile);
+                    log.info(
+                        "startRepository: Using new default configuration file {}",
+                        configFile.getAbsolutePath());
+
+                }
+            }
+
+            final RepositoryConfig crc = RepositoryConfig.create(ins, home);
+            final JackrabbitRepository repository = RepositoryImpl.create(crc);
+
+            final Dictionary<String, Object> props = copyProperties(properties);
+            props.put(Constants.SERVICE_DESCRIPTION,
+                getRepositoryServiceDescription());
+            props.put(Constants.SERVICE_VENDOR, getVendor());
+
+            final ServiceRegistration registration = bundleContext.registerService(
+                getRepositoryServiceNames(), repository, props);
+
+            repositories.put(pid,
+                new RepositoryHolder(repository, registration));
+
+        } catch (IOException ioe) {
+
+            log.error("startRepository: IO problem starting repository from "
+                + config + " in " + home, ioe);
+
+        } catch (RepositoryException re) {
+
+            log.error(
+                "startRepository: Repository problem starting repository from "
+                    + config + " in " + home, re);
+        } finally {
+            if (ins != null) {
+                try {
+                    ins.close();
+                } catch (IOException ioe) {
+                    // ignore
+                }
+            }
+        }
+    }
+
+    private void stopRepository(final String pid) {
+        RepositoryHolder repository = repositories.remove(pid);
+        if (repository != null) {
+            try {
+                repository.stop();
+            } catch (Throwable t) {
+                log.error(
+                    "stopRepository: Unexpected problem stopping repository "
+                        + pid, t);
+            }
+        }
+    }
+
+    // ---------- Repository setup helper
+
+    private String getProperty(final Dictionary<String, ?> properties,
+            final String propName, final String defaultValue)
+            throws ConfigurationException {
+        final Object propertyObj = properties.get(REPOSITORY_CONFIG_URL);
+        if (propertyObj == null) {
+            return defaultValue;
+        }
+
+        if (propertyObj instanceof String) {
+            final String tmp = (String) propertyObj;
+            if (tmp == null || tmp.length() == 0) {
+                return getDefaultRepositoryUrl();
+            }
+
+            return tmp;
+        }
+
+        // property is not a string
+        throw new ConfigurationException(REPOSITORY_CONFIG_URL,
+            "Repository Configuration URL must be a single string");
+    }
+
+    private Dictionary<String, Object> copyProperties(
+            final Dictionary<String, ?> source) {
+        Hashtable<String, Object> dest = new Hashtable<String, Object>();
+        final Enumeration<String> keys = source.keys();
+        while (keys.hasMoreElements()) {
+            final String key = keys.nextElement();
+            if (!key.startsWith(".")) {
+                dest.put(key, source.get(key));
+            }
+        }
+        return dest;
+    }
+
+    private void copyFile(File destFile) throws FileNotFoundException,
+            IOException {
+
+        // if the file already exists, there is nothing more to do
+        if (destFile.canRead()) {
+            return;
+        }
+
+        // access the defualt file (fail if missing)
+        final String entryPath = "OSGI-INF/repository_osgi.xml";
+        URL entryURL = bundleContext.getBundle().getEntry(entryPath);
+        if (entryURL == null) {
+            throw new FileNotFoundException(entryPath);
+        }
+
+        // check for a file property
+        InputStream source = entryURL.openStream();
+
+        OutputStream dest = null;
+        try {
+
+            // ensure the path to the config file exists
+            destFile.getParentFile().mkdirs();
+
+            dest = new FileOutputStream(destFile);
+            IOUtils.copy(source, dest);
+
+        } finally {
+
+            IOUtils.closeQuietly(dest);
+            IOUtils.closeQuietly(source);
+
+        }
+    }
+
+    private static class RepositoryHolder {
+
+        private final JackrabbitRepository repository;
+
+        private final ServiceRegistration registration;
+
+        public RepositoryHolder(final JackrabbitRepository repository,
+
+        final ServiceRegistration registration) {
+            this.repository = repository;
+            this.registration = registration;
+        }
+
+        void stop() {
+            registration.unregister();
+            repository.shutdown();
+        }
+    }
+}

Propchange: jackrabbit/sandbox/jackrabbit2-bundle/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/osgi/RepositoryConfigurer.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: jackrabbit/sandbox/jackrabbit2-bundle/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/osgi/RepositoryConfigurer.java
------------------------------------------------------------------------------
    svn:keywords = Author Date Id Revision Rev Url

Added: jackrabbit/sandbox/jackrabbit2-bundle/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/osgi/security/PluggableDefaultLoginModule.java
URL: http://svn.apache.org/viewvc/jackrabbit/sandbox/jackrabbit2-bundle/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/osgi/security/PluggableDefaultLoginModule.java?rev=902734&view=auto
==============================================================================
--- jackrabbit/sandbox/jackrabbit2-bundle/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/osgi/security/PluggableDefaultLoginModule.java
(added)
+++ jackrabbit/sandbox/jackrabbit2-bundle/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/osgi/security/PluggableDefaultLoginModule.java
Mon Jan 25 08:56:14 2010
@@ -0,0 +1,169 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.jackrabbit.core.osgi.security;
+
+import java.security.Principal;
+import java.util.Set;
+
+import javax.jcr.Credentials;
+import javax.jcr.RepositoryException;
+import javax.security.auth.login.FailedLoginException;
+
+import org.apache.jackrabbit.core.api.security.LoginModulePlugin;
+import org.apache.jackrabbit.core.osgi.Activator;
+import org.apache.jackrabbit.core.security.authentication.DefaultLoginModule;
+
+/**
+ * The <code>PluggableDefaultLoginModule</code> class is a special login module
+ * which allows the list of actual login modules to be extended by OSGi services
+ * registered as {@link LoginModulePlugin}. If no plugins are registered or no
+ * registered plugin is willing to authenticate some kind of credentials this
+ * login module falls back to the {@link DefaultLoginModule} implementation.
+ * <p>
+ * To use this login module, the Jackrabbit Core Bundle must be deployed in an
+ * OSGi framework and this <code>PluggableDefaultLoginModule</code> must
+ * be configured in the <code>&lt;LoginModule&gt;</code> element of the
+ * repository configuration.
+ *
+ * @see LoginModulePlugin
+ * @see org.apache.jackrabbit.core.api.security.AbstractLoginModulePlugin
+ * @see DefaultLoginModule
+ */
+public class PluggableDefaultLoginModule extends DefaultLoginModule {
+
+    /**
+     * The {@link LoginModulePlugin} used for this login process.
+     * <p>
+     * This is set on demand by the {@link #getActivePlugin(Credentials)} method
+     * to cache the reference during the login process.
+     */
+    private LoginModulePlugin activePlugin;
+
+    /**
+     * Overwrites the <code>AbstractLoginModule</code> implementation to check
+     * whether one of the plugins can provide the user ID for the given
+     * credentials. If not the base class implementation is called.
+     */
+    @Override
+    protected String getUserID(Credentials creds) {
+        final LoginModulePlugin plugin = getActivePlugin(creds);
+        if (plugin != null) {
+            final String userId = plugin.getUserId(creds);
+            if (userId != null) {
+                return userId;
+            }
+        }
+
+        return super.getUserID(creds);
+    }
+
+    /**
+     * Overwrites the <code>DefaultLoginModule</code> implementation to check
+     * whether one of the plugins can provide a Principal. If not the base class
+     * implementation is called.
+     */
+    @Override
+    protected Principal getPrincipal(Credentials creds) {
+        final LoginModulePlugin plugin = getActivePlugin(creds);
+        if (plugin != null) {
+            final Principal p = plugin.getPrincipal(creds);
+            if (p != null) {
+                return p;
+            }
+        }
+
+        return super.getPrincipal(creds);
+    }
+
+    /**
+     * Calls base class implementation to get the base set of principals and the
+     * calls the currently used plugin to add further prinicipals and returns
+     * the completed set.
+     */
+    @Override
+    protected Set<Principal> getPrincipals() {
+        final Set<Principal> principals = super.getPrincipals();
+        final LoginModulePlugin plugin = getActivePlugin(null);
+        if (plugin != null) {
+            plugin.addPrincipals(principals);
+        }
+        return principals;
+    }
+
+    /**
+     * Calls the authenticate method of the plugin handling the given
+     * credentials and returns <code>true</code>. If no plugin can handle the
+     * credentials the result of calling the base class implementation is
+     * returned. If the plugin fails to authenticate the credentials for the
+     * principal a <code>FailedLoginException</code> is thrown.
+     */
+    @Override
+    protected boolean authenticate(Principal principal, Credentials credentials)
+            throws FailedLoginException, RepositoryException {
+        final LoginModulePlugin plugin = getActivePlugin(credentials);
+        if (plugin != null) {
+            plugin.authenticate(principal, credentials);
+            return true;
+        }
+
+        return super.authenticate(principal, credentials);
+    }
+
+    /**
+     * Calls the impersonate method of the plugin handling the given credentials
+     * and returns <code>true</code> or <code>false</code> if the
plugin handles
+     * impersonation. If no plugin can handle the credentials or the selected
+     * plugin does not handle impersonation the result of calling the base class
+     * implementation is returned. If impersonation either by the plugin or the
+     * base class implementation fails a <code>FailedLoginException</code> is
+     * thrown.
+     */
+    @Override
+    protected boolean impersonate(Principal principal, Credentials creds)
+            throws RepositoryException, FailedLoginException {
+
+        final LoginModulePlugin plugin = getActivePlugin(creds);
+        if (plugin != null) {
+            final int result = plugin.impersonate(principal, creds);
+            if (result != LoginModulePlugin.IMPERSONATION_DEFAULT) {
+                return result == LoginModulePlugin.IMPERSONATION_SUCCESS;
+            }
+        }
+
+        return super.impersonate(principal, creds);
+    }
+
+    /**
+     * Returns the {@link LoginModulePlugin} handling the given credentials or
+     * <code>null</code> if no plugin is able to handle the credentials.
+     *
+     * @param creds
+     * @return
+     */
+    private LoginModulePlugin getActivePlugin(Credentials creds) {
+        if (activePlugin == null && creds != null) {
+            LoginModulePlugin[] modules = Activator.getLoginModules();
+            for (int i = 0; i < modules.length; i++) {
+                if (modules[i].canHandle(creds)) {
+                    activePlugin = modules[i];
+                    break;
+                }
+            }
+        }
+        return activePlugin;
+    }
+}

Propchange: jackrabbit/sandbox/jackrabbit2-bundle/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/osgi/security/PluggableDefaultLoginModule.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: jackrabbit/sandbox/jackrabbit2-bundle/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/osgi/security/PluggableDefaultLoginModule.java
------------------------------------------------------------------------------
    svn:keywords = Author Date Id Revision Rev Url



Mime
View raw message