mirror of
https://github.com/scm-manager/scm-manager.git
synced 2025-11-14 17:26:22 +01:00
cleanup
This commit is contained in:
@@ -44,7 +44,7 @@ public class Signature implements Serializable {
|
|||||||
private final String type;
|
private final String type;
|
||||||
private final SignatureStatus status;
|
private final SignatureStatus status;
|
||||||
private final String owner;
|
private final String owner;
|
||||||
private final Set<String> contacts;
|
private final Set<Person> contacts;
|
||||||
|
|
||||||
public Optional<String> getOwner() {
|
public Optional<String> getOwner() {
|
||||||
return Optional.ofNullable(owner);
|
return Optional.ofNullable(owner);
|
||||||
|
|||||||
@@ -55,6 +55,7 @@ import sonia.scm.repository.RepositoryPermissions;
|
|||||||
import sonia.scm.repository.spi.RepositoryServiceProvider;
|
import sonia.scm.repository.spi.RepositoryServiceProvider;
|
||||||
import sonia.scm.repository.spi.RepositoryServiceResolver;
|
import sonia.scm.repository.spi.RepositoryServiceResolver;
|
||||||
import sonia.scm.repository.work.WorkdirProvider;
|
import sonia.scm.repository.work.WorkdirProvider;
|
||||||
|
import sonia.scm.security.PublicKeyCreatedEvent;
|
||||||
import sonia.scm.security.PublicKeyDeletedEvent;
|
import sonia.scm.security.PublicKeyDeletedEvent;
|
||||||
import sonia.scm.security.ScmSecurityException;
|
import sonia.scm.security.ScmSecurityException;
|
||||||
|
|
||||||
@@ -335,6 +336,11 @@ public final class RepositoryServiceFactory {
|
|||||||
cacheManager.getCache(LogCommandBuilder.CACHE_NAME).clear();
|
cacheManager.getCache(LogCommandBuilder.CACHE_NAME).clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Subscribe
|
||||||
|
public void onEvent(PublicKeyCreatedEvent event) {
|
||||||
|
cacheManager.getCache(LogCommandBuilder.CACHE_NAME).clear();
|
||||||
|
}
|
||||||
|
|
||||||
@SuppressWarnings({"unchecked", "java:S3740", "rawtypes"})
|
@SuppressWarnings({"unchecked", "java:S3740", "rawtypes"})
|
||||||
private void clearCaches(final String repositoryId) {
|
private void clearCaches(final String repositoryId) {
|
||||||
if (logger.isDebugEnabled()) {
|
if (logger.isDebugEnabled()) {
|
||||||
|
|||||||
@@ -24,6 +24,8 @@
|
|||||||
|
|
||||||
package sonia.scm.security;
|
package sonia.scm.security;
|
||||||
|
|
||||||
|
import sonia.scm.repository.Person;
|
||||||
|
|
||||||
import java.io.ByteArrayInputStream;
|
import java.io.ByteArrayInputStream;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
@@ -62,7 +64,7 @@ public interface PublicKey {
|
|||||||
*
|
*
|
||||||
* @return owner or empty optional
|
* @return owner or empty optional
|
||||||
*/
|
*/
|
||||||
Set<String> getContacts();
|
Set<Person> getContacts();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Verifies that the signature is valid for the given data.
|
* 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`] = `
|
exports[`Storyshots Changesets Co-Authors with avatar 1`] = `
|
||||||
<div
|
<div
|
||||||
className="Changesetsstories__Wrapper-sc-122npan-0 hHKBXk box box-link-shadow"
|
className="Changesetsstories__Wrapper-sc-122npan-0 gDnzNS box box-link-shadow"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
className="ChangesetRow__Wrapper-tkpti5-0 bRWdJS"
|
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`] = `
|
exports[`Storyshots Changesets Commiter and Co-Authors with avatar 1`] = `
|
||||||
<div
|
<div
|
||||||
className="Changesetsstories__Wrapper-sc-122npan-0 hHKBXk box box-link-shadow"
|
className="Changesetsstories__Wrapper-sc-122npan-0 gDnzNS box box-link-shadow"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
className="ChangesetRow__Wrapper-tkpti5-0 bRWdJS"
|
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`] = `
|
exports[`Storyshots Changesets Default 1`] = `
|
||||||
<div
|
<div
|
||||||
className="Changesetsstories__Wrapper-sc-122npan-0 hHKBXk box box-link-shadow"
|
className="Changesetsstories__Wrapper-sc-122npan-0 gDnzNS box box-link-shadow"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
className="ChangesetRow__Wrapper-tkpti5-0 bRWdJS"
|
className="ChangesetRow__Wrapper-tkpti5-0 bRWdJS"
|
||||||
@@ -2162,7 +2162,7 @@ exports[`Storyshots Changesets Default 1`] = `
|
|||||||
|
|
||||||
exports[`Storyshots Changesets Replacements 1`] = `
|
exports[`Storyshots Changesets Replacements 1`] = `
|
||||||
<div
|
<div
|
||||||
className="Changesetsstories__Wrapper-sc-122npan-0 hHKBXk box box-link-shadow"
|
className="Changesetsstories__Wrapper-sc-122npan-0 gDnzNS box box-link-shadow"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
className="ChangesetRow__Wrapper-tkpti5-0 bRWdJS"
|
className="ChangesetRow__Wrapper-tkpti5-0 bRWdJS"
|
||||||
@@ -2289,7 +2289,7 @@ exports[`Storyshots Changesets Replacements 1`] = `
|
|||||||
|
|
||||||
exports[`Storyshots Changesets With Committer 1`] = `
|
exports[`Storyshots Changesets With Committer 1`] = `
|
||||||
<div
|
<div
|
||||||
className="Changesetsstories__Wrapper-sc-122npan-0 hHKBXk box box-link-shadow"
|
className="Changesetsstories__Wrapper-sc-122npan-0 gDnzNS box box-link-shadow"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
className="ChangesetRow__Wrapper-tkpti5-0 bRWdJS"
|
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`] = `
|
exports[`Storyshots Changesets With Committer and Co-Author 1`] = `
|
||||||
<div
|
<div
|
||||||
className="Changesetsstories__Wrapper-sc-122npan-0 hHKBXk box box-link-shadow"
|
className="Changesetsstories__Wrapper-sc-122npan-0 gDnzNS box box-link-shadow"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
className="ChangesetRow__Wrapper-tkpti5-0 bRWdJS"
|
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`] = `
|
exports[`Storyshots Changesets With avatar 1`] = `
|
||||||
<div
|
<div
|
||||||
className="Changesetsstories__Wrapper-sc-122npan-0 hHKBXk box box-link-shadow"
|
className="Changesetsstories__Wrapper-sc-122npan-0 gDnzNS box box-link-shadow"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
className="ChangesetRow__Wrapper-tkpti5-0 bRWdJS"
|
className="ChangesetRow__Wrapper-tkpti5-0 bRWdJS"
|
||||||
@@ -2684,9 +2684,138 @@ exports[`Storyshots Changesets With avatar 1`] = `
|
|||||||
</div>
|
</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`] = `
|
exports[`Storyshots Changesets With multiple Co-Authors 1`] = `
|
||||||
<div
|
<div
|
||||||
className="Changesetsstories__Wrapper-sc-122npan-0 hHKBXk box box-link-shadow"
|
className="Changesetsstories__Wrapper-sc-122npan-0 gDnzNS box box-link-shadow"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
className="ChangesetRow__Wrapper-tkpti5-0 bRWdJS"
|
className="ChangesetRow__Wrapper-tkpti5-0 bRWdJS"
|
||||||
@@ -2814,6 +2943,261 @@ exports[`Storyshots Changesets With multiple Co-Authors 1`] = `
|
|||||||
</div>
|
</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`] = `
|
exports[`Storyshots Date Date from now 1`] = `
|
||||||
<div
|
<div
|
||||||
className="Datestories__Wrapper-sc-1uda3v2-0 duRVzW"
|
className="Datestories__Wrapper-sc-1uda3v2-0 duRVzW"
|
||||||
@@ -38972,6 +39356,15 @@ exports[`Storyshots Layout|Footer Default 1`] = `
|
|||||||
profile.changePasswordNavLink
|
profile.changePasswordNavLink
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
|
<li>
|
||||||
|
<a
|
||||||
|
className=""
|
||||||
|
href="/me/settings/publicKeys"
|
||||||
|
onClick={[Function]}
|
||||||
|
>
|
||||||
|
profile.publicKeysNavLink
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
</section>
|
</section>
|
||||||
<section
|
<section
|
||||||
@@ -39088,6 +39481,15 @@ exports[`Storyshots Layout|Footer Full 1`] = `
|
|||||||
profile.changePasswordNavLink
|
profile.changePasswordNavLink
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
|
<li>
|
||||||
|
<a
|
||||||
|
className=""
|
||||||
|
href="/me/settings/publicKeys"
|
||||||
|
onClick={[Function]}
|
||||||
|
>
|
||||||
|
profile.publicKeysNavLink
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
<li>
|
<li>
|
||||||
<a
|
<a
|
||||||
className=""
|
className=""
|
||||||
@@ -39240,6 +39642,15 @@ exports[`Storyshots Layout|Footer With Avatar 1`] = `
|
|||||||
profile.changePasswordNavLink
|
profile.changePasswordNavLink
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
|
<li>
|
||||||
|
<a
|
||||||
|
className=""
|
||||||
|
href="/me/settings/publicKeys"
|
||||||
|
onClick={[Function]}
|
||||||
|
>
|
||||||
|
profile.publicKeysNavLink
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
</section>
|
</section>
|
||||||
<section
|
<section
|
||||||
@@ -39351,6 +39762,15 @@ exports[`Storyshots Layout|Footer With Plugin Links 1`] = `
|
|||||||
profile.changePasswordNavLink
|
profile.changePasswordNavLink
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
|
<li>
|
||||||
|
<a
|
||||||
|
className=""
|
||||||
|
href="/me/settings/publicKeys"
|
||||||
|
onClick={[Function]}
|
||||||
|
>
|
||||||
|
profile.publicKeysNavLink
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
<li>
|
<li>
|
||||||
<a
|
<a
|
||||||
className=""
|
className=""
|
||||||
|
|||||||
@@ -69,10 +69,11 @@ const SignatureIcon: FC<Props> = ({ signatures, className }) => {
|
|||||||
"changeset.signatureStatus"
|
"changeset.signatureStatus"
|
||||||
)}: ${status}`;
|
)}: ${status}`;
|
||||||
|
|
||||||
|
console.log(signature.contacts)
|
||||||
if (signature.contacts?.length > 0) {
|
if (signature.contacts?.length > 0) {
|
||||||
message += `\n${t("changeset.keyContacts")}:`;
|
message += `\n${t("changeset.keyContacts")}:`;
|
||||||
signature.contacts.forEach((contact) => {
|
signature.contacts.forEach((contact) => {
|
||||||
message += `\n- ${contact}`;
|
message += `\n- ${contact.name} <${contact.mail}>`;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
return message;
|
return message;
|
||||||
|
|||||||
@@ -47,7 +47,7 @@ export type Signature = {
|
|||||||
type: string;
|
type: string;
|
||||||
status: "VERIFIED" | "NOT_FOUND" | "INVALID";
|
status: "VERIFIED" | "NOT_FOUND" | "INVALID";
|
||||||
owner: string;
|
owner: string;
|
||||||
contacts: string[];
|
contacts: Person[];
|
||||||
}
|
}
|
||||||
|
|
||||||
export type Contributor = {
|
export type Contributor = {
|
||||||
|
|||||||
@@ -36,6 +36,7 @@ import org.bouncycastle.openpgp.jcajce.JcaPGPObjectFactory;
|
|||||||
import org.bouncycastle.openpgp.operator.jcajce.JcaPGPContentVerifierBuilderProvider;
|
import org.bouncycastle.openpgp.operator.jcajce.JcaPGPContentVerifierBuilderProvider;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
import sonia.scm.repository.Person;
|
||||||
import sonia.scm.security.PublicKey;
|
import sonia.scm.security.PublicKey;
|
||||||
|
|
||||||
import java.io.ByteArrayInputStream;
|
import java.io.ByteArrayInputStream;
|
||||||
@@ -52,9 +53,9 @@ public class GpgKey implements PublicKey {
|
|||||||
private final String id;
|
private final String id;
|
||||||
private final String owner;
|
private final String owner;
|
||||||
private final String raw;
|
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.id = id;
|
||||||
this.owner = owner;
|
this.owner = owner;
|
||||||
this.raw = raw;
|
this.raw = raw;
|
||||||
@@ -80,7 +81,7 @@ public class GpgKey implements PublicKey {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Set<String> getContacts() {
|
public Set<Person> getContacts() {
|
||||||
return contacts;
|
return contacts;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -51,6 +51,7 @@ public abstract class PublicKeyMapper {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Mapping(target = "attributes", ignore = true)
|
@Mapping(target = "attributes", ignore = true)
|
||||||
|
@Mapping(target = "raw", ignore = true)
|
||||||
abstract RawGpgKeyDto map(RawGpgKey rawGpgKey);
|
abstract RawGpgKeyDto map(RawGpgKey rawGpgKey);
|
||||||
|
|
||||||
@ObjectFactory
|
@ObjectFactory
|
||||||
|
|||||||
@@ -25,11 +25,11 @@
|
|||||||
package sonia.scm.security.gpg;
|
package sonia.scm.security.gpg;
|
||||||
|
|
||||||
import org.bouncycastle.openpgp.PGPPublicKey;
|
import org.bouncycastle.openpgp.PGPPublicKey;
|
||||||
import org.slf4j.Logger;
|
|
||||||
import org.slf4j.LoggerFactory;
|
|
||||||
import sonia.scm.ContextEntry;
|
import sonia.scm.ContextEntry;
|
||||||
import sonia.scm.event.ScmEventBus;
|
import sonia.scm.event.ScmEventBus;
|
||||||
|
import sonia.scm.repository.Person;
|
||||||
import sonia.scm.security.NotPublicKeyException;
|
import sonia.scm.security.NotPublicKeyException;
|
||||||
|
import sonia.scm.security.PublicKeyCreatedEvent;
|
||||||
import sonia.scm.security.PublicKeyDeletedEvent;
|
import sonia.scm.security.PublicKeyDeletedEvent;
|
||||||
import sonia.scm.store.DataStore;
|
import sonia.scm.store.DataStore;
|
||||||
import sonia.scm.store.DataStoreFactory;
|
import sonia.scm.store.DataStoreFactory;
|
||||||
@@ -38,6 +38,7 @@ import sonia.scm.user.UserPermissions;
|
|||||||
import javax.inject.Inject;
|
import javax.inject.Inject;
|
||||||
import javax.inject.Singleton;
|
import javax.inject.Singleton;
|
||||||
import java.time.Instant;
|
import java.time.Instant;
|
||||||
|
import java.util.ArrayList;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
@@ -80,22 +81,24 @@ public class PublicKeyStore {
|
|||||||
RawGpgKey key = new RawGpgKey(master, displayName, username, rawKey, getContactsFromPublicKey(rawKey), Instant.now());
|
RawGpgKey key = new RawGpgKey(master, displayName, username, rawKey, getContactsFromPublicKey(rawKey), Instant.now());
|
||||||
|
|
||||||
store.put(master, key);
|
store.put(master, key);
|
||||||
|
eventBus.post(new PublicKeyCreatedEvent());
|
||||||
|
|
||||||
return key;
|
return key;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private Set<String> getContactsFromPublicKey(String rawKey) {
|
private Set<Person> getContactsFromPublicKey(String rawKey) {
|
||||||
Set<String> contacts = new HashSet<>();
|
List<String> userIds = new ArrayList<>();
|
||||||
Optional<PGPPublicKey> publicKeyFromRawKey = getFromRawKey(rawKey);
|
Optional<PGPPublicKey> publicKeyFromRawKey = getFromRawKey(rawKey);
|
||||||
publicKeyFromRawKey.ifPresent(pgpPublicKey -> pgpPublicKey.getUserIDs().forEachRemaining(contacts::add));
|
publicKeyFromRawKey.ifPresent(pgpPublicKey -> pgpPublicKey.getUserIDs().forEachRemaining(userIds::add));
|
||||||
return contacts;
|
|
||||||
|
return userIds.stream().map(Person::toPerson).collect(Collectors.toSet());
|
||||||
}
|
}
|
||||||
|
|
||||||
public void delete(String id) {
|
public void delete(String id) {
|
||||||
RawGpgKey rawGpgKey = store.get(id);
|
RawGpgKey rawGpgKey = store.get(id);
|
||||||
if (rawGpgKey != null) {
|
if (rawGpgKey != null) {
|
||||||
UserPermissions.modify(rawGpgKey.getOwner()).check();
|
UserPermissions.changePublicKeys(rawGpgKey.getOwner()).check();
|
||||||
store.remove(id);
|
store.remove(id);
|
||||||
eventBus.post(new PublicKeyDeletedEvent());
|
eventBus.post(new PublicKeyDeletedEvent());
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -28,6 +28,7 @@ import lombok.AllArgsConstructor;
|
|||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
import lombok.NoArgsConstructor;
|
import lombok.NoArgsConstructor;
|
||||||
import lombok.Setter;
|
import lombok.Setter;
|
||||||
|
import sonia.scm.repository.Person;
|
||||||
import sonia.scm.xml.XmlInstantAdapter;
|
import sonia.scm.xml.XmlInstantAdapter;
|
||||||
|
|
||||||
import javax.xml.bind.annotation.XmlAccessType;
|
import javax.xml.bind.annotation.XmlAccessType;
|
||||||
@@ -49,7 +50,7 @@ public class RawGpgKey {
|
|||||||
private String displayName;
|
private String displayName;
|
||||||
private String owner;
|
private String owner;
|
||||||
private String raw;
|
private String raw;
|
||||||
private Set<String> contacts;
|
private Set<Person> contacts;
|
||||||
|
|
||||||
@XmlJavaTypeAdapter(XmlInstantAdapter.class)
|
@XmlJavaTypeAdapter(XmlInstantAdapter.class)
|
||||||
private Instant created;
|
private Instant created;
|
||||||
|
|||||||
@@ -31,6 +31,7 @@ import org.junit.jupiter.api.extension.ExtendWith;
|
|||||||
import org.mockito.InjectMocks;
|
import org.mockito.InjectMocks;
|
||||||
import org.mockito.Mock;
|
import org.mockito.Mock;
|
||||||
import org.mockito.junit.jupiter.MockitoExtension;
|
import org.mockito.junit.jupiter.MockitoExtension;
|
||||||
|
import sonia.scm.repository.Person;
|
||||||
import sonia.scm.security.PublicKey;
|
import sonia.scm.security.PublicKey;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
@@ -52,16 +53,17 @@ class DefaultGPGTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
void shouldFindIdInSignature() throws IOException {
|
void shouldFindIdInSignature() throws IOException {
|
||||||
String raw = GPGTestHelper.readResourceAsString("signature.asc");
|
String raw = GPGTestHelper.readResourceAsString("slarti.txt.asc");
|
||||||
String publicKeyId = gpg.findPublicKeyId(raw.getBytes());
|
String publicKeyId = gpg.findPublicKeyId(raw.getBytes());
|
||||||
|
|
||||||
assertThat(publicKeyId).isEqualTo("0x1F17B79A09DAD5B9");
|
assertThat(publicKeyId).isEqualTo("0x247E908C6FD35473");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void shouldFindPublicKey() throws IOException {
|
void shouldFindPublicKey() throws IOException {
|
||||||
String raw = GPGTestHelper.readResourceAsString("subkeys.asc");
|
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));
|
when(store.findById("42")).thenReturn(Optional.of(key1));
|
||||||
|
|
||||||
@@ -71,7 +73,7 @@ class DefaultGPGTest {
|
|||||||
assertThat(publicKey.get().getOwner()).isPresent();
|
assertThat(publicKey.get().getOwner()).isPresent();
|
||||||
assertThat(publicKey.get().getOwner().get()).contains("trillian");
|
assertThat(publicKey.get().getOwner().get()).contains("trillian");
|
||||||
assertThat(publicKey.get().getId()).isEqualTo("42");
|
assertThat(publicKey.get().getId()).isEqualTo("42");
|
||||||
assertThat(publicKey.get().getContacts()).contains("trillian", "zaphod");
|
assertThat(publicKey.get().getContacts()).contains(trillian);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|||||||
@@ -36,12 +36,12 @@ class PgpPublicKeyExtractorTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
void shouldExtractPublicKeyFromRawKey() throws IOException {
|
void shouldExtractPublicKeyFromRawKey() throws IOException {
|
||||||
String raw = GPGTestHelper.readResourceAsString("pubKeyEH.asc");
|
String raw = GPGTestHelper.readResourceAsString("single.asc");
|
||||||
|
|
||||||
Optional<PGPPublicKey> publicKey = PgpPublicKeyExtractor.getFromRawKey(raw);
|
Optional<PGPPublicKey> publicKey = PgpPublicKeyExtractor.getFromRawKey(raw);
|
||||||
|
|
||||||
assertThat(publicKey).isPresent();
|
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);
|
RawGpgKeyDto dto = mapper.map(key);
|
||||||
|
|
||||||
assertThat(dto.getDisplayName()).isEqualTo(key.getDisplayName());
|
assertThat(dto.getDisplayName()).isEqualTo(key.getDisplayName());
|
||||||
assertThat(dto.getRaw()).isEqualTo(key.getRaw());
|
|
||||||
assertThat(dto.getCreated()).isEqualTo(key.getCreated());
|
assertThat(dto.getCreated()).isEqualTo(key.getCreated());
|
||||||
assertThat(dto.getLinks().getLinkBy("self").get().getHref()).isEqualTo("/v2/public_keys/1");
|
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");
|
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.Mock;
|
||||||
import org.mockito.junit.jupiter.MockitoExtension;
|
import org.mockito.junit.jupiter.MockitoExtension;
|
||||||
import sonia.scm.event.ScmEventBus;
|
import sonia.scm.event.ScmEventBus;
|
||||||
|
import sonia.scm.repository.Person;
|
||||||
import sonia.scm.security.NotPublicKeyException;
|
import sonia.scm.security.NotPublicKeyException;
|
||||||
|
import sonia.scm.security.PublicKeyCreatedEvent;
|
||||||
import sonia.scm.security.PublicKeyDeletedEvent;
|
import sonia.scm.security.PublicKeyDeletedEvent;
|
||||||
import sonia.scm.store.DataStoreFactory;
|
import sonia.scm.store.DataStoreFactory;
|
||||||
import sonia.scm.store.InMemoryDataStoreFactory;
|
import sonia.scm.store.InMemoryDataStoreFactory;
|
||||||
@@ -103,6 +105,9 @@ class PublicKeyStoreTest {
|
|||||||
assertThat(key.getOwner()).isEqualTo("trillian");
|
assertThat(key.getOwner()).isEqualTo("trillian");
|
||||||
assertThat(key.getCreated()).isAfterOrEqualTo(now);
|
assertThat(key.getCreated()).isAfterOrEqualTo(now);
|
||||||
assertThat(key.getRaw()).isEqualTo(rawKey);
|
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
|
@Test
|
||||||
|
|||||||
Reference in New Issue
Block a user