Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@

package icu.h2l.api.profile

import icu.h2l.api.util.IPropertiesHolder
import java.util.*

/**
Expand All @@ -29,7 +30,7 @@ import java.util.*
* 注意:凭证对象本身不直接携带 profileId 字段;
* 是否已绑定、如何绑定到 Profile,都由各认证模块自己的实现负责。
*/
interface HyperZoneCredential {
interface HyperZoneCredential : IPropertiesHolder {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Medium · Java credential implementations break

When a Java module has an existing HyperZoneCredential implementation compiled against this API, a Java credential implementation compiled against the previous API has no implementations of the newly inherited property methods; with Kotlin’s default JVM interface mode, calls to those methods fail with AbstractMethodError, and recompiling the implementation requires adding them.

Preserve Java implementor compatibility by emitting JVM default methods for the new interface API or by providing a compatibility adapter that does not require new implementation methods.

Prompt for AI agents
In `api/src/main/kotlin/icu/h2l/api/profile/HyperZoneCredential.kt` at line 33:

**Java credential implementations break**

When a Java module has an existing `HyperZoneCredential` implementation compiled against this API, a Java credential implementation compiled against the previous API has no implementations of the newly inherited property methods; with Kotlin’s default JVM interface mode, calls to those methods fail with `AbstractMethodError`, and recompiling the implementation requires adding them.

Preserve Java implementor compatibility by emitting JVM default methods for the new interface API or by providing a compatibility adapter that does not require new implementation methods.

/**
* 认证渠道唯一标识,由子模块负责稳定定义。
*/
Expand Down
43 changes: 43 additions & 0 deletions api/src/main/kotlin/icu/h2l/api/util/IPropertiesHolder.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
/*
* This file is part of HyperZoneLogin, licensed under the GNU Affero General Public License v3.0 or later.
*
* Copyright (C) ksqeib (庆灵) <ksqeib@qq.com>
* Copyright (C) contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*
*/

package icu.h2l.api.util

interface IPropertiesHolder {
val properties: HashMap<String, String>
get() = HashMap()

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟠 High · Credential properties disappear

When a credential uses the inherited default properties getter, properties returns a fresh HashMap on each access, so setProperty writes to a discarded map and later hasProperty or getProperty calls cannot see the value; callers cannot retrieve the Yggdrasil entry ID.

Keep one persistent backing map per credential instead of creating a new map on each access.

Also at auth-yggd/src/main/kotlin/icu/h2l/login/auth/online/credential/YggdrasilHyperZoneCredential.kt:40.

Prompt for AI agents
In `api/src/main/kotlin/icu/h2l/api/util/IPropertiesHolder.kt` at line 26:

**Credential properties disappear**

When a credential uses the inherited default `properties` getter, `properties` returns a fresh `HashMap` on each access, so `setProperty` writes to a discarded map and later `hasProperty` or `getProperty` calls cannot see the value; callers cannot retrieve the Yggdrasil entry ID.

Keep one persistent backing map per credential instead of creating a new map on each access.

Also at `auth-yggd/src/main/kotlin/icu/h2l/login/auth/online/credential/YggdrasilHyperZoneCredential.kt:40`.


fun getProperty(key: String): String {
return properties[key] as String
}

fun setProperty(key: String, value: String) {
properties[key] = value
}

fun hasProperty(key: String): Boolean {
return properties.containsKey(key)
}

fun removeProperty(key: String) {
properties.remove(key)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,10 @@ class YggdrasilHyperZoneCredential(
override val channelId: String = CHANNEL_ID
override val credentialId: String = "$entryId:$authenticatedUUID"

init {
setProperty(YGGDRASIL_ENTRY_ID_PROPERTY, entryId.lowercase(Locale.getDefault()))

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Medium · Entry IDs stop matching

When the default locale is Turkish and an entry ID contains uppercase I, entryId.lowercase(Locale.getDefault()) produces a dotless ı, while EntryTableManager normalizes IDs with locale-independent lowercase(); consumers receive a property value that does not match the registered entry ID.

Normalize the entry ID with locale-independent lowercasing.

Prompt for AI agents
In `auth-yggd/src/main/kotlin/icu/h2l/login/auth/online/credential/YggdrasilHyperZoneCredential.kt` at line 40:

**Entry IDs stop matching**

When the default locale is Turkish and an entry ID contains uppercase `I`, `entryId.lowercase(Locale.getDefault())` produces a dotless `ı`, while `EntryTableManager` normalizes IDs with locale-independent `lowercase()`; consumers receive a property value that does not match the registered entry ID.

Normalize the entry ID with locale-independent lowercasing.

}

override fun getRegistrationName(): String = authenticatedName

override fun getBoundProfileId(): UUID? {
Expand Down Expand Up @@ -86,5 +90,7 @@ class YggdrasilHyperZoneCredential(

companion object {
private const val CHANNEL_ID = "yggdrasil"

public const val YGGDRASIL_ENTRY_ID_PROPERTY = "yggdrasil-entry-id"
}
}
Loading