View Javadoc
1   package fr.paris.lutece.portal.service.securityheader;
2   
3   import java.util.ArrayList;
4   import java.util.Collection;
5   import java.util.Collections;
6   import java.util.Comparator;
7   import java.util.HashMap;
8   import java.util.List;
9   import java.util.Locale;
10  import java.util.Map;
11  import java.util.stream.Collectors;
12  
13  import org.apache.logging.log4j.LogManager;
14  import org.apache.logging.log4j.Logger;
15  
16  import fr.paris.lutece.portal.business.securityheader.SecurityHeader;
17  import fr.paris.lutece.portal.business.securityheader.SecurityHeaderHome;
18  import fr.paris.lutece.portal.business.securityheader.SecurityHeaderPageCategory;
19  import fr.paris.lutece.portal.business.securityheader.SecurityHeaderType;
20  import fr.paris.lutece.util.ReferenceList;
21  
22  /**
23   * This class provides a service that offers methods to manage security headers. 
24   */
25  public class SecurityHeaderService 
26  {
27  	
28  	//This map contains the security headers loaded from the database. It prevents to make database calls when data are up to date.
29  	//It must be refreshed each time that an action is performed. Currently, These actions are creating a new header, modifying an 
30  	//existing header, removing a header and enabling or disabling a header
31  	private Map<String, SecurityHeader> _mapSecurityHeaders = new HashMap<String, SecurityHeader>( );
32  	
33  	Map<String, Map<String, List<SecurityHeader>>> _mapActiveSecurityHeadersForFilters = new HashMap<String, Map<String, List<SecurityHeader>>>( );
34  	
35  	//This boolean indicates if a refresh has been done on the security headers map. In other words, it tells if the map is up to date
36  	//with data from database
37  	private boolean _bMapRefreshDone = false;
38  	
39  	private Logger _logger = LogManager.getLogger( "lutece.securityHeader" );
40  	
41  	/**
42  	 * Returns all the security headers that match specified name, type and page category (if not null for the latter).
43  	 * If page category is null (REST api case), this criterion is ignored.
44  	 * 
45  	 * @param strName
46  	 *           The name of the security header
47  	 * @param strType
48  	 *           The type of the security header
49  	 * @param pageCategory
50  	 *           The page category of the security header
51  	 * @return list od security headers matching the criteria
52  	 */
53  	public List<SecurityHeader> find( String strName, String strType, String pageCategory )
54  	{
55  		ArrayList<SecurityHeader> securityHeadersResultList = new ArrayList<SecurityHeader>( );
56  		
57  		for( SecurityHeader securityHeader : findAll( ) )
58  		{
59  			if( securityHeader.getName( ).equals( strName ) && securityHeader.getType( ).equals( strType ) )
60  			{
61  				if( pageCategory == null || securityHeader.getPageCategory( ).equals( pageCategory ) )
62  				{
63  					securityHeadersResultList.add( securityHeader );
64  				}				
65  			}
66  		}
67  		
68  		return securityHeadersResultList;
69  	}
70  	
71  	/**
72  	 * Returns all security headers from the specified type and the specified category (if not null)
73  	 * that are active (attribute is_enable = true).
74  	 * 
75  	 * @param strType
76  	 * @param strPageCategory
77  	 * @return collection on security headers
78  	 */
79  	public Collection<SecurityHeader> findActive( String strType, String strPageCategory )
80  	{
81  		if( !_bMapRefreshDone )
82  		{
83  			refreshSecurityHeadersMap( SecurityHeaderHome.findAll() );
84  		}
85  		if( _mapActiveSecurityHeadersForFilters.get(strType) != null )
86  		{
87  			return _mapActiveSecurityHeadersForFilters.get( strType ).get( strPageCategory );
88  		}
89  		return null;
90  	}
91  	
92  	/**
93  	 * Returns a collection of all security headers.
94  	 * Security headers are fetched from database (if data are stale) or from security headers map in memory (if the data in map are still up to date).
95  	 * 
96  	 * @return collection of security headers
97  	 */
98  	public Collection<SecurityHeader> findAll( )
99      {
100 		Collection<SecurityHeader> securityHeadersList = null;
101 		
102 		if( _bMapRefreshDone )
103 		{
104 			securityHeadersList = _mapSecurityHeaders.values( );
105 		} 
106 		else
107 		{			
108 			securityHeadersList = SecurityHeaderHome.findAll( );
109 			refreshSecurityHeadersMap( securityHeadersList );	
110 		}
111 		
112 		return securityHeadersList;
113     }
114 	
115 	/**
116 	 * Returns a list of all security headers used in manage security headers page. They are sorted by type, page category and name.
117 	 * 
118 	 * @param locale
119 	 *           The locale
120 	 * @return list of all security headers
121 	 */
122 	public List<SecurityHeader> findAllSorted( Locale locale )
123     {
124 		List<SecurityHeader> securityHeadersList = findAll( ).stream().collect( Collectors.toList( ) );   	
125 		Collections.sort( securityHeadersList, Comparator.comparing( SecurityHeader::getType )
126 				                                         .thenComparing( SecurityHeader::getPageCategory, Comparator.nullsLast( Comparator.naturalOrder( ) ) )
127 				                                         .thenComparing( SecurityHeader::getName ) );
128     	
129 		return securityHeadersList;
130     }
131 	
132 	/**
133 	 * Returns the reference list of security headers types
134 	 * 
135 	 * @return ReferenceList object with security headers types
136 	 */
137 	public ReferenceList getTypeList( )
138 	{
139 		ReferenceList listTypes = new ReferenceList( );
140 		
141 		for (SecurityHeaderType type : SecurityHeaderType.values()) { 
142 			listTypes.addItem( type.getCode(), type.getCode() );
143 		}
144 		
145 		return listTypes;
146 	}
147 	
148 	/**
149 	 * Returns the reference list of security headers page categories
150 	 * 
151 	 * @return ReferenceList object with security headers page categories
152 	 */
153 	public ReferenceList getPageCategoryList( )
154 	{
155 		ReferenceList listPageCategory = new ReferenceList( );
156 		
157 		for (SecurityHeaderPageCategory pageCategory : SecurityHeaderPageCategory.values()) { 
158 			listPageCategory.addItem( pageCategory.getCode(), pageCategory.getCode() );
159 		}
160 		
161 		return listPageCategory;
162 	}
163 	
164 	/**
165 	 * Create the security header given in parameter. Like all the actions, this one invalidates the map containing the security headers.
166 	 * 
167 	 * @param securityHeader
168 	 *                The security header to create
169 	 */
170 	public void create( SecurityHeader securityHeader )
171 	{
172 		_logger.debug( "Security header to create : name : {}, value : {}, type : {}, page category : {}", securityHeader.getName( ), securityHeader.getValue( ), securityHeader.getType( ), securityHeader.getPageCategory( ) );
173 		SecurityHeaderHome.create( securityHeader );
174 		clearMapSecurityHeaders( );
175 		_logger.debug( "Security header created" );
176 	}
177 
178 	/**
179 	 * Update the security header given in parameter. Like all the actions, this one invalidates the map containing the security headers.
180 	 * 
181 	 * @param securityHeader
182 	 *                The security header to update
183 	 */
184 	public void update( SecurityHeader securityHeader )
185 	{
186 		_logger.debug( "Security header to update : id : {}, name : {}, value : {}, type : {}, page category : {}", securityHeader.getId( ), securityHeader.getName( ), securityHeader.getValue( ), securityHeader.getType( ), securityHeader.getPageCategory( ) );
187 		SecurityHeaderHome.update( securityHeader );
188 		clearMapSecurityHeaders( );
189 		_logger.debug( "Security header updated" );
190 	}
191 	
192 	/**
193 	 * Remove the security header given in parameter. Like all the actions, this one invalidates the map containing the security headers.
194 	 * 
195 	 * @param securityHeader
196 	 *                The security header to remove
197 	 */
198 	public void remove( int nSecurityHeaderId )
199 	{
200 		SecurityHeader securityHeader = _mapSecurityHeaders.get( String.valueOf( nSecurityHeaderId ) );
201 		_logger.debug( "Security header to delete : id : {}, name : {}, value : {}, type : {}, page category : {}", securityHeader.getId( ), securityHeader.getName( ), securityHeader.getValue( ), securityHeader.getType( ), securityHeader.getPageCategory( ) );
202 		SecurityHeaderHome.remove( nSecurityHeaderId );
203 		clearMapSecurityHeaders( );
204 		_logger.debug( "Security header deleted" );
205 	}
206 	
207 	/**
208 	 * Enable the security header with the id given in parameter. Like all the actions, this one invalidates the map containing the security headers.
209 	 * 
210 	 * @param nSecurityHeaderId
211 	 *                The id of the security header to enable
212 	 */
213 	public void enable( int nSecurityHeaderId )
214 	{
215 		SecurityHeader securityHeader = _mapSecurityHeaders.get( String.valueOf( nSecurityHeaderId ) );
216 		_logger.debug( "Security header to enable : id : {}, name : {}, value : {}, type : {}, page category : {}", securityHeader.getId( ), securityHeader.getName( ), securityHeader.getValue( ), securityHeader.getType( ), securityHeader.getPageCategory( ) );
217 		SecurityHeaderHome.updateIsActive( nSecurityHeaderId, true );
218 		clearMapSecurityHeaders( );
219 		_logger.debug( "Security header enabled" );
220 	}
221 	
222 	/**
223 	 * Disable the security header with the id given in parameter. Like all the actions, this one invalidates the map containing the security headers.
224 	 * 
225 	 * @param nSecurityHeaderId
226 	 *                The id of the security header to disable
227 	 */
228 	public void disable( int nSecurityHeaderId )
229 	{
230 		SecurityHeader securityHeader = _mapSecurityHeaders.get( String.valueOf( nSecurityHeaderId ) );
231 		_logger.debug( "Security header to disable : id : {}, name : {}, value : {}, type : {}, page category : {}", securityHeader.getId( ), securityHeader.getName( ), securityHeader.getValue( ), securityHeader.getType( ), securityHeader.getPageCategory( ) );
232 		SecurityHeaderHome.updateIsActive( nSecurityHeaderId, false );
233 		clearMapSecurityHeaders( );
234 		_logger.debug( "Security header disabled" );
235 	}
236 	
237 	/**
238 	 * Clears the map containing the security headers
239 	 * 
240 	 */
241 	private void clearMapSecurityHeaders( )
242 	{
243 		_mapSecurityHeaders.clear( );
244 		_mapActiveSecurityHeadersForFilters.clear( );
245 		_bMapRefreshDone = false;
246 		_logger.debug( "Security header maps cleared" );
247 	}
248 	
249 	/**
250 	 * Refreshes the map of security headers with the list given in parameter. After a call to this method, data of the map are up to date with database data.
251 	 * 
252 	 * @param securityHeadersList
253 	 *                 The security headers collection
254 	 */
255 	private void refreshSecurityHeadersMap( Collection<SecurityHeader> securityHeadersList )
256 	{
257 		for( SecurityHeader securityHeader : securityHeadersList )
258 		{
259 			_mapSecurityHeaders.put( String.valueOf( securityHeader.getId( ) ), securityHeader );
260 			
261 			if( securityHeader.isActive() )
262 			{
263 				_mapActiveSecurityHeadersForFilters.put( securityHeader.getType( ), addHeaderToTypeMap( securityHeader ) );
264 			}					
265 		}
266 		_bMapRefreshDone = true;
267 		_logger.debug( "Security header map refreshed" );
268 	}
269 	
270 	/**
271 	 * Adds a security header in the map of active security headers of the same type as the security header passed in parameter
272 	 * 
273 	 * @param securityHeader Security header to add to the map
274 	 * @return map of active security headers of the same type as the security header passed in parameter updated with this security header
275 	 */
276 	private Map<String, List<SecurityHeader>> addHeaderToTypeMap( SecurityHeader securityHeader )
277 	{
278 		//In _mapActiveSecurityHeadersForFilters, 2 keys are necessary to retrieve a list of security headers.
279 		//For Page headers, those keys are respectively type and page category
280 		//For Rest api headers, those keys are respectively type and null value. 
281 		//As page category is not irrelevant for rest api headers, they are grouped using the null key. 
282 		String firstKey = securityHeader.getType( );
283 		Map<String, List<SecurityHeader>> mapHeadersForType = _mapActiveSecurityHeadersForFilters.get( firstKey );
284 		List<SecurityHeader> headersListToUpdate = null;
285 		
286 		String secondKey = null;					
287 		if( securityHeader.getType( ).equals( SecurityHeaderType.PAGE.getCode( ) ) )
288 		{
289 			secondKey = securityHeader.getPageCategory( );
290 		}
291 		
292 		if( mapHeadersForType == null )
293 		{
294 			mapHeadersForType = new HashMap<String, List<SecurityHeader>>( );
295 			headersListToUpdate = new ArrayList<SecurityHeader>( );					
296 		}
297 		else
298 		{					
299 			headersListToUpdate = mapHeadersForType.get( secondKey );
300 			if( headersListToUpdate == null )
301 			{
302 				headersListToUpdate = new ArrayList<SecurityHeader>( );							
303 			}
304 		}
305 		headersListToUpdate.add( securityHeader );
306 		mapHeadersForType.put( secondKey, headersListToUpdate );
307 		
308 		return mapHeadersForType;
309 	}
310 }