Переглянути джерело

- LdapXmlUserHistory add test cases and convert to use internal xml factory

Jason Rivard 4 роки тому
батько
коміт
9f4b852d13

+ 3 - 3
server/src/main/java/password/pwm/config/stored/StoredConfigZipJsonSerializer.java

@@ -143,7 +143,7 @@ public class StoredConfigZipJsonSerializer implements StoredConfigSerializer
         {
             if ( SETTINGS_FILENAME.equals( zipEntry.getName() ) )
             {
-                final String stringData = JavaHelper.copy( zipInputStream );
+                final String stringData = JavaHelper.copyToString( zipInputStream );
                 final List<SerializedValue> readComponents = JsonUtil.deserialize( stringData, new TypeToken<List<SerializedValue>>()
                 {
                 } );
@@ -151,7 +151,7 @@ public class StoredConfigZipJsonSerializer implements StoredConfigSerializer
             }
             else if ( META_VALUES_FILENAME.equals( zipEntry.getName() ) )
             {
-                final String stringData = JavaHelper.copy( zipInputStream );
+                final String stringData = JavaHelper.copyToString( zipInputStream );
                 final List<SerializedValue> readMetaValues = JsonUtil.deserialize( stringData, new TypeToken<List<SerializedMetaValue>>()
                 {
                 } );
@@ -159,7 +159,7 @@ public class StoredConfigZipJsonSerializer implements StoredConfigSerializer
             }
             else if ( META_FILENAME.equals( zipEntry.getName() ) )
             {
-                final String stringData = JavaHelper.copy( zipInputStream );
+                final String stringData = JavaHelper.copyToString( zipInputStream );
                 metaData = JsonUtil.deserialize( stringData, MetaData.class );
             }
             else if ( zipEntry.getName().endsWith( XREF_SUFFIX ) )

+ 5 - 10
server/src/main/java/password/pwm/config/stored/StoredConfigZipXmlSerializer.java

@@ -20,18 +20,16 @@
 
 package password.pwm.config.stored;
 
-import org.apache.commons.io.IOUtils;
 import password.pwm.PwmConstants;
 import password.pwm.config.PwmSettingSyntax;
-import password.pwm.config.value.StoredValue;
 import password.pwm.config.value.FileValue;
+import password.pwm.config.value.StoredValue;
 import password.pwm.error.PwmUnrecoverableException;
 import password.pwm.http.bean.ImmutableByteArray;
 import password.pwm.util.java.JavaHelper;
 import password.pwm.util.secure.PwmHashAlgorithm;
 import password.pwm.util.secure.SecureEngine;
 
-import java.io.ByteArrayInputStream;
 import java.io.ByteArrayOutputStream;
 import java.io.IOException;
 import java.io.InputStream;
@@ -63,17 +61,14 @@ public class StoredConfigZipXmlSerializer implements StoredConfigSerializer
         {
             if ( SETTINGS_FILENAME.equals( zipEntry.getName() ) )
             {
-                final byte[] xmlBytes = IOUtils.toByteArray( zipInputStream );
-                final ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream( xmlBytes );
-                storedConfiguration = new StoredConfigXmlSerializer().readInput( byteArrayInputStream );
+                final ImmutableByteArray byteArray = JavaHelper.copyToBytes( zipInputStream );
+                storedConfiguration = new StoredConfigXmlSerializer().readInput( byteArray.newByteArrayInputStream() );
             }
             else if ( zipEntry.getName().endsWith( XREF_SUFFIX ) )
             {
                 final String hash = zipEntry.getName().substring( 0, zipEntry.getName().length() - XREF_SUFFIX.length() );
-                final ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
-                JavaHelper.copy( zipInputStream, byteArrayOutputStream );
-                final byte[] contents = byteArrayOutputStream.toByteArray();
-                exrefMap.put( hash, ImmutableByteArray.of( contents ) );
+                final ImmutableByteArray contents = JavaHelper.copyToBytes( zipInputStream );
+                exrefMap.put( hash, contents );
             }
             zipInputStream.closeEntry();
 

+ 2 - 1
server/src/main/java/password/pwm/http/servlet/configeditor/ConfigEditorServlet.java

@@ -70,6 +70,7 @@ import password.pwm.svc.email.EmailServer;
 import password.pwm.svc.email.EmailServerUtil;
 import password.pwm.svc.email.EmailService;
 import password.pwm.util.PasswordData;
+import password.pwm.util.SampleDataGenerator;
 import password.pwm.util.java.JavaHelper;
 import password.pwm.util.java.JsonUtil;
 import password.pwm.util.java.StringUtil;
@@ -883,7 +884,7 @@ public class ConfigEditorServlet extends ControlledPwmServlet
                 return ProcessStatus.Halt;
             }
 
-            final MacroRequest macroRequest = MacroRequest.sampleMacroRequest( pwmRequest.getPwmApplication() );
+            final MacroRequest macroRequest = SampleDataGenerator.sampleMacroRequest( pwmRequest.getPwmApplication() );
             final String input = inputMap.get( "input" );
             final String output = macroRequest.expandMacros( input );
             pwmRequest.outputJsonResult( RestResultBean.withData( output ) );

+ 52 - 77
server/src/main/java/password/pwm/svc/event/LdapXmlUserHistory.java

@@ -24,14 +24,9 @@ import com.novell.ldapchai.ChaiUser;
 import com.novell.ldapchai.exception.ChaiOperationException;
 import com.novell.ldapchai.exception.ChaiUnavailableException;
 import com.novell.ldapchai.util.ConfigObjectRecord;
-import org.jdom2.CDATA;
-import org.jdom2.Document;
-import org.jdom2.Element;
-import org.jdom2.JDOMException;
-import org.jdom2.input.SAXBuilder;
-import org.jdom2.output.Format;
-import org.jdom2.output.XMLOutputter;
+import lombok.Value;
 import password.pwm.PwmApplication;
+import password.pwm.PwmConstants;
 import password.pwm.bean.UserIdentity;
 import password.pwm.config.PwmSetting;
 import password.pwm.config.profile.LdapProfile;
@@ -39,11 +34,18 @@ import password.pwm.error.ErrorInformation;
 import password.pwm.error.PwmError;
 import password.pwm.error.PwmUnrecoverableException;
 import password.pwm.ldap.UserInfo;
+import password.pwm.util.java.JavaHelper;
+import password.pwm.util.java.StringUtil;
+import password.pwm.util.java.XmlDocument;
+import password.pwm.util.java.XmlElement;
+import password.pwm.util.java.XmlFactory;
 import password.pwm.util.logging.PwmLogger;
 
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
 import java.io.IOException;
+import java.io.InputStream;
 import java.io.Serializable;
-import java.io.StringReader;
 import java.time.Instant;
 import java.util.ArrayDeque;
 import java.util.ArrayList;
@@ -122,7 +124,7 @@ class LdapXmlUserHistory implements UserHistoryStore
         // read current value;
         final StoredHistory storedHistory;
         final ConfigObjectRecord theCor;
-        final List corList;
+        final List<ConfigObjectRecord> corList;
         try
         {
             corList = ConfigObjectRecord.readRecordFromLDAP( theUser, corAttribute, corRecordIdentifer, null, null );
@@ -131,7 +133,7 @@ class LdapXmlUserHistory implements UserHistoryStore
         {
             final String errorMsg = "error reading LDAP user event history for user " + userIdentity.toDisplayString() + ", error: " + e.getMessage();
             final ErrorInformation errorInformation = new ErrorInformation( PwmError.ERROR_INTERNAL, errorMsg );
-            LOGGER.error( () -> errorInformation.toDebugStr(), e );
+            LOGGER.error( errorInformation::toDebugStr, e );
             throw new PwmUnrecoverableException( errorInformation, e );
         }
 
@@ -194,7 +196,7 @@ class LdapXmlUserHistory implements UserHistoryStore
             final UserIdentity userIdentity,
             final ChaiUser chaiUser
     )
-            throws ChaiUnavailableException, PwmUnrecoverableException
+            throws ChaiUnavailableException
     {
         final LdapProfile ldapProfile = userIdentity.getLdapProfile( pwmApplication.getConfig() );
         final String corAttribute = ldapProfile.readSettingAsString( PwmSetting.EVENTS_LDAP_ATTRIBUTE );
@@ -207,11 +209,11 @@ class LdapXmlUserHistory implements UserHistoryStore
 
         try
         {
-            final List corList = ConfigObjectRecord.readRecordFromLDAP( chaiUser, corAttribute, COR_RECORD_ID, null, null );
+            final List<ConfigObjectRecord> corList = ConfigObjectRecord.readRecordFromLDAP( chaiUser, corAttribute, COR_RECORD_ID, null, null );
 
-            if ( !corList.isEmpty() )
+            if ( !JavaHelper.isEmpty( corList ) )
             {
-                final ConfigObjectRecord theCor = ( ConfigObjectRecord ) corList.get( 0 );
+                final ConfigObjectRecord theCor = corList.get( 0 );
                 return StoredHistory.fromXml( theCor.getPayload() );
             }
         }
@@ -222,7 +224,7 @@ class LdapXmlUserHistory implements UserHistoryStore
         return new StoredHistory();
     }
 
-    private static class StoredHistory
+    public static class StoredHistory
     {
         private final Deque<StoredEvent> records = new ArrayDeque<>();
 
@@ -249,15 +251,16 @@ class LdapXmlUserHistory implements UserHistoryStore
             return Collections.unmodifiableList( returnList );
         }
 
-        String toXml( )
+        public String toXml( )
         {
-            final Element rootElement = new Element( XML_NODE_ROOT );
+            final XmlFactory xmlFactory = XmlFactory.getFactory();
+            final XmlDocument doc = xmlFactory.newDocument( XML_NODE_ROOT );
 
             for ( final StoredEvent loopEvent : records )
             {
                 if ( loopEvent.getAuditEvent() != null )
                 {
-                    final Element hrElement = new Element( XML_NODE_RECORD );
+                    final XmlElement hrElement = xmlFactory.newElement( XML_NODE_RECORD );
                     hrElement.setAttribute( XML_ATTR_TIMESTAMP, String.valueOf( loopEvent.getTimestamp() ) );
                     hrElement.setAttribute( XML_ATTR_TRANSACTION, loopEvent.getAuditEvent().getMessage().getKey() );
                     if ( loopEvent.getSourceAddress() != null && loopEvent.getSourceAddress().length() > 0 )
@@ -270,48 +273,54 @@ class LdapXmlUserHistory implements UserHistoryStore
                     }
                     if ( loopEvent.getMessage() != null )
                     {
-                        hrElement.setContent( new CDATA( loopEvent.getMessage() ) );
+                        hrElement.addText( loopEvent.getMessage() );
                     }
-                    rootElement.addContent( hrElement );
+                    doc.getRootElement().addContent( hrElement );
                 }
             }
 
-            final Document doc = new Document( rootElement );
-            final XMLOutputter outputter = new XMLOutputter();
-            outputter.setFormat( Format.getCompactFormat() );
-            return outputter.outputString( doc );
+            try ( ByteArrayOutputStream outputStream = new ByteArrayOutputStream() )
+            {
+                xmlFactory.outputDocument( doc,  outputStream, XmlFactory.OutputFlag.Compact );
+                return new String( outputStream.toByteArray(), PwmConstants.DEFAULT_CHARSET );
+            }
+            catch ( final IOException e )
+            {
+                throw new IllegalStateException( "error converting xml to string data: " + e.getMessage() );
+            }
         }
 
