private boolean hasGrant(ConnectAddonBean addonBean) { return AUTHORISED_ADD_ON_KEYS.contains(addonBean.getKey()); } }
private void addPluginInfoParameterForPageIfDeclared(PluginInformation pluginInfo, String parameterKey, ConnectAddonBean addon, String moduleType) { Optional<List<ModuleBean>> optionalPages = addon.getModules().getValidModuleListOfType( moduleType, moduleValidationExceptionHandler); optionalPages.ifPresent(moduleBeans -> { ConnectPageModuleBean page = (ConnectPageModuleBean) moduleBeans.get(0); if (null != page && !Strings.isNullOrEmpty(page.getUrl())) { pluginInfo.addParameter(parameterKey, ConnectIFrameServletPath.forModule(addon.getKey(), page.getRawKey())); } }); } }
public ConnectAddonBean build() { return new ConnectAddonBean(this); } }
/** * Supplies an accessor for remote plugin operations but always creates a new one. Instances are still only meant * to be used for the current operation and should not be cached across operations. * * This method is useful for when the display url is known but the application link has not yet been created * * @param addon The addon bean * @param displayUrl The display url * @return An accessor for a remote plugin */ private RemotablePluginAccessor create(ConnectAddonBean addon, Supplier<URI> displayUrl) { return create(addon.getKey(), addon.getName(), displayUrl); }
@Override public Map<String, String> getAllAddonBaseUrls() { Map<String, String> addonKeyToBaseUrl = new HashMap<>(); getAllAddons().forEach((addon) -> addonKeyToBaseUrl.put(addon.getKey(), addon.getBaseUrl())); return addonKeyToBaseUrl; }
public JwtSigningRemotablePluginAccessor(ConnectAddonBean addon, Supplier<URI> baseUrlSupplier, JwtJsonBuilderFactory jwtBuilderFactory, JwtService jwtService, ConsumerService consumerService, HttpContentRetriever httpContentRetriever, ConnectAddonInformationProvider connectAddonInformationProvider) { super(addon.getKey(), addon.getName(), baseUrlSupplier, httpContentRetriever); this.consumerService = consumerService; this.jwtEncoder = new JwtEncoder(jwtBuilderFactory, jwtService); this.authorizationGenerator = new JwtAuthorizationGenerator(jwtService, jwtBuilderFactory, this::requireSharedSecret, consumerService, URI.create(addon.getBaseUrl())); this.connectAddonInformationProvider = connectAddonInformationProvider; }
String pluginKey = addon.getKey(); String previousDescriptor = addonRegistry.getDescriptor(pluginKey); AuthenticationType newAuthType = addon.getAuthentication().getType(); final boolean newUseSharedSecret = addonUsesSymmetricSharedSecret(newAuthType, JWT_ALGORITHM); final Optional<OAuthClient> newOAuthClient = getUpdatedOAuthClient(pluginKey, addon.getAuthentication().getPublicKey(); // the key stored on the applink: used to sign outgoing requests and verify incoming requests .setAuth(newAuthType.name()) .setOAuthClient(newOAuthClient) .setBaseUrl(addon.getBaseUrl()) .setDescriptor(jsonDescriptor) .setRestartState(PluginState.DISABLED) connectApplinkManager.createAppLink(addon, addon.getBaseUrl(), newAuthType, newAddonSigningKey, userKey); if (!Strings.isNullOrEmpty(addon.getLifecycle().getInstalled())) { if (darkFeatureManager.isFeatureEnabledForAllUsers(DARK_FEATURE_DISABLE_SIGN_INSTALL_WITH_PREV_KEY)) { requestInstallCallback(addon, newOAuthClient, true); // sign using whatever shared secret is looked up (the old code path) log.info("Connect addon '" + addon.getKey() + "' installed in " + (endTime - startTime) + "ms");
private void requestInstallCallback(ConnectAddonBean addon, Optional<OAuthClient> oauthClient, final boolean sign) throws ConnectAddonInstallException { final URI callbackUri = getURI(addon.getBaseUrl(), addon.getLifecycle().getInstalled()); final Optional<String> authHeader = sign ? getAuthHeader(callbackUri, remotablePluginAccessorFactory.get(addon.getKey()).getAuthorizationGenerator()) : Optional.<String>empty(); requestInstallCallback(addon, oauthClient, callbackUri, authHeader); }
public synchronized void registerDescriptorsForBeans(ConnectAddonBean addon) throws ConnectModuleRegistrationException { //don't register modules more than once if (registrations.containsKey(addon.getKey())) { return; } Collection<ConnectModuleProvider<?>> moduleProviders = pluginAccessor.getModules( new ModuleDescriptorOfClassPredicate<>(ConnectModuleProviderModuleDescriptor.class)); // The time period bracketed here is the one we are naively obligated to pay each request with the current // vertigo spike logic as per EXT-43. I'm not including the module provider logic above as we could easily // hoist this in the vertigo logic. final long start = System.currentTimeMillis(); Map<String, List<ModuleBean>> moduleLists = getModuleLists(addon); List<ModuleBean> lifecycleWebhooks = getLifecycleWebhooks(addon.getLifecycle()); Map<String, List<ModuleBean>> lifecycleWebhookModuleList = Collections.singletonMap(new WebHookModuleMeta().getDescriptorKey(), lifecycleWebhooks); List<ModuleDescriptor<?>> descriptorsToRegister = new ArrayList<>(); getDescriptorsToRegisterForModules(moduleLists, addon, moduleProviders, descriptorsToRegister); getDescriptorsToRegisterForModules(lifecycleWebhookModuleList, addon, moduleProviders, descriptorsToRegister); final long stop = System.currentTimeMillis(); log.info("registerDescriptorsForBeans computed descriptors for {} in {}ms", addon.getKey(), (stop - start)); registrations.putIfAbsent(addon.getKey(), dynamicDescriptorRegistration.registerDescriptors(descriptorsToRegister)); }
private Response beanToResponse(ConnectAddonBean bean) { String addonKey = bean.getKey(); List<String> scopes = bean.getScopes().stream().map(ScopeName::toString).collect(Collectors.toList()); Optional<String> maybeSecret = this.connectAddonInfoProvider.getSharedSecret(addonKey); return maybeSecret.map(secret -> Response.ok(new RestOAuth2Client(addonKey, secret, scopes)).build()) .orElse(notFound); }
/** * Create a ConnectRunner for an add-on with a specified key * @param productBaseUrl the url of the product to install the add-on into * @param key the key for the add-on */ public ConnectRunner(String productBaseUrl, String key) { this.productBaseUrl = checkNotNull(productBaseUrl); this.addonBuilder = newConnectAddonBean() .withKey(key) .withName(key) .withVersion("1.0"); this.installer = new AtlassianConnectRestClient(productBaseUrl, "admin", "admin"); }
Set<String> installed = addon.getAddonDependencies().stream() .filter(this::isInstalled) .collect(Collectors.toSet()); throw getMultiLevelDependencyException(addon.getKey()); Set<String> notInstalled = Sets.difference(addon.getAddonDependencies(), installed);
private void enableSingleConnectAddon(final String pluginKey) throws ConnectAddonInitException, ConnectAddonEnableException, TranslationsDownloadException { long startTime = System.currentTimeMillis(); if (addonRegistry.hasDescriptor(pluginKey)) { ConnectAddonBean addon; try { addon = connectAddonAccessor.getAddon(pluginKey).get(); moduleReconfigurationBatcher.runBatched(() -> beanToModuleRegistrar.registerDescriptorsForBeans(addon)); } catch (InvalidDescriptorException | ConnectModuleRegistrationException e) { addonEventManager.publishEnableFailedEvent(pluginKey, e.getMessage()); throw new ConnectAddonEnableException(pluginKey, "Module registration failed while enabling add-on, skipping.", e); } if (addonRequiresAuth(addon.getAuthentication())) { connectUserService.getOrCreateAddonUserName(addon); } addonRegistry.storeRestartState(pluginKey, PluginState.ENABLED); try { addonEventManager.publishEnabledEvent(pluginKey); } catch (Exception e) { log.warn(String.format("Could not fire enabled webhook event for add-on %s, continuing anyway", pluginKey), e); } long endTime = System.currentTimeMillis(); log.info("Connect addon '" + addon.getKey() + "' enabled in " + (endTime - startTime) + "ms"); } else { String message = "Tried to enable add-on before it was installed."; addonEventManager.publishEnableFailedEvent(pluginKey, message); throw new ConnectAddonEnableException(pluginKey, message); } }
@Override public boolean equals(Object otherObj) { if (!(otherObj instanceof ConnectAddonBean)) { return false; } ConnectAddonBean other = (ConnectAddonBean) otherObj; return new EqualsBuilder().append(getModules(), other.getModules()).isEquals() && super.equals(otherObj); }
private RestLimitedAddon createJsonAddonRest(ConnectAddonBean addonBean) { String key = addonBean.getKey(); String version = addonBean.getVersion(); PluginState state = addonRegistry.getRestartState(key); String stateString = state.name(); RestHost host = getHostResource(); RestAddonLicense license = getLicenseResourceForAddon(key); RestRelatedLinks addonLinks = getAddonLinks(key); RestInternalAddon.AddonApplink appLinkResource = getApplinkResourceForAddon(key); RestLimitedAddon resource; if (userManager.isSystemAdmin(userManager.getRemoteUserKey())) { resource = new RestInternalAddon(key, version, stateString, host, license, addonLinks, appLinkResource); } else { if (state.equals(PluginState.DISABLED) || state.equals(PluginState.DISABLING)) { resource = new RestLimitedAddon(key, version, stateString); } else { resource = new RestAddon(key, version, stateString, host, license, addonLinks); } } return resource; }
private void requestInstallCallback(ConnectAddonBean addon, Optional<OAuthClient> oauthClient, final boolean sign) throws ConnectAddonInstallException { final URI callbackUri = getURI(addon.getBaseUrl(), addon.getLifecycle().getInstalled()); final Optional<String> authHeader = sign ? getAuthHeader(callbackUri, remotablePluginAccessorFactory.get(addon).getAuthorizationGenerator()) : Optional.empty(); requestInstallCallback(addon, oauthClient, callbackUri, authHeader); }
private boolean isJsonJwtAddon(ConnectAddonBean addonBean) { return addonBean.getAuthentication() != null && addonBean.getAuthentication().getType() == AuthenticationType.JWT; }
public Translations download(ConnectAddonBean addonBean) throws TranslationsDownloadException { List<DownloadResult<ResponsePromise>> downloadsInProgress = Stream.of(Language.values()) .map(lang -> pair(lang, lang.getPath(addonBean.getTranslations().getPaths()))) .filter(langAndPath -> langAndPath.right().isPresent()) .map(langAndPath -> scheduleDownload(langAndPath.left(), addonBean.getBaseUrl(), langAndPath.right().get())) .collect(toList()); final Map<Language, I18nKeysMapping> translations = downloadsInProgress.stream() .map(this::download) .collect(Collectors.toMap(DownloadResult::getLanguage, DownloadResult::getResult)); return Translations.from(translations); }
private boolean dependsOn(ConnectRunner other) { return this.getAddon().getAddonDependencies().contains(other.getAddonKey()); }
private Supplier<URI> getDisplayUrl(final ConnectAddonBean addon) { final String storedBaseUrl = addon.getBaseUrl(); if (!Strings.isNullOrEmpty(storedBaseUrl)) { return Suppliers.compose(URI::create, () -> storedBaseUrl); } else { return Suppliers.compose(URI::create, () -> applicationProperties.getBaseUrl(UrlMode.CANONICAL) ); } }