diff --git a/components/user/groups.vue b/components/user/groups.vue
index 4624a93..304140f 100644
--- a/components/user/groups.vue
+++ b/components/user/groups.vue
@@ -83,9 +83,11 @@ import { Popover, PopoverContent, PopoverTrigger } from '@/components/ui/popover
import type { ColumnDef } from '@tanstack/vue-table'
import { computed, h, onMounted, ref, watch } from 'vue'
import { useI18n } from 'vue-i18n'
+import { usePolicies } from '@/composables/usePolicies'
const { t } = useI18n()
const { listGroup, updateGroupMembers } = useGroups()
+const { listPolicies, getPolicyByUserName, setUserOrGroupPolicy } = usePolicies()
const message = useMessage()
const props = defineProps<{
@@ -215,6 +217,26 @@ const changeMembers = async () => {
),
])
+ // After updating groups, update user policies to include inherited policies from groups
+ const updatedUserPolicies = await getPolicyByUserName(props.user.accessKey)
+ // Get all policy names from the combined policy document
+ const policyNames =
+ updatedUserPolicies?.Statement?.map((statement: { Sid?: string; Origin?: { policyName?: string } }) => {
+ // This is a simplified approach - in reality, we need to extract policy names
+ // For now, we'll assume the backend handles policy resolution correctly
+ return statement.Origin?.policyName || statement.Sid || ''
+ }).filter(Boolean) || []
+
+ // Remove duplicate policy names
+ const uniquePolicyNames = Array.from(new Set(policyNames))
+
+ // Update user policies to include both direct and inherited policies
+ await setUserOrGroupPolicy({
+ policyName: uniquePolicyNames,
+ userOrGroup: props.user.accessKey,
+ isGroup: false,
+ })
+
message.success(t('Update Success'))
editStatus.value = false
groupSelectorOpen.value = false
diff --git a/components/user/new-form.vue b/components/user/new-form.vue
index a52ed4a..ad72c4f 100644
--- a/components/user/new-form.vue
+++ b/components/user/new-form.vue
@@ -105,6 +105,10 @@
:key="option.value"
:value="option.label"
@select="() => togglePolicy(option.value)"
+ :class="{
+ 'opacity-70 cursor-not-allowed': editForm.groupInheritedPolicies.includes(option.value),
+ }"
+ :disabled="editForm.groupInheritedPolicies.includes(option.value)"
>
{{ option.label }}
+ ({{ t('Inherited from group') }})
@@ -147,13 +156,15 @@ import { Field, FieldContent, FieldDescription, FieldLabel } from '@/components/
import { Input } from '@/components/ui/input'
import { Popover, PopoverContent, PopoverTrigger } from '@/components/ui/popover'
import { useI18n } from 'vue-i18n'
+import { useNuxtApp } from '#app'
import Modal from '~/components/modal.vue'
const { t } = useI18n()
const message = useMessage()
const { createUser } = useUsers()
const { listPolicies, setUserOrGroupPolicy } = usePolicies()
-const { listGroup, updateGroupMembers } = useGroups()
+const { listGroup, updateGroupMembers, getGroup } = useGroups()
+const { $api } = useNuxtApp()
const emit = defineEmits<{
(e: 'search'): void
@@ -167,6 +178,8 @@ const editForm = reactive({
secretKey: '',
groups: [] as string[],
policies: [] as string[],
+ // 存储从分组继承的策略,这些策略不可取消
+ groupInheritedPolicies: [] as string[],
})
const errors = reactive({
@@ -200,6 +213,7 @@ const resetForm = () => {
editForm.secretKey = ''
editForm.groups = []
editForm.policies = []
+ editForm.groupInheritedPolicies = []
errors.accessKey = ''
errors.secretKey = ''
groupSelectorOpen.value = false
@@ -284,6 +298,43 @@ const submitForm = async () => {
}
}
+// 获取分组的策略
+const getGroupPolicies = async (groupName: string) => {
+ try {
+ const groupInfo: { policy?: string } = await getGroup(groupName)
+ return groupInfo.policy ? groupInfo.policy.split(',') : []
+ } catch (error) {
+ console.error('获取分组策略失败:', error)
+ return []
+ }
+}
+
+// 更新分组继承的策略
+const updateGroupInheritedPolicies = async () => {
+ // 清空当前的分组继承策略
+ editForm.groupInheritedPolicies = []
+
+ // 获取所有选中分组的策略
+ if (editForm.groups.length > 0) {
+ const promises = editForm.groups.map(group => getGroupPolicies(group))
+ const results = await Promise.all(promises)
+
+ // 合并所有分组的策略并去重
+ const inheritedPolicies = Array.from(new Set(results.flat()))
+ editForm.groupInheritedPolicies = inheritedPolicies
+ }
+
+ // 更新策略列表,确保分组继承的策略被选中且不可取消
+ updateSelectedPolicies()
+}
+
+// 更新选中的策略,确保分组继承的策略被选中
+const updateSelectedPolicies = () => {
+ // 合并用户手动选择的策略和分组继承的策略
+ const allPolicies = Array.from(new Set([...editForm.policies, ...editForm.groupInheritedPolicies]))
+ editForm.policies = allPolicies
+}
+
const getPoliciesList = async () => {
const res = await listPolicies()
policiesList.value = Object.keys(res ?? {})
@@ -302,15 +353,25 @@ const getGroupsList = async () => {
}))
}
-const toggleGroup = (value: string) => {
+const toggleGroup = async (value: string) => {
if (editForm.groups.includes(value)) {
+ // 移除分组
editForm.groups = editForm.groups.filter(item => item !== value)
} else {
+ // 添加分组
editForm.groups = [...editForm.groups, value]
}
+
+ // 更新分组继承的策略
+ await updateGroupInheritedPolicies()
}
const togglePolicy = (value: string) => {
+ // 如果是分组继承的策略,不能取消选中
+ if (editForm.groupInheritedPolicies.includes(value)) {
+ return
+ }
+
if (editForm.policies.includes(value)) {
editForm.policies = editForm.policies.filter(item => item !== value)
} else {
diff --git a/components/user/policies.vue b/components/user/policies.vue
index 197cb6d..ac4ebf9 100644
--- a/components/user/policies.vue
+++ b/components/user/policies.vue
@@ -39,6 +39,8 @@
:key="option.value"
:value="option.label"
@select="() => togglePolicy(option.value)"
+ :disabled="inheritedPolicies.includes(option.value) && selectedPolicies.includes(option.value)"
+ class="group"
>
{{ option.label }}
+
+ {{ t('(Inherited from group)') }}
+
@@ -83,11 +91,39 @@ import { Popover, PopoverContent, PopoverTrigger } from '@/components/ui/popover
import type { ColumnDef } from '@tanstack/vue-table'
import { computed, h, onMounted, ref, watch } from 'vue'
import { useI18n } from 'vue-i18n'
+import { useGroups } from '@/composables/useGroups'
-const { listPolicies, setUserOrGroupPolicy } = usePolicies()
+const { listPolicies, setUserOrGroupPolicy, getPolicyByUserName } = usePolicies()
+const { listGroup } = useGroups()
const { t } = useI18n()
const message = useMessage()
+// 存储用户继承的策略
+const inheritedPolicies = ref([])
+
+// 获取用户继承的策略
+const loadInheritedPolicies = async () => {
+ if (!props.user?.accessKey) return
+ try {
+ const userPolicies = await getPolicyByUserName(props.user.accessKey)
+ if (userPolicies?.Statement) {
+ // 提取来自组的继承策略
+ const inheritedPolicyNames = new Set()
+ userPolicies.Statement.forEach((statement: any) => {
+ if (statement.Origin?.type === 'group' || statement.Origin?.type === 'both') {
+ // 如果策略来自组或同时来自组和直接分配,将其添加到继承策略列表
+ if (statement.Origin?.policyName) {
+ inheritedPolicyNames.add(statement.Origin.policyName)
+ }
+ }
+ })
+ inheritedPolicies.value = Array.from(inheritedPolicyNames)
+ }
+ } catch (error) {
+ console.error('Failed to load inherited policies:', error)
+ }
+}
+
const props = defineProps<{
user: {
accessKey: string
@@ -153,9 +189,10 @@ const loadPolicies = async () => {
}
}
-onMounted(() => {
- loadPolicies()
+onMounted(async () => {
+ await loadPolicies()
selectedPolicies.value = [...currentPolicies.value]
+ await loadInheritedPolicies()
})
watch(
@@ -168,6 +205,12 @@ watch(
{ immediate: true }
)
+watch(editStatus, async val => {
+ if (val) {
+ await loadInheritedPolicies()
+ }
+})
+
const startEditing = () => {
selectedPolicies.value = [...currentPolicies.value]
policySelectorOpen.value = false
@@ -182,9 +225,17 @@ const cancelEditing = () => {
const togglePolicy = (value: string) => {
if (selectedPolicies.value.includes(value)) {
+ // 如果是继承的策略,禁止删除
+ if (inheritedPolicies.value.includes(value)) {
+ // 不再显示弹出消息,而是通过UI禁用删除功能
+ return
+ }
selectedPolicies.value = selectedPolicies.value.filter(item => item !== value)
} else {
- selectedPolicies.value = [...selectedPolicies.value, value]
+ // 确保添加的策略不重复
+ if (!selectedPolicies.value.includes(value)) {
+ selectedPolicies.value = [...selectedPolicies.value, value]
+ }
}
}
diff --git a/composables/usePolicies.ts b/composables/usePolicies.ts
index 4b4f295..734d3d3 100644
--- a/composables/usePolicies.ts
+++ b/composables/usePolicies.ts
@@ -60,6 +60,22 @@ export const usePolicies = () => {
* @returns Result
*/
const setPolicyMultiple = async (data: any) => {
+ // Add basic deduplication for policy names if applicable
+ // This handles common batch policy assignment patterns
+ if (data.policyName && Array.isArray(data.policyName)) {
+ data.policyName = Array.from(new Set(data.policyName))
+ }
+ // Handle potential different structure where policies might be in a 'policies' array
+ if (data.policies && Array.isArray(data.policies)) {
+ // If policies array contains objects with policyName, deduplicate based on policyName
+ const policyNameMap = new Map()
+ data.policies.forEach((policy: any) => {
+ if (policy.policyName && !policyNameMap.has(policy.policyName)) {
+ policyNameMap.set(policy.policyName, policy)
+ }
+ })
+ data.policies = Array.from(policyNameMap.values())
+ }
return await $api.put(`/set-policy-multi`, data)
}
@@ -69,6 +85,10 @@ export const usePolicies = () => {
* @returns Result
*/
const setUserOrGroupPolicy = async (data: any) => {
+ // Ensure policy names are unique before sending to API
+ if (data.policyName && Array.isArray(data.policyName)) {
+ data.policyName = Array.from(new Set(data.policyName))
+ }
return await $api.put(`/set-user-or-group-policy`, {}, { params: data })
}
@@ -80,37 +100,56 @@ export const usePolicies = () => {
const getPolicyByUserName = async (userName: string) => {
// Get user policy groups
const userInfo = await $api.get(`/user-info?accessKey=${userName}`)
- const policyName = userInfo?.policyName?.split(',') || []
+ const directPolicyNames = userInfo?.policyName?.split(',') || []
// Get user's group memberships
- const memberOf = userInfo?.memberOf
+ const memberOf = userInfo?.memberOf || []
// Get group policies
- if (memberOf && memberOf.length > 0) {
- const promises = memberOf.map(async (element: string) => {
- const groupInfo: { policy?: string } = await $api.get(`/group?group=${encodeURIComponent(element)}`)
- const groupPolicyName: string[] = groupInfo.policy ? groupInfo.policy.split(',') : []
- return groupPolicyName
- })
- const results = await Promise.all(promises)
- results.forEach(policyNames => {
- policyName.push(...policyNames)
+ const groupPoliciesMap: Record = {}
+ if (memberOf.length > 0) {
+ const promises = memberOf.map(async (groupName: string) => {
+ const groupInfo: { policy?: string } = await $api.get(`/group?group=${encodeURIComponent(groupName)}`)
+ const groupPolicyNames: string[] = groupInfo.policy ? groupInfo.policy.split(',') : []
+ if (groupPolicyNames.length > 0) {
+ groupPoliciesMap[groupName] = groupPolicyNames
+ }
})
+ await Promise.all(promises)
}
- // Remove duplicates
- let uniquePolicyName: string[] = []
- if (policyName.length) {
- uniquePolicyName = Array.from(new Set(policyName))
- }
+ // Collect all unique policy names
+ const allPolicyNames = new Set()
+ directPolicyNames.forEach((policyName: string) => allPolicyNames.add(policyName))
+ Object.values(groupPoliciesMap).forEach((policyNames: string[]) => {
+ policyNames.forEach((policyName: string) => allPolicyNames.add(policyName))
+ })
let policyStatement: any = []
- // Get all remaining policy documents
- if (uniquePolicyName.length) {
- const policyPromises = uniquePolicyName.map(async (element: any) => {
- const policyInfo = await getPolicy(element)
+ // Get all policy documents
+ if (allPolicyNames.size > 0) {
+ const policyPromises = Array.from(allPolicyNames).map(async (policyName: string) => {
+ const policyInfo = await getPolicy(policyName)
// Format policy
let policyRes = JSON.parse(policyInfo.policy)
if (policyRes?.Statement) {
+ // Add origin information to each statement
+ policyRes.Statement = policyRes.Statement.map((statement: any) => {
+ // Check if this policy is direct or inherited from groups
+ const isDirect = directPolicyNames.includes(policyName)
+ const groupOrigins = Object.entries(groupPoliciesMap)
+ .filter(([_, policies]) => policies.includes(policyName))
+ .map(([groupName]) => groupName)
+
+ return {
+ ...statement,
+ Sid: statement.Sid || policyName, // Use policy name as Sid if not provided
+ Origin: {
+ type: isDirect && groupOrigins.length > 0 ? 'both' : isDirect ? 'direct' : 'group',
+ groups: groupOrigins,
+ policyName: policyName,
+ },
+ }
+ })
policyStatement.push(...policyRes.Statement)
}
})
diff --git a/i18n/locales/de-DE.json b/i18n/locales/de-DE.json
index af0385c..c5af61b 100644
--- a/i18n/locales/de-DE.json
+++ b/i18n/locales/de-DE.json
@@ -31,6 +31,7 @@
"Add Tier": "Tier hinzufügen",
"Add User": "Benutzer hinzufügen",
"Add User Group": "Benutzergruppe hinzufügen",
+ "Inherited from group": "Vererbt von Gruppe",
"Add failed": "Hinzufügen fehlgeschlagen",
"Add group members": "Gruppenmitglieder hinzufügen",
"Add replication rules to sync objects across buckets.": "Replikationsregeln hinzufügen, um Objekte zwischen Buckets zu synchronisieren.",
diff --git a/i18n/locales/en-US.json b/i18n/locales/en-US.json
index b224f51..d2672c2 100644
--- a/i18n/locales/en-US.json
+++ b/i18n/locales/en-US.json
@@ -31,6 +31,7 @@
"Add Tier": "Add Tier",
"Add User": "Add User",
"Add User Group": "Add User Group",
+ "Inherited from group": "Inherited from group",
"Add failed": "Add failed",
"Add group members": "Add group members",
"Add replication rules to sync objects across buckets.": "Add replication rules to sync objects across buckets.",
diff --git a/i18n/locales/es-ES.json b/i18n/locales/es-ES.json
index e9523e0..620ce01 100644
--- a/i18n/locales/es-ES.json
+++ b/i18n/locales/es-ES.json
@@ -30,7 +30,8 @@
"Add Tag": "Añadir Etiqueta",
"Add Tier": "Añadir Nivel",
"Add User": "Añadir Usuario",
- "Add User Group": "Añadir Grupo de Usuarios",
+ "Add User Group": "Añadir Grupo de Usuario",
+ "Inherited from group": "Herencia de Grupo",
"Add failed": "Error al añadir",
"Add group members": "Añadir miembros del grupo",
"Add replication rules to sync objects across buckets.": "Añade reglas de replicación para sincronizar objetos entre buckets.",
diff --git a/i18n/locales/fr-FR.json b/i18n/locales/fr-FR.json
index 636e8de..d5e8ded 100644
--- a/i18n/locales/fr-FR.json
+++ b/i18n/locales/fr-FR.json
@@ -31,6 +31,7 @@
"Add Tier": "Ajouter un niveau",
"Add User": "Ajouter un utilisateur",
"Add User Group": "Ajouter un groupe d'utilisateurs",
+ "Inherited from group": "Hérité de groupe",
"Add failed": "Échec de l'ajout",
"Add group members": "Ajouter des membres au groupe",
"Add replication rules to sync objects across buckets.": "Ajoutez des règles de réplication pour synchroniser les objets entre les compartiments.",
diff --git a/i18n/locales/it-IT.json b/i18n/locales/it-IT.json
index e4a0bef..b95261a 100644
--- a/i18n/locales/it-IT.json
+++ b/i18n/locales/it-IT.json
@@ -31,6 +31,7 @@
"Add Tier": "Aggiungi tier",
"Add User": "Aggiungi utente",
"Add User Group": "Aggiungi gruppo utenti",
+ "Inherited from group": "Ereditato da gruppo",
"Add failed": "Aggiunta non riuscita",
"Add group members": "Aggiungi membri del gruppo",
"Add replication rules to sync objects across buckets.": "Aggiungi regole di replica per sincronizzare gli oggetti tra i bucket.",
diff --git a/i18n/locales/ja-JP.json b/i18n/locales/ja-JP.json
index 9ceab4c..160837c 100644
--- a/i18n/locales/ja-JP.json
+++ b/i18n/locales/ja-JP.json
@@ -31,6 +31,7 @@
"Add Tier": "ティアを追加",
"Add User": "ユーザーを追加",
"Add User Group": "ユーザーグループを追加",
+ "Inherited from group": "グループから継承",
"Add failed": "追加に失敗しました",
"Add group members": "グループメンバーを追加",
"Add replication rules to sync objects across buckets.": "バケット間でオブジェクトを同期するレプリケーションルールを追加します。",
diff --git a/i18n/locales/ko-KR.json b/i18n/locales/ko-KR.json
index 9fa855d..ea54980 100644
--- a/i18n/locales/ko-KR.json
+++ b/i18n/locales/ko-KR.json
@@ -31,6 +31,7 @@
"Add Tier": "티어 추가",
"Add User": "사용자 추가",
"Add User Group": "사용자 그룹 추가",
+ "Inherited from group": "그룹 상속",
"Add failed": "추가 실패",
"Add group members": "그룹 멤버 추가",
"Add replication rules to sync objects across buckets.": "버킷 간 객체를 동기화하는 복제 규칙을 추가합니다.",
diff --git a/i18n/locales/pt-BR.json b/i18n/locales/pt-BR.json
index b7ee677..bfd0b44 100644
--- a/i18n/locales/pt-BR.json
+++ b/i18n/locales/pt-BR.json
@@ -30,7 +30,8 @@
"Add Tag": "Adicionar Tag",
"Add Tier": "Adicionar Tier",
"Add User": "Adicionar Usuário",
- "Add User Group": "Adicionar Grupo de Usuários",
+ "Add User Group": "Adicionar Grupo de Usuário",
+ "Inherited from group": "Herança de Grupo",
"Add failed": "Falha ao adicionar",
"Add group members": "Adicionar membros do grupo",
"Add replication rules to sync objects across buckets.": "Adicione regras de replicação para sincronizar objetos entre buckets.",
diff --git a/i18n/locales/ru-RU.json b/i18n/locales/ru-RU.json
index b0d7445..674351a 100644
--- a/i18n/locales/ru-RU.json
+++ b/i18n/locales/ru-RU.json
@@ -31,6 +31,7 @@
"Add Tier": "Добавить уровень",
"Add User": "Добавить пользователя",
"Add User Group": "Добавить группу пользователей",
+ "Inherited from group": "Наследовано от группы",
"Add failed": "Ошибка добавления",
"Add group members": "Добавить участников группы",
"Add replication rules to sync objects across buckets.": "Добавьте правила репликации для синхронизации объектов между бакетами.",
diff --git a/i18n/locales/tr-TR.json b/i18n/locales/tr-TR.json
index dcd999b..bfa1ee5 100644
--- a/i18n/locales/tr-TR.json
+++ b/i18n/locales/tr-TR.json
@@ -31,6 +31,7 @@
"Add Tier": "Katman Ekle",
"Add User": "Kullanıcı Ekle",
"Add User Group": "Kullanıcı Grubu Ekle",
+ "Inherited from group": "Kalıtım Grubu",
"Add failed": "Ekleme başarısız",
"Add group members": "Grup üyeleri ekle",
"Add replication rules to sync objects across buckets.": "Add replication rules to sync objects across buckets.",
diff --git a/i18n/locales/zh-CN.json b/i18n/locales/zh-CN.json
index 9f4dacb..4b45008 100644
--- a/i18n/locales/zh-CN.json
+++ b/i18n/locales/zh-CN.json
@@ -31,6 +31,7 @@
"Add Tier": "添加存储层",
"Add User": "新增用户",
"Add User Group": "新增用户组",
+ "Inherited from group": "继承自分组",
"Add failed": "添加失败",
"Add group members": "添加组成员",
"Add replication rules to sync objects across buckets.": "添加复制规则以同步存储桶之间的对象。",
diff --git a/package-lock.json b/package-lock.json
index e2b07c1..316a84e 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -38,7 +38,7 @@
"jszip": "^3.10.1",
"lucide-vue-next": "^0.487.0",
"motion-v": "^1.5.0",
- "node-forge": "^1.3.1",
+ "node-forge": "^1.3.2",
"nuxt": "^4.2.0",
"pinia": "^2.3.1",
"pnpm": "^10.19.0",
@@ -67,6 +67,7 @@
"@types/aws4": "^1.11.6",
"@types/lodash": "^4.17.20",
"@types/node-forge": "^1.3.12",
+ "@vitalets/google-translate-api": "^9.2.1",
"@vitest/coverage-v8": "^3.2.4",
"@vitest/ui": "^3.2.4",
"@vueuse/core": "^13.5.0",
@@ -80,6 +81,7 @@
"prettier": "^3.2.5",
"tailwind-merge": "^3.3.1",
"tailwindcss-animate": "^1.0.7",
+ "tar": "^7.5.2",
"tw-animate-css": "^1.4.0",
"typescript": "^5.8.3",
"vitest": "^3.2.4",
@@ -7372,6 +7374,12 @@
"integrity": "sha512-6C8nqWur3j98U6+lXDfTUWIfgvZU+EumvpHKcYjujKH7woYyLj2sUmff0tRhrqM7BohUw7Pz3ZB1jj2gW9Fvmg==",
"license": "MIT"
},
+ "node_modules/@types/http-errors": {
+ "version": "1.8.2",
+ "resolved": "https://registry.npmmirror.com/@types/http-errors/-/http-errors-1.8.2.tgz",
+ "integrity": "sha512-EqX+YQxINb+MeXaIqYDASb6U6FCHbWjkj4a1CKDBks3d/QiB2+PqBLyO72vLDgAO1wUI4O+9gweRcQK11bTL/w==",
+ "dev": true
+ },
"node_modules/@types/istanbul-lib-coverage": {
"version": "2.0.6",
"resolved": "https://registry.npmmirror.com/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.6.tgz",
@@ -7837,6 +7845,20 @@
"node": ">=8"
}
},
+ "node_modules/@vitalets/google-translate-api": {
+ "version": "9.2.1",
+ "resolved": "https://registry.npmmirror.com/@vitalets/google-translate-api/-/google-translate-api-9.2.1.tgz",
+ "integrity": "sha512-zlwQWSjXUZhbZQ6qwtIQ7GdYXFQmJ4wYqzcrYJUxtvzQQwUP+uKUb/SRJaBOQuBntjBjzcdcJoLFrpCKUbIkOg==",
+ "dev": true,
+ "dependencies": {
+ "@types/http-errors": "^1.8.2",
+ "http-errors": "^2.0.0",
+ "node-fetch": "^2.6.7"
+ },
+ "engines": {
+ "node": ">=14"
+ }
+ },
"node_modules/@vitejs/plugin-vue": {
"version": "6.0.1",
"resolved": "https://registry.npmmirror.com/@vitejs/plugin-vue/-/plugin-vue-6.0.1.tgz",
@@ -15077,10 +15099,9 @@
}
},
"node_modules/node-forge": {
- "version": "1.3.1",
- "resolved": "https://registry.npmmirror.com/node-forge/-/node-forge-1.3.1.tgz",
- "integrity": "sha512-dPEtOeMvF9VMcYV/1Wb8CPoVAXtp6MKMlcbAt4ddqmGqUJ6fQZFXkNZNkNlfevtNkGtaSoXf/vNNNSvgrdXwtA==",
- "license": "(BSD-3-Clause OR GPL-2.0)",
+ "version": "1.3.3",
+ "resolved": "https://registry.npmmirror.com/node-forge/-/node-forge-1.3.3.tgz",
+ "integrity": "sha512-rLvcdSyRCyouf6jcOIPe/BgwG/d7hKjzMKOas33/pHEr6gbq18IK9zV7DiPvzsz0oBJPme6qr6H6kGZuI9/DZg==",
"engines": {
"node": ">= 6.13.0"
}
@@ -18819,10 +18840,9 @@
}
},
"node_modules/tar": {
- "version": "7.5.1",
- "resolved": "https://registry.npmmirror.com/tar/-/tar-7.5.1.tgz",
- "integrity": "sha512-nlGpxf+hv0v7GkWBK2V9spgactGOp0qvfWRxUMjqHyzrt3SgwE48DIv/FhqPHJYLHpgW1opq3nERbz5Anq7n1g==",
- "license": "ISC",
+ "version": "7.5.2",
+ "resolved": "https://registry.npmmirror.com/tar/-/tar-7.5.2.tgz",
+ "integrity": "sha512-7NyxrTE4Anh8km8iEy7o0QYPs+0JKBTj5ZaqHg6B39erLg0qYXN3BijtShwbsNSvQ+LN75+KV+C4QR/f6Gwnpg==",
"dependencies": {
"@isaacs/fs-minipass": "^4.0.0",
"chownr": "^3.0.0",