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