/*!
* Copyright 2010 - 2017 Pentaho Corporation. All rights reserved.
*
* Licensed 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.pentaho.di.repository.pur;
import java.util.List;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Future;
import javax.xml.ws.WebServiceException;
import org.apache.commons.lang.BooleanUtils;
import org.pentaho.di.core.encryption.Encr;
import org.pentaho.di.core.exception.KettleException;
import org.pentaho.di.core.exception.KettleSecurityException;
import org.pentaho.di.core.logging.LogChannel;
import org.pentaho.di.core.logging.LogChannelInterface;
import org.pentaho.di.core.util.ExecutorUtil;
import org.pentaho.di.i18n.BaseMessages;
import org.pentaho.di.repository.IUser;
import org.pentaho.di.repository.RepositorySecurityManager;
import org.pentaho.di.repository.RepositorySecurityProvider;
import org.pentaho.di.repository.pur.model.EEUserInfo;
import org.pentaho.di.ui.repository.pur.services.IAbsSecurityManager;
import org.pentaho.di.ui.repository.pur.services.IAbsSecurityProvider;
import org.pentaho.di.ui.repository.pur.services.IAclService;
import org.pentaho.di.ui.repository.pur.services.IConnectionAclService;
import org.pentaho.di.ui.repository.pur.services.ILockService;
import org.pentaho.di.ui.repository.pur.services.IRevisionService;
import org.pentaho.di.ui.repository.pur.services.IRoleSupportSecurityManager;
import org.pentaho.di.ui.repository.pur.services.ITrashService;
import org.pentaho.platform.api.repository2.unified.IUnifiedRepository;
import org.pentaho.platform.engine.core.system.PentahoSessionHolder;
import org.pentaho.platform.engine.core.system.PentahoSystem;
import org.pentaho.platform.repository2.unified.webservices.jaxws.IUnifiedRepositoryJaxwsWebService;
import org.pentaho.platform.repository2.unified.webservices.jaxws.UnifiedRepositoryToWebServiceAdapter;
import com.pentaho.pdi.ws.IRepositorySyncWebService;
import com.pentaho.pdi.ws.RepositorySyncException;
import com.sun.xml.ws.client.ClientTransportException;
public class PurRepositoryConnector implements IRepositoryConnector {
private static final String SINGLE_DI_SERVER_INSTANCE = "singleDiServerInstance";
private static final String REMOTE_DI_SERVER_INSTANCE = "remoteDiServerInstance";
private static Class<?> PKG = PurRepository.class;
private final LogChannelInterface log;
private final PurRepository purRepository;
private final PurRepositoryMeta repositoryMeta;
private final RootRef rootRef;
private ServiceManager serviceManager;
public PurRepositoryConnector( PurRepository purRepository, PurRepositoryMeta repositoryMeta, RootRef rootRef ) {
log = new LogChannel( this.getClass().getSimpleName() );
if ( purRepository != null & purRepository.getLog() != null ) {
log.setLogLevel( purRepository.getLog().getLogLevel() );
}
this.purRepository = purRepository;
this.repositoryMeta = repositoryMeta;
this.rootRef = rootRef;
}
private boolean allowedActionsContains( AbsSecurityProvider provider, String action ) throws KettleException {
List<String> allowedActions = provider.getAllowedActions( IAbsSecurityProvider.NAMESPACE );
for ( String actionName : allowedActions ) {
if ( action != null && action.equals( actionName ) ) {
return true;
}
}
return false;
}
public synchronized RepositoryConnectResult connect( final String username, final String password )
throws KettleException, KettleSecurityException {
if ( serviceManager != null ) {
disconnect();
}
serviceManager = new WebServiceManager( repositoryMeta.getRepositoryLocation().getUrl(), username );
RepositoryServiceRegistry purRepositoryServiceRegistry = new RepositoryServiceRegistry();
IUser user1 = new EEUserInfo();
final String decryptedPassword = Encr.decryptPasswordOptionallyEncrypted( password );
final RepositoryConnectResult result = new RepositoryConnectResult( purRepositoryServiceRegistry );
try {
/*
* Three scenarios: 1. Connect in process: username fetched using PentahoSessionHolder; no authentication occurs
* 2. Connect externally with trust: username specified is assumed authenticated if IP of calling code is trusted
* 3. Connect externally: authentication occurs normally (i.e. password is checked)
*/
user1.setLogin( username );
user1.setPassword( decryptedPassword );
user1.setName( username );
result.setUser( user1 );
// We need to have the application context and the session available in order for us to skip authentication
if ( PentahoSystem.getApplicationContext() != null && PentahoSessionHolder.getSession() != null
&& PentahoSessionHolder.getSession().isAuthenticated() ) {
if ( inProcess() ) {
// connect to the IUnifiedRepository through PentahoSystem
// this assumes we're running in a BI Platform
result.setUnifiedRepository( PentahoSystem.get( IUnifiedRepository.class ) );
if ( result.getUnifiedRepository() != null ) {
if ( log.isDebug() ) {
log.logDebug( "begin connectInProcess()" );
}
String name = PentahoSessionHolder.getSession().getName();
user1 = new EEUserInfo();
user1.setLogin( name );
user1.setName( name );
user1.setPassword( decryptedPassword );
result.setUser( user1 );
result.setSuccess( true );
if ( log.isDebug() ) {
log.logDebug( "connected in process as '" + name + "' pur repository = " + result.getUnifiedRepository() );
}
// for now, there is no need to support the security manager
// what about security provider?
return result;
}
}
}
ExecutorService executor = ExecutorUtil.getExecutor();
Future<Boolean> authorizationWebserviceFuture = executor.submit( new Callable<Boolean>() {
@Override
public Boolean call() throws Exception {
// We need to add the service class in the list in the order of dependencies
// IRoleSupportSecurityManager depends RepositorySecurityManager to be present
if ( log.isBasic() ) {
log.logBasic( BaseMessages.getString( PKG, "PurRepositoryConnector.CreateServiceProvider.Start" ) );
}
result.setSecurityProvider( new AbsSecurityProvider( purRepository, repositoryMeta, result.getUser(),
serviceManager ) );
if ( log.isBasic() ) {
log.logBasic( BaseMessages.getString( PKG, "PurRepositoryConnector.CreateServiceProvider.End" ) ); //$NON-NLS-1$
}
// If the user does not have access to administer security we do not
// need to added them to the service list
if ( allowedActionsContains( (AbsSecurityProvider) result.getSecurityProvider(),
IAbsSecurityProvider.ADMINISTER_SECURITY_ACTION ) ) {
result.setSecurityManager( new AbsSecurityManager( purRepository, repositoryMeta, result.getUser(),
serviceManager ) );
// Set the reference of the security manager to security provider for user role list change event
( (PurRepositorySecurityProvider) result.getSecurityProvider() )
.setUserRoleDelegate( ( (PurRepositorySecurityManager) result.getSecurityManager() )
.getUserRoleDelegate() );
return true;
}
return false;
}
} );
Future<WebServiceException> repoWebServiceFuture = executor.submit( new Callable<WebServiceException>() {
@Override
public WebServiceException call() throws Exception {
try {
IUnifiedRepositoryJaxwsWebService repoWebService = null;
if ( log.isBasic() ) {
log.logBasic( BaseMessages.getString( PKG, "PurRepositoryConnector.CreateRepositoryWebService.Start" ) ); //$NON-NLS-1$
}
repoWebService =
serviceManager.createService( username, decryptedPassword, IUnifiedRepositoryJaxwsWebService.class ); //$NON-NLS-1$
if ( log.isBasic() ) {
log.logBasic( BaseMessages.getString( PKG, "PurRepositoryConnector.CreateRepositoryWebService.End" ) ); //$NON-NLS-1$
}
if ( log.isBasic() ) {
log.logBasic( BaseMessages.getString( PKG, "PurRepositoryConnector.CreateUnifiedRepositoryToWebServiceAdapter.Start" ) ); //$NON-NLS-1$
}
result.setUnifiedRepository( new UnifiedRepositoryToWebServiceAdapter( repoWebService ) );
} catch ( WebServiceException wse ) {
return wse;
}
return null;
}
} );
Future<Exception> syncWebserviceFuture = executor.submit( new Callable<Exception>() {
@Override
public Exception call() throws Exception {
try {
if ( log.isBasic() ) {
log.logBasic( BaseMessages.getString( PKG, "PurRepositoryConnector.CreateRepositorySyncWebService.Start" ) );
}
IRepositorySyncWebService syncWebService =
serviceManager.createService( username, decryptedPassword, IRepositorySyncWebService.class ); //$NON-NLS-1$
if ( log.isBasic() ) {
log.logBasic( BaseMessages.getString( PKG, "PurRepositoryConnector.CreateRepositorySyncWebService.Sync" ) ); //$NON-NLS-1$
}
syncWebService.sync( repositoryMeta.getName(), repositoryMeta.getRepositoryLocation().getUrl() );
} catch ( RepositorySyncException e ) {
log.logError( e.getMessage(), e );
// this message will be presented to the user in spoon
result.setConnectMessage( e.getMessage() );
return null;
} catch ( ClientTransportException e ) {
// caused by authentication errors, etc
return e;
} catch ( WebServiceException e ) {
// if we can speak to the repository okay but not the sync service, assume we're talking to a BA Server
log.logError( e.getMessage(), e );
return new Exception( BaseMessages.getString( PKG, "PurRepository.BAServerLogin.Message" ), e );
}
return null;
}
} );
WebServiceException repoException = repoWebServiceFuture.get();
if ( repoException != null ) {
log.logError( repoException.getMessage() );
throw new Exception( BaseMessages.getString( PKG, "PurRepository.FailedLogin.Message" ), repoException );
}
Exception syncException = syncWebserviceFuture.get();
if ( syncException != null ) {
throw syncException;
}
Boolean isAdmin = authorizationWebserviceFuture.get();
if ( log.isBasic() ) {
log.logBasic( BaseMessages.getString( PKG, "PurRepositoryConnector.RegisterSecurityProvider.Start" ) );
}
purRepositoryServiceRegistry.registerService( RepositorySecurityProvider.class, result.getSecurityProvider() );
purRepositoryServiceRegistry.registerService( IAbsSecurityProvider.class, result.getSecurityProvider() );
if ( isAdmin ) {
purRepositoryServiceRegistry.registerService( RepositorySecurityManager.class, result.getSecurityManager() );
purRepositoryServiceRegistry.registerService( IRoleSupportSecurityManager.class, result.getSecurityManager() );
purRepositoryServiceRegistry.registerService( IAbsSecurityManager.class, result.getSecurityManager() );
}
purRepositoryServiceRegistry.registerService( PurRepositoryRestService.PurRepositoryPluginApiRevision.class,
serviceManager.createService( username, decryptedPassword,
PurRepositoryRestService.PurRepositoryPluginApiRevision.class ) );
purRepositoryServiceRegistry.registerService( IRevisionService.class, new UnifiedRepositoryRevisionService(
result.getUnifiedRepository(), rootRef ) );
purRepositoryServiceRegistry.registerService( IAclService.class, new UnifiedRepositoryConnectionAclService(
result.getUnifiedRepository() ) );
purRepositoryServiceRegistry.registerService( IConnectionAclService.class,
new UnifiedRepositoryConnectionAclService( result.getUnifiedRepository() ) );
purRepositoryServiceRegistry.registerService( ITrashService.class, new UnifiedRepositoryTrashService( result
.getUnifiedRepository(), rootRef ) );
purRepositoryServiceRegistry.registerService( ILockService.class, new UnifiedRepositoryLockService( result
.getUnifiedRepository() ) );
if ( log.isBasic() ) {
log.logBasic( BaseMessages.getString( PKG, "PurRepositoryConnector.RepositoryServicesRegistered.End" ) );
}
result.setSuccess( true );
} catch ( NullPointerException npe ) {
result.setSuccess( false );
throw new KettleException( BaseMessages.getString( PKG, "PurRepository.LoginException.Message" ) );
} catch ( Throwable e ) {
result.setSuccess( false );
serviceManager.close();
throw new KettleException( e );
}
return result;
}
@Override
public synchronized void disconnect() {
if ( serviceManager != null ) {
serviceManager.close();
}
serviceManager = null;
}
public LogChannelInterface getLog() {
return log;
}
@Override
public ServiceManager getServiceManager() {
return serviceManager;
}
public static boolean inProcess() {
boolean inProcess = false;
boolean remoteDiServer =
BooleanUtils.toBoolean( PentahoSystem.getSystemSetting( REMOTE_DI_SERVER_INSTANCE, "false" ) ); //$NON-NLS-1$
if ( "true".equals( PentahoSystem.getSystemSetting( SINGLE_DI_SERVER_INSTANCE, "true" ) ) ) { //$NON-NLS-1$ //$NON-NLS-2$
inProcess = true;
} else if ( !remoteDiServer && PentahoSystem.getApplicationContext().getFullyQualifiedServerURL() != null ) {
inProcess = true;
}
return inProcess;
}
}