diff --git a/CHANGELOG.md b/CHANGELOG.md index d36750c360..c858d212ce 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Align actionbar item horizontal and enforce correct margin between them ([#1358](https://github.com/scm-manager/scm-manager/pull/1358)) ## [2.6.1] - 2020-09-30 +### Added +- Users can create API keys with limited permissions ([#1359](https://github.com/scm-manager/scm-manager/pull/1359)) + ### Fixed - Not found error when using browse command in empty hg repository ([#1355](https://github.com/scm-manager/scm-manager/pull/1355)) @@ -23,6 +26,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Permissions can be specified for namespaces ([#1335](https://github.com/scm-manager/scm-manager/pull/1335)) - Show update info on admin information page ([#1342](https://github.com/scm-manager/scm-manager/pull/1342)) +### Changed +- Rework modal to use react portal ([#1349](https://github.com/scm-manager/scm-manager/pull/1349)) + ### Fixed - Missing synchronization during repository creation ([#1328](https://github.com/scm-manager/scm-manager/pull/1328)) - Missing BranchCreatedEvent for mercurial ([#1334](https://github.com/scm-manager/scm-manager/pull/1334)) diff --git a/docs/de/navigation.yml b/docs/de/navigation.yml index bf2e185da2..0e25b8ec53 100644 --- a/docs/de/navigation.yml +++ b/docs/de/navigation.yml @@ -4,3 +4,4 @@ - /user/user/ - /user/group/ - /user/admin/ + - /user/profile/ diff --git a/docs/de/user/profile/assets/api-key-created.png b/docs/de/user/profile/assets/api-key-created.png new file mode 100644 index 0000000000..1bc4b0cc17 Binary files /dev/null and b/docs/de/user/profile/assets/api-key-created.png differ diff --git a/docs/de/user/profile/assets/api-key-overview.png b/docs/de/user/profile/assets/api-key-overview.png new file mode 100644 index 0000000000..d150b8fb1b Binary files /dev/null and b/docs/de/user/profile/assets/api-key-overview.png differ diff --git a/docs/de/user/profile/index.md b/docs/de/user/profile/index.md new file mode 100644 index 0000000000..6e399bfa13 --- /dev/null +++ b/docs/de/user/profile/index.md @@ -0,0 +1,50 @@ +--- +title: Profil +partiallyActive: true +--- + +Über den Link zum Profil im Footer können Einstellungen zum eigenen Konto vorgenommen werden. + +## Passwort ändern + +Hier kann das Passwort für das Konto geändert werden, wenn es sich um ein lokales Konto handelt (wenn die Anmeldung +also nicht über ein Fremdsystem erfolgt). Um die Änderung zu autorisieren, muss zunächst das aktuelle Passwort +eingegeben werden. Danach muss das neue Passwort zweimal eingegeben werden. + +## Öffentliche Schlüssel + +Zum Prüfen von Signaturen für z. B. Commits können hier die entsprechenden öffentlichen Schlüssel hinterlegt werden. +Zudem können hier die vom SCM-Manager erstellten Signaturschlüssel heruntergeladen werden. + +## API Schlüssel + +Zur Nutzung in anderen Systemen wie z. B. CI Systemen können sogenannte API Schlüssel erstellt werden. Sie können für +den Zugriff auf Repositories über die REST API sowie über SCM-Clients genutzt werden. Dazu wird ein Anzeigename sowie +eine [Rolle](../admin/roles/) ausgewählt. Der Anzeigename ist ausschließlich zur Verwaltung gedacht und hat keine +weitere technische Bewandnis. Mithilfe der Rolle können die Berechtigungen eingeschränkt werden, die bei einer Anmeldung +zur Verfügung stehen. + +Hat z. B. ein Konto schreibende Rechte für ein Repository und wird ein API-Schlüssel mit der Rolle "READ" erzeugt, so +kann über diesen Schlüssel nur lesend auf das Repository zugegriffen werden. Eine Ausweitung der Rechte hingegen ist +selbstverständlich nicht möglich. Es kann also mithilfe eines API-Schlüssels mit der Rolle "WRITE" nicht schreibend auf +ein Repository zugegriffen werden, für das bei dem Konto nur ein lesender Zugriff gestattet ist. + +![API Key Overview](assets/api-key-overview.png) + +Nach der Erstellung eines Schlüssels, wird dieser **einmalig** angezeigt. Nachdem dieses Fenster +geschlossen wurde, kann der Schlüssel nicht mehr abgerufen und nicht wiederhergestellt werden. + +![API Key Created](assets/api-key-created.png) + +### Beispiel REST API + +Um einen Schlüssel mit der REST API zu nutzen, muss der Schlüssel als Cookie mit dem Namen „X-Bearer-Token“ +übergeben werden. Für die Nutzung mit curl sieht ein Aufruf z. B. wie folgt aus: + +``` +curl -v localhost:8081/scm/api/v2/repositories/ -H "Cookie: X-Bearer-Token=eyJhcGlLZXlJZCI...RTRHeCJ9" +``` + +### Zugriff mit SCM-Client + +Für einen Zugriff mit einem SCM-Client (z. B. `git`, `hg` oder `svn`) muss der Schlüssel als Passwort übergeben werden. diff --git a/docs/en/navigation.yml b/docs/en/navigation.yml index 3d5b6518a8..89a8367a9f 100644 --- a/docs/en/navigation.yml +++ b/docs/en/navigation.yml @@ -12,6 +12,7 @@ - /user/user/ - /user/group/ - /user/admin/ + - /user/profile/ - section: Administration entries: diff --git a/docs/en/user/profile/assets/api-key-created.png b/docs/en/user/profile/assets/api-key-created.png new file mode 100644 index 0000000000..1bc4b0cc17 Binary files /dev/null and b/docs/en/user/profile/assets/api-key-created.png differ diff --git a/docs/en/user/profile/assets/api-key-overview.png b/docs/en/user/profile/assets/api-key-overview.png new file mode 100644 index 0000000000..d150b8fb1b Binary files /dev/null and b/docs/en/user/profile/assets/api-key-overview.png differ diff --git a/docs/en/user/profile/index.md b/docs/en/user/profile/index.md new file mode 100644 index 0000000000..50746c2f56 --- /dev/null +++ b/docs/en/user/profile/index.md @@ -0,0 +1,49 @@ +--- +title: Profile +partiallyActive: true +--- + +Settings for the active user account can be managed with the link "Profile" in the footer. + +## Change password + +Here the password for the current account can be changed when it is a local account (when the login is not managed by an +external system). To authorize the change, the current password has to be put first. Then the new password has to be +entered twice. + +## Öffentliche Schlüssel + +To check signatures for example for commits, public keys can be stored here. Additionally the keys created by +SCM-Manager can be accessed here, too. + +## API keys + +To access SCM-Manager from other systems like for example CI servers, API keys can be created. They can be used to call +the REST APi and for the access with SCM clients. To create a key you have to specify a display name and a +[role](../admin/roles/). The display name is solely to keep track of your keys. The role limits the permissions granted +when the SCM-Manager is accessed with such a key. + +If, for exapmple, an account has write access for a repository and an API key with the role "READ" is created for this +account, this repository can only be accessed read only using this key. Of course it is not possible to extend +permissions. So you cannot create an API key with the role "WRITE" to get write access to a repository, where the +original account has only read access for. + +![API Key Overview](assets/api-key-overview.png) + +After the creation of a key, it will be displayed **once only**. After the window has been closed, the key cannot be +retrieved or reconstructed again. + +![API Key Created](assets/api-key-created.png) + +### Example for the REST API + +To use an API key for the REST API, the key has to sent as a cookie with the name “X-Bearer-Token”. Using curl, this +can be done like this: + +``` +curl -v localhost:8081/scm/api/v2/repositories/ -H "Cookie: X-Bearer-Token=eyJhcGlLZXlJZCI...RTRHeCJ9" +``` + +### Access with an SCM-Client + +For access with an SCM client like `git`, `hg`, or `svn` the key simply has to be passed as a password. diff --git a/pom.xml b/pom.xml index 98445bbf98..c3bd92bfe4 100644 --- a/pom.xml +++ b/pom.xml @@ -905,7 +905,7 @@ 3.5.10 2.1 - 5.6.2 + 5.7.0 1.7.30 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 1b0a3b2388..3379041100 100644 --- a/scm-core/src/main/java/sonia/scm/user/User.java +++ b/scm-core/src/main/java/sonia/scm/user/User.java @@ -21,7 +21,7 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ - + package sonia.scm.user; //~--- non-JDK imports -------------------------------------------------------- @@ -50,7 +50,7 @@ import java.security.Principal; @StaticPermissions( value = "user", globalPermissions = {"create", "list", "autocomplete"}, - permissions = {"read", "modify", "delete", "changePassword", "changePublicKeys"}, + permissions = {"read", "modify", "delete", "changePassword", "changePublicKeys", "changeApiKeys"}, custom = true, customGlobal = true ) @XmlRootElement(name = "users") diff --git a/scm-core/src/main/java/sonia/scm/web/VndMediaType.java b/scm-core/src/main/java/sonia/scm/web/VndMediaType.java index 6112d233f4..b3dbcb5b6e 100644 --- a/scm-core/src/main/java/sonia/scm/web/VndMediaType.java +++ b/scm-core/src/main/java/sonia/scm/web/VndMediaType.java @@ -85,6 +85,9 @@ public class VndMediaType { public static final String REPOSITORY_ROLE = PREFIX + "repositoryRole" + SUFFIX; public static final String REPOSITORY_ROLE_COLLECTION = PREFIX + "repositoryRoleCollection" + SUFFIX; + public static final String API_KEY = PREFIX + "apiKey" + SUFFIX; + public static final String API_KEY_COLLECTION = PREFIX + "apiKeyCollection" + SUFFIX; + private VndMediaType() { } diff --git a/scm-it/src/test/java/sonia/scm/it/ApiKeyITCase.java b/scm-it/src/test/java/sonia/scm/it/ApiKeyITCase.java new file mode 100644 index 0000000000..b9f75a9dec --- /dev/null +++ b/scm-it/src/test/java/sonia/scm/it/ApiKeyITCase.java @@ -0,0 +1,115 @@ +/* + * 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.it; + +import io.restassured.RestAssured; +import org.junit.After; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.TemporaryFolder; +import sonia.scm.it.utils.RepositoryUtil; +import sonia.scm.it.utils.RestUtil; +import sonia.scm.it.utils.TestData; +import sonia.scm.repository.client.api.RepositoryClient; +import sonia.scm.repository.client.api.RepositoryClientException; +import sonia.scm.web.VndMediaType; + +import javax.ws.rs.core.MediaType; +import java.io.IOException; +import java.util.Objects; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertThrows; +import static sonia.scm.it.utils.RepositoryUtil.addAndCommitRandomFile; +import static sonia.scm.it.utils.RestUtil.given; +import static sonia.scm.it.utils.TestData.WRITE; + +public class ApiKeyITCase { + + @Rule + public TemporaryFolder temporaryFolder = new TemporaryFolder(); + + @Before + public void prepareEnvironment() { + TestData.createDefault(); + TestData.createNotAdminUser("user", "user"); + TestData.createUserPermission("user", WRITE, "git"); + } + + @After + public void cleanup() { + TestData.cleanup(); + } + + @Test + public void shouldCloneWithRestrictedApiKey() throws IOException { + String passphrase = registerApiKey(); + + RepositoryClient client = RepositoryUtil.createRepositoryClient("git", temporaryFolder.newFolder(), "user", passphrase); + + assertEquals(1, Objects.requireNonNull(client.getWorkingCopy().list()).length); + } + + @Test + public void shouldFailToCommit() throws IOException { + String passphrase = registerApiKey(); + + RepositoryClient client = RepositoryUtil.createRepositoryClient("git", temporaryFolder.newFolder(), "user", passphrase); + + assertThrows(RepositoryClientException.class, () -> addAndCommitRandomFile(client, "user")); + } + + + public String registerApiKey() { + String apiKeysUrl = given(VndMediaType.ME, "user", "user") + .when() + .get(RestUtil.createResourceUrl("me/")) + .then() + .statusCode(200) + .extract() + .body().jsonPath().getString("_links.apiKeys.href"); + String createUrl = given(VndMediaType.API_KEY_COLLECTION) + .when() + .get(apiKeysUrl) + .then() + .statusCode(200) + .extract() + .body().jsonPath().getString("_links.create.href"); + String passphrase = new String(RestAssured.given() + .contentType(VndMediaType.API_KEY) + .accept(MediaType.TEXT_PLAIN) + .auth().preemptive().basic("user", "user") + .when() + .body("{\"displayName\":\"integration test\",\"permissionRole\":\"READ\"}") + .post(createUrl) + .then() + .statusCode(201) + .extract() + .body() + .asByteArray()); + return passphrase; + } +} diff --git a/scm-ui/ui-components/src/__snapshots__/storyshots.test.ts.snap b/scm-ui/ui-components/src/__snapshots__/storyshots.test.ts.snap index a6e523b5b3..eec24b0966 100644 --- a/scm-ui/ui-components/src/__snapshots__/storyshots.test.ts.snap +++ b/scm-ui/ui-components/src/__snapshots__/storyshots.test.ts.snap @@ -47242,575 +47242,30 @@ exports[`Storyshots MarkdownView Xml Code Block 1`] = ` `; -exports[`Storyshots Modal|ConfirmAlert Default 1`] = ` -
-
-
-
-

- Are you sure about that? -

-
-
- Mind-paralyzing change needed improbability vortex machine sorts sought same theory upending job just allows - hostess’s really oblong Infinite Improbability thing into the starship against which behavior accordance with - Kakrafoon humanoid undergarment ship powered by GPP-guided bowl of petunias nothing was frequently away incredibly - ordinary mob. -
- -
-
+ Open ConfirmAlert + , +
, +] `; -exports[`Storyshots Modal|Modal Closeable 1`] = ` -
-
-
-
-

- Hitchhiker Modal -

-
-
-

- Mind-paralyzing change needed improbability vortex machine sorts sought same theory upending job just allows - hostess’s really oblong Infinite Improbability thing into the starship against which behavior accordance.with - Kakrafoon humanoid undergarment ship powered by GPP-guided bowl of petunias nothing was frequently away incredibly - ordinary mob. -

-
-
-
-`; +exports[`Storyshots Modal|Modal Closeable 1`] = `null`; -exports[`Storyshots Modal|Modal Default 1`] = ` -
-
-
-
-

- Hitchhiker Modal -

-
-
-

- Mind-paralyzing change needed improbability vortex machine sorts sought same theory upending job just allows - hostess’s really oblong Infinite Improbability thing into the starship against which behavior accordance.with - Kakrafoon humanoid undergarment ship powered by GPP-guided bowl of petunias nothing was frequently away incredibly - ordinary mob. -

-
-
-
-`; +exports[`Storyshots Modal|Modal Default 1`] = `null`; -exports[`Storyshots Modal|Modal Long content 1`] = ` -
-
-
-
-

- Hitchhiker Modal -

-
-
-

- Marvin -

-

- The Paranoid Android -

-
-
- - The following content comes from the awesome - - - Hitchhikers Wiki - -
-
-
- Marvin -
-
-

- Marvin, more fully known as Marvin the Paranoid Android, is an incredibly brilliant but overwhelmingly depressed robot manufactured by the Sirius Cybernetics Corporation and unwilling servant to the crew of the Heart of Gold. -

-
-
-

- Physical Appearance -

-

- In the novels, Marvin is described thusly: "...though it was beautifully constructed and polished it looked somehow as if the various parts of its more or less humanoid body didn't quite fit properly. In fact, they fit perfectly well, but something in its bearing suggested that they might have fitted better." -

-

- On the radio show, there's no physical description of Marvin, though his voice is digitally altered to sound more robotic, and any scene that focuses on him is accompanied by sounds of mechanical clanking and hissing. -

-

- In the TV series, Marvin is built in the style of a 1950's robot similar to Robbie the Robot from Forbidden Planet or Twiki from Buck Rogers. His body is blocky and angular, with a pair of clamp-claw hands, shuffling feet and a squarish head with a dour face. -

-

- In the movie, Marvin is a short, stout robot built of smooth, white plastic. His arms are much longer than his legs, and his head is a massive sphere with only a pair of triangle eyes for a face. His large head and simian-like proportions give Marvin a perpetual slouch, adding to his melancholy personality. At the start of the film his eyes glow, but at the end he is shot but unharmed, leaving a hole in his head and dimming his eyes. This is probably the most depressing and unacceptable manifestation of Marvin ever conceived, and thus paradoxically the most accurate. -

-
-
-
-

- Personality -

-

- Marvin the robot has a prototype version of the Genuine People Personality (GPP) software from SCC, allowing him sentience and the ability to feel emotions and develop a personality. He's also incredibly smart, having a "brain the size of a planet" capable of computing extremely complex mathematics, as well as solving difficult problems and operating high-tech devices. -

-

- However, despite being so smart, Marvin is typically made to perform menial tasks and labour such as escorting people, opening doors, picking up pieces of paper, and other tasks well beneath his skills. Even extremely hard tasks, such as computing for the vast Krikkit robot army, are trivial for Marvin. All this leaves him extremely bored, frustrated, and overwhelmingly depressed. Because of this, all modern GPP-capable machines, such as Eddie the computer and the Heart of Gold's automatic doors, are programmed to be extremely cheerful and happy, much to Marvin's disgust. -

-

- Marvin hates everyone and everything he comes into contact with, having no respect for anybody and will criticise and insult others at any opportunity, or otherwise rant and complain for hours on end about his own problems, such as the terrible pain he suffers in all the diodes down his left side. His contempt for everyone is often justified, as almost every person he comes across, even those who consider him a friend, (such as Arthur and Trillian, who treat him more kindly than Ford and Zaphod) treat Marvin as an expendable servant, even sending him to his death more than once (such as when Zaphod ordered Marvin to fight the gigantic, heavy-duty Frogstar Scout Robot Class D so he could escape). Being a robot, he still does what he's told (he won't enjoy it, nor will he let you forget it, but he'll do it anyway), though he'd much rather sulk in a corner by himself. -

-

- Several times in the series Marvin ends up alone and isolated for extremely long periods of time, sometimes spanning millions of years, either by sheer bad luck (such as the explosion that propelled everyone but Marvin to Milliways in the far-off future) or because his unpleasantly depressing personality drives them away or, in more than one case, makes them commit suicide. In his spare time (which he has a lot of), Marvin will attempt to occupy himself by composing songs and writing poetry. Of course, none of them are particularly cheerful, or even that good. -

-
-
-
-
-`; +exports[`Storyshots Modal|Modal Long content 1`] = `null`; -exports[`Storyshots Modal|Modal With form elements 1`] = ` -
-
-
-
-

- Hitchhiker Modal -

-
-
-
- - -
-
-

- Mind-paralyzing change needed improbability vortex machine sorts sought same theory upending job just allows - hostess’s really oblong Infinite Improbability thing into the starship against which behavior accordance.with - Kakrafoon humanoid undergarment ship powered by GPP-guided bowl of petunias nothing was frequently away incredibly - ordinary mob. -

-
-
- -
-