update default limit of apikey number per user to one and disallow key deletion by default

This commit is contained in:
Feng Yue
2025-09-02 21:42:48 +08:00
parent d56da4d799
commit 3860f7d9b3
5 changed files with 30 additions and 7 deletions

View File

@@ -96,4 +96,5 @@ LDAP_USER_ATTR_LAST_NAME=sn
USER_MANAGEMENT_ENABLED=false USER_MANAGEMENT_ENABLED=false
DEFAULT_USER_ROLE=user DEFAULT_USER_ROLE=user
USER_SESSION_TIMEOUT=86400000 USER_SESSION_TIMEOUT=86400000
MAX_API_KEYS_PER_USER=5 MAX_API_KEYS_PER_USER=1
ALLOW_USER_DELETE_API_KEYS=false

View File

@@ -175,7 +175,8 @@ const config = {
enabled: process.env.USER_MANAGEMENT_ENABLED === 'true', enabled: process.env.USER_MANAGEMENT_ENABLED === 'true',
defaultUserRole: process.env.DEFAULT_USER_ROLE || 'user', defaultUserRole: process.env.DEFAULT_USER_ROLE || 'user',
userSessionTimeout: parseInt(process.env.USER_SESSION_TIMEOUT) || 86400000, // 24小时 userSessionTimeout: parseInt(process.env.USER_SESSION_TIMEOUT) || 86400000, // 24小时
maxApiKeysPerUser: parseInt(process.env.MAX_API_KEYS_PER_USER) || 5 maxApiKeysPerUser: parseInt(process.env.MAX_API_KEYS_PER_USER) || 1,
allowUserDeleteApiKeys: process.env.ALLOW_USER_DELETE_API_KEYS === 'true' // 默认不允许用户删除自己的API Keys
}, },
// 📢 Webhook通知配置 // 📢 Webhook通知配置

View File

@@ -208,7 +208,8 @@ router.get('/profile', authenticateUser, async (req, res) => {
totalUsage: user.totalUsage totalUsage: user.totalUsage
}, },
config: { config: {
maxApiKeysPerUser: config.userManagement.maxApiKeysPerUser maxApiKeysPerUser: config.userManagement.maxApiKeysPerUser,
allowUserDeleteApiKeys: config.userManagement.allowUserDeleteApiKeys
} }
}) })
} catch (error) { } catch (error) {
@@ -352,6 +353,15 @@ router.delete('/api-keys/:keyId', authenticateUser, async (req, res) => {
try { try {
const { keyId } = req.params const { keyId } = req.params
// 检查是否允许用户删除自己的API Keys
if (!config.userManagement.allowUserDeleteApiKeys) {
return res.status(403).json({
error: 'Operation not allowed',
message:
'Users are not allowed to delete their own API keys. Please contact an administrator.'
})
}
// 检查API Key是否属于当前用户 // 检查API Key是否属于当前用户
const existingKey = await apiKeyService.getApiKeyById(keyId) const existingKey = await apiKeyService.getApiKeyById(keyId)
if (!existingKey || existingKey.userId !== req.user.id) { if (!existingKey || existingKey.userId !== req.user.id) {

View File

@@ -534,9 +534,15 @@ class UserService {
// 构建匹配字符串数组只考虑displayName、username、email去除空值和重复值 // 构建匹配字符串数组只考虑displayName、username、email去除空值和重复值
const matchStrings = new Set() const matchStrings = new Set()
if (displayName) matchStrings.add(displayName.toLowerCase().trim()) if (displayName) {
if (username) matchStrings.add(username.toLowerCase().trim()) matchStrings.add(displayName.toLowerCase().trim())
if (email) matchStrings.add(email.toLowerCase().trim()) }
if (username) {
matchStrings.add(username.toLowerCase().trim())
}
if (email) {
matchStrings.add(email.toLowerCase().trim())
}
const matchingKeys = [] const matchingKeys = []

View File

@@ -159,7 +159,11 @@
</button> </button>
<button <button
v-if="!(apiKey.isDeleted === 'true' || apiKey.deletedAt) && apiKey.isActive" v-if="
!(apiKey.isDeleted === 'true' || apiKey.deletedAt) &&
apiKey.isActive &&
allowUserDeleteApiKeys
"
class="inline-flex items-center rounded border border-transparent p-1 text-red-400 hover:text-red-600" class="inline-flex items-center rounded border border-transparent p-1 text-red-400 hover:text-red-600"
title="Delete API Key" title="Delete API Key"
@click="deleteApiKey(apiKey)" @click="deleteApiKey(apiKey)"
@@ -255,6 +259,7 @@ const userStore = useUserStore()
const loading = ref(true) const loading = ref(true)
const apiKeys = ref([]) const apiKeys = ref([])
const maxApiKeys = computed(() => userStore.config?.maxApiKeysPerUser || 5) const maxApiKeys = computed(() => userStore.config?.maxApiKeysPerUser || 5)
const allowUserDeleteApiKeys = computed(() => userStore.config?.allowUserDeleteApiKeys !== false)
const showCreateModal = ref(false) const showCreateModal = ref(false)
const showViewModal = ref(false) const showViewModal = ref(false)