{"action":"created","comment":{"url":"https://api.github.com/repos/apache/kafka/pulls/comments/188429879","pull_request_review_id":120407250,"id":188429879,"diff_hunk":"@@ -0,0 +1,127 @@\n+/*\n+ * Licensed to the Apache Software Foundation (ASF) under one or more\n+ * contributor license agreements. See the NOTICE file distributed with\n+ * this work for additional information regarding copyright ownership.\n+ * The ASF licenses this file to You under the Apache License, Version 2.0\n+ * (the \"License\"); you may not use this file except in compliance with\n+ * the License. You may obtain a copy of the License at\n+ *\n+ *    http://www.apache.org/licenses/LICENSE-2.0\n+ *\n+ * Unless required by applicable law or agreed to in writing, software\n+ * distributed under the License is distributed on an \"AS IS\" BASIS,\n+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n+ * See the License for the specific language governing permissions and\n+ * limitations under the License.\n+ */\n+package org.apache.kafka.common.security.oauthbearer.internal.unsecured;\n+\n+import static org.junit.Assert.assertEquals;\n+import static org.junit.Assert.assertNotNull;\n+\n+import java.io.IOException;\n+import java.util.Arrays;\n+import java.util.Collections;\n+import java.util.HashMap;\n+import java.util.HashSet;\n+import java.util.List;\n+import java.util.Map;\n+\n+import javax.security.auth.callback.Callback;\n+import javax.security.auth.callback.UnsupportedCallbackException;\n+import javax.security.auth.login.LoginException;\n+\n+import org.apache.kafka.common.security.authenticator.TestJaasConfig;\n+import org.apache.kafka.common.security.oauthbearer.OAuthBearerLoginModule;\n+import org.apache.kafka.common.security.oauthbearer.OAuthBearerTokenCallback;\n+import org.apache.kafka.common.utils.MockTime;\n+import org.junit.Test;\n+\n+public class OAuthBearerUnsecuredLoginCallbackHandlerTest {\n+    @Test\n+    public void minimalToken() throws IOException, UnsupportedCallbackException {\n+        Map<String, String> options = new HashMap<>();\n+        String user = \"user\";\n+        options.put(\"unsecuredLoginStringClaim_sub\", user);\n+        MockTime mockTime = new MockTime();\n+        OAuthBearerUnsecuredLoginCallbackHandler callbackHandler = createCallbackHandler(options, mockTime);\n+        OAuthBearerTokenCallback callback = new OAuthBearerTokenCallback();\n+        callbackHandler.handle(new Callback[] {callback});\n+        OAuthBearerUnsecuredJws jws = (OAuthBearerUnsecuredJws) callback.token();\n+        assertNotNull(\"create token failed\", jws);\n+        long startMs = mockTime.milliseconds();\n+        confirmCorrectValues(jws, user, startMs, 1000 * 60 * 60);\n+        assertEquals(new HashSet<>(Arrays.asList(\"sub\", \"iat\", \"exp\")), jws.claims().keySet());\n+    }\n+\n+    @SuppressWarnings(\"unchecked\")\n+    @Test\n+    public void validOptionsWithExplicitOptionValues()\n+            throws IOException, UnsupportedCallbackException, LoginException {\n+        String explicitScope1 = \"scope1\";\n+        String explicitScope2 = \"scope2\";\n+        String explicitScopeClaimName = \"putScopeInHere\";\n+        String principalClaimName = \"principal\";\n+        final String[] scopeClaimNameOptionValues = {null, explicitScopeClaimName};\n+        for (String scopeClaimNameOptionValue : scopeClaimNameOptionValues) {\n+            Map<String, String> options = new HashMap<>();\n+            String user = \"user\";\n+            options.put(\"unsecuredLoginStringClaim_\" + principalClaimName, user);\n+            options.put(\"unsecuredLoginListClaim_\" + \"list\", \",1,2,\");\n+            options.put(\"unsecuredLoginListClaim_\" + \"emptyList1\", \"\");\n+            options.put(\"unsecuredLoginListClaim_\" + \"emptyList2\", \",\");\n+            options.put(\"unsecuredLoginNumberClaim_\" + \"number\", \"1\");\n+            long lifetmeSeconds = 10000;\n+            options.put(\"unsecuredLoginLifetimeSeconds\", String.valueOf(lifetmeSeconds));\n+            options.put(\"unsecuredLoginPrincipalClaimName\", principalClaimName);\n+            if (scopeClaimNameOptionValue != null)\n+                options.put(\"unsecuredLoginScopeClaimName\", scopeClaimNameOptionValue);\n+            String actualScopeClaimName = scopeClaimNameOptionValue == null ? \"scope\" : explicitScopeClaimName;\n+            options.put(\"unsecuredLoginListClaim_\" + actualScopeClaimName,\n+                    String.format(\"|%s|%s\", explicitScope1, explicitScope2));\n+            MockTime mockTime = new MockTime();\n+            OAuthBearerUnsecuredLoginCallbackHandler callbackHandler = createCallbackHandler(options, mockTime);\n+            OAuthBearerTokenCallback callback = new OAuthBearerTokenCallback();\n+            callbackHandler.handle(new Callback[] {callback});\n+            OAuthBearerUnsecuredJws jws = (OAuthBearerUnsecuredJws) callback.token();\n+            assertNotNull(\"create token failed\", jws);\n+            long startMs = mockTime.milliseconds();\n+            confirmCorrectValues(jws, user, startMs, lifetmeSeconds * 1000);\n+            Map<String, Object> claims = jws.claims();\n+            assertEquals(new HashSet<>(Arrays.asList(actualScopeClaimName, principalClaimName, \"iat\", \"exp\", \"number\",\n+                    \"list\", \"emptyList1\", \"emptyList2\")), claims.keySet());\n+            assertEquals(new HashSet<>(Arrays.asList(explicitScope1, explicitScope2)),\n+                    new HashSet<String>((List<String>) claims.get(actualScopeClaimName)));","path":"clients/src/test/java/org/apache/kafka/common/security/oauthbearer/internal/unsecured/OAuthBearerUnsecuredLoginCallbackHandlerTest.java","position":94,"original_position":94,"commit_id":"d2947b81dd15c189002693479ea9e3a91faa1ffc","original_commit_id":"27db5644cb50168e1a0aaab217fea26281cb85bd","user":{"login":"rondagostino","id":6076159,"avatar_url":"https://avatars0.githubusercontent.com/u/6076159?v=4","gravatar_id":"","url":"https://api.github.com/users/rondagostino","html_url":"https://github.com/rondagostino","followers_url":"https://api.github.com/users/rondagostino/followers","following_url":"https://api.github.com/users/rondagostino/following{/other_user}","gists_url":"https://api.github.com/users/rondagostino/gists{/gist_id}","starred_url":"https://api.github.com/users/rondagostino/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/rondagostino/subscriptions","organizations_url":"https://api.github.com/users/rondagostino/orgs","repos_url":"https://api.github.com/users/rondagostino/repos","events_url":"https://api.github.com/users/rondagostino/events{/privacy}","received_events_url":"https://api.github.com/users/rondagostino/received_events","type":"User","site_admin":false},"body":"👍 ","created_at":"2018-05-15T20:44:09Z","updated_at":"2018-05-15T20:44:09Z","html_url":"https://github.com/apache/kafka/pull/4994#discussion_r188429879","pull_request_url":"https://api.github.com/repos/apache/kafka/pulls/4994","author_association":"CONTRIBUTOR","_links":{"self":{"href":"https://api.github.com/repos/apache/kafka/pulls/comments/188429879"},"html":{"href":"https://github.com/apache/kafka/pull/4994#discussion_r188429879"},"pull_request":{"href":"https://api.github.com/repos/apache/kafka/pulls/4994"}},"in_reply_to_id":188192024},"pull_request":{"url":"https://api.github.com/repos/apache/kafka/pulls/4994","id":187098993,"html_url":"https://github.com/apache/kafka/pull/4994","diff_url":"https://github.com/apache/kafka/pull/4994.diff","patch_url":"https://github.com/apache/kafka/pull/4994.patch","issue_url":"https://api.github.com/repos/apache/kafka/issues/4994","number":4994,"state":"open","locked":false,"title":"KIP-255: OAuth Authentication via SASL/OAUTHBEARER","user":{"login":"rondagostino","id":6076159,"avatar_url":"https://avatars0.githubusercontent.com/u/6076159?v=4","gravatar_id":"","url":"https://api.github.com/users/rondagostino","html_url":"https://github.com/rondagostino","followers_url":"https://api.github.com/users/rondagostino/followers","following_url":"https://api.github.com/users/rondagostino/following{/other_user}","gists_url":"https://api.github.com/users/rondagostino/gists{/gist_id}","starred_url":"https://api.github.com/users/rondagostino/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/rondagostino/subscriptions","organizations_url":"https://api.github.com/users/rondagostino/orgs","repos_url":"https://api.github.com/users/rondagostino/repos","events_url":"https://api.github.com/users/rondagostino/events{/privacy}","received_events_url":"https://api.github.com/users/rondagostino/received_events","type":"User","site_admin":false},"body":"This KIP proposes to add the following functionality related to\r\nSASL/OAUTHBEARER:\r\n\r\n1) Allow clients (both brokers when SASL/OAUTHBEARER is the\r\ninter-broker protocol as well as non-broker clients) to flexibly\r\nretrieve an access token from an OAuth 2 authorization server\r\nbased on the declaration of a custom login CallbackHandler\r\nimplementation and have that access token transparently and\r\nautomatically transmitted to a broker for authentication.\r\n\r\n2) Allow brokers to flexibly validate provided access tokens when\r\na client establishes a connection based on the declaration of a\r\ncustom SASL Server CallbackHandler implementation.\r\n\r\n3) Provide implementations of the above retrieval and validation\r\nfeatures based on an unsecured JSON Web Token that function\r\nout-of-the-box with minimal configuration required (i.e.\r\nimplementations of the two types of callback handlers mentioned\r\nabove will be used by default with no need to explicitly declare\r\nthem).\r\n\r\n4) Allow clients (both brokers when SASL/OAUTHBEARER is the\r\ninter-broker protocol as well as non-broker clients) to\r\ntransparently retrieve a new access token in the background\r\nbefore the existing access token expires in case the client has\r\nto open new connections.\r\n\r\nSigned-off-by: Ron Dagostino <rndgstn@gmail.com>","created_at":"2018-05-10T04:34:40Z","updated_at":"2018-05-15T20:44:09Z","closed_at":null,"merged_at":null,"merge_commit_sha":"9901fea57cdda0a150925236167a0f04c0b59645","assignee":null,"assignees":[],"requested_reviewers":[],"requested_teams":[],"labels":[],"milestone":null,"commits_url":"https://api.github.com/repos/apache/kafka/pulls/4994/commits","review_comments_url":"https://api.github.com/repos/apache/kafka/pulls/4994/comments","review_comment_url":"https://api.github.com/repos/apache/kafka/pulls/comments{/number}","comments_url":"https://api.github.com/repos/apache/kafka/issues/4994/comments","statuses_url":"https://api.github.com/repos/apache/kafka/statuses/d2947b81dd15c189002693479ea9e3a91faa1ffc","head":{"label":"rondagostino:KAFKA-6562","ref":"KAFKA-6562","sha":"d2947b81dd15c189002693479ea9e3a91faa1ffc","user":{"login":"rondagostino","id":6076159,"avatar_url":"https://avatars0.githubusercontent.com/u/6076159?v=4","gravatar_id":"","url":"https://api.github.com/users/rondagostino","html_url":"https://github.com/rondagostino","followers_url":"https://api.github.com/users/rondagostino/followers","following_url":"https://api.github.com/users/rondagostino/following{/other_user}","gists_url":"https://api.github.com/users/rondagostino/gists{/gist_id}","starred_url":"https://api.github.com/users/rondagostino/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/rondagostino/subscriptions","organizations_url":"https://api.github.com/users/rondagostino/orgs","repos_url":"https://api.github.com/users/rondagostino/repos","events_url":"https://api.github.com/users/rondagostino/events{/privacy}","received_events_url":"https://api.github.com/users/rondagostino/received_events","type":"User","site_admin":false},"repo":{"id":117853091,"name":"kafka","full_name":"rondagostino/kafka","owner":{"login":"rondagostino","id":6076159,"avatar_url":"https://avatars0.githubusercontent.com/u/6076159?v=4","gravatar_id":"","url":"https://api.github.com/users/rondagostino","html_url":"https://github.com/rondagostino","followers_url":"https://api.github.com/users/rondagostino/followers","following_url":"https://api.github.com/users/rondagostino/following{/other_user}","gists_url":"https://api.github.com/users/rondagostino/gists{/gist_id}","starred_url":"https://api.github.com/users/rondagostino/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/rondagostino/subscriptions","organizations_url":"https://api.github.com/users/rondagostino/orgs","repos_url":"https://api.github.com/users/rondagostino/repos","events_url":"https://api.github.com/users/rondagostino/events{/privacy}","received_events_url":"https://api.github.com/users/rondagostino/received_events","type":"User","site_admin":false},"private":false,"html_url":"https://github.com/rondagostino/kafka","description":"Mirror of Apache Kafka","fork":true,"url":"https://api.github.com/repos/rondagostino/kafka","forks_url":"https://api.github.com/repos/rondagostino/kafka/forks","keys_url":"https://api.github.com/repos/rondagostino/kafka/keys{/key_id}","collaborators_url":"https://api.github.com/repos/rondagostino/kafka/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/rondagostino/kafka/teams","hooks_url":"https://api.github.com/repos/rondagostino/kafka/hooks","issue_events_url":"https://api.github.com/repos/rondagostino/kafka/issues/events{/number}","events_url":"https://api.github.com/repos/rondagostino/kafka/events","assignees_url":"https://api.github.com/repos/rondagostino/kafka/assignees{/user}","branches_url":"https://api.github.com/repos/rondagostino/kafka/branches{/branch}","tags_url":"https://api.github.com/repos/rondagostino/kafka/tags","blobs_url":"https://api.github.com/repos/rondagostino/kafka/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/rondagostino/kafka/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/rondagostino/kafka/git/refs{/sha}","trees_url":"https://api.github.com/repos/rondagostino/kafka/git/trees{/sha}","statuses_url":"https://api.github.com/repos/rondagostino/kafka/statuses/{sha}","languages_url":"https://api.github.com/repos/rondagostino/kafka/languages","stargazers_url":"https://api.github.com/repos/rondagostino/kafka/stargazers","contributors_url":"https://api.github.com/repos/rondagostino/kafka/contributors","subscribers_url":"https://api.github.com/repos/rondagostino/kafka/subscribers","subscription_url":"https://api.github.com/repos/rondagostino/kafka/subscription","commits_url":"https://api.github.com/repos/rondagostino/kafka/commits{/sha}","git_commits_url":"https://api.github.com/repos/rondagostino/kafka/git/commits{/sha}","comments_url":"https://api.github.com/repos/rondagostino/kafka/comments{/number}","issue_comment_url":"https://api.github.com/repos/rondagostino/kafka/issues/comments{/number}","contents_url":"https://api.github.com/repos/rondagostino/kafka/contents/{+path}","compare_url":"https://api.github.com/repos/rondagostino/kafka/compare/{base}...{head}","merges_url":"https://api.github.com/repos/rondagostino/kafka/merges","archive_url":"https://api.github.com/repos/rondagostino/kafka/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/rondagostino/kafka/downloads","issues_url":"https://api.github.com/repos/rondagostino/kafka/issues{/number}","pulls_url":"https://api.github.com/repos/rondagostino/kafka/pulls{/number}","milestones_url":"https://api.github.com/repos/rondagostino/kafka/milestones{/number}","notifications_url":"https://api.github.com/repos/rondagostino/kafka/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/rondagostino/kafka/labels{/name}","releases_url":"https://api.github.com/repos/rondagostino/kafka/releases{/id}","deployments_url":"https://api.github.com/repos/rondagostino/kafka/deployments","created_at":"2018-01-17T15:15:29Z","updated_at":"2018-05-14T00:48:46Z","pushed_at":"2018-05-14T18:42:40Z","git_url":"git://github.com/rondagostino/kafka.git","ssh_url":"git@github.com:rondagostino/kafka.git","clone_url":"https://github.com/rondagostino/kafka.git","svn_url":"https://github.com/rondagostino/kafka","homepage":null,"size":64797,"stargazers_count":0,"watchers_count":0,"language":"Java","has_issues":false,"has_projects":true,"has_downloads":true,"has_wiki":false,"has_pages":false,"forks_count":0,"mirror_url":null,"archived":false,"open_issues_count":0,"license":{"key":"apache-2.0","name":"Apache License 2.0","spdx_id":"Apache-2.0","url":"https://api.github.com/licenses/apache-2.0"},"forks":0,"open_issues":0,"watchers":0,"default_branch":"trunk"}},"base":{"label":"apache:trunk","ref":"trunk","sha":"9947cd40c617d4e12690b81a2d28624355580e34","user":{"login":"apache","id":47359,"avatar_url":"https://avatars0.githubusercontent.com/u/47359?v=4","gravatar_id":"","url":"https://api.github.com/users/apache","html_url":"https://github.com/apache","followers_url":"https://api.github.com/users/apache/followers","following_url":"https://api.github.com/users/apache/following{/other_user}","gists_url":"https://api.github.com/users/apache/gists{/gist_id}","starred_url":"https://api.github.com/users/apache/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/apache/subscriptions","organizations_url":"https://api.github.com/users/apache/orgs","repos_url":"https://api.github.com/users/apache/repos","events_url":"https://api.github.com/users/apache/events{/privacy}","received_events_url":"https://api.github.com/users/apache/received_events","type":"Organization","site_admin":false},"repo":{"id":2211243,"name":"kafka","full_name":"apache/kafka","owner":{"login":"apache","id":47359,"avatar_url":"https://avatars0.githubusercontent.com/u/47359?v=4","gravatar_id":"","url":"https://api.github.com/users/apache","html_url":"https://github.com/apache","followers_url":"https://api.github.com/users/apache/followers","following_url":"https://api.github.com/users/apache/following{/other_user}","gists_url":"https://api.github.com/users/apache/gists{/gist_id}","starred_url":"https://api.github.com/users/apache/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/apache/subscriptions","organizations_url":"https://api.github.com/users/apache/orgs","repos_url":"https://api.github.com/users/apache/repos","events_url":"https://api.github.com/users/apache/events{/privacy}","received_events_url":"https://api.github.com/users/apache/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/apache/kafka","description":"Mirror of Apache Kafka","fork":false,"url":"https://api.github.com/repos/apache/kafka","forks_url":"https://api.github.com/repos/apache/kafka/forks","keys_url":"https://api.github.com/repos/apache/kafka/keys{/key_id}","collaborators_url":"https://api.github.com/repos/apache/kafka/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/apache/kafka/teams","hooks_url":"https://api.github.com/repos/apache/kafka/hooks","issue_events_url":"https://api.github.com/repos/apache/kafka/issues/events{/number}","events_url":"https://api.github.com/repos/apache/kafka/events","assignees_url":"https://api.github.com/repos/apache/kafka/assignees{/user}","branches_url":"https://api.github.com/repos/apache/kafka/branches{/branch}","tags_url":"https://api.github.com/repos/apache/kafka/tags","blobs_url":"https://api.github.com/repos/apache/kafka/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/apache/kafka/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/apache/kafka/git/refs{/sha}","trees_url":"https://api.github.com/repos/apache/kafka/git/trees{/sha}","statuses_url":"https://api.github.com/repos/apache/kafka/statuses/{sha}","languages_url":"https://api.github.com/repos/apache/kafka/languages","stargazers_url":"https://api.github.com/repos/apache/kafka/stargazers","contributors_url":"https://api.github.com/repos/apache/kafka/contributors","subscribers_url":"https://api.github.com/repos/apache/kafka/subscribers","subscription_url":"https://api.github.com/repos/apache/kafka/subscription","commits_url":"https://api.github.com/repos/apache/kafka/commits{/sha}","git_commits_url":"https://api.github.com/repos/apache/kafka/git/commits{/sha}","comments_url":"https://api.github.com/repos/apache/kafka/comments{/number}","issue_comment_url":"https://api.github.com/repos/apache/kafka/issues/comments{/number}","contents_url":"https://api.github.com/repos/apache/kafka/contents/{+path}","compare_url":"https://api.github.com/repos/apache/kafka/compare/{base}...{head}","merges_url":"https://api.github.com/repos/apache/kafka/merges","archive_url":"https://api.github.com/repos/apache/kafka/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/apache/kafka/downloads","issues_url":"https://api.github.com/repos/apache/kafka/issues{/number}","pulls_url":"https://api.github.com/repos/apache/kafka/pulls{/number}","milestones_url":"https://api.github.com/repos/apache/kafka/milestones{/number}","notifications_url":"https://api.github.com/repos/apache/kafka/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/apache/kafka/labels{/name}","releases_url":"https://api.github.com/repos/apache/kafka/releases{/id}","deployments_url":"https://api.github.com/repos/apache/kafka/deployments","created_at":"2011-08-15T18:06:16Z","updated_at":"2018-05-15T16:56:35Z","pushed_at":"2018-05-15T20:17:54Z","git_url":"git://github.com/apache/kafka.git","ssh_url":"git@github.com:apache/kafka.git","clone_url":"https://github.com/apache/kafka.git","svn_url":"https://github.com/apache/kafka","homepage":null,"size":65376,"stargazers_count":8325,"watchers_count":8325,"language":"Java","has_issues":false,"has_projects":true,"has_downloads":true,"has_wiki":false,"has_pages":false,"forks_count":4848,"mirror_url":null,"archived":false,"open_issues_count":459,"license":{"key":"apache-2.0","name":"Apache License 2.0","spdx_id":"Apache-2.0","url":"https://api.github.com/licenses/apache-2.0"},"forks":4848,"open_issues":459,"watchers":8325,"default_branch":"trunk"}},"_links":{"self":{"href":"https://api.github.com/repos/apache/kafka/pulls/4994"},"html":{"href":"https://github.com/apache/kafka/pull/4994"},"issue":{"href":"https://api.github.com/repos/apache/kafka/issues/4994"},"comments":{"href":"https://api.github.com/repos/apache/kafka/issues/4994/comments"},"review_comments":{"href":"https://api.github.com/repos/apache/kafka/pulls/4994/comments"},"review_comment":{"href":"https://api.github.com/repos/apache/kafka/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/apache/kafka/pulls/4994/commits"},"statuses":{"href":"https://api.github.com/repos/apache/kafka/statuses/d2947b81dd15c189002693479ea9e3a91faa1ffc"}},"author_association":"CONTRIBUTOR"}}
{"actor":{"display_login":"rondagostino"}}