-        static StoredHistory fromXml( final String input )
+        public static StoredHistory fromXml( final String input )
         {
             final StoredHistory returnHistory = new StoredHistory();
 
-            if ( input == null || input.length() < 1 )
+            if ( StringUtil.isEmpty( input ) )
             {
                 return returnHistory;
             }
 
-            try
+            try ( InputStream inputStream = new ByteArrayInputStream( input.getBytes( PwmConstants.DEFAULT_CHARSET ) ) )
             {
-                final SAXBuilder builder = new SAXBuilder();
-                final Document doc = builder.build( new StringReader( input ) );
-                final Element rootElement = doc.getRootElement();
+                final XmlFactory xmlFactory = XmlFactory.getFactory();
+                final XmlDocument xmlDocument = xmlFactory.parseXml( inputStream );
+                final XmlElement rootElement = xmlDocument.getRootElement();
 
-                for ( final Element hrElement : rootElement.getChildren( XML_NODE_RECORD ) )
+                for ( final XmlElement hrElement : rootElement.getChildren( XML_NODE_RECORD ) )
                 {
-                    final long timeStamp = hrElement.getAttribute( XML_ATTR_TIMESTAMP ).getLongValue();
-                    final String transactionCode = hrElement.getAttribute( XML_ATTR_TRANSACTION ).getValue();
+                    final String timeStampStr = hrElement.getAttributeValue( XML_ATTR_TIMESTAMP );
+                    final long timeStamp = Long.parseLong( timeStampStr );
+                    final String transactionCode = hrElement.getAttributeValue( XML_ATTR_TRANSACTION );
                     AuditEvent.forKey( transactionCode ).ifPresent( ( eventCode ) ->
                     {
-                        final String srcAddr = hrElement.getAttribute( XML_ATTR_SRC_IP ) != null ? hrElement.getAttribute( XML_ATTR_SRC_IP ).getValue() : "";
-                        final String srcHost = hrElement.getAttribute( XML_ATTR_SRC_HOST ) != null ? hrElement.getAttribute( XML_ATTR_SRC_HOST ).getValue() : "";
+                        final String srcAddr = hrElement.getAttributeValue( XML_ATTR_SRC_IP ) != null ? hrElement.getAttributeValue( XML_ATTR_SRC_IP ) : "";
+                        final String srcHost = hrElement.getAttributeValue( XML_ATTR_SRC_HOST ) != null ? hrElement.getAttributeValue( XML_ATTR_SRC_HOST ) : "";
                         final String message = hrElement.getText();
                         final StoredEvent storedEvent = new StoredEvent( eventCode, timeStamp, message, srcAddr, srcHost );
                         returnHistory.addEvent( storedEvent );
                     } );
                 }
             }
-            catch ( final JDOMException | IOException e )
+            catch ( final PwmUnrecoverableException | IOException e )
             {
                 LOGGER.error( () -> "error parsing user event history record: " + e.getMessage() );
             }
@@ -319,48 +328,14 @@ class LdapXmlUserHistory implements UserHistoryStore
         }
     }
 
