Start line:  
End line:  

Snippet Preview

Snippet HTML Code

Stack Overflow Questions
  /*
   * 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.karaf.service.guard.impl;
 
 import java.util.List;
 import java.util.Set;
 
 
 
 public class GuardProxyCatalog implements ServiceListener {
     public static final String KARAF_SECURED_SERVICES_SYSPROP = "karaf.secured.services";
     public static final String SERVICE_GUARD_ROLES_PROPERTY = "org.apache.karaf.service.guard.roles";
 
     static final String PROXY_CREATOR_THREAD_NAME = "Secure OSGi Service Proxy Creator";
     static final String PROXY_SERVICE_KEY = "." + GuardProxyCatalog.class.getName(); // The only currently used value is Boolean.TRUE
     static final String SERVICE_ACL_PREFIX = "org.apache.karaf.service.acl.";
     static final String SERVICE_GUARD_KEY = "service.guard";
     static final Logger LOG = LoggerFactory.getLogger(GuardProxyCatalog.class);
 
     private static final Pattern JAVA_METHOD_NAME_PATTERN = Pattern.compile("[a-zA-Z_$][a-zA-Z0-9_$]*");
     private static final String ROLE_WILDCARD = "*";
 
     private final BundleContext myBundleContext;
 
 
     // These two variables control the proxy creator thread, which is started as soon as a ProxyManager Service
     // becomes available.
     volatile boolean runProxyCreator = true;
     volatile Thread proxyCreatorThread = null;
 
     GuardProxyCatalog(BundleContext bcthrows Exception {
         .trace("Starting GuardProxyCatalog");
          = bc;
 
         // The service listener is used to update/unregister proxies if the backing service changes/goes away
         bc.addServiceListener(this);
 
         Filter caFilter = getNonProxyFilter(bcConfigurationAdmin.class);
         .trace("Creating Config Admin Tracker using filter {}"caFilter);
         = new ServiceTracker<ConfigurationAdminConfigurationAdmin>(bccaFilternull);
        .open();
        Filter pmFilter = getNonProxyFilter(bcProxyManager.class);
        .trace("Creating Proxy Manager Tracker using filter {}"pmFilter);
        .open();
    }
    static Filter getNonProxyFilter(BundleContext bcClass<?> clazzthrows InvalidSyntaxException {
        Filter caFilter = bc.createFilter(
                "(&(" + . + "=" + clazz.getName() +
                        ")(!(" +  + "=*)))");
        return caFilter;
    }
    void close() {
        .trace("Stopping GuardProxyCatalog");
        stopProxyCreator();
        .close();
        .close();
        // Remove all proxy registrations
        for (ServiceRegistrationHolder holder : .values()) {
            ServiceRegistration<?> reg = holder.registration;
            if (reg != null) {
                .info("Unregistering proxy service of {} with properties {}",
                        reg.getReference().getProperty(.), copyProperties(reg.getReference()));
                reg.unregister();
            }
        }
        .clear();
    }
    @Override
    public void serviceChanged(ServiceEvent event) {
        // This method is to ensure that proxied services follow the original service. I.e. if the original service
        // goes away the proxies should go away too. If the original service is modified, the proxies should be
        // modified accordingly
        ServiceReference<?> sr = event.getServiceReference();
        if (event.getType() == .) {
            // Nothing to do for new services
            return;
        }
        if (isProxy(sr)) {
            // Ignore proxies, we only react to real service changes
            return;
        }
        Long orgServiceID = (Longsr.getProperty(.);
        if (event.getType() == .) {
            handleOriginalServiceUnregistering(orgServiceID);
        }
        if ((event.getType() & (. | .)) > 0) {
            handleOriginalServiceModifed(orgServiceIDsr);
        }
    }
    private void handleOriginalServiceUnregistering(Long orgServiceID) {
        // If the service queued up to be proxied, remove it.
        for (Iterator<CreateProxyRunnablei = .iterator(); i.hasNext(); ) {
            CreateProxyRunnable cpr = i.next();
            if (orgServiceID.equals(cpr.getOriginalServiceID())) {
                i.remove();
            }
        }
        ServiceRegistrationHolder holder = .remove(orgServiceID);
        if (holder != null) {
            if (holder.registration != null) {
                holder.registration.unregister();
            }
        }
    }
    private void handleOriginalServiceModifed(Long orgServiceIDServiceReference<?> orgServiceRef) {
        // We don't need to do anything for services that are queued up to be proxied, as the
        // properties are only taken at the point of proxyfication...
        ServiceRegistrationHolder holder = .get(orgServiceID);
        if (holder != null) {
            ServiceRegistration<?> reg = holder.registration;
            if (reg != null) {
                // Preserve the roles as they are expensive to compute
                Object roles = reg.getReference().getProperty();
                Dictionary<StringObjectnewProxyProps = proxyProperties(orgServiceRef);
                if (roles != null) {
                    newProxyProps.put(roles);
                } else {
                    newProxyProps.remove();
                }
                reg.setProperties(newProxyProps);
            }
        }
    }
    boolean isProxy(ServiceReference<?> sr) {
        return sr.getProperty() != null;
    }
    // Called by hooks to find out whether the service should be hidden.
    // Also handles the proxy creation of services if applicable.
    // Return true if the hook should hide the service for the bundle
        // Note that when running under an OSGi R6 framework the number of proxies created
        // can be limited by looking at the new 'service.scope' property. If the value is
        // 'singleton' then the same proxy can be shared across all clients.
        // Pre OSGi R6 it is not possible to find out whether a service is backed by a
        // Service Factory, so we assume that every service is.
        if (isProxy(sr)) {
            return false;
        }
        proxyIfNotAlreadyProxied(sr); // Note does most of the work async
        return true;
    }
    void proxyIfNotAlreadyProxied(final ServiceReference<?> originalRef)  {
        final long orgServiceID = (LongoriginalRef.getProperty(.);
        // make sure it's on the map before the proxy is registered, as that can trigger
        // another call into this method, and we need to make sure that it doesn't proxy
        // the service again.
        final ServiceRegistrationHolder registrationHolder = new ServiceRegistrationHolder();
        ServiceRegistrationHolder previousHolder = .putIfAbsent(orgServiceIDregistrationHolder);
        if (previousHolder != null) {
            // There is already a proxy for this service
            return;
        }
        .trace("Will create proxy of service {}({})"originalRef.getProperty(.), orgServiceID);
        // Instead of immediately creating the proxy, we add the code that creates the proxy to the proxyQueue.
        // This means that we can better react to the fact that the ProxyManager service might arrive
        // later. As soon as the Proxy Manager is available, the queue is emptied and the proxies created.
        CreateProxyRunnable cpr = new CreateProxyRunnable() {
            @Override
            public long getOriginalServiceID() {
                return orgServiceID;
            }
            @Override
            public void run(final ProxyManager pmthrows Exception {
                String[] objectClassProperty = (String[]) originalRef.getProperty(.);
                ServiceFactory<Objectsf = new ProxyServiceFactory(pmoriginalRef);
                registrationHolder.registration = originalRef.getBundle().getBundleContext().registerService(
                        objectClassPropertysfproxyPropertiesRoles());
                Dictionary<StringObjectactualProxyProps = copyProperties(registrationHolder.registration.getReference());
                .debug("Created proxy of service {} under {} with properties {}",
                        orgServiceIDactualProxyProps.get(.), actualProxyProps);
            }
            private Dictionary<StringObjectproxyPropertiesRoles() throws Exception {
                Dictionary<StringObjectp = proxyProperties(originalRef);
                Set<Stringroles = getServiceInvocationRoles(originalRef);
                if (roles != null) {
                    roles.remove(); // we don't expose that on the service property
                    p.put(roles);
                } else {
                    // In this case there are no roles defined for the service so anyone can invoke it
                    p.remove();
                }
                return p;
            }
        };
        try {
            .put(cpr);
        } catch (InterruptedException e) {
            .warn("Problem scheduling a proxy creator for service {}({})",
                    originalRef.getProperty(.), orgServiceIDe);
            e.printStackTrace();
        }
    }
    private static Dictionary<StringObjectproxyProperties(ServiceReference<?> sr) {
        Dictionary<StringObjectp = copyProperties(sr);
        p.put(.);
        return p;
    }
    private static Dictionary<StringObjectcopyProperties(ServiceReference<?> sr) {
        Dictionary<StringObjectp = new Hashtable<StringObject>();
        for (String key : sr.getPropertyKeys()) {
            p.put(keysr.getProperty(key));
        }
        return p;
    }
    // Returns what roles can possibly ever invoke this service. Note that not every invocation may be successful
    // as there can be different roles for different methods and also roles based on arguments passed in.
    Set<StringgetServiceInvocationRoles(ServiceReference<?> serviceReferencethrows Exception {
        boolean definitionFound = false;
        Set<StringallRoles = new HashSet<String>();
        // This can probably be optimized. Maybe we can cache the config object relevant instead of
        // walking through all of the ones that have 'service.guard'.
        for (Configuration config : getServiceGuardConfigs()) {
            Object guardFilter = config.getProperties().get();
            if (guardFilter instanceof String) {
                Filter filter = .createFilter((StringguardFilter);
                if (filter.match(serviceReference)) {
                    definitionFound = true;
                    for (Enumeration<Stringe = config.getProperties().keys(); e.hasMoreElements(); ) {
                        String key = e.nextElement();
                        String bareKey = key;
                        int idx = bareKey.indexOf('(');
                        if (idx >= 0) {
                            bareKey = bareKey.substring(0, idx);
                        }
                        int idx1 = bareKey.indexOf('[');
                        if (idx1 >= 0) {
                            bareKey = bareKey.substring(0, idx1);
                        }
                        int idx2 = bareKey.indexOf('*');
                        if (idx2 >= 0) {
                            bareKey = bareKey.substring(0, idx2);
                        }
                        if (!isValidMethodName(bareKey)) {
                            continue;
                        }
                        Object value = config.getProperties().get(key);
                        if (value instanceof String) {
                            allRoles.addAll(ACLConfigurationParser.parseRoles((Stringvalue));
                        }
                    }
                }
            }
        }
        return definitionFound ? allRoles : null;
    }
    // Ensures that it never returns null
        ConfigurationAdmin ca = null;
        try {
            ca = .waitForService(5000);
        } catch (InterruptedException e) {
        }
        if (ca == null) {
            throw new IllegalStateException("Role based access for services requires the OSGi Configuration Admin Service to be present");
        }
        Configuration[] configs = ca.listConfigurations(
                "(&(" + .  + "=" +  + "*)(" +  + "=*))");
        if (configs == null) {
            return new Configuration [] {};
        }
        return configs;
    }
    private boolean isValidMethodName(String name) {
        return .matcher(name).matches();
    }
    void stopProxyCreator() {
         = false// Will end the proxy creation thread
        if ( != null) {
            .interrupt();
        }
    }
    static boolean currentUserHasRole(String reqRole) {
        if (.equals(reqRole)) {
            return true;
        }
        String clazz;
        String role;
        int idx = reqRole.indexOf(':');
        if (idx > 0) {
            clazz = reqRole.substring(0, idx);
            role = reqRole.substring(idx + 1);
        } else {
            clazz = RolePrincipal.class.getName();
            role = reqRole;
        }
        AccessControlContext acc = AccessController.getContext();
        if (acc == null) {
            return false;
        }
        Subject subject = Subject.getSubject(acc);
        if (subject == null) {
            return false;
        }
        for (Principal p : subject.getPrincipals()) {
            if (clazz.equals(p.getClass().getName()) && role.equals(p.getName())) {
                return true;
            }
        }
        return false;
    }
    static class ServiceRegistrationHolder {
        volatile ServiceRegistration<?> registration;
    }
    class ProxyServiceFactory implements ServiceFactory<Object> {
        private final ProxyManager pm;
        private final ServiceReference<?> originalRef;
        ProxyServiceFactory(ProxyManager pmServiceReference<?> originalRef) {
            this. = pm;
            this. = originalRef;
        }
        @Override
        public Object getService(Bundle bundleServiceRegistration<Objectregistration) {
            Set<Class<?>> allClasses = new HashSet<Class<?>>();
            // This needs to be done on the Client BundleContext since the bundle might be backed by a Service Factory
            // in which case it needs to be given a chance to produce the right service for this client.
            Object svc = bundle.getBundleContext().getService();
            Class<?> curClass = svc.getClass();
            while (!Object.class.equals(curClass)) {
                allClasses.add(curClass);
                allClasses.addAll(Arrays.asList(curClass.getInterfaces()));
                curClass = curClass.getSuperclass(); // Collect super types too
            }
            for (Iterator<Class<?>> i = allClasses.iterator(); i.hasNext(); ) {
                Class<?> cls = i.next();
                if (((cls.getModifiers() & (. | .)) == 0) ||
                        ((cls.getModifiers() & .) > 0) ||
                        cls.isAnonymousClass()  || cls.isLocalClass()) {
                    // Do not attempt to proxy private, package-default, final,  anonymous or local classes
                    i.remove();
                } else {
                    for (Method m : cls.getDeclaredMethods()) {
                        if ((m.getModifiers() & .) > 0) {
                            // If the class contains final methods, don't attempt to proxy it
                            i.remove();
                            break;
                        }
                    }
                }
            }
            InvocationListener il = new ProxyInvocationListener();
            try {
                return .createInterceptingProxy(.getBundle(), allClassessvcil);
            } catch (UnableToProxyException e) {
                throw new RuntimeException(e);
            }
        }
        @Override
        public void ungetService(Bundle bundleServiceRegistration<ObjectregistrationObject service) {
            bundle.getBundleContext().ungetService();
        }
    }
    class ProxyInvocationListener implements InvocationListener {
        private final ServiceReference<?> serviceReference;
        ProxyInvocationListener(ServiceReference<?> sr) {
            this. = sr;
        }
        @Override
        public Object preInvoke(Object proxyMethod mObject[] argsthrows Throwable {
            String[] sig = new String[m.getParameterTypes().length];
            for (int i = 0; i < m.getParameterTypes().lengthi++) {
                sig[i] = m.getParameterTypes()[i].getName();
            }
            // The ordering of the keys is important because the first value when iterating has the highest specificity
            TreeMap<SpecificityList<String>> roleMappings = new TreeMap<ACLConfigurationParser.SpecificityList<String>>();
            boolean foundMatchingConfig = false;
            // This can probably be optimized. Maybe we can cache the config object relevant instead of
            // walking through all of the ones that have 'service.guard'.
            for (Configuration config : getServiceGuardConfigs()) {
                Object guardFilter = config.getProperties().get();
                if (guardFilter instanceof String) {
                    Filter filter = .createFilter((StringguardFilter);
                    if (filter.match()) {
                        foundMatchingConfig = true;
                        List<Stringroles = new ArrayList<String>();
                        Specificity s = ACLConfigurationParser.
                                getRolesForInvocation(m.getName(), argssigconfig.getProperties(), roles);
                        if (s != .) {
                            roleMappings.put(sroles);
                            if (s == .) {
                                // No more specific mapping can be found
                                break;
                            }
                        }
                    }
                }
            }
            if (!foundMatchingConfig) {
                // No mappings for this service, anyone can invoke
                return null;
            }
            if (roleMappings.size() == 0) {
                .info("Service {} has role mapping, but assigned no roles to method {}"m);
                throw new SecurityException("Insufficient credentials.");
            }
            // The first entry on the map has the highest significance because the keys are sorted in the order of
            // the Specificity enum.
            List<StringallowedRoles = roleMappings.values().iterator().next();
            for (String role : allowedRoles) {
                if (currentUserHasRole(role)) {
                    .trace("Allow user with role {} to invoke service {} method {}"rolem);
                    return null;
                }
            }
            // The current user does not have the required roles to invoke the service.
            .info("Current user does not have required roles ({}) for service {} method {} and/or arguments",
                    allowedRolesm);
            throw new SecurityException("Insufficient credentials.");
        }
        @Override
        public void postInvokeExceptionalReturn(Object tokenObject proxyMethod mThrowable exceptionthrows Throwable {
        }
        @Override
        public void postInvoke(Object tokenObject proxyMethod mObject returnValuethrows Throwable {
        }
    }
    // This customizer comes into action as the ProxyManager service arrives.
        @Override
        public ProxyManager addingService(ServiceReference<ProxyManagerreference) {
             = true;
            final ProxyManager svc = .getService(reference);
            if ( == null && svc != null) {
                 = newProxyProducingThread(svc);
            }
            return svc;
        }
        private Thread newProxyProducingThread(final ProxyManager proxyManager) {
            Thread t = new Thread(new Runnable() {
                @Override
                public void run() {
                    while () {
                        CreateProxyRunnable proxyCreator = null;
                        try {
                            proxyCreator = .take(); // take waits until there is something on the queue
                        } catch (InterruptedException ie) {
                            // part of normal behaviour
                        }
                        if (proxyCreator != null) {
                            try {
                                proxyCreator.run(proxyManager);
                            } catch (Exception e) {
                                .warn("Problem creating secured service proxy"e);
                            }
                        }
                    }
                    // finished running
                     = null;
                }
            });
            t.setName();
            t.setDaemon(true);
            t.start();
            return t;
        }
        @Override
        public void modifiedService(ServiceReference<ProxyManagerreferenceProxyManager service) {
            // no need to react
        }
        @Override
        public void removedService(ServiceReference<ProxyManagerreferenceProxyManager service) {
            stopProxyCreator();
        }
    }
    interface CreateProxyRunnable {
        long getOriginalServiceID();
        void run(ProxyManager pmthrows Exception;
    }
New to GrepCode? Check out our FAQ X