diff --git a/iotdb-core/datanode/pom.xml b/iotdb-core/datanode/pom.xml
index 8b5302048964f..e894851fc11ca 100644
--- a/iotdb-core/datanode/pom.xml
+++ b/iotdb-core/datanode/pom.xml
@@ -277,33 +277,11 @@
mockito-core
test
-
-
- io.jsonwebtoken
- jjwt-impl
- test
-
-
-
- io.jsonwebtoken
- jjwt-jackson
- test
-
-
- net.minidev
- json-smart
- test
-
org.apache.ratis
ratis-thirdparty-misc
runtime
-
- com.nimbusds
- oauth2-oidc-sdk
- test
-
org.powermock
powermock-core
@@ -488,11 +466,6 @@
org.apache.iotdb:isession
-
-
- io.jsonwebtoken:jjwt-impl
- io.jsonwebtoken:jjwt-jackson
-
diff --git a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/auth/authorizer/OpenIdAuthorizerTest.java b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/auth/authorizer/OpenIdAuthorizerTest.java
deleted file mode 100644
index 196cc80e5b6da..0000000000000
--- a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/auth/authorizer/OpenIdAuthorizerTest.java
+++ /dev/null
@@ -1,114 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF 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
- *
- * 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.
- */
-package org.apache.iotdb.db.auth.authorizer;
-
-import org.apache.iotdb.commons.auth.AuthException;
-import org.apache.iotdb.commons.auth.authorizer.OpenIdAuthorizer;
-import org.apache.iotdb.commons.conf.CommonConfig;
-import org.apache.iotdb.commons.conf.CommonDescriptor;
-import org.apache.iotdb.db.utils.EnvironmentUtils;
-
-import com.nimbusds.oauth2.sdk.ParseException;
-import com.nimbusds.oauth2.sdk.util.JSONObjectUtils;
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Ignore;
-import org.junit.Test;
-
-import java.io.IOException;
-import java.net.URISyntaxException;
-
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertTrue;
-
-public class OpenIdAuthorizerTest {
-
- private static final String OPEN_ID_PUBLIC_JWK =
- "{\"kty\":\"RSA\",\"x5t#S256\":\"TZFbbj6HsRU28HYvrcVnDs03KreV3DE24-Cxb9EPdS4\",\"e\":\"AQAB\",\"use\":\"sig\",\"x5t\":\"l_N2UlC_a624iu5eYFypnB1Wr20\",\"kid\":\"q1-Wm0ozQ5O0mQH8-SJap2ZcN4MmucWwnQWKYxZJ4ow\",\"x5c\":[\"MIICmTCCAYECBgFyRdXW2DANBgkqhkiG9w0BAQsFADAQMQ4wDAYDVQQDDAVJb1REQjAeFw0yMDA1MjQwODM3MjJaFw0zMDA1MjQwODM5MDJaMBAxDjAMBgNVBAMMBUlvVERCMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAozDCZTVc9946VvhZ6E\\/OP8Yx6tJe0i9GR2Q9jR9S3jQoo0haT\\/P1b\\/zvQK52qA1xj6tBVg64xl3+LUxtCvh3HfAM5Q3PeSa0e2MkZaKCt335lKnKCSuaQGYoHULmg\\/FDOgCA0wJYOonGGJkgWmkzSAzdnHmBATosTl0XkBXHTdFOq5HaKw+bfghYp5097Gkl\\/Dp4sixVjIWLTh5l9diy4D\\/XKxadGumPCmTOS5E7y92jiHE64XFe1Q7v1qD+qKJKFvamAMIFPGBKegIajt42IcOIcIaJZnM1lBZApq1a\\/E6oL24QnP\\/j2e9coseDtGNywaADQdO8PaJadH\\/BV4aPCwIDAQABMA0GCSqGSIb3DQEBCwUAA4IBAQBX4rsWPIAwgSK6BEZmtEkh\\/FMfZtkvCFANpwkCX5Pph8yuk\\/8xrvx30yb4fIgqsxxQk6H+Q1qptm1cXs0tNu1yft+t+B2VuVjrWtkCkV0hAy6eZcdW411Pt523pHoOTxg6ehQd5DsvCIlsvWo83ePTKME+092vfs3irfQcRzc5xINdpopSvZlZuQ83tNEJY8gWvspQZr+uj8AP2x6w0BOrPJIiLlV+peNJuD3UgJKlSfOueKbKeM1kIVOG\\/a2AoEkBgqktnaIWzkXbk475\\/0xfGegsSZrxGR3\\/SA3jegS0sHFCY7\\/Ie\\/UvDgqMjd207oT64jxEGrd4mObxOx7aS0tp\"],\"alg\":\"RS256\",\"n\":\"ozDCZTVc9946VvhZ6E_OP8Yx6tJe0i9GR2Q9jR9S3jQoo0haT_P1b_zvQK52qA1xj6tBVg64xl3-LUxtCvh3HfAM5Q3PeSa0e2MkZaKCt335lKnKCSuaQGYoHULmg_FDOgCA0wJYOonGGJkgWmkzSAzdnHmBATosTl0XkBXHTdFOq5HaKw-bfghYp5097Gkl_Dp4sixVjIWLTh5l9diy4D_XKxadGumPCmTOS5E7y92jiHE64XFe1Q7v1qD-qKJKFvamAMIFPGBKegIajt42IcOIcIaJZnM1lBZApq1a_E6oL24QnP_j2e9coseDtGNywaADQdO8PaJadH_BV4aPCw\"}";
- private static CommonConfig config;
-
- @Before
- public void setUp() throws Exception {
- EnvironmentUtils.envSetUp();
- config = CommonDescriptor.getInstance().getConfig();
- }
-
- @After
- public void tearDown() throws Exception {
- EnvironmentUtils.cleanEnv();
- }
-
- @Test
- public void loginWithJWT() throws AuthException, ParseException {
- String jwt =
- "eyJhbGciOiJSUzI1NiIsInR5cCIgOiAiSldUIiwia2lkIiA6ICJxMS1XbTBvelE1TzBtUUg4LVNKYXAyWmNONE1tdWNXd25RV0tZeFpKNG93In0.eyJleHAiOjE1OTAzMTcxNzYsImlhdCI6MTU5MDMxNjg3NiwianRpIjoiY2MyNWQ3MDAtYjc5NC00OTA4LTg0OGUtOTRhNzYzNmM5YzQxIiwiaXNzIjoiaHR0cDovL2F1dGguZGVtby5wcmFnbWF0aWNpbmR1c3RyaWVzLmRlL2F1dGgvcmVhbG1zL0lvVERCIiwiYXVkIjoiYWNjb3VudCIsInN1YiI6Ijg2YWRmNGIzLWE4ZTUtNDc1NC1iNWEwLTQ4OGI0OWY0M2VkMiIsInR5cCI6IkJlYXJlciIsImF6cCI6ImlvdGRiIiwic2Vzc2lvbl9zdGF0ZSI6Ijk0ZmI5NGZjLTg3YTMtNDg4Ny04M2Q3LWE5MmQ1MzMzOTMzMCIsImFjciI6IjEiLCJyZWFsbV9hY2Nlc3MiOnsicm9sZXMiOlsib2ZmbGluZV9hY2Nlc3MiLCJ1bWFfYXV0aG9yaXphdGlvbiJdfSwicmVzb3VyY2VfYWNjZXNzIjp7ImFjY291bnQiOnsicm9sZXMiOlsibWFuYWdlLWFjY291bnQiLCJtYW5hZ2UtYWNjb3VudC1saW5rcyIsInZpZXctcHJvZmlsZSJdfX0sInNjb3BlIjoiZW1haWwgcHJvZmlsZSIsImNsaWVudEhvc3QiOiIxOTIuMTY4LjE2OS4yMSIsImNsaWVudElkIjoiaW90ZGIiLCJlbWFpbF92ZXJpZmllZCI6ZmFsc2UsInByZWZlcnJlZF91c2VybmFtZSI6InNlcnZpY2UtYWNjb3VudC1pb3RkYiIsImNsaWVudEFkZHJlc3MiOiIxOTIuMTY4LjE2OS4yMSJ9.GxQFltm1PrZzVL7rR6K-GpQINFLymjqAxxoDt_DGfQEMt61M6ebmx2oHiP_3G0HDSl7sbamajQbbRrfyTg--emBC2wfhdZ7v_7O0qWC60Yd8cWZ9qxwqwTFKYb8a0Z6_TeH9-vUmsy6kp2BfJZXq3mSy0My21VGUAXRmWTbghiM4RFoHKjAZVhsPHWelFmtLftYPdOGxv-7c9iUOVh_W-nOcCNRJpYY7BEjUYN24TsjvCEwWDQWD9E29LMYfA6LNeG0KdL9Jvqad4bc2FTJn9TaCnJMCiAJ7wEEiotqhXn70uEBWYxGXIVlm3vn3MDe3pTKA2TZy7U5xcrE7S8aGMg";
-
- OpenIdAuthorizer authorizer = new OpenIdAuthorizer(JSONObjectUtils.parse(OPEN_ID_PUBLIC_JWK));
- boolean login = authorizer.login(jwt, null, false);
-
- assertTrue(login);
- }
-
- @Test
- public void isAdmin_hasAccess() throws AuthException, ParseException {
- // IOTDB_ADMIN = true
- String jwt =
- "eyJhbGciOiJSUzI1NiIsInR5cCIgOiAiSldUIiwia2lkIiA6ICJxMS1XbTBvelE1TzBtUUg4LVNKYXAyWmNONE1tdWNXd25RV0tZeFpKNG93In0.eyJleHAiOjE1OTAzMjM5MjgsImlhdCI6MTU5MDMyMzYyOCwianRpIjoiZGQ5ZDZhNmItZjgzOC00Mjk3LTg5YWUtMjdlZTgxNzVhMThiIiwiaXNzIjoiaHR0cDovL2F1dGguZGVtby5wcmFnbWF0aWNpbmR1c3RyaWVzLmRlL2F1dGgvcmVhbG1zL0lvVERCIiwiYXVkIjoiYWNjb3VudCIsInN1YiI6ImJhMzJlNDcxLWM3NzItNGIzMy04ZGE2LTZmZThhY2RhMDA3MyIsInR5cCI6IkJlYXJlciIsImF6cCI6ImlvdGRiIiwic2Vzc2lvbl9zdGF0ZSI6IjViZDRhNmM5LTBmYzItNGIxMy05Y2QxLTFhN2NjMzk3NjVhNyIsImFjciI6IjEiLCJyZWFsbV9hY2Nlc3MiOnsicm9sZXMiOlsib2ZmbGluZV9hY2Nlc3MiLCJ1bWFfYXV0aG9yaXphdGlvbiIsImlvdGRiX2FkbWluIl19LCJyZXNvdXJjZV9hY2Nlc3MiOnsiYWNjb3VudCI6eyJyb2xlcyI6WyJtYW5hZ2UtYWNjb3VudCIsIm1hbmFnZS1hY2NvdW50LWxpbmtzIiwidmlldy1wcm9maWxlIl19fSwic2NvcGUiOiJlbWFpbCBwcm9maWxlIiwiZW1haWxfdmVyaWZpZWQiOmZhbHNlLCJwcmVmZXJyZWRfdXNlcm5hbWUiOiJ1c2VyIn0.LthDI93A3jqATc_8Fm0ismqQSN62TUknD6ara6w43eao2hY6KBoMXVY1c6SXSy8hhQeHLiWpopjJE6fsG9xbaV2Gs24SJYnP4DkHvhULlBJ_PUjFy18QxzeexwYK358a99eVHG_8yu-f2kN3mJslOSrlny8oZDxeSxUi9wYNIuQFeLPmGfSISVFn_5V8lpoUAHeENmf9h8mSyEcUHGqtZfVm5zEYIbPPSBqvNei2NvKAFle6qoaJ1l13dpbw39KkOtIUF8dJ7v8XY_xgO2GXCJCvZ5YGr-q4UnA9v_GM3h3vSa5dyCuG0HXBmAujxSxywzPl5RB_QCTiYcTm7MGKLg";
-
- OpenIdAuthorizer authorizer = new OpenIdAuthorizer(JSONObjectUtils.parse(OPEN_ID_PUBLIC_JWK));
- boolean admin = authorizer.isAdmin(jwt);
-
- assertTrue(admin);
- }
-
- @Test
- public void isAdmin_noAdminClaim() throws AuthException, ParseException {
- // IOTDB_ADMIN = false
- String jwt =
- "eyJhbGciOiJSUzI1NiIsInR5cCIgOiAiSldUIiwia2lkIiA6ICJxMS1XbTBvelE1TzBtUUg4LVNKYXAyWmNONE1tdWNXd25RV0tZeFpKNG93In0.eyJleHAiOjE1OTAzMTcxNzYsImlhdCI6MTU5MDMxNjg3NiwianRpIjoiY2MyNWQ3MDAtYjc5NC00OTA4LTg0OGUtOTRhNzYzNmM5YzQxIiwiaXNzIjoiaHR0cDovL2F1dGguZGVtby5wcmFnbWF0aWNpbmR1c3RyaWVzLmRlL2F1dGgvcmVhbG1zL0lvVERCIiwiYXVkIjoiYWNjb3VudCIsInN1YiI6Ijg2YWRmNGIzLWE4ZTUtNDc1NC1iNWEwLTQ4OGI0OWY0M2VkMiIsInR5cCI6IkJlYXJlciIsImF6cCI6ImlvdGRiIiwic2Vzc2lvbl9zdGF0ZSI6Ijk0ZmI5NGZjLTg3YTMtNDg4Ny04M2Q3LWE5MmQ1MzMzOTMzMCIsImFjciI6IjEiLCJyZWFsbV9hY2Nlc3MiOnsicm9sZXMiOlsib2ZmbGluZV9hY2Nlc3MiLCJ1bWFfYXV0aG9yaXphdGlvbiJdfSwicmVzb3VyY2VfYWNjZXNzIjp7ImFjY291bnQiOnsicm9sZXMiOlsibWFuYWdlLWFjY291bnQiLCJtYW5hZ2UtYWNjb3VudC1saW5rcyIsInZpZXctcHJvZmlsZSJdfX0sInNjb3BlIjoiZW1haWwgcHJvZmlsZSIsImNsaWVudEhvc3QiOiIxOTIuMTY4LjE2OS4yMSIsImNsaWVudElkIjoiaW90ZGIiLCJlbWFpbF92ZXJpZmllZCI6ZmFsc2UsInByZWZlcnJlZF91c2VybmFtZSI6InNlcnZpY2UtYWNjb3VudC1pb3RkYiIsImNsaWVudEFkZHJlc3MiOiIxOTIuMTY4LjE2OS4yMSJ9.GxQFltm1PrZzVL7rR6K-GpQINFLymjqAxxoDt_DGfQEMt61M6ebmx2oHiP_3G0HDSl7sbamajQbbRrfyTg--emBC2wfhdZ7v_7O0qWC60Yd8cWZ9qxwqwTFKYb8a0Z6_TeH9-vUmsy6kp2BfJZXq3mSy0My21VGUAXRmWTbghiM4RFoHKjAZVhsPHWelFmtLftYPdOGxv-7c9iUOVh_W-nOcCNRJpYY7BEjUYN24TsjvCEwWDQWD9E29LMYfA6LNeG0KdL9Jvqad4bc2FTJn9TaCnJMCiAJ7wEEiotqhXn70uEBWYxGXIVlm3vn3MDe3pTKA2TZy7U5xcrE7S8aGMg";
-
- OpenIdAuthorizer authorizer = new OpenIdAuthorizer(JSONObjectUtils.parse(OPEN_ID_PUBLIC_JWK));
- boolean admin = authorizer.isAdmin(jwt);
-
- assertFalse(admin);
- }
-
- /** Can be run manually as long as the site below is active... */
- @Test
- @Ignore("We have to find a way to test this against a defined OIDC Provider")
- public void fetchMetadata()
- throws ParseException, IOException, URISyntaxException, AuthException {
- OpenIdAuthorizer openIdAuthorizer =
- new OpenIdAuthorizer("https://auth.demo.pragmaticindustries.de/auth/realms/IoTDB/");
- boolean login =
- openIdAuthorizer.login(
- "eyJhbGciOiJSUzI1NiIsInR5cCIgOiAiSldUIiwia2lkIiA6ICJxMS1XbTBvelE1TzBtUUg4LVNKYXAyWmNONE1tdWNXd25RV0tZeFpKNG93In0.eyJleHAiOjE1OTAzMTcxNzYsImlhdCI6MTU5MDMxNjg3NiwianRpIjoiY2MyNWQ3MDAtYjc5NC00OTA4LTg0OGUtOTRhNzYzNmM5YzQxIiwiaXNzIjoiaHR0cDovL2F1dGguZGVtby5wcmFnbWF0aWNpbmR1c3RyaWVzLmRlL2F1dGgvcmVhbG1zL0lvVERCIiwiYXVkIjoiYWNjb3VudCIsInN1YiI6Ijg2YWRmNGIzLWE4ZTUtNDc1NC1iNWEwLTQ4OGI0OWY0M2VkMiIsInR5cCI6IkJlYXJlciIsImF6cCI6ImlvdGRiIiwic2Vzc2lvbl9zdGF0ZSI6Ijk0ZmI5NGZjLTg3YTMtNDg4Ny04M2Q3LWE5MmQ1MzMzOTMzMCIsImFjciI6IjEiLCJyZWFsbV9hY2Nlc3MiOnsicm9sZXMiOlsib2ZmbGluZV9hY2Nlc3MiLCJ1bWFfYXV0aG9yaXphdGlvbiJdfSwicmVzb3VyY2VfYWNjZXNzIjp7ImFjY291bnQiOnsicm9sZXMiOlsibWFuYWdlLWFjY291bnQiLCJtYW5hZ2UtYWNjb3VudC1saW5rcyIsInZpZXctcHJvZmlsZSJdfX0sInNjb3BlIjoiZW1haWwgcHJvZmlsZSIsImNsaWVudEhvc3QiOiIxOTIuMTY4LjE2OS4yMSIsImNsaWVudElkIjoiaW90ZGIiLCJlbWFpbF92ZXJpZmllZCI6ZmFsc2UsInByZWZlcnJlZF91c2VybmFtZSI6InNlcnZpY2UtYWNjb3VudC1pb3RkYiIsImNsaWVudEFkZHJlc3MiOiIxOTIuMTY4LjE2OS4yMSJ9.GxQFltm1PrZzVL7rR6K-GpQINFLymjqAxxoDt_DGfQEMt61M6ebmx2oHiP_3G0HDSl7sbamajQbbRrfyTg--emBC2wfhdZ7v_7O0qWC60Yd8cWZ9qxwqwTFKYb8a0Z6_TeH9-vUmsy6kp2BfJZXq3mSy0My21VGUAXRmWTbghiM4RFoHKjAZVhsPHWelFmtLftYPdOGxv-7c9iUOVh_W-nOcCNRJpYY7BEjUYN24TsjvCEwWDQWD9E29LMYfA6LNeG0KdL9Jvqad4bc2FTJn9TaCnJMCiAJ7wEEiotqhXn70uEBWYxGXIVlm3vn3MDe3pTKA2TZy7U5xcrE7S8aGMg",
- "",
- false);
- assertTrue(login);
- config.setOpenIdProviderUrl("https://auth.demo.pragmaticindustries.de/auth/realms/IoTDB/");
- OpenIdAuthorizer openIdAuthorizer1 = new OpenIdAuthorizer();
- login =
- openIdAuthorizer1.login(
- "eyJhbGciOiJSUzI1NiIsInR5cCIgOiAiSldUIiwia2lkIiA6ICJxMS1XbTBvelE1TzBtUUg4LVNKYXAyWmNONE1tdWNXd25RV0tZeFpKNG93In0.eyJleHAiOjE1OTAzMTcxNzYsImlhdCI6MTU5MDMxNjg3NiwianRpIjoiY2MyNWQ3MDAtYjc5NC00OTA4LTg0OGUtOTRhNzYzNmM5YzQxIiwiaXNzIjoiaHR0cDovL2F1dGguZGVtby5wcmFnbWF0aWNpbmR1c3RyaWVzLmRlL2F1dGgvcmVhbG1zL0lvVERCIiwiYXVkIjoiYWNjb3VudCIsInN1YiI6Ijg2YWRmNGIzLWE4ZTUtNDc1NC1iNWEwLTQ4OGI0OWY0M2VkMiIsInR5cCI6IkJlYXJlciIsImF6cCI6ImlvdGRiIiwic2Vzc2lvbl9zdGF0ZSI6Ijk0ZmI5NGZjLTg3YTMtNDg4Ny04M2Q3LWE5MmQ1MzMzOTMzMCIsImFjciI6IjEiLCJyZWFsbV9hY2Nlc3MiOnsicm9sZXMiOlsib2ZmbGluZV9hY2Nlc3MiLCJ1bWFfYXV0aG9yaXphdGlvbiJdfSwicmVzb3VyY2VfYWNjZXNzIjp7ImFjY291bnQiOnsicm9sZXMiOlsibWFuYWdlLWFjY291bnQiLCJtYW5hZ2UtYWNjb3VudC1saW5rcyIsInZpZXctcHJvZmlsZSJdfX0sInNjb3BlIjoiZW1haWwgcHJvZmlsZSIsImNsaWVudEhvc3QiOiIxOTIuMTY4LjE2OS4yMSIsImNsaWVudElkIjoiaW90ZGIiLCJlbWFpbF92ZXJpZmllZCI6ZmFsc2UsInByZWZlcnJlZF91c2VybmFtZSI6InNlcnZpY2UtYWNjb3VudC1pb3RkYiIsImNsaWVudEFkZHJlc3MiOiIxOTIuMTY4LjE2OS4yMSJ9.GxQFltm1PrZzVL7rR6K-GpQINFLymjqAxxoDt_DGfQEMt61M6ebmx2oHiP_3G0HDSl7sbamajQbbRrfyTg--emBC2wfhdZ7v_7O0qWC60Yd8cWZ9qxwqwTFKYb8a0Z6_TeH9-vUmsy6kp2BfJZXq3mSy0My21VGUAXRmWTbghiM4RFoHKjAZVhsPHWelFmtLftYPdOGxv-7c9iUOVh_W-nOcCNRJpYY7BEjUYN24TsjvCEwWDQWD9E29LMYfA6LNeG0KdL9Jvqad4bc2FTJn9TaCnJMCiAJ7wEEiotqhXn70uEBWYxGXIVlm3vn3MDe3pTKA2TZy7U5xcrE7S8aGMg",
- "",
- false);
- assertTrue(login);
- }
-}
diff --git a/iotdb-core/node-commons/pom.xml b/iotdb-core/node-commons/pom.xml
index 24f33d0de8bec..7c54c64b32873 100644
--- a/iotdb-core/node-commons/pom.xml
+++ b/iotdb-core/node-commons/pom.xml
@@ -149,6 +149,16 @@
io.jsonwebtoken
jjwt-api
+
+ io.jsonwebtoken
+ jjwt-impl
+ test
+
+
+ io.jsonwebtoken
+ jjwt-jackson
+ test
+
com.nimbusds
oauth2-oidc-sdk
@@ -267,6 +277,16 @@
+
+ org.apache.maven.plugins
+ maven-dependency-plugin
+
+
+ io.jsonwebtoken:jjwt-impl
+ io.jsonwebtoken:jjwt-jackson
+
+
+
org.apache.maven.plugins
maven-surefire-plugin
diff --git a/iotdb-core/node-commons/src/assembly/resources/conf/iotdb-system.properties.template b/iotdb-core/node-commons/src/assembly/resources/conf/iotdb-system.properties.template
index 3f1c4ab41ffaf..5d5cce4123f8c 100644
--- a/iotdb-core/node-commons/src/assembly/resources/conf/iotdb-system.properties.template
+++ b/iotdb-core/node-commons/src/assembly/resources/conf/iotdb-system.properties.template
@@ -1753,6 +1753,12 @@ authorizer_provider_class=org.apache.iotdb.commons.auth.authorizer.LocalFileAuth
# Privilege: SECURITY
openID_url=
+# If OpenIdAuthorizer is enabled, then openID_audience must contain the IoTDB client ID
+# or a comma-separated allowlist of accepted audiences.
+# effectiveMode: restart
+# Privilege: SECURITY
+openID_audience=
+
# encryption provider class
# effectiveMode: first_start
iotdb_server_encrypt_decrypt_provider=org.apache.iotdb.commons.security.encrypt.MessageDigestEncrypt
diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/auth/authorizer/OpenIdAuthorizer.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/auth/authorizer/OpenIdAuthorizer.java
index ee66ee5bced95..f073984060999 100644
--- a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/auth/authorizer/OpenIdAuthorizer.java
+++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/auth/authorizer/OpenIdAuthorizer.java
@@ -45,22 +45,30 @@
import java.net.URISyntaxException;
import java.net.URL;
import java.security.interfaces.RSAPublicKey;
+import java.util.Arrays;
+import java.util.Collections;
import java.util.HashMap;
+import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Scanner;
+import java.util.Set;
import java.util.UUID;
+import java.util.stream.Collectors;
/** Uses an OpenID Connect provider for Authorization / Authentication. */
public class OpenIdAuthorizer extends BasicAuthorizer {
private static final Logger logger = LoggerFactory.getLogger(OpenIdAuthorizer.class);
+ private static final long MAX_CLOCK_SKEW_SECONDS = 300;
public static final String IOTDB_ADMIN_ROLE_NAME = "iotdb_admin";
public static final String OPENID_USER_PREFIX = "openid-";
private static final CommonConfig config = CommonDescriptor.getInstance().getConfig();
private final RSAPublicKey providerKey;
+ private final String expectedIssuer;
+ private final Set acceptedAudiences;
/** Stores all claims to the respective user */
private final Map loggedClaims = new HashMap<>();
@@ -70,6 +78,11 @@ public OpenIdAuthorizer() throws AuthException, ParseException, IOException, URI
}
public OpenIdAuthorizer(JSONObject jwk) throws AuthException {
+ this(jwk, null, Collections.emptySet());
+ }
+
+ public OpenIdAuthorizer(JSONObject jwk, String expectedIssuer, Set acceptedAudiences)
+ throws AuthException {
super(
new LocalFileUserManager(config.getUserFolder()),
new LocalFileRoleManager(config.getRoleFolder()));
@@ -79,15 +92,20 @@ public OpenIdAuthorizer(JSONObject jwk) throws AuthException {
throw new AuthException(
TSStatusCode.INIT_AUTH_ERROR, "Unable to get OIDC Provider Key from JWK " + jwk, e);
}
- logger.info("Initialized with providerKey: {}", providerKey);
+ this.expectedIssuer = expectedIssuer;
+ this.acceptedAudiences = Collections.unmodifiableSet(new HashSet<>(acceptedAudiences));
}
public OpenIdAuthorizer(String providerUrl)
throws AuthException, URISyntaxException, ParseException, IOException {
- this(getJwkFromProvider(providerUrl));
+ this(loadProviderContext(providerUrl));
+ }
+
+ private OpenIdAuthorizer(ProviderContext providerContext) throws AuthException {
+ this(providerContext.jwk, providerContext.issuer, providerContext.acceptedAudiences);
}
- private static JSONObject getJwkFromProvider(String providerUrl)
+ private static ProviderContext loadProviderContext(String providerUrl)
throws URISyntaxException, IOException, ParseException, AuthException {
if (providerUrl == null) {
throw new IllegalArgumentException("OpenID Connect Provider URI must be given!");
@@ -96,12 +114,23 @@ private static JSONObject getJwkFromProvider(String providerUrl)
// Fetch Metadata
OIDCProviderMetadata providerMetadata = fetchMetadata(providerUrl);
- logger.debug("Using Provider Metadata: {}", providerMetadata);
+ Set acceptedAudiences = parseAudiences(config.getOpenIdAudience());
+ if (acceptedAudiences.isEmpty()) {
+ throw new AuthException(
+ TSStatusCode.INIT_AUTH_ERROR,
+ "openID_audience must be configured when OpenIdAuthorizer is enabled");
+ }
+
+ String issuer =
+ providerMetadata.getIssuer() == null ? null : providerMetadata.getIssuer().getValue();
+ if (issuer == null || issuer.isEmpty()) {
+ throw new AuthException(
+ TSStatusCode.INIT_AUTH_ERROR, "OIDC provider metadata does not contain an issuer");
+ }
try {
URL url = new URI(providerMetadata.getJWKSetURI().toString()).toURL();
- logger.debug("Using url {}", url);
- return getProviderRsaJwk(url.openStream());
+ return new ProviderContext(getProviderRsaJwk(url.openStream()), issuer, acceptedAudiences);
} catch (IOException e) {
throw new AuthException(TSStatusCode.INIT_AUTH_ERROR, "Unable to start the Auth", e);
}
@@ -161,14 +190,9 @@ public boolean login(String token, String password, final boolean useEncryptedPa
try {
claims = validateToken(token);
} catch (JwtException e) {
- logger.error("Unable to login the user with Username (token) {}", token, e);
+ logger.error("Unable to login the user with Username (token), {}", e.getMessage());
return false;
}
- logger.debug("JWT was validated successfully!");
- logger.debug("ID: {}", claims.getId());
- logger.debug("Subject: {}", claims.getSubject());
- logger.debug("Issuer: {}", claims.getIssuer());
- logger.debug("Expiration: {}", claims.getExpiration());
// Create User if not exists
String iotdbUsername = getUsername(claims);
if (!super.listAllUsers().contains(iotdbUsername)) {
@@ -193,13 +217,51 @@ public String getIoTDBUserName(String token) {
}
private Claims validateToken(String token) {
- return Jwts.parser()
- // Basically ignore the Expiration Date, if there is any???
- .clockSkewSeconds(Long.MAX_VALUE / 1000)
- .verifyWith(providerKey)
- .build()
- .parseSignedClaims(token)
- .getPayload();
+ Claims claims =
+ Jwts.parser()
+ .clockSkewSeconds(MAX_CLOCK_SKEW_SECONDS)
+ .verifyWith(providerKey)
+ .build()
+ .parseSignedClaims(token)
+ .getPayload();
+ validateClaims(claims);
+ return claims;
+ }
+
+ private void validateClaims(Claims claims) {
+ if (expectedIssuer != null && !expectedIssuer.equals(claims.getIssuer())) {
+ throw new JwtException(
+ String.format("Unexpected issuer %s, expected %s", claims.getIssuer(), expectedIssuer));
+ }
+ if (!acceptedAudiences.isEmpty() && !hasAcceptedAudience(claims.get("aud"))) {
+ throw new JwtException(
+ String.format(
+ "Unexpected audience %s, expected one of %s", claims.get("aud"), acceptedAudiences));
+ }
+ }
+
+ private boolean hasAcceptedAudience(Object audienceClaim) {
+ if (audienceClaim instanceof String) {
+ return acceptedAudiences.contains(audienceClaim);
+ }
+ if (audienceClaim instanceof List>) {
+ for (Object audience : (List>) audienceClaim) {
+ if (audience instanceof String && acceptedAudiences.contains(audience)) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+ private static Set parseAudiences(String configuredAudiences) {
+ if (configuredAudiences == null || configuredAudiences.trim().isEmpty()) {
+ return Collections.emptySet();
+ }
+ return Arrays.stream(configuredAudiences.split(","))
+ .map(String::trim)
+ .filter(audience -> !audience.isEmpty())
+ .collect(Collectors.toCollection(HashSet::new));
}
private String getUsername(Claims claims) {
@@ -237,7 +299,7 @@ public boolean isAdmin(String token) {
try {
claims = validateToken(token);
} catch (JwtException e) {
- logger.warn("Unable to validate token {}!", token, e);
+ logger.warn("Unable to validate token! {}", e.getMessage());
return false;
}
}
@@ -258,6 +320,18 @@ public boolean checkUserPrivileges(String userName, PrivilegeUnion union) throws
return isAdmin(userName);
}
+ private static class ProviderContext {
+ private final JSONObject jwk;
+ private final String issuer;
+ private final Set acceptedAudiences;
+
+ private ProviderContext(JSONObject jwk, String issuer, Set acceptedAudiences) {
+ this.jwk = jwk;
+ this.issuer = issuer;
+ this.acceptedAudiences = acceptedAudiences;
+ }
+ }
+
@Override
public void updateUserPassword(String userName, String newPassword) {
throwUnsupportedOperationException();
diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/conf/CommonConfig.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/conf/CommonConfig.java
index a490107ded324..760cccb973baf 100644
--- a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/conf/CommonConfig.java
+++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/conf/CommonConfig.java
@@ -60,6 +60,7 @@ public class CommonConfig {
// Open ID Secret
private String openIdProviderUrl = "";
+ private String openIdAudience = "";
// The authorizer provider class which extends BasicAuthorizer
private String authorizerProvider =
@@ -543,6 +544,14 @@ public void setOpenIdProviderUrl(String openIdProviderUrl) {
this.openIdProviderUrl = openIdProviderUrl;
}
+ public String getOpenIdAudience() {
+ return openIdAudience;
+ }
+
+ public void setOpenIdAudience(String openIdAudience) {
+ this.openIdAudience = openIdAudience;
+ }
+
public String getAuthorizerProvider() {
return authorizerProvider;
}
diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/conf/CommonDescriptor.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/conf/CommonDescriptor.java
index 8483d1425cfec..004b147938c6c 100644
--- a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/conf/CommonDescriptor.java
+++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/conf/CommonDescriptor.java
@@ -80,6 +80,8 @@ public void loadCommonProps(TrimProperties properties) throws IOException {
// if using org.apache.iotdb.db.auth.authorizer.OpenIdAuthorizer, openID_url is needed.
config.setOpenIdProviderUrl(
properties.getProperty("openID_url", config.getOpenIdProviderUrl()).trim());
+ config.setOpenIdAudience(
+ properties.getProperty("openID_audience", config.getOpenIdAudience()).trim());
config.setEncryptDecryptProvider(
properties
.getProperty(
diff --git a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/auth/authorizer/LocalFileAuthorizerTest.java b/iotdb-core/node-commons/src/test/java/org/apache/iotdb/commons/auth/authorizer/LocalFileAuthorizerTest.java
similarity index 96%
rename from iotdb-core/datanode/src/test/java/org/apache/iotdb/db/auth/authorizer/LocalFileAuthorizerTest.java
rename to iotdb-core/node-commons/src/test/java/org/apache/iotdb/commons/auth/authorizer/LocalFileAuthorizerTest.java
index 194f33e8d67b3..90ee3afd86b85 100644
--- a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/auth/authorizer/LocalFileAuthorizerTest.java
+++ b/iotdb-core/node-commons/src/test/java/org/apache/iotdb/commons/auth/authorizer/LocalFileAuthorizerTest.java
@@ -16,16 +16,13 @@
* specific language governing permissions and limitations
* under the License.
*/
-package org.apache.iotdb.db.auth.authorizer;
+package org.apache.iotdb.commons.auth.authorizer;
import org.apache.iotdb.commons.auth.AuthException;
-import org.apache.iotdb.commons.auth.authorizer.BasicAuthorizer;
-import org.apache.iotdb.commons.auth.authorizer.IAuthorizer;
import org.apache.iotdb.commons.auth.entity.PrivilegeType;
import org.apache.iotdb.commons.auth.entity.PrivilegeUnion;
import org.apache.iotdb.commons.conf.CommonDescriptor;
import org.apache.iotdb.commons.path.PartialPath;
-import org.apache.iotdb.db.utils.EnvironmentUtils;
import org.junit.After;
import org.junit.Assert;
@@ -51,16 +48,13 @@ public class LocalFileAuthorizerTest {
@Before
public void setUp() throws Exception {
- EnvironmentUtils.envSetUp();
authorizer = BasicAuthorizer.getInstance();
authorizer.reset();
nodeName = new PartialPath("root.laptop.d1");
}
@After
- public void tearDown() throws Exception {
- EnvironmentUtils.cleanEnv();
- }
+ public void tearDown() throws Exception {}
@Test
public void testLogin() throws AuthException {
diff --git a/iotdb-core/node-commons/src/test/java/org/apache/iotdb/commons/auth/authorizer/OpenIdAuthorizerTest.java b/iotdb-core/node-commons/src/test/java/org/apache/iotdb/commons/auth/authorizer/OpenIdAuthorizerTest.java
new file mode 100644
index 0000000000000..cf7fb53ae12b2
--- /dev/null
+++ b/iotdb-core/node-commons/src/test/java/org/apache/iotdb/commons/auth/authorizer/OpenIdAuthorizerTest.java
@@ -0,0 +1,261 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF 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
+ *
+ * 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.
+ */
+
+package org.apache.iotdb.commons.auth.authorizer;
+
+import org.apache.iotdb.commons.conf.CommonConfig;
+import org.apache.iotdb.commons.conf.CommonDescriptor;
+
+import com.nimbusds.jose.JOSEException;
+import com.nimbusds.jose.JWSAlgorithm;
+import com.nimbusds.jose.JWSHeader;
+import com.nimbusds.jose.crypto.RSASSASigner;
+import com.nimbusds.jose.jwk.KeyUse;
+import com.nimbusds.jose.jwk.RSAKey;
+import com.nimbusds.jwt.JWTClaimsSet;
+import com.nimbusds.jwt.SignedJWT;
+import com.sun.net.httpserver.HttpExchange;
+import com.sun.net.httpserver.HttpServer;
+import net.minidev.json.JSONObject;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.net.InetSocketAddress;
+import java.security.KeyPair;
+import java.security.KeyPairGenerator;
+import java.security.PrivateKey;
+import java.security.interfaces.RSAPublicKey;
+import java.time.Instant;
+import java.util.Collections;
+import java.util.Date;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+public class OpenIdAuthorizerTest {
+
+ private final CommonConfig config = CommonDescriptor.getInstance().getConfig();
+ private PrivateKey privateKey;
+ private JSONObject publicJwk;
+
+ @Before
+ public void setUp() throws Exception {
+ KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA");
+ keyPairGenerator.initialize(2048);
+ KeyPair keyPair = keyPairGenerator.generateKeyPair();
+ privateKey = keyPair.getPrivate();
+ publicJwk =
+ new JSONObject(
+ new RSAKey.Builder((RSAPublicKey) keyPair.getPublic())
+ .keyUse(KeyUse.SIGNATURE)
+ .keyID("datanode-openid-test-key")
+ .build()
+ .toJSONObject());
+ }
+
+ @After
+ public void tearDown() throws IOException {}
+
+ @Test
+ public void loginWithJWT() throws Exception {
+ String jwt = createJwt(false);
+ OpenIdAuthorizer authorizer = new OpenIdAuthorizer(publicJwk);
+ assertTrue(authorizer.login(jwt, null, false));
+ }
+
+ @Test
+ public void isAdmin_hasAccess() throws Exception {
+ String jwt = createJwt(true);
+ OpenIdAuthorizer authorizer = new OpenIdAuthorizer(publicJwk);
+ assertTrue(authorizer.isAdmin(jwt));
+ }
+
+ @Test
+ public void isAdmin_noAdminClaim() throws Exception {
+ String jwt = createJwt(false);
+ OpenIdAuthorizer authorizer = new OpenIdAuthorizer(publicJwk);
+ assertFalse(authorizer.isAdmin(jwt));
+ }
+
+ @Test
+ public void testExpiredTokenRejectedByLoginAndIsAdmin() throws Exception {
+ KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA");
+ keyPairGenerator.initialize(2048);
+ KeyPair keyPair = keyPairGenerator.generateKeyPair();
+
+ JSONObject jwk =
+ new JSONObject(
+ new RSAKey.Builder((RSAPublicKey) keyPair.getPublic())
+ .privateKey(keyPair.getPrivate())
+ .keyUse(KeyUse.SIGNATURE)
+ .keyID("expired-token-test-key")
+ .build()
+ .toJSONObject());
+
+ OpenIdAuthorizer authorizer = new OpenIdAuthorizer(jwk);
+ String expiredToken =
+ createSignedToken(
+ keyPair.getPrivate(),
+ new JWTClaimsSet.Builder()
+ .subject("attacker")
+ .expirationTime(Date.from(Instant.now().minusSeconds(3600)))
+ .claim(
+ "realm_access",
+ Collections.singletonMap(
+ "roles",
+ Collections.singletonList(OpenIdAuthorizer.IOTDB_ADMIN_ROLE_NAME))));
+
+ assertFalse(authorizer.login(expiredToken, "", false));
+ assertFalse(authorizer.isAdmin(expiredToken));
+ }
+
+ @Test
+ public void testWrongIssuerRejected() throws Exception {
+ config.setOpenIdAudience("iotdb");
+ KeyPair keyPair = generateKeyPair();
+ HttpServer server = startProviderServer(keyPair);
+ String issuer = "http://127.0.0.1:" + server.getAddress().getPort() + "/";
+
+ try {
+ OpenIdAuthorizer authorizer = new OpenIdAuthorizer(issuer);
+ String token =
+ createSignedToken(
+ keyPair.getPrivate(),
+ new JWTClaimsSet.Builder()
+ .subject("attacker")
+ .issuer("https://evil.example/issuer")
+ .audience("iotdb")
+ .expirationTime(Date.from(Instant.now().plusSeconds(3600)))
+ .claim(
+ "realm_access",
+ Collections.singletonMap(
+ "roles",
+ Collections.singletonList(OpenIdAuthorizer.IOTDB_ADMIN_ROLE_NAME))));
+
+ assertFalse(authorizer.login(token, "", false));
+ assertFalse(authorizer.isAdmin(token));
+ } finally {
+ server.stop(0);
+ }
+ }
+
+ @Test
+ public void testWrongAudienceRejected() throws Exception {
+ config.setOpenIdAudience("iotdb");
+ KeyPair keyPair = generateKeyPair();
+ HttpServer server = startProviderServer(keyPair);
+ String issuer = "http://127.0.0.1:" + server.getAddress().getPort() + "/";
+
+ try {
+ OpenIdAuthorizer authorizer = new OpenIdAuthorizer(issuer);
+ String token =
+ createSignedToken(
+ keyPair.getPrivate(),
+ new JWTClaimsSet.Builder()
+ .subject("attacker")
+ .issuer(issuer)
+ .audience("unrelated-client")
+ .expirationTime(Date.from(Instant.now().plusSeconds(3600)))
+ .claim(
+ "realm_access",
+ Collections.singletonMap(
+ "roles",
+ Collections.singletonList(OpenIdAuthorizer.IOTDB_ADMIN_ROLE_NAME))));
+
+ assertFalse(authorizer.login(token, "", false));
+ assertFalse(authorizer.isAdmin(token));
+ } finally {
+ server.stop(0);
+ }
+ }
+
+ private KeyPair generateKeyPair() throws Exception {
+ KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA");
+ keyPairGenerator.initialize(2048);
+ return keyPairGenerator.generateKeyPair();
+ }
+
+ private String createSignedToken(PrivateKey privateKey, JWTClaimsSet.Builder claimsBuilder)
+ throws JOSEException {
+ SignedJWT signedJwt = new SignedJWT(new JWSHeader(JWSAlgorithm.RS256), claimsBuilder.build());
+ signedJwt.sign(new RSASSASigner(privateKey));
+ return signedJwt.serialize();
+ }
+
+ private HttpServer startProviderServer(KeyPair keyPair) throws Exception {
+ JSONObject publicJwk =
+ new JSONObject(
+ new RSAKey.Builder((RSAPublicKey) keyPair.getPublic())
+ .keyUse(KeyUse.SIGNATURE)
+ .keyID("openid-provider-test-key")
+ .build()
+ .toJSONObject());
+
+ HttpServer server = HttpServer.create(new InetSocketAddress("127.0.0.1", 0), 0);
+ String issuer = "http://127.0.0.1:" + server.getAddress().getPort() + "/";
+ String metadata =
+ "{"
+ + "\"issuer\":\""
+ + issuer
+ + "\","
+ + "\"jwks_uri\":\""
+ + issuer
+ + "jwks.json\","
+ + "\"subject_types_supported\":[\"public\"],"
+ + "\"response_types_supported\":[\"code\"],"
+ + "\"id_token_signing_alg_values_supported\":[\"RS256\"]"
+ + "}";
+ String jwks = "{\"keys\":[" + publicJwk.toJSONString() + "]}";
+
+ server.createContext(
+ "/.well-known/openid-configuration", exchange -> writeJson(exchange, metadata));
+ server.createContext("/jwks.json", exchange -> writeJson(exchange, jwks));
+ server.start();
+ return server;
+ }
+
+ private void writeJson(HttpExchange exchange, String json) throws IOException {
+ byte[] response = json.getBytes(java.nio.charset.StandardCharsets.UTF_8);
+ exchange.getResponseHeaders().set("Content-Type", "application/json");
+ exchange.sendResponseHeaders(200, response.length);
+ try (OutputStream outputStream = exchange.getResponseBody()) {
+ outputStream.write(response);
+ }
+ }
+
+ private String createJwt(boolean hasAdminRole) throws JOSEException {
+ JWTClaimsSet.Builder claimsBuilder =
+ new JWTClaimsSet.Builder()
+ .subject("datanode-test-user")
+ .expirationTime(Date.from(Instant.now().plusSeconds(3600)))
+ .claim(
+ "realm_access",
+ Collections.singletonMap(
+ "roles",
+ hasAdminRole
+ ? Collections.singletonList(OpenIdAuthorizer.IOTDB_ADMIN_ROLE_NAME)
+ : Collections.singletonList("offline_access")));
+ SignedJWT signedJwt = new SignedJWT(new JWSHeader(JWSAlgorithm.RS256), claimsBuilder.build());
+ signedJwt.sign(new RSASSASigner(privateKey));
+ return signedJwt.serialize();
+ }
+}