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.collect.Iterables;
11  import com.google.common.collect.Sets;
12  import com.google.common.io.InputSupplier;
13  
14  import java.io.InputStream;
15  import java.io.Reader;
16  import java.util.Collections;
17  import java.util.Map;
18  import java.util.Set;
19  import java.util.stream.Collectors;
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.transform;
25  
26  /**
27   * A simple validator that given a descriptor and a schema will check that the permissions set in the plugin are valid
28   * and all required have been asked.
29   *
30   * @since 3.0.0
31   */
32  public final class DescriptorValidator {
33      private static final String REMOTE_PLUGIN_CONTAINER_MODULE_NAME = "remote-plugin-container";
34  
35      private final PluginDescriptorReader descriptorReader;
36      private final SchemaReader schemaReader;
37  
38      public DescriptorValidator(InputStream descriptor, InputStream schema, Set<Application> applications) {
39          descriptorReader = new PluginDescriptorReader(readDocument(descriptor), copyOf(checkNotNull(applications)));
40          schemaReader = new SchemaReader(readDocument(schema));
41      }
42  
43      public Either<ValidationError, ValidationSuccess> validate(InstallationMode installationMode) {
44          final Set<String> allowedPermissions = schemaReader.getAllowedPermissions();
45          final Set<String> askedPermissions = descriptorReader.getPluginInformationReader().getPermissions(installationMode);
46  
47          final Sets.SetView<String> invalidPermissions = Sets.difference(askedPermissions, allowedPermissions);
48  
49          final Set<String> requiredPermissions = getRequiredPermissions(installationMode);
50          final Sets.SetView<String> notAskedPermissions = Sets.difference(requiredPermissions, askedPermissions);
51  
52          if (!invalidPermissions.isEmpty() || (!descriptorReader.getPluginInformationReader().hasAllPermissions() && !notAskedPermissions.isEmpty())) {
53              return Either.left(new ValidationError(invalidPermissions, notAskedPermissions));
54          } else {
55              return Either.right(new ValidationSuccess(isRemotable(installationMode)));
56          }
57      }
58  
59      private boolean isRemotable(InstallationMode installationMode) {
60          final ModuleReader remotePluginContainerModuleReader = Iterables.find(descriptorReader.getModuleReaders(installationMode),
61                  moduleReader -> moduleReader.getType().equals(REMOTE_PLUGIN_CONTAINER_MODULE_NAME), null);
62  
63          return remotePluginContainerModuleReader != null;
64      }
65  
66      @VisibleForTesting
67      Set<String> getRequiredPermissions(InstallationMode installationMode) {
68          final Set<String> moduleKeys = getModuleKeys(installationMode);
69          final Map<String, Set<String>> modulesRequiredPermissions = schemaReader.getModulesRequiredPermissions();
70  
71          return Collections.unmodifiableSet(modulesRequiredPermissions.entrySet().stream()
72                  .filter(entry -> moduleKeys.contains(entry.getKey()))
73                  .flatMap(entry -> entry.getValue().stream())
74                  .collect(Collectors.toSet()));
75      }
76  
77      private Set<String> getModuleKeys(InstallationMode installationMode) {
78          return copyOf(transform(descriptorReader.getModuleReaders(installationMode), ModuleReader::getType));
79      }
80  
81      public static final class ValidationError {
82          private final Set<String> nonValidPermissions;
83          private final Set<String> notAskedPermissions;
84  
85          private ValidationError(Sets.SetView<String> nonValidPermissions, Sets.SetView<String> notAskedPermissions) {
86              this.nonValidPermissions = nonValidPermissions.immutableCopy();
87              this.notAskedPermissions = notAskedPermissions.immutableCopy();
88          }
89  
90          public Set<String> getNonValidPermissions() {
91              return nonValidPermissions;
92          }
93  
94          public Set<String> getNotAskedPermissions() {
95              return notAskedPermissions;
96          }
97      }
98  
99      public static final class ValidationSuccess {
100         private final boolean remotable;
101 
102         private ValidationSuccess(boolean remotable) {
103             this.remotable = remotable;
104         }
105 
106         public boolean isRemotable() {
107             return remotable;
108         }
109     }
110 }