From 4290ca4077839da29995979dcedac18a4a879e71 Mon Sep 17 00:00:00 2001 From: Eduard Heimbuch Date: Fri, 24 Jul 2020 14:59:28 +0200 Subject: [PATCH] add config form for public keys --- .../scm/security/NotPublicKeyException.java | 45 +++++ .../src/main/java/sonia/scm/user/User.java | 2 +- scm-ui/ui-webapp/public/locales/de/users.json | 11 +- scm-ui/ui-webapp/public/locales/en/users.json | 11 +- scm-ui/ui-webapp/src/containers/Profile.tsx | 4 + .../navLinks/SetPublicKeysNavLink.tsx | 43 ++++ .../src/users/components/navLinks/index.ts | 1 + .../components/publicKeys/AddPublicKey.tsx | 89 ++++++++ .../components/publicKeys/PublicKeyEntry.tsx | 61 ++++++ .../components/publicKeys/PublicKeyTable.tsx | 62 ++++++ .../components/publicKeys/SetPublicKeys.tsx | 94 +++++++++ .../publicKeys/formatPublicKey.test.ts | 51 +++++ .../components/publicKeys/formatPublicKey.ts | 43 ++++ .../src/users/containers/SingleUser.tsx | 9 +- .../scm/api/v2/resources/MapperModule.java | 2 + .../scm/api/v2/resources/MeDtoFactory.java | 3 + .../scm/api/v2/resources/ResourceLinks.java | 7 + .../api/v2/resources/UserToUserDtoMapper.java | 1 + .../gpg/PublicKeyCollectionMapper.java | 86 ++++++++ .../scm/security/gpg/PublicKeyMapper.java | 79 ++++++++ .../scm/security/gpg/PublicKeyResource.java | 191 ++++++++++++++++++ .../scm/security/gpg/PublicKeyStore.java | 43 ++-- .../sonia/scm/security/gpg/RawGpgKey.java | 2 + .../sonia/scm/security/gpg/RawGpgKeyDto.java | 48 +++++ .../api/v2/resources/MeDtoFactoryTest.java | 22 ++ .../gpg/PublicKeyCollectionMapperTest.java | 110 ++++++++++ .../scm/security/gpg/PublicKeyMapperTest.java | 92 +++++++++ .../security/gpg/PublicKeyResourceTest.java | 142 +++++++++++++ .../scm/security/gpg/PublicKeyStoreTest.java | 85 +++++++- 29 files changed, 1416 insertions(+), 23 deletions(-) create mode 100644 scm-core/src/main/java/sonia/scm/security/NotPublicKeyException.java create mode 100644 scm-ui/ui-webapp/src/users/components/navLinks/SetPublicKeysNavLink.tsx create mode 100644 scm-ui/ui-webapp/src/users/components/publicKeys/AddPublicKey.tsx create mode 100644 scm-ui/ui-webapp/src/users/components/publicKeys/PublicKeyEntry.tsx create mode 100644 scm-ui/ui-webapp/src/users/components/publicKeys/PublicKeyTable.tsx create mode 100644 scm-ui/ui-webapp/src/users/components/publicKeys/SetPublicKeys.tsx create mode 100644 scm-ui/ui-webapp/src/users/components/publicKeys/formatPublicKey.test.ts create mode 100644 scm-ui/ui-webapp/src/users/components/publicKeys/formatPublicKey.ts create mode 100644 scm-webapp/src/main/java/sonia/scm/security/gpg/PublicKeyCollectionMapper.java create mode 100644 scm-webapp/src/main/java/sonia/scm/security/gpg/PublicKeyMapper.java create mode 100644 scm-webapp/src/main/java/sonia/scm/security/gpg/PublicKeyResource.java create mode 100644 scm-webapp/src/main/java/sonia/scm/security/gpg/RawGpgKeyDto.java create mode 100644 scm-webapp/src/test/java/sonia/scm/security/gpg/PublicKeyCollectionMapperTest.java create mode 100644 scm-webapp/src/test/java/sonia/scm/security/gpg/PublicKeyMapperTest.java create mode 100644 scm-webapp/src/test/java/sonia/scm/security/gpg/PublicKeyResourceTest.java diff --git a/scm-core/src/main/java/sonia/scm/security/NotPublicKeyException.java b/scm-core/src/main/java/sonia/scm/security/NotPublicKeyException.java new file mode 100644 index 0000000000..2433b31429 --- /dev/null +++ b/scm-core/src/main/java/sonia/scm/security/NotPublicKeyException.java @@ -0,0 +1,45 @@ +/* + * 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.BadRequestException; +import sonia.scm.ContextEntry; + +import java.util.List; + +public class NotPublicKeyException extends BadRequestException { + public NotPublicKeyException(List context, String message) { + super(context, message); + } + + public NotPublicKeyException(List context, String message, Exception cause) { + super(context, message, cause); + } + + @Override + public String getCode() { + return "BxS5wX2v71"; + } +} diff --git a/scm-core/src/main/java/sonia/scm/user/User.java b/scm-core/src/main/java/sonia/scm/user/User.java index cd2afb282d..1b0a3b2388 100644 --- a/scm-core/src/main/java/sonia/scm/user/User.java +++ b/scm-core/src/main/java/sonia/scm/user/User.java @@ -50,7 +50,7 @@ import java.security.Principal; @StaticPermissions( value = "user", globalPermissions = {"create", "list", "autocomplete"}, - permissions = {"read", "modify", "delete", "changePassword"}, + permissions = {"read", "modify", "delete", "changePassword", "changePublicKeys"}, custom = true, customGlobal = true ) @XmlRootElement(name = "users") diff --git a/scm-ui/ui-webapp/public/locales/de/users.json b/scm-ui/ui-webapp/public/locales/de/users.json index fd744e768d..8cb79d6d6f 100644 --- a/scm-ui/ui-webapp/public/locales/de/users.json +++ b/scm-ui/ui-webapp/public/locales/de/users.json @@ -37,7 +37,8 @@ "settingsNavLink": "Einstellungen", "generalNavLink": "Generell", "setPasswordNavLink": "Passwort", - "setPermissionsNavLink": "Berechtigungen" + "setPermissionsNavLink": "Berechtigungen", + "setPublicKeyNavLink": "Öffentliche Schlüssel" } }, "createUser": { @@ -60,5 +61,13 @@ "userForm": { "subtitle": "Benutzer bearbeiten", "button": "Speichern" + }, + "publicKey": { + "noStoredKeys": "Es wurden keine Schlüssel gefunden.", + "displayName": "Anzeigename", + "raw": "Schlüssel", + "created": "Eingetragen an", + "addKey": "Schlüssel hinzufügen", + "delete": "Löschen" } } diff --git a/scm-ui/ui-webapp/public/locales/en/users.json b/scm-ui/ui-webapp/public/locales/en/users.json index 3353977a18..0db2b9175f 100644 --- a/scm-ui/ui-webapp/public/locales/en/users.json +++ b/scm-ui/ui-webapp/public/locales/en/users.json @@ -37,7 +37,8 @@ "settingsNavLink": "Settings", "generalNavLink": "General", "setPasswordNavLink": "Password", - "setPermissionsNavLink": "Permissions" + "setPermissionsNavLink": "Permissions", + "setPublicKeyNavLink": "Public Keys" } }, "createUser": { @@ -60,5 +61,13 @@ "userForm": { "subtitle": "Edit User", "button": "Submit" + }, + "publicKey": { + "noStoredKeys": "No keys found.", + "displayName": "Display Name", + "raw": "Key", + "created": "Created on", + "addKey": "Add key", + "delete": "Delete" } } diff --git a/scm-ui/ui-webapp/src/containers/Profile.tsx b/scm-ui/ui-webapp/src/containers/Profile.tsx index 8f14bed1e7..e0700306f8 100644 --- a/scm-ui/ui-webapp/src/containers/Profile.tsx +++ b/scm-ui/ui-webapp/src/containers/Profile.tsx @@ -42,6 +42,8 @@ import { import ChangeUserPassword from "./ChangeUserPassword"; import ProfileInfo from "./ProfileInfo"; import { ExtensionPoint } from "@scm-manager/ui-extensions"; +import SetPublicKeys from "../users/components/publicKeys/SetPublicKeys"; +import SetPublicKeyNavLink from "../users/components/navLinks/SetPublicKeysNavLink"; type Props = RouteComponentProps & WithTranslation & { @@ -93,6 +95,7 @@ class Profile extends React.Component { } /> } /> + } /> @@ -109,6 +112,7 @@ class Profile extends React.Component { title={t("profile.settingsNavLink")} > + diff --git a/scm-ui/ui-webapp/src/users/components/navLinks/SetPublicKeysNavLink.tsx b/scm-ui/ui-webapp/src/users/components/navLinks/SetPublicKeysNavLink.tsx new file mode 100644 index 0000000000..f3144442f5 --- /dev/null +++ b/scm-ui/ui-webapp/src/users/components/navLinks/SetPublicKeysNavLink.tsx @@ -0,0 +1,43 @@ +/* + * 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. + */ +import React, { FC } from "react"; +import { Link, User, Me } from "@scm-manager/ui-types"; +import { NavLink } from "@scm-manager/ui-components"; +import { useTranslation } from "react-i18next"; + +type Props = { + user: User | Me; + publicKeyUrl: string; +}; + +const SetPublicKeyNavLink: FC = ({ user, publicKeyUrl }) => { + const [t] = useTranslation("users"); + + if ((user?._links?.publicKeys as Link)?.href) { + return ; + } + return null; +}; + +export default SetPublicKeyNavLink; diff --git a/scm-ui/ui-webapp/src/users/components/navLinks/index.ts b/scm-ui/ui-webapp/src/users/components/navLinks/index.ts index 0ccd16b42a..f732ea83ee 100644 --- a/scm-ui/ui-webapp/src/users/components/navLinks/index.ts +++ b/scm-ui/ui-webapp/src/users/components/navLinks/index.ts @@ -25,3 +25,4 @@ export { default as EditUserNavLink } from "./EditUserNavLink"; export { default as SetPasswordNavLink } from "./SetPasswordNavLink"; export { default as SetPermissionsNavLink } from "./SetPermissionsNavLink"; +export { default as SetPublicKeysNavLink } from "./SetPublicKeysNavLink"; diff --git a/scm-ui/ui-webapp/src/users/components/publicKeys/AddPublicKey.tsx b/scm-ui/ui-webapp/src/users/components/publicKeys/AddPublicKey.tsx new file mode 100644 index 0000000000..e0129e8245 --- /dev/null +++ b/scm-ui/ui-webapp/src/users/components/publicKeys/AddPublicKey.tsx @@ -0,0 +1,89 @@ +/* + * 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. + */ + +import React, { FC, useState } from "react"; +import { User, Link, Links, Collection } from "@scm-manager/ui-types/src"; +import { + ErrorNotification, + InputField, + Level, + Textarea, + SubmitButton, + apiClient, + Loading +} from "@scm-manager/ui-components"; +import { useTranslation } from "react-i18next"; +import { CONTENT_TYPE_PUBLIC_KEY } from "./SetPublicKeys"; + +type Props = { + createLink: string; + refresh: () => void; +}; + +const AddPublicKey: FC = ({ createLink, refresh }) => { + const [t] = useTranslation("users"); + const [loading, setLoading] = useState(false); + const [error, setError] = useState(); + const [displayName, setDisplayName] = useState(""); + const [raw, setRaw] = useState(""); + + const isValid = () => { + return !!displayName && !!raw; + }; + + const resetForm = () => { + setDisplayName(""); + setRaw(""); + }; + + const addKey = () => { + setLoading(true); + apiClient + .post(createLink, { displayName: displayName, raw: raw }, CONTENT_TYPE_PUBLIC_KEY) + .then(resetForm) + .then(refresh) + .then(() => setLoading(false)) + .catch(setError); + }; + + if (error) { + return ; + } + + if (loading) { + return ; + } + + return ( + <> + +