Package org.sonatype.nexus.groovyremote

Source Code of org.sonatype.nexus.groovyremote.RemoteControl$SecurityHelper

/*
* Sonatype Nexus (TM) Open Source Version
* Copyright (c) 2007-2014 Sonatype, Inc.
* All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions.
*
* This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0,
* which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html.
*
* Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks
* of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the
* Eclipse Foundation. All other trademarks are the property of their respective owners.
*/
package org.sonatype.nexus.groovyremote;

import java.io.IOException;
import java.lang.annotation.Annotation;
import java.net.InetSocketAddress;
import java.util.Iterator;
import java.util.Map;
import java.util.Map.Entry;
import java.util.concurrent.Callable;
import java.util.concurrent.atomic.AtomicInteger;

import javax.inject.Inject;
import javax.inject.Named;

import org.sonatype.gossip.support.DC;
import org.sonatype.nexus.threads.FakeAlmightySubject;
import org.sonatype.sisu.goodies.lifecycle.LifecycleSupport;

import com.google.common.base.Throwables;
import com.google.common.collect.Maps;
import com.google.inject.Key;
import com.google.inject.name.Names;
import com.sun.net.httpserver.HttpExchange;
import com.sun.net.httpserver.HttpHandler;
import com.sun.net.httpserver.HttpServer;
import groovyx.remote.server.Receiver;
import groovyx.remote.transport.http.RemoteControlHttpHandler;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.subject.Subject;
import org.codehaus.mojo.animal_sniffer.IgnoreJRERequirement;
import org.eclipse.sisu.BeanEntry;
import org.eclipse.sisu.EagerSingleton;
import org.eclipse.sisu.inject.BeanLocator;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import static com.google.common.base.Preconditions.checkNotNull;