-    private static class StoredEvent implements Serializable
+    @Value
+    public static class StoredEvent implements Serializable
     {
-        private AuditEvent auditEvent;
-        private long timestamp;
-        private String message;
-        private String sourceAddress;
-        private String sourceHost;
-
-
-        private StoredEvent( final AuditEvent auditEvent, final long timestamp, final String message, final String sourceAddress, final String sourceHost )
-        {
-            this.auditEvent = auditEvent;
-            this.timestamp = timestamp;
-            this.message = message;
-            this.sourceAddress = sourceAddress;
-            this.sourceHost = sourceHost;
-        }
-
-        AuditEvent getAuditEvent( )
-        {
-            return auditEvent;
-        }
-
-        public long getTimestamp( )
-        {
-            return timestamp;
-        }
-
-        public String getMessage( )
-        {
-            return message;
-        }
-
-        String getSourceAddress( )
-        {
-            return sourceAddress;
-        }
-
-        String getSourceHost( )
-        {
-            return sourceHost;
-        }
+        private final AuditEvent auditEvent;
+        private final long timestamp;
+        private final String message;
+        private final String sourceAddress;
+        private final String sourceHost;
 
         static StoredEvent fromAuditRecord( final UserAuditRecord auditRecord )
         {

+ 166 - 0
server/src/main/java/password/pwm/util/SampleDataGenerator.java

@@ -0,0 +1,166 @@
+/*
+ * Password Management Servlets (PWM)
+ * http://www.pwm-project.org
+ *
+ * Copyright (c) 2006-2009 Novell, Inc.
+ * Copyright (c) 2009-2020 The PWM Project
+ *
+ * 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 password.pwm.util;
+
+import com.novell.ldapchai.cr.Answer;
+import password.pwm.PwmApplication;
+import password.pwm.PwmConstants;
+import password.pwm.bean.LoginInfoBean;
+import password.pwm.bean.ResponseInfoBean;
+import password.pwm.bean.UserIdentity;
+import password.pwm.config.option.DataStorageMethod;
+import password.pwm.error.PwmUnrecoverableException;
+import password.pwm.ldap.UserInfo;
+import password.pwm.ldap.UserInfoBean;
+import password.pwm.util.macro.MacroRequest;
+import password.pwm.util.operations.otp.OTPUserRecord;
+
+import java.time.Instant;
+import java.util.Collections;
+import java.util.LinkedHashMap;
+import java.util.Map;
+
+public class SampleDataGenerator
+{
+    public static UserInfo sampleUserData()
+    {
+        final Map<String, String> userAttributes = new LinkedHashMap<>();
+        userAttributes.put( "givenName", "First" );
+        userAttributes.put( "sn", "Last" );
+        userAttributes.put( "cn", "FLast" );
+        userAttributes.put( "fullname", "First Last" );
+        userAttributes.put( "uid", "FLast" );
+        userAttributes.put( "mail", "FLast@example.com" );
+        userAttributes.put( "carLicense", "6YJ S32" );
+        userAttributes.put( "mobile", "800-555-1212" );
+        userAttributes.put( "objectClass", "inetOrgPerson" );
+        userAttributes.put( "personalMobile", "800-555-1313" );
+        userAttributes.put( "title", "Title" );
+        userAttributes.put( "c", "USA" );
+        userAttributes.put( "co", "County" );
+        userAttributes.put( "description", "User Description" );
+        userAttributes.put( "department", "Department" );
+        userAttributes.put( "initials", "M" );
+        userAttributes.put( "postalcode", "12345-6789" );
+        userAttributes.put( "samaccountname", "FLast" );
+        userAttributes.put( "userprincipalname", "FLast" );
+
+
+        final OTPUserRecord otpUserRecord = new OTPUserRecord();
+        otpUserRecord.setTimestamp( Instant.ofEpochSecond( 941259364 ) );
+        final ResponseInfoBean responseInfoBean = new ResponseInfoBean(
+                Collections.emptyMap(),
+                Collections.emptyMap(),
+                PwmConstants.DEFAULT_LOCALE,
+                8 + 3,
+                null,
+                DataStorageMethod.LOCALDB,
+                Answer.FormatType.PBKDF2
+        );
+        responseInfoBean.setTimestamp( Instant.ofEpochSecond( 941246275 ) );
+
+        final UserIdentity userIdentity = new UserIdentity( "cn=FLast,ou=test,o=org", "profile1" );
+
+        return UserInfoBean.builder()
+                .userIdentity( userIdentity )
+                .username( "FLast" )
+                .userEmailAddress( "FLast@example.com" )
+                .attributes( userAttributes )
+                .passwordExpirationTime( Instant.ofEpochSecond( 949539661 ) )
+                .responseInfoBean( responseInfoBean )
+                .otpUserRecord( otpUserRecord )
+                .build();
+    }
+
+    public static UserInfo sampleTargetUserInfo()
+    {
+
+            final Map<String, String> userAttributes = new LinkedHashMap<>();
+            userAttributes.put( "givenName", "Target" );
+            userAttributes.put( "sn", "User" );
+            userAttributes.put( "cn", "TUser" );
+            userAttributes.put( "fullname", "Target User" );
+            userAttributes.put( "uid", "TUser" );
+            userAttributes.put( "mail", "TUser@example.com" );
+            userAttributes.put( "carLicense", "6YJ S32" );
+            userAttributes.put( "mobile", "800-555-1212" );
+            userAttributes.put( "objectClass", "inetOrgPerson" );
+            userAttributes.put( "personalMobile", "800-555-1313" );
+            userAttributes.put( "title", "Title" );
+            userAttributes.put( "c", "USA" );
+            userAttributes.put( "co", "County" );
+            userAttributes.put( "description", "Target User Description" );
+            userAttributes.put( "department", "Department" );
+            userAttributes.put( "initials", "M" );
+            userAttributes.put( "postalcode", "12345-6789" );
+            userAttributes.put( "samaccountname", "TUser" );
+            userAttributes.put( "userprincipalname", "TUser" );
+
+
+            final OTPUserRecord otpUserRecord = new OTPUserRecord();
+            otpUserRecord.setTimestamp( Instant.ofEpochSecond( 941252344 ) );
+            final ResponseInfoBean responseInfoBean = new ResponseInfoBean(
+                    Collections.emptyMap(),
+                    Collections.emptyMap(),
+                    PwmConstants.DEFAULT_LOCALE,
+                    8 + 3,
+                    null,
+                    DataStorageMethod.LOCALDB,
+                    Answer.FormatType.PBKDF2
+            );
+            responseInfoBean.setTimestamp( Instant.ofEpochSecond( 941244474 ) );
+
+            final UserIdentity userIdentity = new UserIdentity( "cn=TUser,ou=test,o=org", "profile1" );
+
+            return UserInfoBean.builder()
+                    .userIdentity( userIdentity )
+                    .username( "TUser" )
+                    .userEmailAddress( "TUser@example.com" )
+                    .attributes( userAttributes )
+                    .passwordExpirationTime( Instant.ofEpochSecond( 94949121 ) )
+                    .responseInfoBean( responseInfoBean )
+                    .otpUserRecord( otpUserRecord )
+                    .build();
+
+    }
+
+    public static MacroRequest sampleMacroRequest( final PwmApplication pwmApplication )
+            throws PwmUnrecoverableException
+    {
+        final UserInfo targetUserInfoBean = sampleTargetUserInfo();
+
+
+        final UserInfo userInfoBean = sampleUserData();
+
+        final LoginInfoBean loginInfoBean = new LoginInfoBean();
+        loginInfoBean.setAuthenticated( true );
+        loginInfoBean.setUserIdentity( userInfoBean.getUserIdentity() );
+        loginInfoBean.setUserCurrentPassword( PasswordData.forStringValue( "PaSSw0rd" ) );
+
+        return MacroRequest.builder()
+                .pwmApplication( pwmApplication )
+                .userInfo( userInfoBean )
+                .targetUserInfo( targetUserInfoBean )
+                .loginInfoBean( loginInfoBean )
+                .build();
+
+    }
+}

+ 10 - 1
server/src/main/java/password/pwm/util/java/JavaHelper.java

@@ -28,6 +28,7 @@ import password.pwm.PwmConstants;
 import password.pwm.config.Configuration;
 import password.pwm.config.PwmSetting;
 import password.pwm.http.ContextManager;
+import password.pwm.http.bean.ImmutableByteArray;
 import password.pwm.util.logging.PwmLogger;
 
 import java.io.ByteArrayInputStream;
@@ -279,7 +280,7 @@ public class JavaHelper
         return IOUtils.copyLarge( input, output, 0, -1, buffer );
     }
 
-    public static String copy( final InputStream input )
+    public static String copyToString( final InputStream input )
             throws IOException
     {
         final ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
@@ -287,6 +288,13 @@ public class JavaHelper
         return new String( byteArrayOutputStream.toByteArray(), PwmConstants.DEFAULT_CHARSET );
     }
 
+    public static ImmutableByteArray copyToBytes( final InputStream inputStream )
+            throws IOException
+    {
+        final byte[] bytes = IOUtils.toByteArray( inputStream );
+        return ImmutableByteArray.of( bytes );
+    }
+
     public static void copy( final String input, final OutputStream output )
             throws IOException
     {
@@ -715,4 +723,5 @@ public class JavaHelper
         }
         return returnMap;
     }
+    
 }

+ 36 - 26
server/src/main/java/password/pwm/util/java/LicenseInfoReader.java

@@ -21,8 +21,11 @@
 package password.pwm.util.java;
 
 import lombok.Value;
+import password.pwm.error.ErrorInformation;
+import password.pwm.error.PwmError;
 import password.pwm.error.PwmUnrecoverableException;
 
+import java.io.IOException;
 import java.io.InputStream;
 import java.util.ArrayList;
 import java.util.Collections;
@@ -43,44 +46,51 @@ public class LicenseInfoReader
 
         for ( final String attributionFile : attributionFiles )
         {
-            final InputStream attributionInputStream = XmlFactory.XmlFactoryJDOM.class.getResourceAsStream( attributionFile );
-
-            if ( attributionInputStream != null )
+            try ( InputStream attributionInputStream = LicenseInfoReader.class.getResourceAsStream( attributionFile ) )
             {
-                final XmlDocument document = factory.parseXml( attributionInputStream );
-                final XmlElement rootElement = document.getRootElement();
-                final XmlElement dependenciesElement = rootElement.getChildren( "dependencies" ).iterator().next();
-
-                for ( final XmlElement dependency : dependenciesElement.getChildren( "dependency" ) )
+                if ( attributionInputStream != null )
                 {
-                    final String projectUrl = dependency.getChildText( "projectUrl" );
-                    final String name = dependency.getChildText( "name" );
-                    final String artifactId = dependency.getChildText( "artifactId" );
-                    final String version = dependency.getChildText( "version" );
-                    final String type = dependency.getChildText( "type" );
+                    final XmlDocument document = factory.parseXml( attributionInputStream );
+                    final XmlElement rootElement = document.getRootElement();
+                    final XmlElement dependenciesElement = rootElement.getChildren( "dependencies" ).iterator().next();
 
-                    final List<LicenseInfo> licenseInfos = new ArrayList<>();
+                    for ( final XmlElement dependency : dependenciesElement.getChildren( "dependency" ) )
                     {
-                        final Optional<XmlElement> licenses = dependency.getChild( "licenses" );
-                        if ( licenses.isPresent() )
+                        final String projectUrl = dependency.getChildText( "projectUrl" );
+                        final String name = dependency.getChildText( "name" );
+                        final String artifactId = dependency.getChildText( "artifactId" );
+                        final String version = dependency.getChildText( "version" );
+                        final String type = dependency.getChildText( "type" );
+
+                        final List<LicenseInfo> licenseInfos = new ArrayList<>();
                         {
-                            final List<XmlElement> licenseList = licenses.get().getChildren( "license" );
-                            for ( final XmlElement license : licenseList )
+                            final Optional<XmlElement> licenses = dependency.getChild( "licenses" );
+                            if ( licenses.isPresent() )
                             {
-                                final String licenseUrl = license.getChildText( "url" );
-                                final String licenseName = license.getChildText( "name" );
-                                final LicenseInfo licenseInfo = new LicenseInfo( licenseUrl, licenseName );
-                                licenseInfos.add( licenseInfo );
+                                final List<XmlElement> licenseList = licenses.get().getChildren( "license" );
+                                for ( final XmlElement license : licenseList )
+                                {
+                                    final String licenseUrl = license.getChildText( "url" );
+                                    final String licenseName = license.getChildText( "name" );
+                                    final LicenseInfo licenseInfo = new LicenseInfo( licenseUrl, licenseName );
+                                    licenseInfos.add( licenseInfo );
+                                }
                             }
                         }
-                    }
 
-                    final DependencyInfo dependencyInfo = new DependencyInfo( projectUrl, name, artifactId, version, type,
-                            Collections.unmodifiableList( licenseInfos ) );
+                        final DependencyInfo dependencyInfo = new DependencyInfo( projectUrl, name, artifactId, version, type,
+                                Collections.unmodifiableList( licenseInfos ) );
 
-                    returnList.add( dependencyInfo );
+                        returnList.add( dependencyInfo );
+                    }
                 }
             }
+            catch ( final IOException e )
+            {
+                final String errorMsg = "unexpected error reading stream license data: " + e.getMessage();
+                final ErrorInformation errorInfo = new ErrorInformation( PwmError.ERROR_MISSING_PARAMETER, errorMsg );
+                throw new PwmUnrecoverableException( errorInfo );
+            }
         }
         return Collections.unmodifiableList( returnList );
     }

+ 7 - 0
server/src/main/java/password/pwm/util/java/StringUtil.java

@@ -29,7 +29,9 @@ import password.pwm.error.PwmError;
 import password.pwm.error.PwmUnrecoverableException;
 import password.pwm.util.logging.PwmLogger;
 
+import java.io.ByteArrayInputStream;
 import java.io.IOException;
+import java.io.InputStream;
 import java.io.UnsupportedEncodingException;
 import java.net.URLDecoder;
 import java.net.URLEncoder;
@@ -665,4 +667,9 @@ public abstract class StringUtil
             throw PwmUnrecoverableException.newException( PwmError.CONFIG_FORMAT_ERROR, "unbalanced parentheses in ldap filter" );
         }
     }
+
+    public static InputStream stringToInputStream( final String input )
+    {
+        return new ByteArrayInputStream( input.getBytes( PwmConstants.DEFAULT_CHARSET ) );
+    }
 }

+ 12 - 4
server/src/main/java/password/pwm/util/java/XmlFactory.java

@@ -61,10 +61,15 @@ public interface XmlFactory
         W3C,
     }
 
+    enum OutputFlag
+    {
+        Compact,
+    }
+
     XmlDocument parseXml( InputStream inputStream )
             throws PwmUnrecoverableException;
 
-    void outputDocument( XmlDocument document, OutputStream outputStream )
+    void outputDocument( XmlDocument document, OutputStream outputStream, OutputFlag... outputFlags )
             throws IOException;
 
     XmlDocument newDocument( String rootElementName );
@@ -133,7 +138,7 @@ public interface XmlFactory
         }
 
         @Override
-        public void outputDocument( final XmlDocument document, final OutputStream outputStream )
+        public void outputDocument( final XmlDocument document, final OutputStream outputStream, final OutputFlag... outputFlags )
                 throws IOException
         {
             final Document jdomDoc =  ( ( XmlDocument.XmlDocumentJDOM )  document ).document;
@@ -226,18 +231,20 @@ public interface XmlFactory
         }
 
         @Override
-        public void outputDocument( final XmlDocument document, final OutputStream outputStream )
+        public void outputDocument( final XmlDocument document, final OutputStream outputStream, final OutputFlag... outputFlags )
                 throws IOException
         {
             final Lock lock = ( ( XmlDocument.XmlDocumentW3c ) document ).lock;
+            final boolean compact = JavaHelper.enumArrayContainsValue( outputFlags, OutputFlag.Compact );
 
             lock.lock();
             try
             {
                 final Transformer tr = TransformerFactory.newInstance().newTransformer();
-                tr.setOutputProperty( OutputKeys.INDENT, "yes" );
+                tr.setOutputProperty( OutputKeys.INDENT, compact ? "no" : "yes" );
                 tr.setOutputProperty( OutputKeys.METHOD, "xml" );
                 tr.setOutputProperty( OutputKeys.ENCODING, STORAGE_CHARSET.toString() );
+
                 tr.transform( new DOMSource( ( ( XmlDocument.XmlDocumentW3c ) document ).document ), new StreamResult( outputStream ) );
             }
             catch ( final TransformerException e )
@@ -273,6 +280,7 @@ public interface XmlFactory
         {
             final DocumentBuilder documentBuilder = getBuilder();
             final org.w3c.dom.Document document = documentBuilder.newDocument();
+            document.setXmlStandalone( true );
             final org.w3c.dom.Element rootElement = document.createElement( rootElementName );
             document.appendChild( rootElement );
             return new XmlDocument.XmlDocumentW3c( document );

+ 0 - 127
server/src/main/java/password/pwm/util/macro/MacroRequest.java

@@ -20,30 +20,19 @@
 
 package password.pwm.util.macro;
 
-import com.novell.ldapchai.cr.Answer;
 import lombok.Builder;
 import lombok.Value;
 import password.pwm.PwmApplication;
-import password.pwm.PwmConstants;
 import password.pwm.bean.LoginInfoBean;
-import password.pwm.bean.ResponseInfoBean;
 import password.pwm.bean.SessionLabel;
 import password.pwm.bean.UserIdentity;
-import password.pwm.config.option.DataStorageMethod;
 import password.pwm.error.PwmUnrecoverableException;
 import password.pwm.http.CommonValues;
 import password.pwm.http.PwmRequest;
 import password.pwm.ldap.UserInfo;
-import password.pwm.ldap.UserInfoBean;
 import password.pwm.ldap.UserInfoFactory;
-import password.pwm.util.PasswordData;
-import password.pwm.util.operations.otp.OTPUserRecord;
 
-import java.time.Instant;
-import java.util.Collections;
-import java.util.LinkedHashMap;
 import java.util.Locale;
-import java.util.Map;
 
 @Value
 @Builder( toBuilder = true )
@@ -148,120 +137,4 @@ public class MacroRequest
         return MacroMachine.expandMacros( this, input );
     }
 
-    public static MacroRequest sampleMacroRequest( final PwmApplication pwmApplication )
-            throws PwmUnrecoverableException
-    {
-        final UserInfoBean userInfoBean;
-        {
-            final Map<String, String> userAttributes = new LinkedHashMap<>();
-            userAttributes.put( "givenName", "First" );
-            userAttributes.put( "sn", "Last" );
-            userAttributes.put( "cn", "FLast" );
-            userAttributes.put( "fullname", "First Last" );
-            userAttributes.put( "uid", "FLast" );
-            userAttributes.put( "mail", "FLast@example.com" );
-            userAttributes.put( "carLicense", "6YJ S32" );
-            userAttributes.put( "mobile", "800-555-1212" );
-            userAttributes.put( "objectClass", "inetOrgPerson" );
-            userAttributes.put( "personalMobile", "800-555-1313" );
-            userAttributes.put( "title", "Title" );
-            userAttributes.put( "c", "USA" );
-            userAttributes.put( "co", "County" );
-            userAttributes.put( "description", "User Description" );
-            userAttributes.put( "department", "Department" );
-            userAttributes.put( "initials", "M" );
-            userAttributes.put( "postalcode", "12345-6789" );
-            userAttributes.put( "samaccountname", "FLast" );
-            userAttributes.put( "userprincipalname", "FLast" );
-
-
-            final OTPUserRecord otpUserRecord = new OTPUserRecord();
-            otpUserRecord.setTimestamp( Instant.ofEpochSecond( 941259364 ) );
-            final ResponseInfoBean responseInfoBean = new ResponseInfoBean(
-                    Collections.emptyMap(),
-                    Collections.emptyMap(),
-                    PwmConstants.DEFAULT_LOCALE,
-                    8 + 3,
-                    null,
-                    DataStorageMethod.LOCALDB,
-                    Answer.FormatType.PBKDF2
-            );
-            responseInfoBean.setTimestamp( Instant.ofEpochSecond( 941246275 ) );
-
-            final UserIdentity userIdentity = new UserIdentity( "cn=FLast,ou=test,o=org", "profile1" );
-
-            userInfoBean = UserInfoBean.builder()
-                    .userIdentity( userIdentity )
-                    .username( "FLast" )
-                    .userEmailAddress( "FLast@example.com" )
-                    .attributes( userAttributes )
-                    .passwordExpirationTime( Instant.ofEpochSecond( 949539661 ) )
-                    .responseInfoBean( responseInfoBean )
-                    .otpUserRecord( otpUserRecord )
-                    .build();
-        }
-
-        final UserInfoBean targetUserInfoBean;
-        {
-            final Map<String, String> userAttributes = new LinkedHashMap<>();
-            userAttributes.put( "givenName", "Target" );
-            userAttributes.put( "sn", "User" );
-            userAttributes.put( "cn", "TUser" );
-            userAttributes.put( "fullname", "Target User" );
-            userAttributes.put( "uid", "TUser" );
-            userAttributes.put( "mail", "TUser@example.com" );
-            userAttributes.put( "carLicense", "6YJ S32" );
-            userAttributes.put( "mobile", "800-555-1212" );
-            userAttributes.put( "objectClass", "inetOrgPerson" );
-            userAttributes.put( "personalMobile", "800-555-1313" );
-            userAttributes.put( "title", "Title" );
-            userAttributes.put( "c", "USA" );
-            userAttributes.put( "co", "County" );
-            userAttributes.put( "description", "Target User Description" );
-            userAttributes.put( "department", "Department" );
-            userAttributes.put( "initials", "M" );
-            userAttributes.put( "postalcode", "12345-6789" );
-            userAttributes.put( "samaccountname", "TUser" );
-            userAttributes.put( "userprincipalname", "TUser" );
-
-
-            final OTPUserRecord otpUserRecord = new OTPUserRecord();
-            otpUserRecord.setTimestamp( Instant.ofEpochSecond( 941252344 ) );
-            final ResponseInfoBean responseInfoBean = new ResponseInfoBean(
-                    Collections.emptyMap(),
-                    Collections.emptyMap(),
-                    PwmConstants.DEFAULT_LOCALE,
-                    8 + 3,
-                    null,
-                    DataStorageMethod.LOCALDB,
-                    Answer.FormatType.PBKDF2
-            );
-            responseInfoBean.setTimestamp( Instant.ofEpochSecond( 941244474 ) );
-
-            final UserIdentity userIdentity = new UserIdentity( "cn=TUser,ou=test,o=org", "profile1" );
-
-            targetUserInfoBean = UserInfoBean.builder()
-                    .userIdentity( userIdentity )
-                    .username( "TUser" )
-                    .userEmailAddress( "TUser@example.com" )
-                    .attributes( userAttributes )
-                    .passwordExpirationTime( Instant.ofEpochSecond( 94949121 ) )
-                    .responseInfoBean( responseInfoBean )
-                    .otpUserRecord( otpUserRecord )
-                    .build();
-        }
-
-        final LoginInfoBean loginInfoBean = new LoginInfoBean();
-        loginInfoBean.setAuthenticated( true );
-        loginInfoBean.setUserIdentity( userInfoBean.getUserIdentity() );
-        loginInfoBean.setUserCurrentPassword( PasswordData.forStringValue( "PaSSw0rd" ) );
-
-        return MacroRequest.builder()
-                .pwmApplication( pwmApplication )
-                .userInfo( userInfoBean )
-                .targetUserInfo( targetUserInfoBean )
-                .loginInfoBean( loginInfoBean )
-                .build();
-
-    }
 }

+ 94 - 0
server/src/test/java/password/pwm/svc/event/LdapXmlUserHistoryTest.java

@@ -0,0 +1,94 @@
+/*
+ * Password Management Servlets (PWM)
+ * http://www.pwm-project.org
+ *
+ * Copyright (c) 2006-2009 Novell, Inc.
+ * Copyright (c) 2009-2020 The PWM Project
+ *
+ * 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 password.pwm.svc.event;
+
+import org.junit.Assert;
+import org.junit.Test;
+import password.pwm.error.PwmUnrecoverableException;
+import password.pwm.util.SampleDataGenerator;
+import password.pwm.util.java.StringUtil;
+import password.pwm.util.java.XmlDocument;
+import password.pwm.util.java.XmlElement;
+import password.pwm.util.java.XmlFactory;
+
+import java.time.Instant;
+import java.util.List;
+import java.util.Optional;
+import java.util.ResourceBundle;
+
+public class LdapXmlUserHistoryTest
+{
+    @Test
+    public void inputParserTest()
+    {
+        final ResourceBundle bundle = ResourceBundle.getBundle( LdapXmlUserHistoryTest.class.getName() );
+        final String xmlValue1 =  bundle.getString( "xmlValue1" );
+        final LdapXmlUserHistory.StoredHistory storedHistory = LdapXmlUserHistory.StoredHistory.fromXml( xmlValue1 );
+
+        final List<UserAuditRecord> auditEventList = storedHistory.asAuditRecords( SampleDataGenerator.sampleUserData() );
+        //System.out.println( JsonUtil.serializeCollection( auditEventList, JsonUtil.Flag.PrettyPrint ) );
+
+        Assert.assertEquals( 20, auditEventList.size() );
+        Assert.assertEquals( 9, auditEventList.stream()
+                .filter( ( record ) -> record.getEventCode() == AuditEvent.CHANGE_PASSWORD ).count() );
+
+        {
+            final UserAuditRecord record0 = auditEventList.get( 0 );
+            Assert.assertEquals( "ort", record0.getSourceHost() );
+            Assert.assertEquals( "172.17.2.1", record0.getSourceAddress() );
+            Assert.assertEquals( Instant.parse( "2019-07-28T01:14:39.054Z" ), record0.getTimestamp() );
+            Assert.assertEquals( AuditEvent.CHANGE_PASSWORD, record0.getEventCode() );
+        }
+
+        {
+            final UserAuditRecord record7 = auditEventList.get( 7 );
+            Assert.assertEquals( "0:0:0:0:0:0:0:1", record7.getSourceHost() );
+            Assert.assertEquals( "0:0:0:0:0:0:0:1", record7.getSourceAddress() );
+            Assert.assertEquals( Instant.parse( "2020-07-12T02:29:22.347Z" ), record7.getTimestamp() );
+            Assert.assertEquals( AuditEvent.AUTHENTICATE, record7.getEventCode() );
+        }
+
+
+    }
+
+    @Test
+    public void outputTest() throws PwmUnrecoverableException
+    {
+        final LdapXmlUserHistory.StoredHistory storedHistory = new LdapXmlUserHistory.StoredHistory();
+        storedHistory.addEvent( LdapXmlUserHistory.StoredEvent.fromAuditRecord( UserAuditRecord.builder()
+                .timestamp( Instant.parse( "2020-02-27T17:26:30Z" ) )
+                .eventCode( AuditEvent.CHANGE_PASSWORD )
+
+                .build() ) );
+
+        final String xmlValue = storedHistory.toXml();
+        final XmlFactory xmlFactory = XmlFactory.getFactory();
+
+        final XmlDocument xmlDocument = xmlFactory.parseXml( StringUtil.stringToInputStream( xmlValue ) );
+        final Optional<XmlElement> optionalRecordElement = xmlDocument.evaluateXpathToElement( "/history/record" );
+        Assert.assertTrue( optionalRecordElement.isPresent() );
+        optionalRecordElement.ifPresent( xmlElement ->
+        {
+            Assert.assertEquals( "EventLog_ChangePassword", xmlElement.getAttributeValue( "eventCode" ) );
+            Assert.assertEquals( "1582824390000", xmlElement.getAttributeValue( "timestamp" ) );
+        } );
+    }
+}

+ 2 - 1
server/src/test/java/password/pwm/util/macro/MacroTest.java

@@ -28,6 +28,7 @@ import org.junit.rules.TemporaryFolder;
 import password.pwm.PwmConstants;
 import password.pwm.config.PwmSetting;
 import password.pwm.error.PwmUnrecoverableException;
+import password.pwm.util.SampleDataGenerator;
 
 import java.time.Duration;
 import java.time.Instant;
@@ -43,7 +44,7 @@ public class MacroTest
     @Before
     public void setUp() throws PwmUnrecoverableException
     {
-        macroRequest = MacroRequest.sampleMacroRequest( null );
+        macroRequest = SampleDataGenerator.sampleMacroRequest( null );
     }
 
 

Різницю між файлами не показано, бо вона завелика
+ 20 - 0
server/src/test/resources/password/pwm/svc/event/LdapXmlUserHistoryTest.properties


Деякі файли не було показано, через те що забагато файлів було змінено