From 167002b34cfe890cf32f6b833d428208ccd57cd3 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 13 Mar 2024 23:12:00 +0000 Subject: [PATCH 1/2] squash to head of master Bump google.golang.org/protobuf from 1.31.0 to 1.33.0 in /go/sendgmail Bumps google.golang.org/protobuf from 1.31.0 to 1.33.0. --- updated-dependencies: - dependency-name: google.golang.org/protobuf dependency-type: indirect ... Signed-off-by: dependabot[bot] Specify `/usr/bin/python3` in the shebang line. Create pom.xml Create build-jar.yml Update pom.xml Delete build.properties Delete build.xml Delete build-java-sample-zip.sh Update pom.xml Update pom.xml Update build-jar.yml Update build-jar.yml Update pom.xml Update pom.xml Update build-jar.yml Update README.md added skeleton classes and pom file was edited for build information with maven added pom and tests for OAuth2SaslClient added hasInitialResponseTest getNegotiatedProperty updated to return values stored by child class added field check test to determine if accessor functions correctly Update build-jar.yml Update build-jar.yml Update pom.xml Update build-jar.yml added test to match Factory mechanism with Client mechanism added test to match Factory mechanism with Client mechanism removed assumptions --- .github/workflows/build-jar.yml | 29 +++++ .gitignore | 2 + README.md | 2 + go/sendgmail/go.mod | 2 +- go/sendgmail/go.sum | 4 +- java/build-java-sample-zip.sh | 70 ----------- java/build.properties | 34 ------ java/build.xml | 58 --------- .../samples/oauth2/OAuth2Authenticator.java | 6 +- .../code/samples/oauth2/OAuth2SaslClient.java | 53 ++++++-- .../oauth2/OAuth2SaslClientFactory.java | 4 +- .../oauth2/OAuth2AuthenticatorTests.java | 32 +++++ .../oauth2/OAuth2SaslClientFactoryTests.java | 31 +++++ .../samples/oauth2/OAuth2SaslClientTests.java | 55 +++++++++ pom.xml | 114 ++++++++++++++++++ 15 files changed, 315 insertions(+), 181 deletions(-) create mode 100644 .github/workflows/build-jar.yml create mode 100644 .gitignore delete mode 100755 java/build-java-sample-zip.sh delete mode 100644 java/build.properties delete mode 100644 java/build.xml rename java/{ => src/main}/com/google/code/samples/oauth2/OAuth2Authenticator.java (96%) rename java/{ => src/main}/com/google/code/samples/oauth2/OAuth2SaslClient.java (70%) rename java/{ => src/main}/com/google/code/samples/oauth2/OAuth2SaslClientFactory.java (97%) create mode 100644 java/src/test/com/google/code/samples/oauth2/OAuth2AuthenticatorTests.java create mode 100644 java/src/test/com/google/code/samples/oauth2/OAuth2SaslClientFactoryTests.java create mode 100644 java/src/test/com/google/code/samples/oauth2/OAuth2SaslClientTests.java create mode 100644 pom.xml diff --git a/.github/workflows/build-jar.yml b/.github/workflows/build-jar.yml new file mode 100644 index 0000000..60674d5 --- /dev/null +++ b/.github/workflows/build-jar.yml @@ -0,0 +1,29 @@ +# This workflow will build a package using Maven and then publish it to GitHub packages when a release is created +# For more information see: https://github.com/actions/setup-java/blob/main/docs/advanced-usage.md#apache-maven-with-a-settings-path + +name: gmail-oauth2-tools + +on: push + +jobs: + build: + + runs-on: ubuntu-latest + permissions: + contents: read + packages: write + + steps: + - uses: actions/checkout@v3 + - name: Set up JDK 11 + uses: actions/setup-java@v3 + with: + java-version: '11' + distribution: 'temurin' + server-id: github # Value of the distributionManagement/repository/id field of the pom.xml + settings-path: ${{ github.workspace }}/java # location for the settings.xml file + + - name: Build with Maven + run: mvn -B package --file pom.xml + - name: Test with Maven + run: mvn test --file pom.xml diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..64ee209 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +target/ +.vscode/ \ No newline at end of file diff --git a/README.md b/README.md index 6264733..5e07690 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,5 @@ +[![gmail-oauth2-tools](https://github.com/FreedomFaighter/gmail-oauth2-tools/actions/workflows/build-jar.yml/badge.svg)](https://github.com/FreedomFaighter/gmail-oauth2-tools/actions/workflows/build-jar.yml) + Tools and sample code for authenticating to Gmail with OAuth2. The specification is available [here](https://developers.google.com/gmail/xoauth2_protocol). diff --git a/go/sendgmail/go.mod b/go/sendgmail/go.mod index 7e0b280..cc2a53b 100644 --- a/go/sendgmail/go.mod +++ b/go/sendgmail/go.mod @@ -12,5 +12,5 @@ require ( cloud.google.com/go/compute/metadata v0.2.3 // indirect github.com/golang/protobuf v1.5.3 // indirect google.golang.org/appengine v1.6.8 // indirect - google.golang.org/protobuf v1.31.0 // indirect + google.golang.org/protobuf v1.33.0 // indirect ) diff --git a/go/sendgmail/go.sum b/go/sendgmail/go.sum index a90979b..e1151fd 100644 --- a/go/sendgmail/go.sum +++ b/go/sendgmail/go.sum @@ -41,5 +41,5 @@ google.golang.org/appengine v1.6.8 h1:IhEN5q69dyKagZPYMSdIjS2HqprW324FRQZJcGqPAs google.golang.org/appengine v1.6.8/go.mod h1:1jJ3jBArFh5pcgW8gCtRJnepW8FzD1V44FJffLiz/Ds= google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= -google.golang.org/protobuf v1.31.0 h1:g0LDEJHgrBl9N9r17Ru3sqWhkIx2NB67okBHPwC7hs8= -google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= +google.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI= +google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= diff --git a/java/build-java-sample-zip.sh b/java/build-java-sample-zip.sh deleted file mode 100755 index 70fe685..0000000 --- a/java/build-java-sample-zip.sh +++ /dev/null @@ -1,70 +0,0 @@ -#!/bin/sh -# -# Copyright 2012 Google Inc. -# -# Licensed 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 -# -# http://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. - - -# Usage: build-java-sample-zip.sh [datestr] -# -# Copies the Java sample files to a temporary directory and zips them up. -# The resulting ZIP file is left in /tmp. -# If datestr is not specified, it defaults to the current date in YYYYMMDD -# format; for example, "20120904". This would result in the zipfile -# /tmp/oauth2-java-sample-20120904.zip. -# -# The script is intended to be run from the root of the Java code hierarchy. -# It makes sure there are no local modifications to the files that are -# being zipped up. - - -top_level_files="README-java-sample.txt build.xml build.properties ../python/oauth2.py" -relative_files="com/google/code/samples/oauth2/*.java" -all_files="$top_level_files $relative_files" - -if [[ "$1" ]] ; then - date="$1" -else - date=$(date "+%Y%m%d") -fi - -relative_tmpdir="oauth2-java-sample-$date" -full_tmpdir="/tmp/$relative_tmpdir" -outfile="/tmp/oauth2-java-sample-$date.zip" - -if [[ -e $full_tmpdir ]]; then - echo "ERROR: directory $full_tmpdir already exists" - exit -1 -fi - -if [[ -e $outfile ]]; then - echo "ERROR: $outfile already exists" - exit -1 -fi - -status=$(svn status $all_files) -if [[ "$status" ]] ; then - echo "ERROR: One or more files has uncommitted changes:" - echo "$status" - exit -1 -fi - -mkdir -p $full_tmpdir -cp $top_level_files $full_tmpdir -cp --parents $relative_files $full_tmpdir - -cd /tmp -zip -r $outfile $relative_tmpdir -rm -r $full_tmpdir - -echo "Created $outfile" diff --git a/java/build.properties b/java/build.properties deleted file mode 100644 index 5eabffc..0000000 --- a/java/build.properties +++ /dev/null @@ -1,34 +0,0 @@ -# Copyright 2012 Google Inc. -# -# Licensed 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 -# -# http://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. - - - -# If you are not running Java EE, you may need to supply a JAR file containing -# the Java Mail package (see http://java.sun.com/products/javamail/). -javamail_jar= - -# These are commandline parameters for OAuth2Authenticator.main(). -email= -oauthToken= - -# -# You shouldn't need to change anything below this point. -# - -out=./out -classes=./classes -src=./ - -# The generated JAR file will go here. -oauth2_jar=${out}/oauth2.jar diff --git a/java/build.xml b/java/build.xml deleted file mode 100644 index 5ea1cd5..0000000 --- a/java/build.xml +++ /dev/null @@ -1,58 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/java/com/google/code/samples/oauth2/OAuth2Authenticator.java b/java/src/main/com/google/code/samples/oauth2/OAuth2Authenticator.java similarity index 96% rename from java/com/google/code/samples/oauth2/OAuth2Authenticator.java rename to java/src/main/com/google/code/samples/oauth2/OAuth2Authenticator.java index 596c5fc..254d7ad 100644 --- a/java/com/google/code/samples/oauth2/OAuth2Authenticator.java +++ b/java/src/main/com/google/code/samples/oauth2/OAuth2Authenticator.java @@ -13,7 +13,7 @@ * limitations under the License. */ -package com.google.code.samples.oauth2; +package main.com.google.code.samples.oauth2; import com.sun.mail.imap.IMAPStore; import com.sun.mail.imap.IMAPSSLStore; @@ -39,7 +39,7 @@ public class OAuth2Authenticator { Logger.getLogger(OAuth2Authenticator.class.getName()); public static final class OAuth2Provider extends Provider { - private static final long serialVersionUID = 1L; + private static final long serialVersionUID = 454502445710734267L; public OAuth2Provider() { super("Google OAuth2 Provider", 1.0, @@ -147,7 +147,7 @@ public static void main(String args[]) throws Exception { email, oauthToken, true); - System.out.println("Successfully authenticated to IMAP.\n"); + System.out.print("Successfully authenticated to IMAP.\n\n"); SMTPTransport smtpTransport = connectToSmtp("smtp.gmail.com", 587, email, diff --git a/java/com/google/code/samples/oauth2/OAuth2SaslClient.java b/java/src/main/com/google/code/samples/oauth2/OAuth2SaslClient.java similarity index 70% rename from java/com/google/code/samples/oauth2/OAuth2SaslClient.java rename to java/src/main/com/google/code/samples/oauth2/OAuth2SaslClient.java index 2bfef8d..43b1247 100644 --- a/java/com/google/code/samples/oauth2/OAuth2SaslClient.java +++ b/java/src/main/com/google/code/samples/oauth2/OAuth2SaslClient.java @@ -13,20 +13,20 @@ * limitations under the License. */ -package com.google.code.samples.oauth2; +package main.com.google.code.samples.oauth2; import java.io.IOException; -import java.net.URISyntaxException; import java.util.logging.Logger; import javax.security.auth.callback.Callback; import javax.security.auth.callback.CallbackHandler; import javax.security.auth.callback.NameCallback; -import javax.security.auth.callback.PasswordCallback; import javax.security.auth.callback.UnsupportedCallbackException; import javax.security.sasl.SaslClient; import javax.security.sasl.SaslException; +import org.apache.maven.surefire.shared.lang3.NotImplementedException; + /** * An OAuth2 implementation of SaslClient. @@ -39,7 +39,8 @@ class OAuth2SaslClient implements SaslClient { private final CallbackHandler callbackHandler; private boolean isComplete = false; - + private String MechanismName = "XOAUTH2"; + private String Email; /** * Creates a new instance of the OAuth2SaslClient. This will ordinarily only * be called from OAuth2SaslClientFactory. @@ -50,8 +51,14 @@ public OAuth2SaslClient(String oauthToken, this.callbackHandler = callbackHandler; } + public OAuth2SaslClient() + { + this.oauthToken = null; + this.callbackHandler = null; + } + public String getMechanismName() { - return "XOAUTH2"; + return this.MechanismName; } public boolean hasInitialResponse() { @@ -73,12 +80,12 @@ public byte[] evaluateChallenge(byte[] challenge) throws SaslException { } catch (IOException e) { throw new SaslException("Failed to execute callback: " + e); } - String email = nameCallback.getName(); + this.Email = nameCallback.getName(); - byte[] response = String.format("user=%s\1auth=Bearer %s\1\1", email, + byte[] postToAppendToServerAddress = String.format("user=%s\1auth=Bearer %s\1\1", this.Email, oauthToken).getBytes(); isComplete = true; - return response; + return postToAppendToServerAddress; } public boolean isComplete() { @@ -87,19 +94,43 @@ public boolean isComplete() { public byte[] unwrap(byte[] incoming, int offset, int len) throws SaslException { - throw new IllegalStateException(); + throw new NotImplementedException(); } public byte[] wrap(byte[] outgoing, int offset, int len) throws SaslException { - throw new IllegalStateException(); + throw new NotImplementedException(); } public Object getNegotiatedProperty(String propName) { if (!isComplete()) { throw new IllegalStateException(); } - return null; + switch(propName) + { + case "Email": + if(this.Email == null) + return null; + else + return this.Email; + case "MechanismName": + if(this.MechanismName == null) + return null; + else + return this.getMechanismName(); + case "OAuthToken": + if(null == this.oauthToken) + return null; + else + return this.oauthToken; + case "CallbackHandler": + if(this.callbackHandler == null) + return null; + else + return this.callbackHandler; + default: + return null; + } } public void dispose() throws SaslException { diff --git a/java/com/google/code/samples/oauth2/OAuth2SaslClientFactory.java b/java/src/main/com/google/code/samples/oauth2/OAuth2SaslClientFactory.java similarity index 97% rename from java/com/google/code/samples/oauth2/OAuth2SaslClientFactory.java rename to java/src/main/com/google/code/samples/oauth2/OAuth2SaslClientFactory.java index f399ad5..d39a974 100644 --- a/java/com/google/code/samples/oauth2/OAuth2SaslClientFactory.java +++ b/java/src/main/com/google/code/samples/oauth2/OAuth2SaslClientFactory.java @@ -13,7 +13,7 @@ * limitations under the License. */ -package com.google.code.samples.oauth2; +package main.com.google.code.samples.oauth2; import java.util.Map; @@ -56,7 +56,7 @@ public SaslClient createSaslClient(String[] mechanisms, return new OAuth2SaslClient((String) props.get(OAUTH_TOKEN_PROP), callbackHandler); } - + public String[] getMechanismNames(Map props) { return new String[] {"XOAUTH2"}; } diff --git a/java/src/test/com/google/code/samples/oauth2/OAuth2AuthenticatorTests.java b/java/src/test/com/google/code/samples/oauth2/OAuth2AuthenticatorTests.java new file mode 100644 index 0000000..704d3b5 --- /dev/null +++ b/java/src/test/com/google/code/samples/oauth2/OAuth2AuthenticatorTests.java @@ -0,0 +1,32 @@ +package main.com.google.code.samples.oauth2; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.Assume.assumeTrue; +import org.junit.jupiter.api.BeforeAll; + +import org.junit.jupiter.api.Test; + +import main.com.google.code.samples.oauth2.OAuth2Authenticator; + +import org.junit.jupiter.api.DisplayName; + +public class OAuth2AuthenticatorTests { + + private final OAuth2Authenticator oAuth2Authenticator = new OAuth2Authenticator(); + + @DisplayName("Assumptions needed for tests") + @Test + void trueAssumption() { + assumeTrue((4 % 1) == 0); + assertEquals(8 % 3, 2); + assumeTrue((7 % 5) == 2); + assertEquals(7 % 3, 1); + assumeTrue(((2 + 2) % 2) == 0); + assertEquals(2 + 2, 4); + } + + @BeforeAll + static void initAll() { + + } +} \ No newline at end of file diff --git a/java/src/test/com/google/code/samples/oauth2/OAuth2SaslClientFactoryTests.java b/java/src/test/com/google/code/samples/oauth2/OAuth2SaslClientFactoryTests.java new file mode 100644 index 0000000..d1db0a4 --- /dev/null +++ b/java/src/test/com/google/code/samples/oauth2/OAuth2SaslClientFactoryTests.java @@ -0,0 +1,31 @@ +package main.pro.freemania.code.samples.oauth2; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.Assume.assumeTrue; + +import org.junit.jupiter.api.Test; + +import com.google.common.annotations.VisibleForTesting; + +import main.com.google.code.samples.oauth2.OAuth2SaslClientFactory; + +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.DisplayName; +import main.com.google.code.samples.oauth2.OAuth2SaslClient; + +public class OAuth2SaslClientFactoryTests { + + private final OAuth2SaslClientFactory oAuth2SaslClientFactory = new OAuth2SaslClientFactory(); + + @BeforeAll + static void initAll() { + + } + + @Test + void getMechanismNameXOAUTH() + { + OAuth2SaslClient oAuth2SaslClient = new OAuth2SaslClient(); + assertEquals(oAuth2SaslClient.getMechanismName(), oAuth2SaslClientFactory.getMechanismNames(null)[0]); + } +} \ No newline at end of file diff --git a/java/src/test/com/google/code/samples/oauth2/OAuth2SaslClientTests.java b/java/src/test/com/google/code/samples/oauth2/OAuth2SaslClientTests.java new file mode 100644 index 0000000..04343bc --- /dev/null +++ b/java/src/test/com/google/code/samples/oauth2/OAuth2SaslClientTests.java @@ -0,0 +1,55 @@ +package main.pro.freemania.code.samples.oauth2; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.Assume.assumeTrue; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import org.apache.maven.surefire.api.testset.TestSetFailedException; +import org.junit.jupiter.api.BeforeAll; +import org.junit.runner.RunWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.DisplayName; +import javax.security.auth.callback.CallbackHandler; +import java.lang.reflect.Field; +import main.com.google.code.samples.oauth2.OAuth2SaslClient; + +class OAuth2SaslClientTests { + + private final OAuth2SaslClient oAuth2SaslClient = new OAuth2SaslClient(); + + @Test + public void oAuth2SaslClientgetNegotiatedPropertyThrows() throws TestSetFailedException { + + String message = new String("propertyToSet"); + + assertThrows(IllegalStateException.class + , () -> { + oAuth2SaslClient.getNegotiatedProperty(message); + }); + } + + @Test + public void hasInitialResponseTest() + { + assertTrue(oAuth2SaslClient.hasInitialResponse()); + } + + public Field getPrivateFieldObject (String nameOfField) throws NoSuchFieldException + { + return OAuth2SaslClient.class.getDeclaredField(nameOfField); + } + + @Test + public void getNegotiatedPropertyMechanismName() throws IllegalAccessException, NoSuchFieldException + { + Field f = getPrivateFieldObject("MechanismName"); + f.setAccessible(true); + String name = (String)f.get(this.oAuth2SaslClient); + assertEquals(oAuth2SaslClient.getMechanismName(), name); + } +} \ No newline at end of file diff --git a/pom.xml b/pom.xml new file mode 100644 index 0000000..f8522f8 --- /dev/null +++ b/pom.xml @@ -0,0 +1,114 @@ + + + 4.0.0 + com.google.code.samples.oauth2 + gmail-oauth2-tools + 1.0.1-SNAPSHOT + jar + https://github.com/FreedomFaighter/gmail-oauth2-tools + gmail-oauth2-tools + Tools and sample code for authenticating to Gmail with OAuth2. + + UTF-8 + UTF-8 + 11 + + + + + + org.junit.platform + junit-platform-runner + 1.10.0 + test + + + org.junit.jupiter + junit-jupiter-engine + 5.9.2 + test + + + javax.mail + mail + 1.4.7 + + + org.mockito + mockito-junit-jupiter + 5.4.0 + test + + + + org.apache.maven.plugins + maven-surefire-plugin + 3.1.2 + + + + java/src/main + java/src/test + ${project.basedir}/target + ${project.build.directory}/classes + ${project.artifactId}-${project.version} + ${project.build.directory}/test-classes + + + maven-compiler-plugin + + 11 + 11 + + + + maven-jar-plugin + 3.3.0 + + + default-jar + package + + jar + + + + + + + true + com.google.code.samples.oauth2.OAuth2Authenticator + + + + + + org.apache.maven.plugins + maven-surefire-plugin + 3.1.2 + + + **/*Tests.java + + + + + + org.junit.platform + junit-platform-surefire-provider + 1.3.2 + + + + org.apache.maven.surefire + surefire-api + 3.1.2 + + + + + + \ No newline at end of file From 9a407f9077bc08382b3d3fec7795b562344a2e51 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 13 Mar 2024 23:12:00 +0000 Subject: [PATCH 2/2] Bump google.golang.org/protobuf from 1.31.0 to 1.33.0 in /go/sendgmail Bumps google.golang.org/protobuf from 1.31.0 to 1.33.0. --- updated-dependencies: - dependency-name: google.golang.org/protobuf dependency-type: indirect ... Signed-off-by: dependabot[bot] Bump google.golang.org/protobuf from 1.31.0 to 1.33.0 in /go/sendgmail Bumps google.golang.org/protobuf from 1.31.0 to 1.33.0. --- updated-dependencies: - dependency-name: google.golang.org/protobuf dependency-type: indirect ... Signed-off-by: dependabot[bot] Add page content from http://oauth2.dance Originally authored by junyer. squash to head of master Bump google.golang.org/protobuf from 1.31.0 to 1.33.0 in /go/sendgmail Bumps google.golang.org/protobuf from 1.31.0 to 1.33.0. --- updated-dependencies: - dependency-name: google.golang.org/protobuf dependency-type: indirect ... Signed-off-by: dependabot[bot] Specify `/usr/bin/python3` in the shebang line. Create pom.xml Create build-jar.yml Update pom.xml Delete build.properties Delete build.xml Delete build-java-sample-zip.sh Update pom.xml Update pom.xml Update build-jar.yml Update build-jar.yml Update pom.xml Update pom.xml Update build-jar.yml Update README.md added skeleton classes and pom file was edited for build information with maven added pom and tests for OAuth2SaslClient added hasInitialResponseTest getNegotiatedProperty updated to return values stored by child class added field check test to determine if accessor functions correctly Update build-jar.yml Update build-jar.yml Update pom.xml Update build-jar.yml added test to match Factory mechanism with Client mechanism added test to match Factory mechanism with Client mechanism removed assumptions Replace https://oauth2.dance with Github pages. https://oauth2.dance hosted some static content that reflected the OAuth2 token back to you. After some research, it appears that this domain was personally owned and hosted by junyer, who sadly passed away last year. The domain subsequently expired and was reregistered by a third party and is currently not resolving. This is my proposal to fix this: host the required HTML via Github pages from this repo itself. In order to work, you will need to create a "gh-pages" branch containing (at least) the oauth2.dance.html file. Update OAuth2AuthenticatorTests.java Added false assume statement about modular arithmetic Update OAuth2AuthenticatorTests.java Corrected typo in false assumption test --- .github/workflows/build-jar.yml | 29 +++++ .gitignore | 2 + README.md | 2 + go/sendgmail/README.md | 5 +- html/oauth2.dance.html | 30 +++++ java/build-java-sample-zip.sh | 70 ----------- java/build.properties | 34 ------ java/build.xml | 58 --------- .../samples/oauth2/OAuth2Authenticator.java | 6 +- .../code/samples/oauth2/OAuth2SaslClient.java | 53 ++++++-- .../oauth2/OAuth2SaslClientFactory.java | 4 +- .../oauth2/OAuth2AuthenticatorTests.java | 34 ++++++ .../oauth2/OAuth2SaslClientFactoryTests.java | 31 +++++ .../samples/oauth2/OAuth2SaslClientTests.java | 55 +++++++++ pom.xml | 114 ++++++++++++++++++ python/oauth2.py | 5 +- 16 files changed, 350 insertions(+), 182 deletions(-) create mode 100644 .github/workflows/build-jar.yml create mode 100644 .gitignore create mode 100644 html/oauth2.dance.html delete mode 100755 java/build-java-sample-zip.sh delete mode 100644 java/build.properties delete mode 100644 java/build.xml rename java/{ => src/main}/com/google/code/samples/oauth2/OAuth2Authenticator.java (96%) rename java/{ => src/main}/com/google/code/samples/oauth2/OAuth2SaslClient.java (70%) rename java/{ => src/main}/com/google/code/samples/oauth2/OAuth2SaslClientFactory.java (97%) create mode 100644 java/src/test/com/google/code/samples/oauth2/OAuth2AuthenticatorTests.java create mode 100644 java/src/test/com/google/code/samples/oauth2/OAuth2SaslClientFactoryTests.java create mode 100644 java/src/test/com/google/code/samples/oauth2/OAuth2SaslClientTests.java create mode 100644 pom.xml diff --git a/.github/workflows/build-jar.yml b/.github/workflows/build-jar.yml new file mode 100644 index 0000000..60674d5 --- /dev/null +++ b/.github/workflows/build-jar.yml @@ -0,0 +1,29 @@ +# This workflow will build a package using Maven and then publish it to GitHub packages when a release is created +# For more information see: https://github.com/actions/setup-java/blob/main/docs/advanced-usage.md#apache-maven-with-a-settings-path + +name: gmail-oauth2-tools + +on: push + +jobs: + build: + + runs-on: ubuntu-latest + permissions: + contents: read + packages: write + + steps: + - uses: actions/checkout@v3 + - name: Set up JDK 11 + uses: actions/setup-java@v3 + with: + java-version: '11' + distribution: 'temurin' + server-id: github # Value of the distributionManagement/repository/id field of the pom.xml + settings-path: ${{ github.workspace }}/java # location for the settings.xml file + + - name: Build with Maven + run: mvn -B package --file pom.xml + - name: Test with Maven + run: mvn test --file pom.xml diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..64ee209 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +target/ +.vscode/ \ No newline at end of file diff --git a/README.md b/README.md index 6264733..5e07690 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,5 @@ +[![gmail-oauth2-tools](https://github.com/FreedomFaighter/gmail-oauth2-tools/actions/workflows/build-jar.yml/badge.svg)](https://github.com/FreedomFaighter/gmail-oauth2-tools/actions/workflows/build-jar.yml) + Tools and sample code for authenticating to Gmail with OAuth2. The specification is available [here](https://developers.google.com/gmail/xoauth2_protocol). diff --git a/go/sendgmail/README.md b/go/sendgmail/README.md index 6a70407..2bb91dd 100644 --- a/go/sendgmail/README.md +++ b/go/sendgmail/README.md @@ -22,8 +22,9 @@ send-email`. * Follow the steps in the **Authorize credentials for a desktop application** section. However, set the application type to *Web application* (i.e. instead of *Desktop app*) and then add - `https://oauth2.dance/` as an authorised redirect URI. This is necessary - for seeing the authorisation code on a page in your browser. + `https://google.github.io/gmail-oauth2-tools/html/oauth2.dance.html` + as an authorised redirect URI. This is necessary for seeing the + authorisation code on a page in your browser. * When you download the credentials as JSON, create the `${XDG_CONFIG_HOME:-${HOME}/.config}/sendgmail` directory with file mode diff --git a/html/oauth2.dance.html b/html/oauth2.dance.html new file mode 100644 index 0000000..639d37a --- /dev/null +++ b/html/oauth2.dance.html @@ -0,0 +1,30 @@ + + + + +oauth2.dance +

