Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

573004 persist generated key pairs #749

Merged
merged 7 commits into from
Apr 22, 2021
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*******************************************************************************
* Copyright (c) 2018, 2020 ArSysOp
* Copyright (c) 2018, 2021 ArSysOp
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
Expand All @@ -12,28 +12,13 @@
*******************************************************************************/
package org.eclipse.passage.loc.internal.products.core;

import java.io.File;
import java.nio.file.Path;
import java.util.Optional;

import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;
import org.eclipse.osgi.service.environment.EnvironmentInfo;
import org.eclipse.passage.lic.emf.ecore.LicensingEcore;
import org.eclipse.passage.lic.internal.api.LicensedProduct;
import org.eclipse.passage.lic.internal.api.io.StreamCodec;
import org.eclipse.passage.lic.internal.base.BaseLicensedProduct;
import org.eclipse.passage.lic.internal.base.io.FileNameFromLicensedProduct;
import org.eclipse.passage.lic.internal.base.io.PassageFileExtension;
import org.eclipse.passage.lic.internal.base.io.UserHomeProductResidence;
import org.eclipse.passage.lic.products.ProductDescriptor;
import org.eclipse.passage.lic.products.ProductVersionDescriptor;
import org.eclipse.passage.lic.products.model.api.Product;
import org.eclipse.passage.lic.products.model.api.ProductVersion;
import org.eclipse.passage.loc.internal.api.OperatorProductEvents;
import org.eclipse.passage.loc.internal.api.OperatorProductService;
import org.eclipse.passage.loc.internal.equinox.OperatorGearAware;
import org.eclipse.passage.loc.internal.products.core.i18n.ProductsCoreMessages;
import org.osgi.framework.BundleContext;
import org.osgi.service.component.annotations.Activate;
import org.osgi.service.component.annotations.Component;
Expand All @@ -43,118 +28,50 @@
@Component
public class ProductOperatorServiceImpl implements OperatorProductService {

private String pluginId;

private EnvironmentInfo environmentInfo;
private EventAdmin eventAdmin;
private String plugin;
private EnvironmentInfo environment;
private EventAdmin events;

@Activate
public void activate(BundleContext context) {
pluginId = context.getBundle().getSymbolicName();
plugin = context.getBundle().getSymbolicName();
}

@Reference
public void bindEnvironmentInfo(EnvironmentInfo info) {
this.environmentInfo = info;
this.environment = info;
}

public void unbindEnvironmentInfo(EnvironmentInfo info) {
if (environmentInfo == info) {
this.environmentInfo = null;
if (environment == info) {
this.environment = null;
}
}

@Reference
public void bindEventAdmin(EventAdmin admin) {
this.eventAdmin = admin;
this.events = admin;
}

public void unbindEventAdmin(EventAdmin admin) {
if (this.eventAdmin == admin) {
this.eventAdmin = null;
if (this.events == admin) {
this.events = null;
}
}

@Override
public String createPassword(ProductVersionDescriptor descriptor) {
String id = null;
String version = null;
if (descriptor != null) {
ProductDescriptor product = descriptor.getProduct();
if (product != null) {
id = product.getIdentifier();
}
version = descriptor.getVersion();
}
StringBuilder sb = new StringBuilder();
sb.append(id).append("###").append(version); //$NON-NLS-1$
return sb.toString();
return new ProductVersionPassword(descriptor).get();
}

@Override
public IStatus createProductKeys(ProductVersionDescriptor descriptor) {
ProductVersion productVersion = null;
if (descriptor instanceof ProductVersion) {
productVersion = (ProductVersion) descriptor;
}
if (productVersion == null) {
return new Status(IStatus.ERROR, pluginId,
ProductsCoreMessages.ProductOperatorServiceImpl_e_invalid_product_version);
}
String installationToken = productVersion.getInstallationToken();
if (installationToken != null) {
File publicFile = new File(installationToken);
if (publicFile.exists()) {
String pattern = ProductsCoreMessages.ProductOperatorServiceImpl_e_public_key_already_defined;
String message = String.format(pattern, publicFile.getAbsolutePath());
return new Status(IStatus.ERROR, pluginId, message);
}
}
String secureToken = productVersion.getSecureToken();
if (secureToken != null) {
File privateFile = new File(secureToken);
if (privateFile.exists()) {
String pattern = ProductsCoreMessages.ProductOperatorServiceImpl_e_private_key_already_defined;
String message = String.format(pattern, privateFile.getAbsolutePath());
return new Status(IStatus.ERROR, pluginId, message);
}
}

Product product = productVersion.getProduct();
String errors = LicensingEcore.extractValidationError(product);
if (errors != null) {
return new Status(IStatus.ERROR, pluginId, errors);
}
LicensedProduct licensed = new BaseLicensedProduct(product.getIdentifier(), productVersion.getVersion());
Optional<StreamCodec> codec = codec(licensed);
if (codec.isEmpty()) {
String pattern = ProductsCoreMessages.ProductOperatorServiceImpl_e_unable_to_create_keys;
String message = String.format(pattern, licensed.version(), product.getName());
return new Status(IStatus.ERROR, pluginId, message);
}
Path path = new UserHomeProductResidence(licensed).get();
try {
new FileNameFromLicensedProduct(licensed, new PassageFileExtension.PrivateKey());
Path open = path.resolve(//
new FileNameFromLicensedProduct(licensed, new PassageFileExtension.PublicKey()).get());
Path secret = path.resolve(//
new FileNameFromLicensedProduct(licensed, new PassageFileExtension.PrivateKey()).get());
codec.get().createKeyPair(open, secret, licensed.identifier(), createPassword(productVersion));
productVersion.setInstallationToken(open.toString());
productVersion.setSecureToken(secret.toString());
eventAdmin.postEvent(OperatorProductEvents.publicCreated(open.toString()));
eventAdmin.postEvent(OperatorProductEvents.privateCreated(secret.toString()));
String format = ProductsCoreMessages.ProductOperatorServiceImpl_ok_keys_exported;
String message = String.format(format, open, secret);
return new Status(IStatus.OK, pluginId, message);
} catch (Exception e) {
return new Status(IStatus.ERROR, pluginId, ProductsCoreMessages.ProductOperatorServiceImpl_e_export_error,
e);
}
public IStatus createProductKeys(ProductVersionDescriptor target) {
return new ProductVersionKeys(plugin, this::broadcast).createKeys(target);
}

private Optional<StreamCodec> codec(LicensedProduct product) {
return new OperatorGearAware().withGear(gear -> gear.codec(product));
private void broadcast(Path open, Path secret) {
events.postEvent(OperatorProductEvents.publicCreated(open.toString()));
events.postEvent(OperatorProductEvents.privateCreated(secret.toString()));
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,146 @@
/*******************************************************************************
* Copyright (c) 2021 ArSysOp
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* https://www.eclipse.org/legal/epl-2.0/.
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* ArSysOp - initial API and implementation
*******************************************************************************/
package org.eclipse.passage.loc.internal.products.core;

import java.io.File;
import java.nio.file.Path;
import java.util.Objects;
import java.util.Optional;
import java.util.function.BiConsumer;
import java.util.function.Supplier;

import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;
import org.eclipse.passage.lic.emf.ecore.LicensingEcore;
import org.eclipse.passage.lic.internal.api.LicensedProduct;
import org.eclipse.passage.lic.internal.api.LicensingException;
import org.eclipse.passage.lic.internal.api.io.StreamCodec;
import org.eclipse.passage.lic.internal.base.BaseLicensedProduct;
import org.eclipse.passage.lic.internal.base.io.FileNameFromLicensedProduct;
import org.eclipse.passage.lic.internal.base.io.PassageFileExtension;
import org.eclipse.passage.lic.internal.base.io.UserHomeProductResidence;
import org.eclipse.passage.lic.products.ProductVersionDescriptor;
import org.eclipse.passage.lic.products.model.api.ProductVersion;
import org.eclipse.passage.loc.internal.equinox.OperatorGearAware;
import org.eclipse.passage.loc.internal.products.core.i18n.ProductsCoreMessages;

final class ProductVersionKeys {

private final String plugin;
private final BiConsumer<Path, Path> notify;

ProductVersionKeys(String plugin, BiConsumer<Path, Path> notify) {
this.notify = notify;
Objects.requireNonNull(plugin, "ProductVersionPassword::plugin"); //$NON-NLS-1$
this.plugin = plugin;
}

public IStatus createKeys(ProductVersionDescriptor target) {
Optional<String> existing = keysArePresent(target);
if (existing.isPresent()) {
return error(existing.get());
}
Optional<String> invalid = validate(target);
if (invalid.isPresent()) {
return error(invalid.get());
}
LicensedProduct product = product(target);
Optional<StreamCodec> codec = codec(product);
if (codec.isEmpty()) {
return noCodec(target, product);
}
try {
return createKeyPair(target, product, codec.get());
} catch (Exception e) {
return failed(e);
}
}

private IStatus createKeyPair(ProductVersionDescriptor target, LicensedProduct product, StreamCodec codec)
throws LicensingException {
Path destination = new UserHomeProductResidence(product).get();
Path open = open(product, destination);
Path secret = secret(product, destination);
codec.createKeyPair(open, secret, product.identifier(), new ProductVersionPassword(target).get());
notify.accept(open, secret);
return created(open, secret);
}

private Status error(String errors) {
return new Status(IStatus.ERROR, plugin, errors);
}

private Status noCodec(ProductVersionDescriptor target, LicensedProduct product) {
return error(String.format(ProductsCoreMessages.ProductOperatorServiceImpl_e_unable_to_create_keys,
product.version(), target.getProduct().getName()));
}

private Status failed(Exception e) {
return new Status(IStatus.ERROR, plugin, ProductsCoreMessages.ProductOperatorServiceImpl_e_export_error, e);
}

private Status created(Path open, Path secret) {
return new Status(IStatus.OK, plugin,
String.format(ProductsCoreMessages.ProductOperatorServiceImpl_ok_keys_exported, open, secret));
}

private BaseLicensedProduct product(ProductVersionDescriptor target) {
return new BaseLicensedProduct(//
target.getProduct().getIdentifier(), //
target.getVersion());
}

private Path secret(LicensedProduct licensed, Path path) {
return path.resolve(//
new FileNameFromLicensedProduct(licensed, new PassageFileExtension.PrivateKey()).get());
}

private Path open(LicensedProduct licensed, Path path) {
return path.resolve(//
new FileNameFromLicensedProduct(licensed, new PassageFileExtension.PublicKey()).get());
}

@SuppressWarnings("restriction")
private Optional<StreamCodec> codec(LicensedProduct product) {
return new OperatorGearAware().withGear(gear -> gear.codec(product));
}

private Optional<String> validate(ProductVersionDescriptor target) {
if (!(target instanceof ProductVersion)) {
return Optional.empty();
}
return Optional.ofNullable(LicensingEcore.extractValidationError(((ProductVersion) target).getProduct()));
}

private Optional<String> keysArePresent(ProductVersionDescriptor target) {
Optional<String> pub = keyIsPresent(target::getInstallationToken,
ProductsCoreMessages.ProductOperatorServiceImpl_e_public_key_already_defined);
if (pub.isPresent()) {
return pub;
}
return keyIsPresent(target::getSecureToken,
ProductsCoreMessages.ProductOperatorServiceImpl_e_private_key_already_defined);
}

private Optional<String> keyIsPresent(Supplier<String> path, String error) {
String existing = path.get();
if (existing == null) { // model(emf)-driven null
return Optional.empty();
}
File file = new File(existing);
if (file.exists()) {
return Optional.of(String.format(error, file.getAbsolutePath()));
}
return Optional.empty();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
/*******************************************************************************
* Copyright (c) 2021 ArSysOp
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* https://www.eclipse.org/legal/epl-2.0/.
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* ArSysOp - initial API and implementation
*******************************************************************************/
package org.eclipse.passage.loc.internal.products.core;

import java.util.Objects;
import java.util.function.Supplier;

import org.eclipse.passage.lic.products.ProductVersionDescriptor;

final class ProductVersionPassword implements Supplier<String> {

private final ProductVersionDescriptor source;

ProductVersionPassword(ProductVersionDescriptor source) {
Objects.requireNonNull(source, "ProductVersionPassword::source"); //$NON-NLS-1$
this.source = source;
}

@Override
public String get() {
return id() + "###" + version(); //$NON-NLS-1$
}

private String id() {
return source.getProduct().getIdentifier();
}

private String version() {
return source.getVersion();
}

}
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*******************************************************************************
* Copyright (c) 2018, 2020 ArSysOp
* Copyright (c) 2018, 2021 ArSysOp
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
Expand Down Expand Up @@ -30,21 +30,23 @@
public class ProductExportHandler {

@Execute
public void execute(@Named(IServiceConstants.ACTIVE_SELECTION) ProductVersionDescriptor productVersion,
public void execute(@Named(IServiceConstants.ACTIVE_SELECTION) ProductVersionDescriptor owner,
IEclipseContext context) {
OperatorProductService service = context.get(OperatorProductService.class);
IStatus status = service.createProductKeys(productVersion);
IStatus status = service.createProductKeys(owner);
Shell shell = context.get(Shell.class);
if (status.isOK()) {
String message = status.getMessage();
MessageDialog.openInformation(shell, ProductsUiMessages.ProductExportHandler_title_ok, message);
} else {
ErrorDialog.openError(shell, ProductsUiMessages.ProductExportHandler_title_error, ProductsUiMessages.ProductExportHandler_message_error, status);
ErrorDialog.openError(shell, ProductsUiMessages.ProductExportHandler_title_error,
ProductsUiMessages.ProductExportHandler_message_error, status);
}
}

@CanExecute
public boolean canExecute(@Named(IServiceConstants.ACTIVE_SELECTION) @Optional ProductVersionDescriptor productVersion) {
public boolean canExecute(
@Named(IServiceConstants.ACTIVE_SELECTION) @Optional ProductVersionDescriptor productVersion) {
return productVersion != null;
}

Expand Down