View Javadoc
1   package com.atlassian.plugin.validation;
2   
3   
4   import com.atlassian.fugue.Either;
5   import com.atlassian.plugin.Application;
6   import com.atlassian.plugin.InstallationMode;
7   import com.atlassian.plugin.parsers.ModuleReader;
8   import com.atlassian.plugin.parsers.PluginDescriptorReader;
9   import com.google.common.annotations.VisibleForTesting;
10  import com.google.common.base.Function;
11  import com.google.common.base.Predicate;
12  import com.google.common.collect.Iterables;
13  import com.google.common.collect.Sets;
14  import com.google.common.io.InputSupplier;
15  
16  import java.io.InputStream;
17  import java.io.Reader;
18  import java.util.Map;
19  import java.util.Set;
20  
21  import static com.atlassian.plugin.validation.Dom4jUtils.readDocument;
22  import static com.google.common.base.Preconditions.checkNotNull;
23  import static com.google.common.collect.ImmutableSet.copyOf;
24  import static com.google.common.collect.Iterables.concat;
25  import static com.google.common.collect.Iterables.filter;
26  import static com.google.common.collect.Iterables.transform;
27  
28  /**
29   * A simple validator that given a descriptor and a schema will check that the permissions set in the plugin are valid
30   * and all required have been asked.
31   *
32   * @since 3.0.0
33   */
34  public final class DescriptorValidator {
35      private static final String REMOTE_PLUGIN_CONTAINER_MODULE_NAME = "remote-plugin-container";
36  
37      private final PluginDescriptorReader descriptorReader;
38      private final SchemaReader schemaReader;
39  
40      /**
41       * @deprecated Please use {@link com.atlassian.plugin.validation.DescriptorValidator#DescriptorValidator(java.io.InputStream, java.io.InputStream, java.util.Set)}. Since v4.0
42       */
43      @Deprecated
44      public DescriptorValidator(InputSupplier<? extends Reader> descriptor, InputSupplier<? extends Reader> schema, Set<Application> applications) {
45          descriptorReader = new PluginDescriptorReader(readDocument(descriptor), copyOf(checkNotNull(applications)));
46          schemaReader = new SchemaReader(readDocument(schema));
47      }
48  
49      public DescriptorValidator(InputStream descriptor, InputStream schema, Set<Application> applications) {
50          descriptorReader = new PluginDescriptorReader(readDocument(descriptor), copyOf(checkNotNull(applications)));
51          schemaReader = new SchemaReader(readDocument(schema));
52      }
53  
54      public Either<ValidationError, ValidationSuccess> validate(InstallationMode installationMode) {
55          final Set<String> allowedPermissions = schemaReader.getAllowedPermissions();
56          final Set<String> askedPermissions = descriptorReader.getPluginInformationReader().getPermissions(installationMode);
57  
58          final Sets.SetView<String> invalidPermissions = Sets.difference(askedPermissions, allowedPermissions);
59  
60          final Set<String> requiredPermissions = getRequiredPermissions(installationMode);
61          final Sets.SetView<String> notAskedPermissions = Sets.difference(requiredPermissions, askedPermissions);
62  
63          if (!invalidPermissions.isEmpty() || (!descriptorReader.getPluginInformationReader().hasAllPermissions() && !notAskedPermissions.isEmpty())) {
64              return Either.left(new ValidationError(invalidPermissions, notAskedPermissions));
65          } else {
66              return Either.right(new ValidationSuccess(isRemotable(installationMode)));
67          }
68      }
69  
70      private boolean isRemotable(InstallationMode installationMode) {
71          final ModuleReader remotePluginContainerModuleReader = Iterables.find(descriptorReader.getModuleReaders(installationMode), new Predicate<ModuleReader>() {
72              @Override
73              public boolean apply(ModuleReader moduleReader) {
74                  return moduleReader.getType().equals(REMOTE_PLUGIN_CONTAINER_MODULE_NAME);
75              }
76          }, null);
77  
78          return remotePluginContainerModuleReader != null;
79      }
80  
81      @VisibleForTesting
82      Set<String> getRequiredPermissions(InstallationMode installationMode) {
83          final Set<String> moduleKeys = getModuleKeys(installationMode);
84          final Map<String, Set<String>> modulesRequiredPermissions = schemaReader.getModulesRequiredPermissions();
85  
86          return copyOf(concat(transform(filter(
87                          modulesRequiredPermissions.entrySet(),
88                          new Predicate<Map.Entry<String, Set<String>>>() {
89                              @Override
90                              public boolean apply(Map.Entry<String, Set<String>> entry) {
91                                  return moduleKeys.contains(entry.getKey());
92                              }
93                          }),
94                  GetEntryValue.<String, Set<String>>newGetEntryValue())));
95      }
96  
97      private Set<String> getModuleKeys(InstallationMode installationMode) {
98          return copyOf(transform(descriptorReader.getModuleReaders(installationMode), new Function<ModuleReader, String>() {
99              @Override
100             public String apply(ModuleReader moduleReader) {
101                 return moduleReader.getType();
102             }
103         }));
104     }
105 
106     public static final class ValidationError {
107         private final Set<String> nonValidPermissions;
108         private final Set<String> notAskedPermissions;
109 
110         private ValidationError(Sets.SetView<String> nonValidPermissions, Sets.SetView<String> notAskedPermissions) {
111             this.nonValidPermissions = nonValidPermissions.immutableCopy();
112             this.notAskedPermissions = notAskedPermissions.immutableCopy();
113         }
114 
115         public Set<String> getNonValidPermissions() {
116             return nonValidPermissions;
117         }
118 
119         public Set<String> getNotAskedPermissions() {
120             return notAskedPermissions;
121         }
122     }
123 
124     public static final class ValidationSuccess {
125         private final boolean remotable;
126 
127         private ValidationSuccess(boolean remotable) {
128             this.remotable = remotable;
129         }
130 
131         public boolean isRemotable() {
132             return remotable;
133         }
134     }
135 
136     private static final class GetEntryValue<K, V> implements Function<Map.Entry<K, V>, V> {
137         private GetEntryValue() {
138         }
139 
140         static <K, V> GetEntryValue<K, V> newGetEntryValue() {
141             return new GetEntryValue<K, V>();
142         }
143 
144         @Override
145         public V apply(Map.Entry<K, V> entry) {
146             return entry.getValue();
147         }
148     }
149 }