mirror of
https://github.com/scm-manager/scm-manager.git
synced 2026-07-05 10:18:38 +02:00
Merge pull request #1407 from scm-manager/bugfix/api-key-to-access-token
Bugfix/api key to access token
This commit is contained in:
@@ -15,6 +15,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||
### Fixed
|
||||
- Internal server error for git sub modules without tree object ([#1397](https://github.com/scm-manager/scm-manager/pull/1397))
|
||||
- Do not expose subversion commit with id 0 ([#1395](https://github.com/scm-manager/scm-manager/pull/1395))
|
||||
- Cloning of Mercurial repositories with api keys ([#1407](https://github.com/scm-manager/scm-manager/pull/1407))
|
||||
- Disable cloning repositories via ssh for anonymous users ([#1403](https://github.com/scm-manager/scm-manager/pull/1403))
|
||||
- Support anonymous file download through rest api for non-browser clients (e.g. curl or postman) when anonymous mode is set to protocol-only ([#1402](https://github.com/scm-manager/scm-manager/pull/1402))
|
||||
- SVN diff with property changes ([#1400](https://github.com/scm-manager/scm-manager/pull/1400))
|
||||
|
||||
@@ -30,6 +30,8 @@ import org.junit.Before;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.rules.TemporaryFolder;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.junit.runners.Parameterized;
|
||||
import sonia.scm.it.utils.RepositoryUtil;
|
||||
import sonia.scm.it.utils.RestUtil;
|
||||
import sonia.scm.it.utils.TestData;
|
||||
@@ -39,16 +41,30 @@ import sonia.scm.web.VndMediaType;
|
||||
|
||||
import javax.ws.rs.core.MediaType;
|
||||
import java.io.IOException;
|
||||
import java.util.Collection;
|
||||
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.ScmTypes.availableScmTypes;
|
||||
import static sonia.scm.it.utils.TestData.WRITE;
|
||||
|
||||
@RunWith(Parameterized.class)
|
||||
public class ApiKeyITCase {
|
||||
|
||||
@Parameterized.Parameters(name = "{0}")
|
||||
public static Collection<String> createParameters() {
|
||||
return availableScmTypes();
|
||||
}
|
||||
|
||||
private final String repositoryType;
|
||||
|
||||
public ApiKeyITCase(String repositoryType) {
|
||||
this.repositoryType = repositoryType;
|
||||
}
|
||||
|
||||
@Rule
|
||||
public TemporaryFolder temporaryFolder = new TemporaryFolder();
|
||||
|
||||
@@ -56,7 +72,7 @@ public class ApiKeyITCase {
|
||||
public void prepareEnvironment() {
|
||||
TestData.createDefault();
|
||||
TestData.createNotAdminUser("user", "user");
|
||||
TestData.createUserPermission("user", WRITE, "git");
|
||||
TestData.createUserPermission("user", WRITE, repositoryType);
|
||||
}
|
||||
|
||||
@After
|
||||
@@ -68,7 +84,7 @@ public class ApiKeyITCase {
|
||||
public void shouldCloneWithRestrictedApiKey() throws IOException {
|
||||
String passphrase = registerApiKey();
|
||||
|
||||
RepositoryClient client = RepositoryUtil.createRepositoryClient("git", temporaryFolder.newFolder(), "user", passphrase);
|
||||
RepositoryClient client = RepositoryUtil.createRepositoryClient(repositoryType, temporaryFolder.newFolder(), "user", passphrase);
|
||||
|
||||
assertEquals(1, Objects.requireNonNull(client.getWorkingCopy().list()).length);
|
||||
}
|
||||
@@ -77,7 +93,7 @@ public class ApiKeyITCase {
|
||||
public void shouldFailToCommit() throws IOException {
|
||||
String passphrase = registerApiKey();
|
||||
|
||||
RepositoryClient client = RepositoryUtil.createRepositoryClient("git", temporaryFolder.newFolder(), "user", passphrase);
|
||||
RepositoryClient client = RepositoryUtil.createRepositoryClient(repositoryType, temporaryFolder.newFolder(), "user", passphrase);
|
||||
|
||||
assertThrows(RepositoryClientException.class, () -> addAndCommitRandomFile(client, "user"));
|
||||
}
|
||||
|
||||
@@ -40,9 +40,7 @@ import java.time.Clock;
|
||||
import java.time.Instant;
|
||||
import java.util.Date;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
/**
|
||||
@@ -71,7 +69,6 @@ public final class JwtAccessTokenBuilder implements AccessTokenBuilder {
|
||||
private Instant refreshExpiration;
|
||||
private String parentKeyId;
|
||||
private Scope scope = Scope.empty();
|
||||
private Set<String> groups = new HashSet<>();
|
||||
|
||||
private final Map<String,Object> custom = Maps.newHashMap();
|
||||
|
||||
@@ -155,8 +152,13 @@ public final class JwtAccessTokenBuilder implements AccessTokenBuilder {
|
||||
|
||||
@Override
|
||||
public JwtAccessToken build() {
|
||||
if (SecurityUtils.getSubject().getPrincipals().getRealmNames().contains(ApiKeyRealm.NAME)) {
|
||||
throw new AuthorizationException("Cannot create access token for api keys");
|
||||
final Scope principalScope = SecurityUtils.getSubject().getPrincipals().oneByType(Scope.class);
|
||||
if (principalScope != null && !principalScope.isEmpty()) {
|
||||
if (scope != null && !scope.isEmpty()) {
|
||||
throw new AuthorizationException(String.format("cannot merge builder scope (%s) with principal scope (%s)", scope, principalScope));
|
||||
}
|
||||
LOG.debug("using existing scope for new access token: {}", principalScope);
|
||||
scope = principalScope;
|
||||
}
|
||||
String id = keyGenerator.createKey();
|
||||
|
||||
|
||||
@@ -39,6 +39,7 @@ import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.junit.jupiter.MockitoExtension;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
@@ -135,14 +136,27 @@ class JwtAccessTokenBuilderTest {
|
||||
@Nested
|
||||
class FromApiKeyRealm {
|
||||
|
||||
private Scope scope;
|
||||
|
||||
@BeforeEach
|
||||
void mockApiKeyRealm() {
|
||||
scope = Scope.valueOf("dummy:scope:*");
|
||||
lenient().when(principalCollection.getRealmNames()).thenReturn(singleton("ApiTokenRealm"));
|
||||
lenient().when(principalCollection.oneByType(Scope.class)).thenReturn(scope);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testRejectedRequest() {
|
||||
void shouldCreateJwtAndUsePreviousScope() {
|
||||
JwtAccessTokenBuilder builder = factory.create().subject("dent");
|
||||
final JwtAccessToken accessToken = builder.build();
|
||||
assertThat(accessToken).isNotNull();
|
||||
assertThat(accessToken.getSubject()).isEqualTo("dent");
|
||||
assertThat((Collection<String>) accessToken.getCustom("scope").get()).containsExactly("dummy:scope:*");
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldThrowExceptionWhenScopeAlreadyDefinedInBuilder() {
|
||||
JwtAccessTokenBuilder builder = factory.create().scope(Scope.valueOf("an:incompatible:scope")).subject("dent");
|
||||
assertThrows(AuthorizationException.class, builder::build);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user