mirror of
https://github.com/scm-manager/scm-manager.git
synced 2025-11-01 11:05:56 +01:00
wip
This commit is contained in:
@@ -164,6 +164,15 @@ public class ModifyCommandBuilder {
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the branch the changes should be made upon.
|
||||||
|
* @return This builder instance.
|
||||||
|
*/
|
||||||
|
public ModifyCommandBuilder disableSignature(String branch) {
|
||||||
|
request.setBranch(branch);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set the expected revision of the branch, before the changes are applied. If the branch does not have the
|
* Set the expected revision of the branch, before the changes are applied. If the branch does not have the
|
||||||
* expected revision, a concurrent modification exception will be thrown when the command is executed and no
|
* expected revision, a concurrent modification exception will be thrown when the command is executed and no
|
||||||
|
|||||||
@@ -40,9 +40,11 @@ import lombok.ToString;
|
|||||||
@NoArgsConstructor
|
@NoArgsConstructor
|
||||||
public class ModificationsCommandRequest implements Resetable {
|
public class ModificationsCommandRequest implements Resetable {
|
||||||
private String revision;
|
private String revision;
|
||||||
|
private boolean sign = true;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void reset() {
|
public void reset() {
|
||||||
revision = null;
|
revision = null;
|
||||||
|
sign = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -49,6 +49,7 @@ public class ModifyCommandRequest implements Resetable, Validateable, CommandWit
|
|||||||
private String branch;
|
private String branch;
|
||||||
private String expectedRevision;
|
private String expectedRevision;
|
||||||
private boolean defaultPath;
|
private boolean defaultPath;
|
||||||
|
private boolean disableSigning;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void reset() {
|
public void reset() {
|
||||||
@@ -57,6 +58,7 @@ public class ModifyCommandRequest implements Resetable, Validateable, CommandWit
|
|||||||
commitMessage = null;
|
commitMessage = null;
|
||||||
branch = null;
|
branch = null;
|
||||||
defaultPath = false;
|
defaultPath = false;
|
||||||
|
disableSigning = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void addRequest(PartialRequest request) {
|
public void addRequest(PartialRequest request) {
|
||||||
@@ -112,6 +114,14 @@ public class ModifyCommandRequest implements Resetable, Validateable, CommandWit
|
|||||||
this.defaultPath = defaultPath;
|
this.defaultPath = defaultPath;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean isDisableSigning() {
|
||||||
|
return disableSigning;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setDisableSigning(boolean disableSigning) {
|
||||||
|
this.disableSigning = disableSigning;
|
||||||
|
}
|
||||||
|
|
||||||
public interface PartialRequest {
|
public interface PartialRequest {
|
||||||
void execute(ModifyCommand.Worker worker) throws IOException;
|
void execute(ModifyCommand.Worker worker) throws IOException;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,62 @@
|
|||||||
|
/*
|
||||||
|
*
|
||||||
|
* MIT License
|
||||||
|
*
|
||||||
|
* Copyright (c) 2020-present Cloudogu GmbH and Contributors
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in all
|
||||||
|
* copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
* SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package sonia.scm.repository;
|
||||||
|
|
||||||
|
import org.eclipse.jgit.api.errors.CanceledException;
|
||||||
|
import org.eclipse.jgit.lib.CommitBuilder;
|
||||||
|
import org.eclipse.jgit.lib.GpgSignature;
|
||||||
|
import org.eclipse.jgit.lib.GpgSigner;
|
||||||
|
import org.eclipse.jgit.lib.PersonIdent;
|
||||||
|
import org.eclipse.jgit.transport.CredentialsProvider;
|
||||||
|
import sonia.scm.security.GPG;
|
||||||
|
|
||||||
|
import javax.inject.Inject;
|
||||||
|
import java.io.UnsupportedEncodingException;
|
||||||
|
|
||||||
|
public class ScmGpgSigner extends GpgSigner {
|
||||||
|
|
||||||
|
private final GPG gpg;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
public ScmGpgSigner(GPG gpg) {
|
||||||
|
this.gpg = gpg;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void sign(CommitBuilder commitBuilder, String s, PersonIdent personIdent, CredentialsProvider credentialsProvider) throws CanceledException {
|
||||||
|
try {
|
||||||
|
final byte[] signature = this.gpg.getPrivateKey().sign(commitBuilder.build());
|
||||||
|
commitBuilder.setGpgSignature(new GpgSignature(signature));
|
||||||
|
} catch (UnsupportedEncodingException e) {
|
||||||
|
throw new IllegalStateException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean canLocateSigningKey(String s, PersonIdent personIdent, CredentialsProvider credentialsProvider) throws CanceledException {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,54 @@
|
|||||||
|
/*
|
||||||
|
*
|
||||||
|
* MIT License
|
||||||
|
*
|
||||||
|
* Copyright (c) 2020-present Cloudogu GmbH and Contributors
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in all
|
||||||
|
* copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
* SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package sonia.scm.repository;
|
||||||
|
|
||||||
|
import org.eclipse.jgit.lib.GpgSigner;
|
||||||
|
import sonia.scm.plugin.Extension;
|
||||||
|
|
||||||
|
import javax.inject.Inject;
|
||||||
|
import javax.servlet.ServletContextEvent;
|
||||||
|
import javax.servlet.ServletContextListener;
|
||||||
|
|
||||||
|
@Extension
|
||||||
|
public class ScmGpgSignerInitializer implements ServletContextListener {
|
||||||
|
|
||||||
|
private final ScmGpgSigner scmGpgSigner;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
public ScmGpgSignerInitializer(ScmGpgSigner scmGpgSigner) {
|
||||||
|
this.scmGpgSigner = scmGpgSigner;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void contextInitialized(ServletContextEvent servletContextEvent) {
|
||||||
|
GpgSigner.setDefault(scmGpgSigner);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void contextDestroyed(ServletContextEvent servletContextEvent) {
|
||||||
|
// Do nothing
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -24,20 +24,36 @@
|
|||||||
|
|
||||||
package sonia.scm.security.gpg;
|
package sonia.scm.security.gpg;
|
||||||
|
|
||||||
|
import com.sun.tools.javac.util.Pair;
|
||||||
|
import org.apache.shiro.SecurityUtils;
|
||||||
import org.bouncycastle.bcpg.ArmoredInputStream;
|
import org.bouncycastle.bcpg.ArmoredInputStream;
|
||||||
|
import org.bouncycastle.openpgp.PGPException;
|
||||||
|
import org.bouncycastle.openpgp.PGPKeyPair;
|
||||||
|
import org.bouncycastle.openpgp.PGPKeyRingGenerator;
|
||||||
import org.bouncycastle.openpgp.PGPObjectFactory;
|
import org.bouncycastle.openpgp.PGPObjectFactory;
|
||||||
|
import org.bouncycastle.openpgp.PGPPublicKey;
|
||||||
|
import org.bouncycastle.openpgp.PGPSecretKeyRing;
|
||||||
import org.bouncycastle.openpgp.PGPSignatureList;
|
import org.bouncycastle.openpgp.PGPSignatureList;
|
||||||
|
import org.bouncycastle.openpgp.PGPUtil;
|
||||||
import org.bouncycastle.openpgp.operator.jcajce.JcaKeyFingerprintCalculator;
|
import org.bouncycastle.openpgp.operator.jcajce.JcaKeyFingerprintCalculator;
|
||||||
|
import org.bouncycastle.openpgp.operator.jcajce.JcaPGPKeyPair;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
import sonia.scm.security.GPG;
|
import sonia.scm.security.GPG;
|
||||||
import sonia.scm.security.PrivateKey;
|
import sonia.scm.security.PrivateKey;
|
||||||
import sonia.scm.security.PublicKey;
|
import sonia.scm.security.PublicKey;
|
||||||
|
import sonia.scm.security.SessionId;
|
||||||
|
import sonia.scm.user.User;
|
||||||
|
|
||||||
import javax.inject.Inject;
|
import javax.inject.Inject;
|
||||||
import java.io.ByteArrayInputStream;
|
import java.io.ByteArrayInputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.security.KeyPair;
|
||||||
|
import java.security.KeyPairGenerator;
|
||||||
|
import java.security.NoSuchAlgorithmException;
|
||||||
|
import java.security.NoSuchProviderException;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
|
import java.util.Date;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
@@ -45,11 +61,13 @@ import java.util.stream.Collectors;
|
|||||||
public class DefaultGPG implements GPG {
|
public class DefaultGPG implements GPG {
|
||||||
|
|
||||||
private static final Logger LOG = LoggerFactory.getLogger(DefaultGPG.class);
|
private static final Logger LOG = LoggerFactory.getLogger(DefaultGPG.class);
|
||||||
private final PublicKeyStore store;
|
private final PublicKeyStore publicKeyStore;
|
||||||
|
private final PrivateKeyStore privateKeyStore;
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
public DefaultGPG(PublicKeyStore store) {
|
public DefaultGPG(PublicKeyStore publicKeyStore, PrivateKeyStore privateKeyStore) {
|
||||||
this.store = store;
|
this.publicKeyStore = publicKeyStore;
|
||||||
|
this.privateKeyStore = privateKeyStore;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -67,14 +85,14 @@ public class DefaultGPG implements GPG {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Optional<PublicKey> findPublicKey(String id) {
|
public Optional<PublicKey> findPublicKey(String id) {
|
||||||
Optional<RawGpgKey> key = store.findById(id);
|
Optional<RawGpgKey> key = publicKeyStore.findById(id);
|
||||||
|
|
||||||
return key.map(rawGpgKey -> new GpgKey(rawGpgKey.getId(), rawGpgKey.getOwner(), rawGpgKey.getRaw(), rawGpgKey.getContacts()));
|
return key.map(rawGpgKey -> new GpgKey(rawGpgKey.getId(), rawGpgKey.getOwner(), rawGpgKey.getRaw(), rawGpgKey.getContacts()));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Iterable<PublicKey> findPublicKeysByUsername(String username) {
|
public Iterable<PublicKey> findPublicKeysByUsername(String username) {
|
||||||
List<RawGpgKey> keys = store.findByUsername(username);
|
List<RawGpgKey> keys = publicKeyStore.findByUsername(username);
|
||||||
|
|
||||||
if (!keys.isEmpty()) {
|
if (!keys.isEmpty()) {
|
||||||
return keys
|
return keys
|
||||||
@@ -88,6 +106,36 @@ public class DefaultGPG implements GPG {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public PrivateKey getPrivateKey() {
|
public PrivateKey getPrivateKey() {
|
||||||
throw new UnsupportedOperationException("getPrivateKey is not yet implemented");
|
final String userId = SecurityUtils.getSubject().getPrincipal().toString();
|
||||||
|
final Optional<String> privateRawKey = privateKeyStore.getForUserId(userId);
|
||||||
|
|
||||||
|
if (!privateRawKey.isPresent()) {
|
||||||
|
try {
|
||||||
|
// Generate key pair
|
||||||
|
KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA", "BC");
|
||||||
|
keyPairGenerator.initialize(2048);
|
||||||
|
|
||||||
|
KeyPair pair = keyPairGenerator.generateKeyPair();
|
||||||
|
|
||||||
|
String identity = "0xAWESOMExBOB";
|
||||||
|
PGPKeyPair keyPair = new JcaPGPKeyPair(PGPPublicKey.RSA_GENERAL, pair, new Date());
|
||||||
|
|
||||||
|
new PGPKeyRingGenerator().generateSecretKeyRing().;
|
||||||
|
} catch (PGPException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
} catch (NoSuchAlgorithmException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
} catch (NoSuchProviderException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
privateKeyStore.setForUserId(user.getId(), privateKeyGpgKeyPair.fst);
|
||||||
|
publicKeyStore.add(user.getDisplayName(), user.getName(), privateKeyGpgKeyPair.snd.getRaw());
|
||||||
|
return privateKeyGpgKeyPair.fst;
|
||||||
|
} else {
|
||||||
|
// PGPUtil.getDecoderStream();
|
||||||
|
return privateRawKey.get();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,78 @@
|
|||||||
|
/*
|
||||||
|
* MIT License
|
||||||
|
*
|
||||||
|
* Copyright (c) 2020-present Cloudogu GmbH and Contributors
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in all
|
||||||
|
* copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
* SOFTWARE.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
package sonia.scm.security.gpg;
|
||||||
|
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
import sonia.scm.security.CipherUtil;
|
||||||
|
import sonia.scm.security.PrivateKey;
|
||||||
|
import sonia.scm.store.DataStore;
|
||||||
|
import sonia.scm.store.DataStoreFactory;
|
||||||
|
import sonia.scm.xml.XmlInstantAdapter;
|
||||||
|
|
||||||
|
import javax.inject.Inject;
|
||||||
|
import javax.inject.Singleton;
|
||||||
|
import javax.xml.bind.annotation.XmlAccessType;
|
||||||
|
import javax.xml.bind.annotation.XmlAccessorType;
|
||||||
|
import javax.xml.bind.annotation.XmlRootElement;
|
||||||
|
import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
|
||||||
|
import java.time.Instant;
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
|
@Singleton
|
||||||
|
class PrivateKeyStore {
|
||||||
|
|
||||||
|
private static final String STORE_NAME = "gpg_private_keys";
|
||||||
|
|
||||||
|
private final DataStore<RawPrivateKey> store;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
PrivateKeyStore(DataStoreFactory dataStoreFactory) {
|
||||||
|
this.store = dataStoreFactory.withType(RawPrivateKey.class).withName(STORE_NAME).build();
|
||||||
|
}
|
||||||
|
|
||||||
|
Optional<String> getForUserId(String userId) {
|
||||||
|
return store.getOptional(userId).map(rawPrivateKey -> CipherUtil.getInstance().decode(rawPrivateKey.key));
|
||||||
|
}
|
||||||
|
|
||||||
|
void setForUserId(String userId, String rawKey) {
|
||||||
|
final String encodedRawKey = CipherUtil.getInstance().encode(rawKey);
|
||||||
|
store.put(userId, new RawPrivateKey(encodedRawKey, Instant.now()));
|
||||||
|
}
|
||||||
|
|
||||||
|
@XmlRootElement
|
||||||
|
@XmlAccessorType(XmlAccessType.FIELD)
|
||||||
|
@AllArgsConstructor
|
||||||
|
@NoArgsConstructor
|
||||||
|
private class RawPrivateKey {
|
||||||
|
private String key;
|
||||||
|
|
||||||
|
@XmlJavaTypeAdapter(XmlInstantAdapter.class)
|
||||||
|
private Instant date;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -58,7 +58,7 @@ public abstract class PublicKeyMapper {
|
|||||||
RawGpgKeyDto createDto(RawGpgKey rawGpgKey) {
|
RawGpgKeyDto createDto(RawGpgKey rawGpgKey) {
|
||||||
Links.Builder linksBuilder = linkingTo();
|
Links.Builder linksBuilder = linkingTo();
|
||||||
linksBuilder.self(createSelfLink(rawGpgKey));
|
linksBuilder.self(createSelfLink(rawGpgKey));
|
||||||
if (UserPermissions.changePublicKeys(rawGpgKey.getOwner()).isPermitted()) {
|
if (UserPermissions.changePublicKeys(rawGpgKey.getOwner()).isPermitted() && !rawGpgKey.isReadonly()) {
|
||||||
linksBuilder.single(Link.link("delete", createDeleteLink(rawGpgKey)));
|
linksBuilder.single(Link.link("delete", createDeleteLink(rawGpgKey)));
|
||||||
}
|
}
|
||||||
return new RawGpgKeyDto(linksBuilder.build());
|
return new RawGpgKeyDto(linksBuilder.build());
|
||||||
|
|||||||
@@ -30,12 +30,14 @@ import io.swagger.v3.oas.annotations.media.Content;
|
|||||||
import io.swagger.v3.oas.annotations.media.Schema;
|
import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
import io.swagger.v3.oas.annotations.responses.ApiResponse;
|
import io.swagger.v3.oas.annotations.responses.ApiResponse;
|
||||||
import sonia.scm.api.v2.resources.ErrorDto;
|
import sonia.scm.api.v2.resources.ErrorDto;
|
||||||
|
import sonia.scm.security.AllowAnonymousAccess;
|
||||||
import sonia.scm.web.VndMediaType;
|
import sonia.scm.web.VndMediaType;
|
||||||
|
|
||||||
import javax.inject.Inject;
|
import javax.inject.Inject;
|
||||||
import javax.ws.rs.Consumes;
|
import javax.ws.rs.Consumes;
|
||||||
import javax.ws.rs.DELETE;
|
import javax.ws.rs.DELETE;
|
||||||
import javax.ws.rs.GET;
|
import javax.ws.rs.GET;
|
||||||
|
import javax.ws.rs.HeaderParam;
|
||||||
import javax.ws.rs.POST;
|
import javax.ws.rs.POST;
|
||||||
import javax.ws.rs.Path;
|
import javax.ws.rs.Path;
|
||||||
import javax.ws.rs.PathParam;
|
import javax.ws.rs.PathParam;
|
||||||
@@ -97,6 +99,7 @@ public class PublicKeyResource {
|
|||||||
@GET
|
@GET
|
||||||
@Path("{id}")
|
@Path("{id}")
|
||||||
@Produces(MEDIA_TYPE)
|
@Produces(MEDIA_TYPE)
|
||||||
|
@AllowAnonymousAccess
|
||||||
@Operation(
|
@Operation(
|
||||||
summary = "Get single key for user",
|
summary = "Get single key for user",
|
||||||
description = "Returns a single public key for username by id.",
|
description = "Returns a single public key for username by id.",
|
||||||
@@ -129,7 +132,7 @@ public class PublicKeyResource {
|
|||||||
schema = @Schema(implementation = ErrorDto.class)
|
schema = @Schema(implementation = ErrorDto.class)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
public Response findById(@PathParam("id") String id) {
|
public Response findByIdJson(@PathParam("id") String id) {
|
||||||
Optional<RawGpgKey> byId = store.findById(id);
|
Optional<RawGpgKey> byId = store.findById(id);
|
||||||
if (byId.isPresent()) {
|
if (byId.isPresent()) {
|
||||||
return Response.ok(mapper.map(byId.get())).build();
|
return Response.ok(mapper.map(byId.get())).build();
|
||||||
|
|||||||
@@ -64,7 +64,7 @@ public class PublicKeyStore {
|
|||||||
this.eventBus = eventBus;
|
this.eventBus = eventBus;
|
||||||
}
|
}
|
||||||
|
|
||||||
public RawGpgKey add(String displayName, String username, String rawKey) {
|
public RawGpgKey add(String displayName, String username, String rawKey, ) {
|
||||||
UserPermissions.changePublicKeys(username).check();
|
UserPermissions.changePublicKeys(username).check();
|
||||||
|
|
||||||
if (!rawKey.contains("PUBLIC KEY")) {
|
if (!rawKey.contains("PUBLIC KEY")) {
|
||||||
|
|||||||
@@ -50,6 +50,7 @@ public class RawGpgKey {
|
|||||||
private String displayName;
|
private String displayName;
|
||||||
private String owner;
|
private String owner;
|
||||||
private String raw;
|
private String raw;
|
||||||
|
private boolean readonly = false;
|
||||||
private Set<Person> contacts;
|
private Set<Person> contacts;
|
||||||
|
|
||||||
@XmlJavaTypeAdapter(XmlInstantAdapter.class)
|
@XmlJavaTypeAdapter(XmlInstantAdapter.class)
|
||||||
|
|||||||
@@ -97,7 +97,7 @@ class PublicKeyResourceTest {
|
|||||||
RawGpgKeyDto dto = new RawGpgKeyDto();
|
RawGpgKeyDto dto = new RawGpgKeyDto();
|
||||||
when(mapper.map(key)).thenReturn(dto);
|
when(mapper.map(key)).thenReturn(dto);
|
||||||
|
|
||||||
Response response = resource.findById("42");
|
Response response = resource.findByIdJson("42");
|
||||||
assertThat(response.getStatus()).isEqualTo(200);
|
assertThat(response.getStatus()).isEqualTo(200);
|
||||||
assertThat(response.getEntity()).isSameAs(dto);
|
assertThat(response.getEntity()).isSameAs(dto);
|
||||||
}
|
}
|
||||||
@@ -106,7 +106,7 @@ class PublicKeyResourceTest {
|
|||||||
void shouldReturn404IfIdDoesNotExists() {
|
void shouldReturn404IfIdDoesNotExists() {
|
||||||
when(store.findById("42")).thenReturn(Optional.empty());
|
when(store.findById("42")).thenReturn(Optional.empty());
|
||||||
|
|
||||||
Response response = resource.findById("42");
|
Response response = resource.findByIdJson("42");
|
||||||
assertThat(response.getStatus()).isEqualTo(404);
|
assertThat(response.getStatus()).isEqualTo(404);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user