From 8469b5013ce8f4182db03cab04f73b1848e7d1e6 Mon Sep 17 00:00:00 2001 From: Taiga Nada Date: Mon, 27 Feb 2023 11:27:41 +0900 Subject: [PATCH 1/4] add credential for access token. --- .../server/internal/mirror/GitWithAuth.java | 7 ++ .../AccessTokenMirrorCredential.java | 78 +++++++++++++++++++ .../server/mirror/MirrorCredential.java | 4 +- .../AccessTokenMirrorCredentialTest.java | 65 ++++++++++++++++ 4 files changed, 153 insertions(+), 1 deletion(-) create mode 100644 server/src/main/java/com/linecorp/centraldogma/server/internal/mirror/credential/AccessTokenMirrorCredential.java create mode 100644 server/src/test/java/com/linecorp/centraldogma/server/internal/mirror/credential/AccessTokenMirrorCredentialTest.java diff --git a/server/src/main/java/com/linecorp/centraldogma/server/internal/mirror/GitWithAuth.java b/server/src/main/java/com/linecorp/centraldogma/server/internal/mirror/GitWithAuth.java index d7a5e19c4..2b3d939ce 100644 --- a/server/src/main/java/com/linecorp/centraldogma/server/internal/mirror/GitWithAuth.java +++ b/server/src/main/java/com/linecorp/centraldogma/server/internal/mirror/GitWithAuth.java @@ -58,6 +58,7 @@ import com.jcraft.jsch.UserInfo; import com.linecorp.centraldogma.server.MirrorException; +import com.linecorp.centraldogma.server.internal.mirror.credential.AccessTokenMirrorCredential; import com.linecorp.centraldogma.server.internal.mirror.credential.PasswordMirrorCredential; import com.linecorp.centraldogma.server.internal.mirror.credential.PublicKeyMirrorCredential; import com.linecorp.centraldogma.server.mirror.MirrorCredential; @@ -155,6 +156,8 @@ public GarbageCollectCommand gc() { case SCHEME_GIT_HTTPS: if (c instanceof PasswordMirrorCredential) { configureHttp(command, (PasswordMirrorCredential) c); + } else if (c instanceof AccessTokenMirrorCredential) { + configureHttp(command, (AccessTokenMirrorCredential) c); } break; case SCHEME_GIT_SSH: @@ -173,6 +176,10 @@ public GarbageCollectCommand gc() { cmd.setCredentialsProvider(new UsernamePasswordCredentialsProvider(cred.username(), cred.password())); } + private static > void configureHttp(T cmd, AccessTokenMirrorCredential cred) { + cmd.setCredentialsProvider(new UsernamePasswordCredentialsProvider("token", cred.accessToken())); + } + private > void configureSsh(T cmd, PublicKeyMirrorCredential cred) { cmd.setTransportConfigCallback(transport -> { final SshTransport sshTransport = (SshTransport) transport; diff --git a/server/src/main/java/com/linecorp/centraldogma/server/internal/mirror/credential/AccessTokenMirrorCredential.java b/server/src/main/java/com/linecorp/centraldogma/server/internal/mirror/credential/AccessTokenMirrorCredential.java new file mode 100644 index 000000000..c5c164b86 --- /dev/null +++ b/server/src/main/java/com/linecorp/centraldogma/server/internal/mirror/credential/AccessTokenMirrorCredential.java @@ -0,0 +1,78 @@ +/* + * Copyright 2023 LINE Corporation + * + * LINE Corporation licenses this file to you under the Apache License, + * version 2.0 (the "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at: + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ + +package com.linecorp.centraldogma.server.internal.mirror.credential; + +import static com.linecorp.centraldogma.server.internal.mirror.credential.MirrorCredentialUtil.requireNonEmpty; + +import java.util.regex.Pattern; + +import javax.annotation.Nullable; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; +import com.google.common.base.MoreObjects.ToStringHelper; + +public final class AccessTokenMirrorCredential extends AbstractMirrorCredential { + + private final String accessToken; + + @JsonCreator + public AccessTokenMirrorCredential(@JsonProperty("id") @Nullable String id, + @JsonProperty("hostnamePatterns") @Nullable + @JsonDeserialize(contentAs = Pattern.class) + Iterable hostnamePatterns, + @JsonProperty("accessToken") String accessToken) { + super(id, hostnamePatterns); + + this.accessToken = requireNonEmpty(accessToken, "accessToken"); + } + + public String accessToken() { + return accessToken; + } + + @Override + public int hashCode() { + int result = super.hashCode(); + result = 31 * result + accessToken.hashCode(); + return result; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + + if (!(o instanceof AccessTokenMirrorCredential)) { + return false; + } + + if (!super.equals(o)) { + return false; + } + + final AccessTokenMirrorCredential that = (AccessTokenMirrorCredential) o; + return accessToken.equals(that.accessToken); + } + + @Override + void addProperties(ToStringHelper helper) { + // Access token must be kept secret. + } +} diff --git a/server/src/main/java/com/linecorp/centraldogma/server/mirror/MirrorCredential.java b/server/src/main/java/com/linecorp/centraldogma/server/mirror/MirrorCredential.java index 3d151c56b..a8151f49b 100644 --- a/server/src/main/java/com/linecorp/centraldogma/server/mirror/MirrorCredential.java +++ b/server/src/main/java/com/linecorp/centraldogma/server/mirror/MirrorCredential.java @@ -26,6 +26,7 @@ import com.fasterxml.jackson.annotation.JsonSubTypes.Type; import com.fasterxml.jackson.annotation.JsonTypeInfo; +import com.linecorp.centraldogma.server.internal.mirror.credential.AccessTokenMirrorCredential; import com.linecorp.centraldogma.server.internal.mirror.credential.NoneMirrorCredential; import com.linecorp.centraldogma.server.internal.mirror.credential.PasswordMirrorCredential; import com.linecorp.centraldogma.server.internal.mirror.credential.PublicKeyMirrorCredential; @@ -37,7 +38,8 @@ @JsonSubTypes({ @Type(value = NoneMirrorCredential.class, name = "none"), @Type(value = PasswordMirrorCredential.class, name = "password"), - @Type(value = PublicKeyMirrorCredential.class, name = "public_key") + @Type(value = PublicKeyMirrorCredential.class, name = "public_key"), + @Type(value = AccessTokenMirrorCredential.class, name = "access_token") }) public interface MirrorCredential { diff --git a/server/src/test/java/com/linecorp/centraldogma/server/internal/mirror/credential/AccessTokenMirrorCredentialTest.java b/server/src/test/java/com/linecorp/centraldogma/server/internal/mirror/credential/AccessTokenMirrorCredentialTest.java new file mode 100644 index 000000000..2644c89c2 --- /dev/null +++ b/server/src/test/java/com/linecorp/centraldogma/server/internal/mirror/credential/AccessTokenMirrorCredentialTest.java @@ -0,0 +1,65 @@ +/* + * Copyright 2023 LINE Corporation + * + * LINE Corporation licenses this file to you under the Apache License, + * version 2.0 (the "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at: + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ + +package com.linecorp.centraldogma.server.internal.mirror.credential; + +import static com.linecorp.centraldogma.server.internal.mirror.credential.MirrorCredentialTest.HOSTNAME_PATTERNS; +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +import org.junit.jupiter.api.Test; + +import com.linecorp.centraldogma.internal.Jackson; +import com.linecorp.centraldogma.server.mirror.MirrorCredential; + +class AccessTokenMirrorCredentialTest { + + @Test + void testConstruction() throws Exception { + // null checks + assertThatThrownBy(() -> new AccessTokenMirrorCredential(null, null, null)) + .isInstanceOf(NullPointerException.class); + + // emptiness checks + assertThatThrownBy(() -> new AccessTokenMirrorCredential(null, null, "")) + .isInstanceOf(IllegalArgumentException.class); + + // successful construction + final AccessTokenMirrorCredential c = new AccessTokenMirrorCredential(null, null, "sesame"); + assertThat(c.accessToken()).isEqualTo("sesame"); + } + + @Test + void testDeserialization() throws Exception { + // With hostnamePatterns + assertThat(Jackson.readValue('{' + + " \"type\": \"access_token\"," + + " \"hostnamePatterns\": [" + + " \"^foo\\\\.com$\"" + + " ]," + + " \"accessToken\": \"sesame\"" + + '}', MirrorCredential.class)) + .isEqualTo(new AccessTokenMirrorCredential(null, HOSTNAME_PATTERNS, + "sesame")); + // With ID + assertThat(Jackson.readValue('{' + + " \"type\": \"access_token\"," + + " \"id\": \"foo\"," + + " \"accessToken\": \"sesame\"" + + '}', MirrorCredential.class)) + .isEqualTo(new AccessTokenMirrorCredential("foo", null, "sesame")); + } +} From 69d19a4b4edb711428b79cfabed4ade10635124a Mon Sep 17 00:00:00 2001 From: Taiga Nada Date: Mon, 27 Feb 2023 13:30:38 +0900 Subject: [PATCH 2/4] fix code style detected by linter. --- .../centraldogma/server/internal/mirror/GitWithAuth.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/server/src/main/java/com/linecorp/centraldogma/server/internal/mirror/GitWithAuth.java b/server/src/main/java/com/linecorp/centraldogma/server/internal/mirror/GitWithAuth.java index 2b3d939ce..be36c5ac9 100644 --- a/server/src/main/java/com/linecorp/centraldogma/server/internal/mirror/GitWithAuth.java +++ b/server/src/main/java/com/linecorp/centraldogma/server/internal/mirror/GitWithAuth.java @@ -176,7 +176,8 @@ public GarbageCollectCommand gc() { cmd.setCredentialsProvider(new UsernamePasswordCredentialsProvider(cred.username(), cred.password())); } - private static > void configureHttp(T cmd, AccessTokenMirrorCredential cred) { + private static > void configureHttp( + T cmd, AccessTokenMirrorCredential cred) { cmd.setCredentialsProvider(new UsernamePasswordCredentialsProvider("token", cred.accessToken())); } From 6b63b46aa1a6c8bda81253bc85c8942b36e29931 Mon Sep 17 00:00:00 2001 From: Taiga Nada Date: Thu, 2 Mar 2023 21:25:09 +0900 Subject: [PATCH 3/4] update docs. --- site/src/sphinx/mirroring.rst | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/site/src/sphinx/mirroring.rst b/site/src/sphinx/mirroring.rst index 2de09eec8..b2e167aea 100644 --- a/site/src/sphinx/mirroring.rst +++ b/site/src/sphinx/mirroring.rst @@ -145,6 +145,11 @@ repositories defined in ``/mirrors.json``: "publicKey": "ssh-rsa ... user@host", "privateKey": "-----BEGIN RSA PRIVATE KEY-----\n...\n-----END RSA PRIVATE KEY-----\n", "passphrase": null + }, + { + "id": "my_access_token", + "type": "access_token", + "accessToken": "github_pat_..." } ] @@ -155,7 +160,7 @@ repositories defined in ``/mirrors.json``: - ``type`` (string) - - the type of authentication mechanism: ``none``, ``password`` or ``public_key``. + - the type of authentication mechanism: ``none``, ``password``, ``public_key`` or ``access_token``. - ``hostnamePatterns`` (array of strings, optional) @@ -165,7 +170,8 @@ repositories defined in ``/mirrors.json``: - ``username`` (string) - - the user name + - the user name. You must specify this field if you use a credential whose type is ``password`` or + ``public_key``. - ``password`` (string) @@ -208,6 +214,10 @@ repositories defined in ``/mirrors.json``: - the passphrase of ``privateKey`` if the private key is encrypted. If unspecified or ``null``, the private key should not be encrypted. +- ``accessToken`` (string) + + - the access token which is used for access token-based authentication such as GitHub Personal Access Token. + If everything was configured correctly, the repository you specified in ``localRepo`` will have a file named ``mirror_state.json`` on a successful run, which contains the commit ID of the Git repository: From 4afa19be8184425bc3b237b60be495ab267668c7 Mon Sep 17 00:00:00 2001 From: Taiga Nada Date: Fri, 3 Mar 2023 12:59:04 +0900 Subject: [PATCH 4/4] add link regarding GitHub PAT. --- site/src/sphinx/mirroring.rst | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/site/src/sphinx/mirroring.rst b/site/src/sphinx/mirroring.rst index b2e167aea..0d64aa1e2 100644 --- a/site/src/sphinx/mirroring.rst +++ b/site/src/sphinx/mirroring.rst @@ -216,7 +216,8 @@ repositories defined in ``/mirrors.json``: - ``accessToken`` (string) - - the access token which is used for access token-based authentication such as GitHub Personal Access Token. + - the access token which is used for access token-based authentication such as + `GitHub Personal Access Token `_. If everything was configured correctly, the repository you specified in ``localRepo`` will have a file named ``mirror_state.json`` on a successful run, which contains the commit ID of the Git repository: