View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements.  See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership.  The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License.  You may obtain a copy of the License at
9    *
10   *   http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing,
13   * software distributed under the License is distributed on an
14   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   * KIND, either express or implied.  See the License for the
16   * specific language governing permissions and limitations
17   * under the License.
18   */
19  package org.apache.syncope.core.provisioning.java;
20  
21  import java.time.OffsetDateTime;
22  import java.util.Arrays;
23  import java.util.Optional;
24  import java.util.stream.Collectors;
25  import org.apache.commons.lang3.SerializationUtils;
26  import org.apache.syncope.common.lib.audit.AuditEntry;
27  import org.apache.syncope.common.lib.request.UserCR;
28  import org.apache.syncope.common.lib.request.UserUR;
29  import org.apache.syncope.common.lib.to.UserTO;
30  import org.apache.syncope.common.lib.types.AuditElements;
31  import org.apache.syncope.common.lib.types.AuditElements.Result;
32  import org.apache.syncope.common.lib.types.AuditLoggerName;
33  import org.apache.syncope.core.persistence.api.dao.AuditConfDAO;
34  import org.apache.syncope.core.persistence.api.entity.AuditConf;
35  import org.apache.syncope.core.provisioning.api.AuditManager;
36  import org.apache.syncope.core.provisioning.api.event.AfterHandlingEvent;
37  import org.apache.syncope.core.provisioning.api.serialization.POJOHelper;
38  import org.apache.syncope.core.provisioning.api.utils.ExceptionUtils2;
39  import org.apache.syncope.core.spring.security.AuthContextUtils;
40  import org.slf4j.Logger;
41  import org.slf4j.LoggerFactory;
42  import org.springframework.transaction.annotation.Propagation;
43  import org.springframework.transaction.annotation.Transactional;
44  
45  @Transactional(readOnly = true)
46  public class DefaultAuditManager implements AuditManager {
47  
48      protected static final String MASKED_VALUE = "<MASKED>";
49  
50      protected static Object maskSensitive(final Object object) {
51          Object masked;
52  
53          if (object instanceof UserTO) {
54              masked = SerializationUtils.clone((UserTO) object);
55              if (((UserTO) masked).getPassword() != null) {
56                  ((UserTO) masked).setPassword(MASKED_VALUE);
57              }
58              if (((UserTO) masked).getSecurityAnswer() != null) {
59                  ((UserTO) masked).setSecurityAnswer(MASKED_VALUE);
60              }
61          } else if (object instanceof UserCR) {
62              masked = SerializationUtils.clone((UserCR) object);
63              if (((UserCR) masked).getPassword() != null) {
64                  ((UserCR) masked).setPassword(MASKED_VALUE);
65              }
66              if (((UserCR) masked).getSecurityAnswer() != null) {
67                  ((UserCR) masked).setSecurityAnswer(MASKED_VALUE);
68              }
69          } else if (object instanceof UserUR && ((UserUR) object).getPassword() != null) {
70              masked = SerializationUtils.clone((UserUR) object);
71              ((UserUR) masked).getPassword().setValue(MASKED_VALUE);
72          } else {
73              masked = object;
74          }
75  
76          return masked;
77      }
78  
79      protected final AuditConfDAO auditConfDAO;
80  
81      public DefaultAuditManager(final AuditConfDAO auditConfDAO) {
82          this.auditConfDAO = auditConfDAO;
83      }
84  
85      @Override
86      public boolean auditRequested(
87              final String who,
88              final AuditElements.EventCategoryType type,
89              final String category,
90              final String subcategory,
91              final String event) {
92  
93          AuditEntry auditEntry = new AuditEntry();
94          auditEntry.setWho(who);
95          auditEntry.setLogger(new AuditLoggerName(type, category, subcategory, event, Result.SUCCESS));
96          auditEntry.setDate(OffsetDateTime.now());
97  
98          AuditConf auditConf = auditConfDAO.find(auditEntry.getLogger().toAuditKey());
99          boolean auditRequested = auditConf != null && auditConf.isActive();
100 
101         if (auditRequested) {
102             return true;
103         }
104 
105         auditEntry.setLogger(new AuditLoggerName(type, category, subcategory, event, Result.FAILURE));
106 
107         auditConf = auditConfDAO.find(auditEntry.getLogger().toAuditKey());
108         auditRequested = auditConf != null && auditConf.isActive();
109 
110         return auditRequested;
111     }
112 
113     @Transactional(propagation = Propagation.NOT_SUPPORTED)
114     @Override
115     public void audit(final AfterHandlingEvent event) {
116         audit(
117                 event.getWho(),
118                 event.getType(),
119                 event.getCategory(),
120                 event.getSubcategory(),
121                 event.getEvent(),
122                 event.getCondition(),
123                 event.getBefore(),
124                 event.getOutput(),
125                 event.getInput());
126     }
127 
128     @Transactional(propagation = Propagation.NOT_SUPPORTED)
129     @Override
130     public void audit(
131             final String who,
132             final AuditElements.EventCategoryType type,
133             final String category,
134             final String subcategory,
135             final String event,
136             final Result condition,
137             final Object before,
138             final Object output,
139             final Object... input) {
140 
141         AuditLoggerName auditLoggerName = new AuditLoggerName(type, category, subcategory, event, condition);
142 
143         Optional.ofNullable(auditConfDAO.find(auditLoggerName.toAuditKey())).
144                 filter(AuditConf::isActive).ifPresent(audit -> {
145 
146             Throwable throwable = output instanceof Throwable
147                     ? (Throwable) output
148                     : null;
149 
150             AuditEntry auditEntry = new AuditEntry();
151             auditEntry.setWho(who);
152             auditEntry.setLogger(auditLoggerName);
153             auditEntry.setDate(OffsetDateTime.now());
154             auditEntry.setBefore(POJOHelper.serialize((maskSensitive(before))));
155             if (throwable == null) {
156                 auditEntry.setOutput(POJOHelper.serialize((maskSensitive(output))));
157             } else {
158                 auditEntry.setOutput(throwable.getMessage());
159                 auditEntry.setThrowable(ExceptionUtils2.getFullStackTrace(throwable));
160             }
161             if (input != null) {
162                 auditEntry.getInputs().addAll(Arrays.stream(input).
163                         map(DefaultAuditManager::maskSensitive).map(POJOHelper::serialize).
164                         collect(Collectors.toList()));
165             }
166 
167             Logger logger = LoggerFactory.getLogger(
168                     AuditLoggerName.getAuditLoggerName(AuthContextUtils.getDomain()));
169             Logger eventLogger = LoggerFactory.getLogger(
170                     AuditLoggerName.getAuditEventLoggerName(AuthContextUtils.getDomain(), audit.getKey()));
171             String serializedAuditEntry = POJOHelper.serialize(auditEntry);
172 
173             if (throwable == null) {
174                 logger.debug(serializedAuditEntry);
175                 eventLogger.debug(serializedAuditEntry);
176             } else {
177                 logger.debug(serializedAuditEntry, throwable);
178                 eventLogger.debug(serializedAuditEntry, throwable);
179             }
180         });
181     }
182 }