1   package com.trendmicro.grid.acl.metadata;
2   
3   import com.trendmicro.grid.acl.commons.Paths;
4   import net.sf.tinyjee.util.FileUtils;
5   
6   import java.io.File;
7   import java.util.Arrays;
8   import java.util.Collection;
9   import java.util.HashSet;
10  import java.util.concurrent.Callable;
11  
12  /**
13   * Is a central singleton that manages whether Metadata access is validated (and restricted) or not.
14   *
15   * @author juergen_kellerer, 2010-11-30
16   * @version 1.0
17   */
18  public final class ValidationContext implements ValidatorResolver {
19  
20  	public static final String DEFAULT_METADATA_PROFILES_DOCUMENT = "default-metadata-profiles.xml";
21  	public static final String METADATA_PROFILES_TO_HTML = "metadata-profile-to-html.xsl";
22  	public static final String METADATA_PROFILES_DOCUMENT =
23  			System.getProperty("gacl.metadata.profiles", "metadata-profiles.xml");
24  
25  	private static final ValidationContext INSTANCE = new ValidationContext();
26  
27  	public static ValidationContext getInstance() {
28  		return INSTANCE;
29  	}
30  
31  	final ValidatorFactory validatorFactory;
32  	final ThreadLocal<ValidationInstructions> references = new ThreadLocal<ValidationInstructions>();
33  
34  	private ValidationContext() {
35  		try {
36  			File configDir = Paths.getConfigPath();
37  			File profiles = new File(configDir, METADATA_PROFILES_DOCUMENT);
38  
39  			ProfilesDocument profilesDocument;
40  			if (profiles.isFile())
41  				profilesDocument = ProfilesDocument.load(profiles);
42  			else {
43  				profiles = new File(configDir, DEFAULT_METADATA_PROFILES_DOCUMENT);
44  				profilesDocument = ProfilesDocument.load(
45  						getClass().getResourceAsStream("/" + DEFAULT_METADATA_PROFILES_DOCUMENT));
46  
47  				if (configDir.isDirectory()) {
48  					ProfilesDocument.getXmlSerializer().save(profilesDocument, profiles, true, 4);
49  					FileUtils.copy(getClass().getResourceAsStream("/" + METADATA_PROFILES_TO_HTML),
50  							new File(configDir, METADATA_PROFILES_TO_HTML), true);
51  				}
52  			}
53  
54  			validatorFactory = new ProfilesDocumentValidatorFactory(profilesDocument);
55  		} catch (Exception e) {
56  			throw new RuntimeException(e);
57  		}
58  	}
59  
60  	/**
61  	 * Returns the thread local reference values that are used by the validator to restrict access.
62  	 *
63  	 * @return the thread local reference values that are used by the validator to restrict access.
64  	 * @see #enableValidation(String, AccessLevel, String...)
65  	 * @see #enableValidation(String, AccessLevel, java.util.Collection)
66  	 * @see #disableValidation()
67  	 */
68  	public ValidationInstructions getActiveValidationInstructions() {
69  		return references.get();
70  	}
71  
72  	/**
73  	 * Enables Metadata access validation and restriction on the current Thread, using the given protection
74  	 * level and user roles as reference.
75  	 *
76  	 * @param fqProfileName The full qualified name of the validation profile to apply.
77  	 *                      See {@link WellKnownProfileNames} for a collection of known profiles.
78  	 * @param level         the level to execute validation on.
79  	 * @param roles         the list of roles that the current user belongs to (if any).
80  	 * @see #enableValidation(String, AccessLevel, String...)
81  	 * @see #disableValidation()
82  	 */
83  	public void enableValidation(String fqProfileName, AccessLevel level, Collection<String> roles) {
84  		if (fqProfileName == null)
85  			disableValidation();
86  		else {
87  			final ValidationInstructions currentInstructions = references.get();
88  			if (currentInstructions != null) {
89  				if (currentInstructions.getActiveProfileName().equals(fqProfileName) &&
90  						currentInstructions.getLevel() == level &&
91  						currentInstructions.getRoles().equals(roles))
92  					return;
93  			}
94  
95  			references.set(new ValidationInstructions(fqProfileName, level, roles));
96  		}
97  	}
98  
99  	/**
100 	 * Enables Metadata access validation and restriction on the current Thread, using the given protection
101 	 * level and user roles as reference.
102 	 *
103 	 * @param fqProfileName The full qualified name of the validation profile to apply.
104 	 *                      See {@link WellKnownProfileNames} for a collection of known profiles.
105 	 * @param level         the level to execute validation on.
106 	 * @param roles         the list of roles that the current user belongs to (if any).
107 	 * @see #enableValidation(String, AccessLevel, java.util.Collection)
108 	 * @see #disableValidation()
109 	 */
110 	public void enableValidation(String fqProfileName, AccessLevel level, String... roles) {
111 		enableValidation(fqProfileName, level, new HashSet<String>(Arrays.asList(roles)));
112 	}
113 
114 	/**
115 	 * Disables Metadata validation on the current Thread.
116 	 *
117 	 * @see #enableValidation(String, AccessLevel, String...)
118 	 * @see #enableValidation(String, AccessLevel, java.util.Collection)
119 	 */
120 	public void disableValidation() {
121 		references.remove();
122 	}
123 
124 	/**
125 	 * {@inheritDoc}
126 	 */
127 	@Override
128 	public Validator getValidator() {
129 		final ValidationInstructions ref = references.get();
130 		return ref == null ? null : validatorFactory.newValidator(ref);
131 	}
132 
133 	/**
134 	 * Performs the given operation with disabled validation on metadata access rights.
135 	 * <p/>
136 	 * Any operations on metadata that must not result in user and profile related filtering has to be
137 	 * wrapped in a callable and used with this method in order to ensure metadata is fully processed
138 	 * including any values that are not visible to the user session, access level and profile that is bound
139 	 * to the executing thread.
140 	 * <p/>
141 	 * Example:<code><pre>
142 	 * // Read a shareable metadata instance containing everything
143 	 * return ValidationContext.getInstance().doUnvalidated(new Callable&lt;Metadata&gt;() {
144 	 *   public Metadata call() throws Exception {
145 	 *      return (Metadata) unmarshaller.unmarshall(streamFromDatabase);
146 	 *   }
147 	 * });
148 	 * // Read a user session bound metadata instance containing what is visible to the current user.
149 	 * return (Metadata) unmarshaller.unmarshall(streamFromDatabase);
150 	 * </pre></code>
151 	 * <p/>
152 	 * <b>Note:</b> The sort of filtering that is applied depends on what was previously configured in
153 	 * {@link #enableValidation(String, AccessLevel, String...)}. Using this method allows to bypass any restrictions
154 	 * that would normally apply to instances of {@link Metadata}.
155 	 *
156 	 * @param callable the callable that performs the operation.
157 	 * @param <V>      the return type.
158 	 * @return the value returned by the callable.
159 	 * @throws Exception the exception thrown by the callable.
160 	 */
161 	public <V> V doUnvalidated(Callable<V> callable) throws Exception {
162 		final ValidationInstructions instructions = references.get();
163 		try {
164 			references.remove();
165 			return callable.call();
166 		} finally {
167 			references.set(instructions);
168 		}
169 	}
170 }