/**
* Groovy-remote control service.
*
* @since 2.6
*/
@Named
@EagerSingleton
@IgnoreJRERequirement
public class RemoteControl
    extends LifecycleSupport
{
  private static final String CPREFIX = "${nexus.groovyremote.";

  private final BeanLocator beanLocator;

  private final ClassLoader uberClassLoader;

  private final int port;

  private HttpServer server;

  private final AtomicInteger requests = new AtomicInteger(0);

  @Inject
  public RemoteControl(final BeanLocator beanLocator,
                       final @Named("nexus-uber") ClassLoader uberClassLoader,
                       final @Named(CPREFIX + "port:-0}") int port)
  {
    this.beanLocator = checkNotNull(beanLocator);
    this.uberClassLoader = checkNotNull(uberClassLoader);
    this.port = port;
    log.info("Port: {}", port);
  }

  //
  // TODO: Consider using RemoteControlServlet implementation instead, and hooking up to NX proper w/security?
  // TODO: ... then this plugin could be used more generally to script Nexus.
  // TODO: May need both actually, as this method is defs easier for testing as it side-steps the rest of the NX abstractions here.
  //

  @Override
  protected void doStart() throws Exception {
    InetSocketAddress addr = new InetSocketAddress(port);
    this.server = HttpServer.create(addr, 0);

    Receiver receiver = new ReceiverImpl(uberClassLoader, createContext());
    final RemoteControlHttpHandler handler = new RemoteControlHttpHandler(receiver);

    server.createContext("/", new HttpHandler()
    {
      @Override
      public void handle(final HttpExchange exchange) throws IOException {
        int id = requests.incrementAndGet();
        log.debug("Handling remote request: [{}] {}", id, exchange);

        DC.put(RemoteScript.class, id);

        try {
          handler.handle(exchange);
        }
        catch (Throwable failure) {
          // HACK: Ignore this package-private exception as it seems to happen normally
          if ("sun.net.httpserver.StreamClosedException".equals(failure.getClass().getCanonicalName())) {
            log.trace("Ignoring", failure);
            return;
          }

          log.error("Handle failed", failure);
          Throwables.propagateIfPossible(failure, IOException.class);
          throw Throwables.propagate(failure);
        }
        finally {
          log.debug("Request finished");

          DC.remove(RemoteScript.class);
        }
      }
    });

    server.start();

    log.info("Listening: {}", server.getAddress());
  }

  /**
   * Ancillary marker for remote-script things (logging, DC, etc).
   */
  private static interface RemoteScript
  {
    // empty
  }

  /**
   * Container helpers for executing closure.
   */
  private final class ContainerHelper
  {
    private final Logger log = LoggerFactory.getLogger(getClass());

    public <T> T lookup(final Key<T> key) {
      log.debug("Lookup: {}", key);
      final Iterator<? extends Entry<Annotation, T>> i = beanLocator.locate(key).iterator();
      return i.hasNext() ? i.next().getValue() : null;
    }

    public <T> T lookup(final Class<T> type) {
      return lookup(Key.get(type));
    }

    public <T> T lookup(final Class<T> type, final String name) {
      return lookup(type, Names.named(name));
    }

    public <T> T lookup(final Class<T> type, final Class<? extends Annotation> qualifier) {
      return lookup(Key.get(type, qualifier));
    }

    public <T> T lookup(final Class<T> type, final Annotation qualifier) {
      return lookup(Key.get(type, qualifier));
    }

    // String type-name helpers to avoid needing const class ref in closure

    public Class<?> type(final String typeName) throws ClassNotFoundException {
      log.debug("Lookup type: {}", typeName);
      return uberClassLoader.loadClass(typeName);
    }

    public Object lookup(final String typeName) throws ClassNotFoundException {
      return lookup(type(typeName));
    }

    public Object lookup(final String typeName, final String name) throws ClassNotFoundException {
      return lookup(type(typeName), name);
    }

    public <Q extends Annotation, T> Iterable<? extends BeanEntry<Q, T>> locate(final Key<T> key) {
      log.debug("Locate: {}", key);
      return beanLocator.locate(key);
    }

    public <Q extends Annotation, T> Iterable<? extends BeanEntry<Q, T>> locate(final Class<T> type) {
      log.debug("Locate: {}", type);
      return beanLocator.locate(Key.get(type));
    }
  }

  /**
   * Security helpers for executing closure.
   */
  private final class SecurityHelper
  {
    private final Logger log = LoggerFactory.getLogger(getClass());

    /**
     * Get the current subject.
     */
    public Subject getSubject() {
      return SecurityUtils.getSubject();
    }

    /**
     * Login a user.
     */
    public void login(final String username, final String password) {
      log.debug("Login; username: {}, password: {}", username, password);
      getSubject().login(new UsernamePasswordToken(username, password));
    }

    /**
     * Logout user.
     */
    public void logout() {
      log.debug("Logout");
      getSubject().logout();
    }

    /**
     * Run a {@link Callable} as a specific user.
     */
    public Object asUser(final String username, final String password, final Callable callable) throws Exception {
      log.debug("Running as user: {}", username);
      login(username, password);
      try {
        return callable.call();
      }
      finally {
        logout();
      }
    }

    /**
     * Run a {@link Callable} as system user.
     */
    public Object asSystem(final Callable callable) {
      log.debug("Running as system");
      return FakeAlmightySubject.forUserId("*SYSTEM").execute(callable);
    }
  }

  /**
   * Creates the context of the executing closure.
   */
  private Map<String, Object> createContext() {
    Map<String, Object> context = Maps.newHashMap();
    context.put("container", new ContainerHelper());
    context.put("security", new SecurityHelper());
    context.put("logger", LoggerFactory.getLogger(RemoteScript.class));
    context.put("classLoader", uberClassLoader);

    // TODO: Expose json marshalling so we can get back objects that aren't serializable w/o having to rebuild structures around them
    // TODO: Make some simple closure helpers: log() and json() ?

    return context;
  }

  @Override
  protected void doStop() throws Exception {
    server.stop(0);
    server = null;
  }
}
TOP

Related Classes of org.sonatype.nexus.groovyremote.RemoteControl$SecurityHelper

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.