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
26
27
28
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 }