oauth2.dance

+ + + diff --git a/java/build-java-sample-zip.sh b/java/build-java-sample-zip.sh deleted file mode 100755 index 70fe685..0000000 --- a/java/build-java-sample-zip.sh +++ /dev/null @@ -1,70 +0,0 @@ -#!/bin/sh -# -# Copyright 2012 Google Inc. -# -# Licensed 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 -# -# http://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. - - -# Usage: build-java-sample-zip.sh [datestr] -# -# Copies the Java sample files to a temporary directory and zips them up. -# The resulting ZIP file is left in /tmp. -# If datestr is not specified, it defaults to the current date in YYYYMMDD -# format; for example, "20120904". This would result in the zipfile -# /tmp/oauth2-java-sample-20120904.zip. -# -# The script is intended to be run from the root of the Java code hierarchy. -# It makes sure there are no local modifications to the files that are -# being zipped up. - - -top_level_files="README-java-sample.txt build.xml build.properties ../python/oauth2.py" -relative_files="com/google/code/samples/oauth2/*.java" -all_files="$top_level_files $relative_files" - -if [[ "$1" ]] ; then - date="$1" -else - date=$(date "+%Y%m%d") -fi - -relative_tmpdir="oauth2-java-sample-$date" -full_tmpdir="/tmp/$relative_tmpdir" -outfile="/tmp/oauth2-java-sample-$date.zip" - -if [[ -e $full_tmpdir ]]; then - echo "ERROR: directory $full_tmpdir already exists" - exit -1 -fi - -if [[ -e $outfile ]]; then - echo "ERROR: $outfile already exists" - exit -1 -fi - -status=$(svn status $all_files) -if [[ "$status" ]] ; then - echo "ERROR: One or more files has uncommitted changes:" - echo "$status" - exit -1 -fi - -mkdir -p $full_tmpdir -cp $top_level_files $full_tmpdir -cp --parents $relative_files $full_tmpdir - -cd /tmp -zip -r $outfile $relative_tmpdir -rm -r $full_tmpdir - -echo "Created $outfile" diff --git a/java/build.properties b/java/build.properties deleted file mode 100644 index 5eabffc..0000000 --- a/java/build.properties +++ /dev/null @@ -1,34 +0,0 @@ -# Copyright 2012 Google Inc. -# -# Licensed 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 -# -# http://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. - - - -# If you are not running Java EE, you may need to supply a JAR file containing -# the Java Mail package (see http://java.sun.com/products/javamail/). -javamail_jar= - -# These are commandline parameters for OAuth2Authenticator.main(). -email= -oauthToken= - -# -# You shouldn't need to change anything below this point. -# - -out=./out -classes=./classes -src=./ - -# The generated JAR file will go here. -oauth2_jar=${out}/oauth2.jar diff --git a/java/build.xml b/java/build.xml deleted file mode 100644 index 5ea1cd5..0000000 --- a/java/build.xml +++ /dev/null @@ -1,58 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/java/com/google/code/samples/oauth2/OAuth2Authenticator.java b/java/src/main/com/google/code/samples/oauth2/OAuth2Authenticator.java similarity index 96% rename from java/com/google/code/samples/oauth2/OAuth2Authenticator.java rename to java/src/main/com/google/code/samples/oauth2/OAuth2Authenticator.java index 596c5fc..254d7ad 100644 --- a/java/com/google/code/samples/oauth2/OAuth2Authenticator.java +++ b/java/src/main/com/google/code/samples/oauth2/OAuth2Authenticator.java @@ -13,7 +13,7 @@ * limitations under the License. */ -package com.google.code.samples.oauth2; +package main.com.google.code.samples.oauth2; import com.sun.mail.imap.IMAPStore; import com.sun.mail.imap.IMAPSSLStore; @@ -39,7 +39,7 @@ public class OAuth2Authenticator { Logger.getLogger(OAuth2Authenticator.class.getName()); public static final class OAuth2Provider extends Provider { - private static final long serialVersionUID = 1L; + private static final long serialVersionUID = 454502445710734267L; public OAuth2Provider() { super("Google OAuth2 Provider", 1.0, @@ -147,7 +147,7 @@ public static void main(String args[]) throws Exception { email, oauthToken, true); - System.out.println("Successfully authenticated to IMAP.\n"); + System.out.print("Successfully authenticated to IMAP.\n\n"); SMTPTransport smtpTransport = connectToSmtp("smtp.gmail.com", 587, email, diff --git a/java/com/google/code/samples/oauth2/OAuth2SaslClient.java b/java/src/main/com/google/code/samples/oauth2/OAuth2SaslClient.java similarity index 70% rename from java/com/google/code/samples/oauth2/OAuth2SaslClient.java rename to java/src/main/com/google/code/samples/oauth2/OAuth2SaslClient.java index 2bfef8d..43b1247 100644 --- a/java/com/google/code/samples/oauth2/OAuth2SaslClient.java +++ b/java/src/main/com/google/code/samples/oauth2/OAuth2SaslClient.java @@ -13,20 +13,20 @@ * limitations under the License. */ -package com.google.code.samples.oauth2; +package main.com.google.code.samples.oauth2; import java.io.IOException; -import java.net.URISyntaxException; import java.util.logging.Logger; import javax.security.auth.callback.Callback; import javax.security.auth.callback.CallbackHandler; import javax.security.auth.callback.NameCallback; -import javax.security.auth.callback.PasswordCallback; import javax.security.auth.callback.UnsupportedCallbackException; import javax.security.sasl.SaslClient; import javax.security.sasl.SaslException; +import org.apache.maven.surefire.shared.lang3.NotImplementedException; + /** * An OAuth2 implementation of SaslClient. @@ -39,7 +39,8 @@ class OAuth2SaslClient implements SaslClient { private final CallbackHandler callbackHandler; private boolean isComplete = false; - + private String MechanismName = "XOAUTH2"; + private String Email; /** * Creates a new instance of the OAuth2SaslClient. This will ordinarily only * be called from OAuth2SaslClientFactory. @@ -50,8 +51,14 @@ public OAuth2SaslClient(String oauthToken, this.callbackHandler = callbackHandler; } + public OAuth2SaslClient() + { + this.oauthToken = null; + this.callbackHandler = null; + } + public String getMechanismName() { - return "XOAUTH2"; + return this.MechanismName; } public boolean hasInitialResponse() { @@ -73,12 +80,12 @@ public byte[] evaluateChallenge(byte[] challenge) throws SaslException { } catch (IOException e) { throw new SaslException("Failed to execute callback: " + e); } - String email = nameCallback.getName(); + this.Email = nameCallback.getName(); - byte[] response = String.format("user=%s\1auth=Bearer %s\1\1", email, + byte[] postToAppendToServerAddress = String.format("user=%s\1auth=Bearer %s\1\1", this.Email, oauthToken).getBytes(); isComplete = true; - return response; + return postToAppendToServerAddress; } public boolean isComplete() { @@ -87,19 +94,43 @@ public boolean isComplete() { public byte[] unwrap(byte[] incoming, int offset, int len) throws SaslException { - throw new IllegalStateException(); + throw new NotImplementedException(); } public byte[] wrap(byte[] outgoing, int offset, int len) throws SaslException { - throw new IllegalStateException(); + throw new NotImplementedException(); } public Object getNegotiatedProperty(String propName) { if (!isComplete()) { throw new IllegalStateException(); } - return null; + switch(propName) + { + case "Email": + if(this.Email == null) + return null; + else + return this.Email; + case "MechanismName": + if(this.MechanismName == null) + return null; + else + return this.getMechanismName(); + case "OAuthToken": + if(null == this.oauthToken) + return null; + else + return this.oauthToken; + case "CallbackHandler": + if(this.callbackHandler == null) + return null; + else + return this.callbackHandler; + default: + return null; + } } public void dispose() throws SaslException { diff --git a/java/com/google/code/samples/oauth2/OAuth2SaslClientFactory.java b/java/src/main/com/google/code/samples/oauth2/OAuth2SaslClientFactory.java similarity index 97% rename from java/com/google/code/samples/oauth2/OAuth2SaslClientFactory.java rename to java/src/main/com/google/code/samples/oauth2/OAuth2SaslClientFactory.java index f399ad5..d39a974 100644 --- a/java/com/google/code/samples/oauth2/OAuth2SaslClientFactory.java +++ b/java/src/main/com/google/code/samples/oauth2/OAuth2SaslClientFactory.java @@ -13,7 +13,7 @@ * limitations under the License. */ -package com.google.code.samples.oauth2; +package main.com.google.code.samples.oauth2; import java.util.Map; @@ -56,7 +56,7 @@ public SaslClient createSaslClient(String[] mechanisms, return new OAuth2SaslClient((String) props.get(OAUTH_TOKEN_PROP), callbackHandler); } - + public String[] getMechanismNames(Map props) { return new String[] {"XOAUTH2"}; } diff --git a/java/src/test/com/google/code/samples/oauth2/OAuth2AuthenticatorTests.java b/java/src/test/com/google/code/samples/oauth2/OAuth2AuthenticatorTests.java new file mode 100644 index 0000000..bf6fd04 --- /dev/null +++ b/java/src/test/com/google/code/samples/oauth2/OAuth2AuthenticatorTests.java @@ -0,0 +1,34 @@ +package main.com.google.code.samples.oauth2; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.Assume.assumeTrue; +import static org.junit.Assume.assumeFalse; +import org.junit.jupiter.api.BeforeAll; + +import org.junit.jupiter.api.Test; + +import main.com.google.code.samples.oauth2.OAuth2Authenticator; + +import org.junit.jupiter.api.DisplayName; + +public class OAuth2AuthenticatorTests { + + private final OAuth2Authenticator oAuth2Authenticator = new OAuth2Authenticator(); + + @DisplayName("Assumptions needed for tests") + @Test + void trueAssumption() { + assumeTrue((4 % 1) == 0); + assertEquals(8 % 3, 2); + assumeTrue((7 % 5) == 2); + assertEquals(7 % 3, 1); + assumeTrue(((2 + 2) % 2) == 0); + assumeFalse(((2 + 2) % 2) == 1); + assertEquals(2 + 2, 4); + } + + @BeforeAll + static void initAll() { + + } +} \ No newline at end of file diff --git a/java/src/test/com/google/code/samples/oauth2/OAuth2SaslClientFactoryTests.java b/java/src/test/com/google/code/samples/oauth2/OAuth2SaslClientFactoryTests.java new file mode 100644 index 0000000..d1db0a4 --- /dev/null +++ b/java/src/test/com/google/code/samples/oauth2/OAuth2SaslClientFactoryTests.java @@ -0,0 +1,31 @@ +package main.pro.freemania.code.samples.oauth2; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.Assume.assumeTrue; + +import org.junit.jupiter.api.Test; + +import com.google.common.annotations.VisibleForTesting; + +import main.com.google.code.samples.oauth2.OAuth2SaslClientFactory; + +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.DisplayName; +import main.com.google.code.samples.oauth2.OAuth2SaslClient; + +public class OAuth2SaslClientFactoryTests { + + private final OAuth2SaslClientFactory oAuth2SaslClientFactory = new OAuth2SaslClientFactory(); + + @BeforeAll + static void initAll() { + + } + + @Test + void getMechanismNameXOAUTH() + { + OAuth2SaslClient oAuth2SaslClient = new OAuth2SaslClient(); + assertEquals(oAuth2SaslClient.getMechanismName(), oAuth2SaslClientFactory.getMechanismNames(null)[0]); + } +} \ No newline at end of file diff --git a/java/src/test/com/google/code/samples/oauth2/OAuth2SaslClientTests.java b/java/src/test/com/google/code/samples/oauth2/OAuth2SaslClientTests.java new file mode 100644 index 0000000..04343bc --- /dev/null +++ b/java/src/test/com/google/code/samples/oauth2/OAuth2SaslClientTests.java @@ -0,0 +1,55 @@ +package main.pro.freemania.code.samples.oauth2; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.Assume.assumeTrue; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import org.apache.maven.surefire.api.testset.TestSetFailedException; +import org.junit.jupiter.api.BeforeAll; +import org.junit.runner.RunWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.DisplayName; +import javax.security.auth.callback.CallbackHandler; +import java.lang.reflect.Field; +import main.com.google.code.samples.oauth2.OAuth2SaslClient; + +class OAuth2SaslClientTests { + + private final OAuth2SaslClient oAuth2SaslClient = new OAuth2SaslClient(); + + @Test + public void oAuth2SaslClientgetNegotiatedPropertyThrows() throws TestSetFailedException { + + String message = new String("propertyToSet"); + + assertThrows(IllegalStateException.class + , () -> { + oAuth2SaslClient.getNegotiatedProperty(message); + }); + } + + @Test + public void hasInitialResponseTest() + { + assertTrue(oAuth2SaslClient.hasInitialResponse()); + } + + public Field getPrivateFieldObject (String nameOfField) throws NoSuchFieldException + { + return OAuth2SaslClient.class.getDeclaredField(nameOfField); + } + + @Test + public void getNegotiatedPropertyMechanismName() throws IllegalAccessException, NoSuchFieldException + { + Field f = getPrivateFieldObject("MechanismName"); + f.setAccessible(true); + String name = (String)f.get(this.oAuth2SaslClient); + assertEquals(oAuth2SaslClient.getMechanismName(), name); + } +} \ No newline at end of file diff --git a/pom.xml b/pom.xml new file mode 100644 index 0000000..f8522f8 --- /dev/null +++ b/pom.xml @@ -0,0 +1,114 @@ + + + 4.0.0 + com.google.code.samples.oauth2 + gmail-oauth2-tools + 1.0.1-SNAPSHOT + jar + https://github.com/FreedomFaighter/gmail-oauth2-tools + gmail-oauth2-tools + Tools and sample code for authenticating to Gmail with OAuth2. + + UTF-8 + UTF-8 + 11 + + + + + + org.junit.platform + junit-platform-runner + 1.10.0 + test + + + org.junit.jupiter + junit-jupiter-engine + 5.9.2 + test + + + javax.mail + mail + 1.4.7 + + + org.mockito + mockito-junit-jupiter + 5.4.0 + test + + + + org.apache.maven.plugins + maven-surefire-plugin + 3.1.2 + + + + java/src/main + java/src/test + ${project.basedir}/target + ${project.build.directory}/classes + ${project.artifactId}-${project.version} + ${project.build.directory}/test-classes + + + maven-compiler-plugin + + 11 + 11 + + + + maven-jar-plugin + 3.3.0 + + + default-jar + package + + jar + + + + + + + true + com.google.code.samples.oauth2.OAuth2Authenticator + + + + + + org.apache.maven.plugins + maven-surefire-plugin + 3.1.2 + + + **/*Tests.java + + + + + + org.junit.platform + junit-platform-surefire-provider + 1.3.2 + + + + org.apache.maven.surefire + surefire-api + 3.1.2 + + + + + + \ No newline at end of file diff --git a/python/oauth2.py b/python/oauth2.py index eb14632..43f7b8e 100755 --- a/python/oauth2.py +++ b/python/oauth2.py @@ -22,7 +22,8 @@ registering and for documentation of the APIs invoked by this code. NOTE: The OAuth2 OOB flow isn't a thing anymore. You will need to set the -application type to "Web application" and then add https://oauth2.dance/ as an +application type to "Web application" and then add +https://google.github.io/gmail-oauth2-tools/html/oauth2.dance.html as an authorised redirect URI. This is necessary for seeing the authorisation code on a page in your browser. @@ -134,7 +135,7 @@ def SetupOptionParser(): # Hardcoded redirect URI. -REDIRECT_URI = 'https://oauth2.dance/' +REDIRECT_URI = 'https://google.github.io/gmail-oauth2-tools/html/oauth2.dance.html' def AccountsUrl(command):