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
28
29
30
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 }