/* * 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.directory.studio.openldap.config.editor; import java.io.File; import java.io.FilenameFilter; import java.io.IOException; import java.util.List; import java.util.UUID; import org.apache.directory.api.ldap.model.constants.SchemaConstants; import org.apache.directory.api.ldap.model.csn.CsnFactory; import org.apache.directory.api.ldap.model.entry.DefaultEntry; import org.apache.directory.api.ldap.model.entry.Entry; import org.apache.directory.api.ldap.model.exception.LdapException; import org.apache.directory.api.ldap.model.ldif.LdifEntry; import org.apache.directory.api.ldap.model.name.Dn; import org.apache.directory.api.ldap.model.schema.ObjectClass; import org.apache.directory.api.ldap.model.schema.SchemaManager; import org.apache.directory.api.ldap.model.schema.registries.ObjectClassRegistry; import org.apache.directory.api.ldap.util.tree.DnNode; import org.apache.directory.api.util.DateUtils; import org.apache.directory.server.core.api.CacheService; import org.apache.directory.studio.common.core.jobs.StudioProgressMonitor; import org.apache.directory.studio.common.ui.CommonUIUtils; import org.apache.directory.studio.common.ui.filesystem.PathEditorInput; import org.apache.directory.studio.connection.core.event.ConnectionEventRegistry; import org.apache.directory.studio.ldapbrowser.core.BrowserCorePlugin; import org.apache.directory.studio.ldapbrowser.core.jobs.ExecuteLdifRunnable; import org.apache.directory.studio.ldapbrowser.core.model.IBrowserConnection; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.NullProgressMonitor; import org.eclipse.core.runtime.Path; import org.eclipse.swt.SWT; import org.eclipse.swt.widgets.DirectoryDialog; import org.eclipse.swt.widgets.Display; import org.eclipse.ui.IEditorInput; import org.eclipse.ui.PlatformUI; import org.apache.directory.studio.openldap.config.ExpandedLdifUtils; import org.apache.directory.studio.openldap.config.OpenLdapConfigurationPlugin; import org.apache.directory.studio.openldap.config.jobs.EntryBasedConfigurationPartition; import org.apache.directory.studio.openldap.config.jobs.PartitionsDiffComputer; import org.apache.directory.studio.openldap.config.model.OpenLdapConfiguration; import org.apache.directory.studio.openldap.config.model.io.ConfigurationException; import org.apache.directory.studio.openldap.config.model.io.ConfigurationReader; import org.apache.directory.studio.openldap.config.model.io.ConfigurationUtils; import org.apache.directory.studio.openldap.config.model.io.ConfigurationWriter; /** * This class contains helpful methods for the Server Configuration Editor. * * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a> */ public class OpenLDAPServerConfigurationEditorUtils { /** * Performs the "Save as..." action. * * @param configuration * the configuration * @param newInput * a flag to indicate if a new input is required * @return * the new input for the editor * @throws Exception */ public static IEditorInput saveAs( OpenLdapConfiguration configuration, boolean newInput ) throws Exception { // The path of the directory String path = null; // Creating a dialog for directory selection DirectoryDialog dialog = new DirectoryDialog( PlatformUI.getWorkbench().getActiveWorkbenchWindow() .getShell(), SWT.SAVE ); while ( true ) { // Opening the dialog path = openDirectoryDialogInUIThread( dialog ); // Checking the returned path if ( path == null ) { // Cancel button has been clicked return null; } // Getting the directory indicated by the user final File directory = new File( path ); // Checking if the directory exists if ( !directory.exists() ) { CommonUIUtils.openErrorDialog( "The directory does not exist." ); continue; } // Checking if the location is a directory if ( !directory.isDirectory() ) { CommonUIUtils.openErrorDialog( "The location is not a directory." ); continue; } // Checking if the directory is writable if ( !directory.canWrite() ) { CommonUIUtils.openErrorDialog( "The directory is not writable." ); continue; } // Checking if the directory is empty if ( !isEmpty( directory ) ) { CommonUIUtils.openErrorDialog( "The directory is not empty." ); continue; } // The directory meets all requirements break; } // Saving the file to disk saveConfiguration( configuration, new File( path ) ); // Checking if a new input is required if ( newInput ) { // Creating the new input for the editor return new PathEditorInput( new Path( path ) ); } else { return null; } } private static boolean isEmpty( File directory ) { if ( directory != null ) { String[] children = directory.list( new FilenameFilter() { public boolean accept( File dir, String name ) { // Only accept visible files (which don't start with a dot). return !name.startsWith( "." ); } } ); return ( ( children == null ) || ( children.length == 0 ) ); } return false; } /** * Opens a {@link DirectoryDialog} in the UI thread. * * @param dialog * the directory dialog * @return * the result of the dialog */ private static String openDirectoryDialogInUIThread( final DirectoryDialog dialog ) { // Defining our own encapsulating class for the result class DialogResult { private String result; public String getResult() { return result; } public void setResult( String result ) { this.result = result; } } // Creating an object to hold the result final DialogResult result = new DialogResult(); // Opening the dialog in the UI thread Display.getDefault().syncExec( new Runnable() { public void run() { result.setResult( dialog.open() ); } } ); return result.getResult(); } /** * Saves the configuration. * * @param configuration * the configuration * @param directory * the directory * @throws Exception */ public static void saveConfiguration( OpenLdapConfiguration configuration, File directory ) throws Exception { saveConfiguration( null, configuration, directory ); } /** * Saves the configuration. * * @param browserConnection * the browser connection * @param configuration * the configuration * @param directory * the directory * @throws Exception */ public static void saveConfiguration( IBrowserConnection browserConnection, OpenLdapConfiguration configuration, File directory ) throws Exception { // Creating the configuration writer ConfigurationWriter configurationWriter = new ConfigurationWriter( browserConnection, configuration ); SchemaManager schemaManager = OpenLdapConfigurationPlugin.getDefault().getSchemaManager(); // Converting the configuration beans to entries List<LdifEntry> entries = configurationWriter.getConvertedLdifEntries( ConfigurationUtils .getDefaultConfigurationDn() ); // Creating a tree to store entries DnNode<Entry> tree = new DnNode<Entry>(); CsnFactory csnFactory = new CsnFactory( 1 ); for ( LdifEntry entry : entries ) { // Getting the current generalized time String currentgeGeneralizedTime = DateUtils.getGeneralizedTime(); // 'createTimestamp' attribute entry.addAttribute( "createTimestamp", currentgeGeneralizedTime ); // 'creatorsName' attribute entry.addAttribute( "creatorsName", "cn=config" ); // 'entryCSN' attribute entry.addAttribute( "entryCSN", csnFactory.newInstance().toString() ); // 'entryUUID' attribute entry.addAttribute( "entryUUID", UUID.randomUUID().toString() ); // 'modifiersName' attribute entry.addAttribute( "modifiersName", "cn=config" ); // 'modifyTimestamp' attribute entry.addAttribute( "modifyTimestamp", currentgeGeneralizedTime ); // 'structuralObjectClass' attribute entry.addAttribute( "structuralObjectClass", getStructuralObjectClass( entry ) ); // Adding the entry to tree tree.add( entry.getDn().apply( schemaManager ), entry.getEntry() ); } try { Dn rootDn = ConfigurationUtils.getDefaultConfigurationDn(); rootDn.apply( schemaManager ); ExpandedLdifUtils.write( tree, rootDn, directory ); } catch ( ConfigurationException e ) { throw new IOException( e ); } } /** * Gets the structural object class of the entry. * * @param ldifEntry the LDIF entry * @return the structural object class of the entry * @throws ConfigurationException */ private static Object getStructuralObjectClass( LdifEntry ldifEntry ) throws ConfigurationException { if ( ldifEntry != null ) { Entry entry = ldifEntry.getEntry(); if ( entry != null ) { ObjectClass structuralObjectClass = ConfigurationReader .getHighestStructuralObjectClass( entry.get( SchemaConstants.OBJECT_CLASS_AT ) ); if ( structuralObjectClass != null ) { return structuralObjectClass.getName(); } } } return SchemaConstants.TOP_OC; } /** * Saves the configuration. * * @param input the connection server configuration input * @param editor the editor * @param monitor the monitor * @return * <code>true</code> if the operation is successful, * <code>false</code> if not * @throws Exception */ public static void saveConfiguration( ConnectionServerConfigurationInput input, OpenLDAPServerConfigurationEditor editor, IProgressMonitor monitor ) throws Exception { // Getting the browser connection associated with the connection in the input IBrowserConnection browserConnection = BrowserCorePlugin.getDefault().getConnectionManager() .getBrowserConnection( input.getConnection() ); // Creating the configuration writer ConfigurationWriter configurationWriter = new ConfigurationWriter( browserConnection, editor.getConfiguration() ); // Getting the original configuration partition and its schema manager EntryBasedConfigurationPartition originalPartition = input.getOriginalPartition(); SchemaManager schemaManager = originalPartition.getSchemaManager(); // Suspends event firing in current thread. ConnectionEventRegistry.suspendEventFiringInCurrentThread(); try { // Creating a new configuration partition EntryBasedConfigurationPartition modifiedPartition = createConfigurationPartition( schemaManager, originalPartition.getSuffixDn() ); for ( LdifEntry ldifEntry : configurationWriter.getConvertedLdifEntries() ) { modifiedPartition.addEntry( new DefaultEntry( schemaManager, ldifEntry.getEntry() ) ); } // Comparing both partitions to get the list of modifications to be applied List<LdifEntry> modificationsList = PartitionsDiffComputer.computeModifications( originalPartition, modifiedPartition, new String[] { SchemaConstants.ALL_USER_ATTRIBUTES } ); // Building the resulting LDIF StringBuilder modificationsLdif = new StringBuilder(); for ( LdifEntry ldifEntry : modificationsList ) { modificationsLdif.append( ldifEntry.toString() ); } // Creating a StudioProgressMonitor to run the LDIF with StudioProgressMonitor studioProgressMonitor = new StudioProgressMonitor( new NullProgressMonitor() ); // Updating the configuration with the resulting LDIF ExecuteLdifRunnable.executeLdif( browserConnection, modificationsLdif.toString(), true, true, studioProgressMonitor ); // Checking if there were errors during the execution of the LDIF if ( studioProgressMonitor.errorsReported() ) { StringBuilder message = new StringBuilder(); message.append( "Changes could not be saved to the connection." ); Exception exception = studioProgressMonitor.getException(); if ( exception != null ) { message.append( "\n\n" ); message.append( "Cause: " ); message.append( exception.getMessage() ); throw new Exception( message.toString(), exception ); } else { throw new Exception( message.toString() ); } } else { // Swapping the new configuration partition input.setOriginalPartition( modifiedPartition ); } } finally { // Resumes event firing in current thread. ConnectionEventRegistry.resumeEventFiringInCurrentThread(); } } /** * Creates a configuration partition to store configuration entries. * * @param schemaManager the schema manager * @param configBaseDn the configuration base DN * @return a configuration partition to store configuration entries * @throws LdapException */ public static EntryBasedConfigurationPartition createConfigurationPartition( SchemaManager schemaManager, Dn configBaseDn ) throws LdapException { CacheService cacheService = new CacheService(); cacheService.initialize( null ); EntryBasedConfigurationPartition configurationPartition = new EntryBasedConfigurationPartition( schemaManager, configBaseDn ); configurationPartition.setCacheService(cacheService); configurationPartition.initialize(); return configurationPartition; } /** * Gets the object class for the given name. * * @param schemaManager the schema manager * @param name the name * @return the object class for the given name */ public static ObjectClass getObjectClass( SchemaManager schemaManager, String name ) { // Checking the schema manager and name if ( ( schemaManager != null ) && ( name != null ) ) { try { // Getting the object class registry ObjectClassRegistry ocRegistry = schemaManager.getObjectClassRegistry(); if ( ocRegistry != null ) { // Getting the oid from the object class name String oid = ocRegistry.getOidByName( name ); if ( oid != null ) { // Getting the object class from the oid return ocRegistry.get( oid ); } } } catch ( LdapException e ) { // No OID found for the given name return null; } } return null; } }