Merge pull request #1407 from scm-manager/bugfix/api-key-to-access-token

Bugfix/api key to access token
This commit is contained in:
Sebastian Sdorra
2020-11-05 12:53:25 +01:00
committed by GitHub
4 changed files with 42 additions and 9 deletions

View File

@@ -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))

View File

@@ -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"));
}

View File

@@ -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();

View File

@@ -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);
}
}