This is an automated email from the ASF dual-hosted git repository. haonan pushed a commit to branch fix_openid in repository https://gitbox.apache.org/repos/asf/iotdb.git
commit 0d754c97a4971dd119aa4dbb9799194b61d02281 Author: HTHou <[email protected]> AuthorDate: Tue Mar 17 10:02:50 2026 +0800 Reject expired OpenID tokens and add regression test --- iotdb-core/node-commons/pom.xml | 10 ++ .../commons/auth/authorizer/OpenIdAuthorizer.java | 4 +- .../auth/authorizer/OpenIdAuthorizerTest.java | 113 +++++++++++++++++++++ 3 files changed, 125 insertions(+), 2 deletions(-) diff --git a/iotdb-core/node-commons/pom.xml b/iotdb-core/node-commons/pom.xml index 24f33d0de8b..9cae757055c 100644 --- a/iotdb-core/node-commons/pom.xml +++ b/iotdb-core/node-commons/pom.xml @@ -149,6 +149,16 @@ <groupId>io.jsonwebtoken</groupId> <artifactId>jjwt-api</artifactId> </dependency> + <dependency> + <groupId>io.jsonwebtoken</groupId> + <artifactId>jjwt-impl</artifactId> + <scope>test</scope> + </dependency> + <dependency> + <groupId>io.jsonwebtoken</groupId> + <artifactId>jjwt-jackson</artifactId> + <scope>test</scope> + </dependency> <dependency> <groupId>com.nimbusds</groupId> <artifactId>oauth2-oidc-sdk</artifactId> 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 ee66ee5bced..f5f74a87b39 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 @@ -55,6 +55,7 @@ import java.util.UUID; 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-"; @@ -194,8 +195,7 @@ public class OpenIdAuthorizer extends BasicAuthorizer { private Claims validateToken(String token) { return Jwts.parser() - // Basically ignore the Expiration Date, if there is any??? - .clockSkewSeconds(Long.MAX_VALUE / 1000) + .clockSkewSeconds(MAX_CLOCK_SKEW_SECONDS) .verifyWith(providerKey) .build() .parseSignedClaims(token) 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 00000000000..f41645998fe --- /dev/null +++ b/iotdb-core/node-commons/src/test/java/org/apache/iotdb/commons/auth/authorizer/OpenIdAuthorizerTest.java @@ -0,0 +1,113 @@ +/* + * 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.jwk.KeyUse; +import com.nimbusds.jose.jwk.RSAKey; +import io.jsonwebtoken.Jwts; +import net.minidev.json.JSONObject; +import org.junit.After; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.security.KeyPair; +import java.security.KeyPairGenerator; +import java.security.interfaces.RSAPublicKey; +import java.time.Instant; +import java.util.Collections; +import java.util.Comparator; +import java.util.Date; + +public class OpenIdAuthorizerTest { + + private final CommonConfig config = CommonDescriptor.getInstance().getConfig(); + + private String originalUserFolder; + private String originalRoleFolder; + private Path baseDir; + + @Before + public void setUp() throws IOException { + originalUserFolder = config.getUserFolder(); + originalRoleFolder = config.getRoleFolder(); + + baseDir = Files.createTempDirectory("openid-authorizer-test-"); + config.setUserFolder(Files.createDirectories(baseDir.resolve("users")).toString()); + config.setRoleFolder(Files.createDirectories(baseDir.resolve("roles")).toString()); + } + + @After + public void tearDown() throws IOException { + config.setUserFolder(originalUserFolder); + config.setRoleFolder(originalRoleFolder); + + if (baseDir != null) { + try (java.util.stream.Stream<Path> stream = Files.walk(baseDir)) { + stream.sorted(Comparator.reverseOrder()).forEach(this::deleteIfExists); + } + } + } + + @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 = + Jwts.builder() + .subject("attacker") + .expiration(Date.from(Instant.now().minusSeconds(3600))) + .claim( + "realm_access", + Collections.singletonMap( + "roles", Collections.singletonList(OpenIdAuthorizer.IOTDB_ADMIN_ROLE_NAME))) + .signWith(keyPair.getPrivate(), Jwts.SIG.RS256) + .compact(); + + Assert.assertFalse(authorizer.login(expiredToken, "", false)); + Assert.assertFalse(authorizer.isAdmin(expiredToken)); + } + + private void deleteIfExists(Path path) { + try { + Files.deleteIfExists(path); + } catch (IOException e) { + throw new RuntimeException("Failed to delete test path " + path, e); + } + } +}
