mirror of
https://github.com/scm-manager/scm-manager.git
synced 2025-11-14 09:25:43 +01:00
cleanup
This commit is contained in:
@@ -44,7 +44,7 @@ public class Signature implements Serializable {
|
||||
private final String type;
|
||||
private final SignatureStatus status;
|
||||
private final String owner;
|
||||
private final Set<String> contacts;
|
||||
private final Set<Person> contacts;
|
||||
|
||||
public Optional<String> getOwner() {
|
||||
return Optional.ofNullable(owner);
|
||||
|
||||
@@ -55,6 +55,7 @@ import sonia.scm.repository.RepositoryPermissions;
|
||||
import sonia.scm.repository.spi.RepositoryServiceProvider;
|
||||
import sonia.scm.repository.spi.RepositoryServiceResolver;
|
||||
import sonia.scm.repository.work.WorkdirProvider;
|
||||
import sonia.scm.security.PublicKeyCreatedEvent;
|
||||
import sonia.scm.security.PublicKeyDeletedEvent;
|
||||
import sonia.scm.security.ScmSecurityException;
|
||||
|
||||
@@ -335,6 +336,11 @@ public final class RepositoryServiceFactory {
|
||||
cacheManager.getCache(LogCommandBuilder.CACHE_NAME).clear();
|
||||
}
|
||||
|
||||
@Subscribe
|
||||
public void onEvent(PublicKeyCreatedEvent event) {
|
||||
cacheManager.getCache(LogCommandBuilder.CACHE_NAME).clear();
|
||||
}
|
||||
|
||||
@SuppressWarnings({"unchecked", "java:S3740", "rawtypes"})
|
||||
private void clearCaches(final String repositoryId) {
|
||||
if (logger.isDebugEnabled()) {
|
||||
|
||||
@@ -24,6 +24,8 @@
|
||||
|
||||
package sonia.scm.security;
|
||||
|
||||
import sonia.scm.repository.Person;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.InputStream;
|
||||
import java.util.Optional;
|
||||
@@ -62,7 +64,7 @@ public interface PublicKey {
|
||||
*
|
||||
* @return owner or empty optional
|
||||
*/
|
||||
Set<String> getContacts();
|
||||
Set<Person> getContacts();
|
||||
|
||||
/**
|
||||
* Verifies that the signature is valid for the given data.
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
/*
|
||||
* 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;
|
||||
|
||||
import sonia.scm.event.Event;
|
||||
|
||||
/**
|
||||
* This event is fired when a public key was created in SCM-Manager.
|
||||
* @since 2.4.0
|
||||
*/
|
||||
@Event
|
||||
public class PublicKeyCreatedEvent {
|
||||
}
|
||||
@@ -1716,7 +1716,7 @@ exports[`Storyshots CardColumnSmall Minimal 1`] = `
|
||||
|
||||
exports[`Storyshots Changesets Co-Authors with avatar 1`] = `
|
||||
<div
|
||||
className="Changesetsstories__Wrapper-sc-122npan-0 hHKBXk box box-link-shadow"
|
||||
className="Changesetsstories__Wrapper-sc-122npan-0 gDnzNS box box-link-shadow"
|
||||
>
|
||||
<div
|
||||
className="ChangesetRow__Wrapper-tkpti5-0 bRWdJS"
|
||||
@@ -1886,7 +1886,7 @@ exports[`Storyshots Changesets Co-Authors with avatar 1`] = `
|
||||
|
||||
exports[`Storyshots Changesets Commiter and Co-Authors with avatar 1`] = `
|
||||
<div
|
||||
className="Changesetsstories__Wrapper-sc-122npan-0 hHKBXk box box-link-shadow"
|
||||
className="Changesetsstories__Wrapper-sc-122npan-0 gDnzNS box box-link-shadow"
|
||||
>
|
||||
<div
|
||||
className="ChangesetRow__Wrapper-tkpti5-0 bRWdJS"
|
||||
@@ -2045,7 +2045,7 @@ exports[`Storyshots Changesets Commiter and Co-Authors with avatar 1`] = `
|
||||
|
||||
exports[`Storyshots Changesets Default 1`] = `
|
||||
<div
|
||||
className="Changesetsstories__Wrapper-sc-122npan-0 hHKBXk box box-link-shadow"
|
||||
className="Changesetsstories__Wrapper-sc-122npan-0 gDnzNS box box-link-shadow"
|
||||
>
|
||||
<div
|
||||
className="ChangesetRow__Wrapper-tkpti5-0 bRWdJS"
|
||||
@@ -2162,7 +2162,7 @@ exports[`Storyshots Changesets Default 1`] = `
|
||||
|
||||
exports[`Storyshots Changesets Replacements 1`] = `
|
||||
<div
|
||||
className="Changesetsstories__Wrapper-sc-122npan-0 hHKBXk box box-link-shadow"
|
||||
className="Changesetsstories__Wrapper-sc-122npan-0 gDnzNS box box-link-shadow"
|
||||
>
|
||||
<div
|
||||
className="ChangesetRow__Wrapper-tkpti5-0 bRWdJS"
|
||||
@@ -2289,7 +2289,7 @@ exports[`Storyshots Changesets Replacements 1`] = `
|
||||
|
||||
exports[`Storyshots Changesets With Committer 1`] = `
|
||||
<div
|
||||
className="Changesetsstories__Wrapper-sc-122npan-0 hHKBXk box box-link-shadow"
|
||||
className="Changesetsstories__Wrapper-sc-122npan-0 gDnzNS box box-link-shadow"
|
||||
>
|
||||
<div
|
||||
className="ChangesetRow__Wrapper-tkpti5-0 bRWdJS"
|
||||
@@ -2418,7 +2418,7 @@ exports[`Storyshots Changesets With Committer 1`] = `
|
||||
|
||||
exports[`Storyshots Changesets With Committer and Co-Author 1`] = `
|
||||
<div
|
||||
className="Changesetsstories__Wrapper-sc-122npan-0 hHKBXk box box-link-shadow"
|
||||
className="Changesetsstories__Wrapper-sc-122npan-0 gDnzNS box box-link-shadow"
|
||||
>
|
||||
<div
|
||||
className="ChangesetRow__Wrapper-tkpti5-0 bRWdJS"
|
||||
@@ -2556,7 +2556,7 @@ exports[`Storyshots Changesets With Committer and Co-Author 1`] = `
|
||||
|
||||
exports[`Storyshots Changesets With avatar 1`] = `
|
||||
<div
|
||||
className="Changesetsstories__Wrapper-sc-122npan-0 hHKBXk box box-link-shadow"
|
||||
className="Changesetsstories__Wrapper-sc-122npan-0 gDnzNS box box-link-shadow"
|
||||
>
|
||||
<div
|
||||
className="ChangesetRow__Wrapper-tkpti5-0 bRWdJS"
|
||||
@@ -2684,9 +2684,138 @@ exports[`Storyshots Changesets With avatar 1`] = `
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`Storyshots Changesets With invalid signature 1`] = `
|
||||
<div
|
||||
className="Changesetsstories__Wrapper-sc-122npan-0 gDnzNS box box-link-shadow"
|
||||
>
|
||||
<div
|
||||
className="ChangesetRow__Wrapper-tkpti5-0 bRWdJS"
|
||||
>
|
||||
<div
|
||||
className="columns is-gapless is-mobile"
|
||||
>
|
||||
<div
|
||||
className="column is-three-fifths"
|
||||
>
|
||||
<div
|
||||
className="columns is-gapless"
|
||||
>
|
||||
<div
|
||||
className="column is-four-fifths"
|
||||
>
|
||||
<div
|
||||
className="media"
|
||||
>
|
||||
<div
|
||||
className="ChangesetRow__Metadata-tkpti5-3 dfKqLe media-right"
|
||||
>
|
||||
<h4
|
||||
className="has-text-weight-bold is-ellipsis-overflow"
|
||||
>
|
||||
|
||||
initialize repository
|
||||
|
||||
</h4>
|
||||
<p
|
||||
className="is-hidden-touch"
|
||||
/>
|
||||
<p
|
||||
className="is-hidden-desktop"
|
||||
/>
|
||||
<div
|
||||
className="ChangesetRow__FlexRow-tkpti5-7 fTLhSo"
|
||||
>
|
||||
<p
|
||||
className="ChangesetRow__AuthorWrapper-tkpti5-4 kDAubY is-size-7 is-ellipsis-overflow"
|
||||
>
|
||||
changeset.contributors.authoredBy
|
||||
|
||||
<a
|
||||
href="mailto:scm-admin@scm-manager.org"
|
||||
title="changeset.contributors.mailto scm-admin@scm-manager.org"
|
||||
>
|
||||
SCM Administrator
|
||||
</a>
|
||||
</p>
|
||||
<span
|
||||
className="tooltip has-tooltip-top"
|
||||
data-tooltip="changeset.keyOwner: trillian
|
||||
changeset.keyId: 0x247E908C6FD35473
|
||||
changeset.signatureStatus: changeset.signatureInvalid
|
||||
changeset.keyContacts:
|
||||
- Tricia Marie McMilla <trillian@hitchhiker.com>"
|
||||
>
|
||||
<i
|
||||
className="fas fa-key has-text-danger SignatureIcon__StyledIcon-sc-1mwf1m5-0 cgFYhq mx-2 pt-1"
|
||||
/>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
className="ChangesetRow__VCenteredColumn-tkpti5-5 jtvbjX column"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
className="ChangesetRow__VCenteredChildColumn-tkpti5-6 cciHUW column is-flex"
|
||||
>
|
||||
<div
|
||||
className="ButtonAddons__Flex-sc-182golj-0 jSuMVB field has-addons is-marginless"
|
||||
>
|
||||
<p
|
||||
className="control"
|
||||
>
|
||||
<button
|
||||
className="button is-default is-reduced-mobile"
|
||||
onClick={[Function]}
|
||||
type="button"
|
||||
>
|
||||
<span
|
||||
className="icon is-medium"
|
||||
>
|
||||
<i
|
||||
className="fas fa-exchange-alt has-text-inherit"
|
||||
/>
|
||||
</span>
|
||||
<span>
|
||||
changeset.buttons.details
|
||||
|
||||
</span>
|
||||
</button>
|
||||
</p>
|
||||
<p
|
||||
className="control"
|
||||
>
|
||||
<button
|
||||
className="button is-default is-reduced-mobile"
|
||||
onClick={[Function]}
|
||||
type="button"
|
||||
>
|
||||
<span
|
||||
className="icon is-medium"
|
||||
>
|
||||
<i
|
||||
className="fas fa-code has-text-inherit"
|
||||
/>
|
||||
</span>
|
||||
<span>
|
||||
changeset.buttons.sources
|
||||
|
||||
</span>
|
||||
</button>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`Storyshots Changesets With multiple Co-Authors 1`] = `
|
||||
<div
|
||||
className="Changesetsstories__Wrapper-sc-122npan-0 hHKBXk box box-link-shadow"
|
||||
className="Changesetsstories__Wrapper-sc-122npan-0 gDnzNS box box-link-shadow"
|
||||
>
|
||||
<div
|
||||
className="ChangesetRow__Wrapper-tkpti5-0 bRWdJS"
|
||||
@@ -2814,6 +2943,261 @@ exports[`Storyshots Changesets With multiple Co-Authors 1`] = `
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`Storyshots Changesets With unknown signature 1`] = `
|
||||
<div
|
||||
className="Changesetsstories__Wrapper-sc-122npan-0 gDnzNS box box-link-shadow"
|
||||
>
|
||||
<div
|
||||
className="ChangesetRow__Wrapper-tkpti5-0 bRWdJS"
|
||||
>
|
||||
<div
|
||||
className="columns is-gapless is-mobile"
|
||||
>
|
||||
<div
|
||||
className="column is-three-fifths"
|
||||
>
|
||||
<div
|
||||
className="columns is-gapless"
|
||||
>
|
||||
<div
|
||||
className="column is-four-fifths"
|
||||
>
|
||||
<div
|
||||
className="media"
|
||||
>
|
||||
<div
|
||||
className="ChangesetRow__Metadata-tkpti5-3 dfKqLe media-right"
|
||||
>
|
||||
<h4
|
||||
className="has-text-weight-bold is-ellipsis-overflow"
|
||||
>
|
||||
|
||||
initialize repository
|
||||
|
||||
</h4>
|
||||
<p
|
||||
className="is-hidden-touch"
|
||||
/>
|
||||
<p
|
||||
className="is-hidden-desktop"
|
||||
/>
|
||||
<div
|
||||
className="ChangesetRow__FlexRow-tkpti5-7 fTLhSo"
|
||||
>
|
||||
<p
|
||||
className="ChangesetRow__AuthorWrapper-tkpti5-4 kDAubY is-size-7 is-ellipsis-overflow"
|
||||
>
|
||||
changeset.contributors.authoredBy
|
||||
|
||||
<a
|
||||
href="mailto:scm-admin@scm-manager.org"
|
||||
title="changeset.contributors.mailto scm-admin@scm-manager.org"
|
||||
>
|
||||
SCM Administrator
|
||||
</a>
|
||||
</p>
|
||||
<span
|
||||
className="tooltip has-tooltip-top"
|
||||
data-tooltip="changeset.signatureStatus: changeset.signatureNotVerified
|
||||
changeset.keyId: 0x247E908C6FD35473"
|
||||
>
|
||||
<i
|
||||
className="fas fa-key has-text-grey-light SignatureIcon__StyledIcon-sc-1mwf1m5-0 cgFYhq mx-2 pt-1"
|
||||
/>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
className="ChangesetRow__VCenteredColumn-tkpti5-5 jtvbjX column"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
className="ChangesetRow__VCenteredChildColumn-tkpti5-6 cciHUW column is-flex"
|
||||
>
|
||||
<div
|
||||
className="ButtonAddons__Flex-sc-182golj-0 jSuMVB field has-addons is-marginless"
|
||||
>
|
||||
<p
|
||||
className="control"
|
||||
>
|
||||
<button
|
||||
className="button is-default is-reduced-mobile"
|
||||
onClick={[Function]}
|
||||
type="button"
|
||||
>
|
||||
<span
|
||||
className="icon is-medium"
|
||||
>
|
||||
<i
|
||||
className="fas fa-exchange-alt has-text-inherit"
|
||||
/>
|
||||
</span>
|
||||
<span>
|
||||
changeset.buttons.details
|
||||
|
||||
</span>
|
||||
</button>
|
||||
</p>
|
||||
<p
|
||||
className="control"
|
||||
>
|
||||
<button
|
||||
className="button is-default is-reduced-mobile"
|
||||
onClick={[Function]}
|
||||
type="button"
|
||||
>
|
||||
<span
|
||||
className="icon is-medium"
|
||||
>
|
||||
<i
|
||||
className="fas fa-code has-text-inherit"
|
||||
/>
|
||||
</span>
|
||||
<span>
|
||||
changeset.buttons.sources
|
||||
|
||||
</span>
|
||||
</button>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`Storyshots Changesets With valid signature 1`] = `
|
||||
<div
|
||||
className="Changesetsstories__Wrapper-sc-122npan-0 gDnzNS box box-link-shadow"
|
||||
>
|
||||
<div
|
||||
className="ChangesetRow__Wrapper-tkpti5-0 bRWdJS"
|
||||
>
|
||||
<div
|
||||
className="columns is-gapless is-mobile"
|
||||
>
|
||||
<div
|
||||
className="column is-three-fifths"
|
||||
>
|
||||
<div
|
||||
className="columns is-gapless"
|
||||
>
|
||||
<div
|
||||
className="column is-four-fifths"
|
||||
>
|
||||
<div
|
||||
className="media"
|
||||
>
|
||||
<div
|
||||
className="ChangesetRow__Metadata-tkpti5-3 dfKqLe media-right"
|
||||
>
|
||||
<h4
|
||||
className="has-text-weight-bold is-ellipsis-overflow"
|
||||
>
|
||||
|
||||
initialize repository
|
||||
|
||||
</h4>
|
||||
<p
|
||||
className="is-hidden-touch"
|
||||
/>
|
||||
<p
|
||||
className="is-hidden-desktop"
|
||||
/>
|
||||
<div
|
||||
className="ChangesetRow__FlexRow-tkpti5-7 fTLhSo"
|
||||
>
|
||||
<p
|
||||
className="ChangesetRow__AuthorWrapper-tkpti5-4 kDAubY is-size-7 is-ellipsis-overflow"
|
||||
>
|
||||
changeset.contributors.authoredBy
|
||||
|
||||
<a
|
||||
href="mailto:scm-admin@scm-manager.org"
|
||||
title="changeset.contributors.mailto scm-admin@scm-manager.org"
|
||||
>
|
||||
SCM Administrator
|
||||
</a>
|
||||
</p>
|
||||
<span
|
||||
className="tooltip has-tooltip-top"
|
||||
data-tooltip="changeset.keyOwner: trillian
|
||||
changeset.keyId: 0x247E908C6FD35473
|
||||
changeset.signatureStatus: changeset.signatureVerified
|
||||
changeset.keyContacts:
|
||||
- Tricia Marie McMilla <trillian@hitchhiker.com>"
|
||||
>
|
||||
<i
|
||||
className="fas fa-key has-text-success SignatureIcon__StyledIcon-sc-1mwf1m5-0 cgFYhq mx-2 pt-1"
|
||||
/>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
className="ChangesetRow__VCenteredColumn-tkpti5-5 jtvbjX column"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
className="ChangesetRow__VCenteredChildColumn-tkpti5-6 cciHUW column is-flex"
|
||||
>
|
||||
<div
|
||||
className="ButtonAddons__Flex-sc-182golj-0 jSuMVB field has-addons is-marginless"
|
||||
>
|
||||
<p
|
||||
className="control"
|
||||
>
|
||||
<button
|
||||
className="button is-default is-reduced-mobile"
|
||||
onClick={[Function]}
|
||||
type="button"
|
||||
>
|
||||
<span
|
||||
className="icon is-medium"
|
||||
>
|
||||
<i
|
||||
className="fas fa-exchange-alt has-text-inherit"
|
||||
/>
|
||||
</span>
|
||||
<span>
|
||||
changeset.buttons.details
|
||||
|
||||
</span>
|
||||
</button>
|
||||
</p>
|
||||
<p
|
||||
className="control"
|
||||
>
|
||||
<button
|
||||
className="button is-default is-reduced-mobile"
|
||||
onClick={[Function]}
|
||||
type="button"
|
||||
>
|
||||
<span
|
||||
className="icon is-medium"
|
||||
>
|
||||
<i
|
||||
className="fas fa-code has-text-inherit"
|
||||
/>
|
||||
</span>
|
||||
<span>
|
||||
changeset.buttons.sources
|
||||
|
||||
</span>
|
||||
</button>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`Storyshots Date Date from now 1`] = `
|
||||
<div
|
||||
className="Datestories__Wrapper-sc-1uda3v2-0 duRVzW"
|
||||
@@ -38972,6 +39356,15 @@ exports[`Storyshots Layout|Footer Default 1`] = `
|
||||
profile.changePasswordNavLink
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a
|
||||
className=""
|
||||
href="/me/settings/publicKeys"
|
||||
onClick={[Function]}
|
||||
>
|
||||
profile.publicKeysNavLink
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</section>
|
||||
<section
|
||||
@@ -39088,6 +39481,15 @@ exports[`Storyshots Layout|Footer Full 1`] = `
|
||||
profile.changePasswordNavLink
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a
|
||||
className=""
|
||||
href="/me/settings/publicKeys"
|
||||
onClick={[Function]}
|
||||
>
|
||||
profile.publicKeysNavLink
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a
|
||||
className=""
|
||||
@@ -39240,6 +39642,15 @@ exports[`Storyshots Layout|Footer With Avatar 1`] = `
|
||||
profile.changePasswordNavLink
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a
|
||||
className=""
|
||||
href="/me/settings/publicKeys"
|
||||
onClick={[Function]}
|
||||
>
|
||||
profile.publicKeysNavLink
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</section>
|
||||
<section
|
||||
@@ -39351,6 +39762,15 @@ exports[`Storyshots Layout|Footer With Plugin Links 1`] = `
|
||||
profile.changePasswordNavLink
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a
|
||||
className=""
|
||||
href="/me/settings/publicKeys"
|
||||
onClick={[Function]}
|
||||
>
|
||||
profile.publicKeysNavLink
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a
|
||||
className=""
|
||||
|
||||
@@ -69,10 +69,11 @@ const SignatureIcon: FC<Props> = ({ signatures, className }) => {
|
||||
"changeset.signatureStatus"
|
||||
)}: ${status}`;
|
||||
|
||||
console.log(signature.contacts)
|
||||
if (signature.contacts?.length > 0) {
|
||||
message += `\n${t("changeset.keyContacts")}:`;
|
||||
signature.contacts.forEach((contact) => {
|
||||
message += `\n- ${contact}`;
|
||||
message += `\n- ${contact.name} <${contact.mail}>`;
|
||||
});
|
||||
}
|
||||
return message;
|
||||
|
||||
@@ -47,7 +47,7 @@ export type Signature = {
|
||||
type: string;
|
||||
status: "VERIFIED" | "NOT_FOUND" | "INVALID";
|
||||
owner: string;
|
||||
contacts: string[];
|
||||
contacts: Person[];
|
||||
}
|
||||
|
||||
export type Contributor = {
|
||||
|
||||
@@ -36,6 +36,7 @@ import org.bouncycastle.openpgp.jcajce.JcaPGPObjectFactory;
|
||||
import org.bouncycastle.openpgp.operator.jcajce.JcaPGPContentVerifierBuilderProvider;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import sonia.scm.repository.Person;
|
||||
import sonia.scm.security.PublicKey;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
@@ -52,9 +53,9 @@ public class GpgKey implements PublicKey {
|
||||
private final String id;
|
||||
private final String owner;
|
||||
private final String raw;
|
||||
private final Set<String> contacts;
|
||||
private final Set<Person> contacts;
|
||||
|
||||
public GpgKey(String id, String owner, String raw, Set<String> contacts) {
|
||||
public GpgKey(String id, String owner, String raw, Set<Person> contacts) {
|
||||
this.id = id;
|
||||
this.owner = owner;
|
||||
this.raw = raw;
|
||||
@@ -80,7 +81,7 @@ public class GpgKey implements PublicKey {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<String> getContacts() {
|
||||
public Set<Person> getContacts() {
|
||||
return contacts;
|
||||
}
|
||||
|
||||
|
||||
@@ -51,6 +51,7 @@ public abstract class PublicKeyMapper {
|
||||
}
|
||||
|
||||
@Mapping(target = "attributes", ignore = true)
|
||||
@Mapping(target = "raw", ignore = true)
|
||||
abstract RawGpgKeyDto map(RawGpgKey rawGpgKey);
|
||||
|
||||
@ObjectFactory
|
||||
|
||||
@@ -25,11 +25,11 @@
|
||||
package sonia.scm.security.gpg;
|
||||
|
||||
import org.bouncycastle.openpgp.PGPPublicKey;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import sonia.scm.ContextEntry;
|
||||
import sonia.scm.event.ScmEventBus;
|
||||
import sonia.scm.repository.Person;
|
||||
import sonia.scm.security.NotPublicKeyException;
|
||||
import sonia.scm.security.PublicKeyCreatedEvent;
|
||||
import sonia.scm.security.PublicKeyDeletedEvent;
|
||||
import sonia.scm.store.DataStore;
|
||||
import sonia.scm.store.DataStoreFactory;
|
||||
@@ -38,6 +38,7 @@ import sonia.scm.user.UserPermissions;
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Singleton;
|
||||
import java.time.Instant;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
@@ -80,22 +81,24 @@ public class PublicKeyStore {
|
||||
RawGpgKey key = new RawGpgKey(master, displayName, username, rawKey, getContactsFromPublicKey(rawKey), Instant.now());
|
||||
|
||||
store.put(master, key);
|
||||
eventBus.post(new PublicKeyCreatedEvent());
|
||||
|
||||
return key;
|
||||
|
||||
}
|
||||
|
||||
private Set<String> getContactsFromPublicKey(String rawKey) {
|
||||
Set<String> contacts = new HashSet<>();
|
||||
private Set<Person> getContactsFromPublicKey(String rawKey) {
|
||||
List<String> userIds = new ArrayList<>();
|
||||
Optional<PGPPublicKey> publicKeyFromRawKey = getFromRawKey(rawKey);
|
||||
publicKeyFromRawKey.ifPresent(pgpPublicKey -> pgpPublicKey.getUserIDs().forEachRemaining(contacts::add));
|
||||
return contacts;
|
||||
publicKeyFromRawKey.ifPresent(pgpPublicKey -> pgpPublicKey.getUserIDs().forEachRemaining(userIds::add));
|
||||
|
||||
return userIds.stream().map(Person::toPerson).collect(Collectors.toSet());
|
||||
}
|
||||
|
||||
public void delete(String id) {
|
||||
RawGpgKey rawGpgKey = store.get(id);
|
||||
if (rawGpgKey != null) {
|
||||
UserPermissions.modify(rawGpgKey.getOwner()).check();
|
||||
UserPermissions.changePublicKeys(rawGpgKey.getOwner()).check();
|
||||
store.remove(id);
|
||||
eventBus.post(new PublicKeyDeletedEvent());
|
||||
}
|
||||
|
||||
@@ -28,6 +28,7 @@ import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.Setter;
|
||||
import sonia.scm.repository.Person;
|
||||
import sonia.scm.xml.XmlInstantAdapter;
|
||||
|
||||
import javax.xml.bind.annotation.XmlAccessType;
|
||||
@@ -49,7 +50,7 @@ public class RawGpgKey {
|
||||
private String displayName;
|
||||
private String owner;
|
||||
private String raw;
|
||||
private Set<String> contacts;
|
||||
private Set<Person> contacts;
|
||||
|
||||
@XmlJavaTypeAdapter(XmlInstantAdapter.class)
|
||||
private Instant created;
|
||||
|
||||
@@ -31,6 +31,7 @@ import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.mockito.InjectMocks;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.junit.jupiter.MockitoExtension;
|
||||
import sonia.scm.repository.Person;
|
||||
import sonia.scm.security.PublicKey;
|
||||
|
||||
import java.io.IOException;
|
||||
@@ -52,16 +53,17 @@ class DefaultGPGTest {
|
||||
|
||||
@Test
|
||||
void shouldFindIdInSignature() throws IOException {
|
||||
String raw = GPGTestHelper.readResourceAsString("signature.asc");
|
||||
String raw = GPGTestHelper.readResourceAsString("slarti.txt.asc");
|
||||
String publicKeyId = gpg.findPublicKeyId(raw.getBytes());
|
||||
|
||||
assertThat(publicKeyId).isEqualTo("0x1F17B79A09DAD5B9");
|
||||
assertThat(publicKeyId).isEqualTo("0x247E908C6FD35473");
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldFindPublicKey() throws IOException {
|
||||
String raw = GPGTestHelper.readResourceAsString("subkeys.asc");
|
||||
RawGpgKey key1 = new RawGpgKey("42", "key_42", "trillian", raw, ImmutableSet.of("trillian", "zaphod"), Instant.now());
|
||||
Person trillian = Person.toPerson("Trillian <tricia.mcmillan@hitchhiker.org>");
|
||||
RawGpgKey key1 = new RawGpgKey("42", "key_42", "trillian", raw, ImmutableSet.of(trillian), Instant.now());
|
||||
|
||||
when(store.findById("42")).thenReturn(Optional.of(key1));
|
||||
|
||||
@@ -71,7 +73,7 @@ class DefaultGPGTest {
|
||||
assertThat(publicKey.get().getOwner()).isPresent();
|
||||
assertThat(publicKey.get().getOwner().get()).contains("trillian");
|
||||
assertThat(publicKey.get().getId()).isEqualTo("42");
|
||||
assertThat(publicKey.get().getContacts()).contains("trillian", "zaphod");
|
||||
assertThat(publicKey.get().getContacts()).contains(trillian);
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
@@ -36,12 +36,12 @@ class PgpPublicKeyExtractorTest {
|
||||
|
||||
@Test
|
||||
void shouldExtractPublicKeyFromRawKey() throws IOException {
|
||||
String raw = GPGTestHelper.readResourceAsString("pubKeyEH.asc");
|
||||
String raw = GPGTestHelper.readResourceAsString("single.asc");
|
||||
|
||||
Optional<PGPPublicKey> publicKey = PgpPublicKeyExtractor.getFromRawKey(raw);
|
||||
|
||||
assertThat(publicKey).isPresent();
|
||||
assertThat(Long.toHexString(publicKey.get().getKeyID())).isEqualTo("39ad4bed55527f1c");
|
||||
assertThat(Long.toHexString(publicKey.get().getKeyID())).isEqualTo("975922f193b07d6e");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -75,7 +75,6 @@ class PublicKeyMapperTest {
|
||||
RawGpgKeyDto dto = mapper.map(key);
|
||||
|
||||
assertThat(dto.getDisplayName()).isEqualTo(key.getDisplayName());
|
||||
assertThat(dto.getRaw()).isEqualTo(key.getRaw());
|
||||
assertThat(dto.getCreated()).isEqualTo(key.getCreated());
|
||||
assertThat(dto.getLinks().getLinkBy("self").get().getHref()).isEqualTo("/v2/public_keys/1");
|
||||
assertThat(dto.getLinks().getLinkBy("delete").get().getHref()).isEqualTo("/v2/public_keys/delete/1");
|
||||
|
||||
@@ -34,7 +34,9 @@ import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.junit.jupiter.MockitoExtension;
|
||||
import sonia.scm.event.ScmEventBus;
|
||||
import sonia.scm.repository.Person;
|
||||
import sonia.scm.security.NotPublicKeyException;
|
||||
import sonia.scm.security.PublicKeyCreatedEvent;
|
||||
import sonia.scm.security.PublicKeyDeletedEvent;
|
||||
import sonia.scm.store.DataStoreFactory;
|
||||
import sonia.scm.store.InMemoryDataStoreFactory;
|
||||
@@ -103,6 +105,9 @@ class PublicKeyStoreTest {
|
||||
assertThat(key.getOwner()).isEqualTo("trillian");
|
||||
assertThat(key.getCreated()).isAfterOrEqualTo(now);
|
||||
assertThat(key.getRaw()).isEqualTo(rawKey);
|
||||
assertThat(key.getContacts()).contains(Person.toPerson("SCM Packages (signing key for packages.scm-manager.org) <scm-team@cloudogu.com>"));
|
||||
|
||||
verify(eventBus).post(any(PublicKeyCreatedEvent.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
Reference in New Issue
Block a user