feat(server): enhance user permissions and access control
Some checks failed
Deploy skills-market-server / deploy (push) Has been cancelled
Some checks failed
Deploy skills-market-server / deploy (push) Has been cancelled
- Introduced root admin checks in various functions to streamline permission handling for users. - Updated skill and agent management routes to simplify access control by removing redundant role checks. - Added favicon links to HTML files for improved branding in the admin and updates sections.
This commit is contained in:
parent
f86cace07d
commit
48e999149c
@ -4,6 +4,9 @@
|
|||||||
<meta charset="UTF-8" />
|
<meta charset="UTF-8" />
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
<title>权限管理平台</title>
|
<title>权限管理平台</title>
|
||||||
|
<link rel="icon" type="image/png" href="https://oss.xintiao85.com/images/20260312_110209_2a7c6d79c7469d640f64cb663d6983b1.png" />
|
||||||
|
<link rel="shortcut icon" type="image/png" href="https://oss.xintiao85.com/images/20260312_110209_2a7c6d79c7469d640f64cb663d6983b1.png" />
|
||||||
|
<link rel="apple-touch-icon" href="https://oss.xintiao85.com/images/20260312_110209_2a7c6d79c7469d640f64cb663d6983b1.png" />
|
||||||
<style>
|
<style>
|
||||||
:root {
|
:root {
|
||||||
--bg: #f8fafc;
|
--bg: #f8fafc;
|
||||||
|
|||||||
@ -98,6 +98,7 @@ function sanitizeUserForClient(userDoc) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function canAccessFeature(user, permissionKey) {
|
function canAccessFeature(user, permissionKey) {
|
||||||
|
if (user?.is_root_admin) return true
|
||||||
const perms = sanitizePermissions(user.permissions)
|
const perms = sanitizePermissions(user.permissions)
|
||||||
return !!perms[permissionKey]
|
return !!perms[permissionKey]
|
||||||
}
|
}
|
||||||
|
|||||||
39
server.js
39
server.js
@ -157,7 +157,12 @@ function normalizeTagList(tags) {
|
|||||||
return out
|
return out
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const ROOT_MODE_TAGS = ['clarify', 'cowork', 'create', 'video', 'code']
|
||||||
|
|
||||||
function getAllowedModeTagsFromUser(user) {
|
function getAllowedModeTagsFromUser(user) {
|
||||||
|
if (user?.is_root_admin) {
|
||||||
|
return normalizeTagList(ROOT_MODE_TAGS)
|
||||||
|
}
|
||||||
const modes = Array.isArray(user?.permissions?.allowedModes) ? user.permissions.allowedModes : []
|
const modes = Array.isArray(user?.permissions?.allowedModes) ? user.permissions.allowedModes : []
|
||||||
return normalizeTagList(modes)
|
return normalizeTagList(modes)
|
||||||
}
|
}
|
||||||
@ -167,24 +172,12 @@ function buildModeIntersectionFilter(user, requestedTags = []) {
|
|||||||
const requested = normalizeTagList(requestedTags)
|
const requested = normalizeTagList(requestedTags)
|
||||||
const isRootAdmin = !!user?.is_root_admin
|
const isRootAdmin = !!user?.is_root_admin
|
||||||
const isManagementList = requested.length === 0
|
const isManagementList = requested.length === 0
|
||||||
|
if (isRootAdmin && isManagementList) return {}
|
||||||
const effective =
|
const effective =
|
||||||
requested.length > 0
|
requested.length > 0
|
||||||
? requested.filter((tag) => allowedTags.includes(tag))
|
? requested.filter((tag) => allowedTags.includes(tag))
|
||||||
: allowedTags
|
: allowedTags
|
||||||
|
|
||||||
if (isRootAdmin && isManagementList) {
|
|
||||||
if (effective.length === 0) {
|
|
||||||
return { $or: [{ tags: { $exists: false } }, { tags: { $size: 0 } }] }
|
|
||||||
}
|
|
||||||
return {
|
|
||||||
$or: [
|
|
||||||
{ tags: { $in: effective } },
|
|
||||||
{ tags: { $exists: false } },
|
|
||||||
{ tags: { $size: 0 } }
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (effective.length === 0) {
|
if (effective.length === 0) {
|
||||||
return { _id: { $exists: false } }
|
return { _id: { $exists: false } }
|
||||||
}
|
}
|
||||||
@ -192,10 +185,10 @@ function buildModeIntersectionFilter(user, requestedTags = []) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function canUserAccessTaggedResource(user, resourceTags) {
|
function canUserAccessTaggedResource(user, resourceTags) {
|
||||||
|
if (user?.is_root_admin) return true
|
||||||
const allowedTags = getAllowedModeTagsFromUser(user)
|
const allowedTags = getAllowedModeTagsFromUser(user)
|
||||||
const tags = normalizeTagList(resourceTags)
|
const tags = normalizeTagList(resourceTags)
|
||||||
const isRootAdmin = !!user?.is_root_admin
|
if (tags.length === 0) return false
|
||||||
if (tags.length === 0) return isRootAdmin
|
|
||||||
if (allowedTags.length === 0) return false
|
if (allowedTags.length === 0) return false
|
||||||
return tags.some((tag) => allowedTags.includes(tag))
|
return tags.some((tag) => allowedTags.includes(tag))
|
||||||
}
|
}
|
||||||
@ -495,7 +488,7 @@ app.get('/api/skills/:name/files/*', async (req, res) => {
|
|||||||
app.post('/api/skills/:name/lock', async (req, res) => {
|
app.post('/api/skills/:name/lock', async (req, res) => {
|
||||||
authRoutes.verifyToken(req, res, async () => {
|
authRoutes.verifyToken(req, res, async () => {
|
||||||
try {
|
try {
|
||||||
if (req.user.role !== 'admin' && !authRoutes.hasPermission(req.user, 'canViewSkillsPage')) {
|
if (!authRoutes.hasPermission(req.user, 'canViewSkillsPage')) {
|
||||||
return res.status(403).json({ success: false, error: '无权编辑技能' })
|
return res.status(403).json({ success: false, error: '无权编辑技能' })
|
||||||
}
|
}
|
||||||
const skill = await skillsCollection.findOne({ name: req.params.name })
|
const skill = await skillsCollection.findOne({ name: req.params.name })
|
||||||
@ -520,7 +513,7 @@ app.post('/api/skills/:name/lock', async (req, res) => {
|
|||||||
app.delete('/api/skills/:name/lock', async (req, res) => {
|
app.delete('/api/skills/:name/lock', async (req, res) => {
|
||||||
authRoutes.verifyToken(req, res, async () => {
|
authRoutes.verifyToken(req, res, async () => {
|
||||||
try {
|
try {
|
||||||
if (req.user.role !== 'admin' && !authRoutes.hasPermission(req.user, 'canViewSkillsPage')) {
|
if (!authRoutes.hasPermission(req.user, 'canViewSkillsPage')) {
|
||||||
return res.status(403).json({ success: false, error: '无权编辑技能' })
|
return res.status(403).json({ success: false, error: '无权编辑技能' })
|
||||||
}
|
}
|
||||||
const skill = await skillsCollection.findOne({ name: req.params.name })
|
const skill = await skillsCollection.findOne({ name: req.params.name })
|
||||||
@ -590,7 +583,7 @@ app.get('/api/skills/mine', async (req, res, next) => {
|
|||||||
app.patch('/api/skills/:name/tags', async (req, res) => {
|
app.patch('/api/skills/:name/tags', async (req, res) => {
|
||||||
authRoutes.verifyToken(req, res, async () => {
|
authRoutes.verifyToken(req, res, async () => {
|
||||||
try {
|
try {
|
||||||
if (req.user.role !== 'admin' && !authRoutes.hasPermission(req.user, 'canViewSkillsPage')) {
|
if (!authRoutes.hasPermission(req.user, 'canViewSkillsPage')) {
|
||||||
return res.status(403).json({ success: false, error: '无权修改技能标签' })
|
return res.status(403).json({ success: false, error: '无权修改技能标签' })
|
||||||
}
|
}
|
||||||
const allowedModeTags = getAllowedModeTagsFromUser(req.user)
|
const allowedModeTags = getAllowedModeTagsFromUser(req.user)
|
||||||
@ -636,7 +629,7 @@ app.patch('/api/skills/:name/tags', async (req, res) => {
|
|||||||
app.post('/api/skills/:name/publish', async (req, res) => {
|
app.post('/api/skills/:name/publish', async (req, res) => {
|
||||||
authRoutes.verifyToken(req, res, async () => {
|
authRoutes.verifyToken(req, res, async () => {
|
||||||
try {
|
try {
|
||||||
if (req.user.role !== 'admin' && !authRoutes.hasPermission(req.user, 'canViewSkillsPage')) {
|
if (!authRoutes.hasPermission(req.user, 'canViewSkillsPage')) {
|
||||||
return res.status(403).json({ success: false, error: '无权发布或修改技能' })
|
return res.status(403).json({ success: false, error: '无权发布或修改技能' })
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -965,7 +958,7 @@ app.get('/api/agents/:name', async (req, res) => {
|
|||||||
app.post('/api/agents/:name/publish', async (req, res) => {
|
app.post('/api/agents/:name/publish', async (req, res) => {
|
||||||
authRoutes.verifyToken(req, res, async () => {
|
authRoutes.verifyToken(req, res, async () => {
|
||||||
try {
|
try {
|
||||||
if (req.user.role !== 'admin' && !authRoutes.hasPermission(req.user, 'canViewAgentsPage')) {
|
if (!authRoutes.hasPermission(req.user, 'canViewAgentsPage')) {
|
||||||
return res.status(403).json({ success: false, error: '无权发布或修改智能体' })
|
return res.status(403).json({ success: false, error: '无权发布或修改智能体' })
|
||||||
}
|
}
|
||||||
const { content, description, tags, localModifiedAt } = req.body
|
const { content, description, tags, localModifiedAt } = req.body
|
||||||
@ -1045,7 +1038,7 @@ app.post('/api/agents/:name/publish', async (req, res) => {
|
|||||||
app.patch('/api/agents/:name/tags', async (req, res) => {
|
app.patch('/api/agents/:name/tags', async (req, res) => {
|
||||||
authRoutes.verifyToken(req, res, async () => {
|
authRoutes.verifyToken(req, res, async () => {
|
||||||
try {
|
try {
|
||||||
if (req.user.role !== 'admin' && !authRoutes.hasPermission(req.user, 'canViewAgentsPage')) {
|
if (!authRoutes.hasPermission(req.user, 'canViewAgentsPage')) {
|
||||||
return res.status(403).json({ success: false, error: '无权修改智能体标签' })
|
return res.status(403).json({ success: false, error: '无权修改智能体标签' })
|
||||||
}
|
}
|
||||||
const allowedModeTags = getAllowedModeTagsFromUser(req.user)
|
const allowedModeTags = getAllowedModeTagsFromUser(req.user)
|
||||||
@ -1087,7 +1080,7 @@ app.delete('/api/agents/:name', requireAdmin(async (req, res) => {
|
|||||||
app.post('/api/agents/:name/lock', async (req, res, next) => {
|
app.post('/api/agents/:name/lock', async (req, res, next) => {
|
||||||
authRoutes.verifyToken(req, res, async () => {
|
authRoutes.verifyToken(req, res, async () => {
|
||||||
try {
|
try {
|
||||||
if (req.user.role !== 'admin' && !authRoutes.hasPermission(req.user, 'canViewAgentsPage')) {
|
if (!authRoutes.hasPermission(req.user, 'canViewAgentsPage')) {
|
||||||
return res.status(403).json({ success: false, error: '无权编辑智能体' })
|
return res.status(403).json({ success: false, error: '无权编辑智能体' })
|
||||||
}
|
}
|
||||||
const agent = await agentsCollection.findOne({ name: req.params.name })
|
const agent = await agentsCollection.findOne({ name: req.params.name })
|
||||||
@ -1107,7 +1100,7 @@ app.post('/api/agents/:name/lock', async (req, res, next) => {
|
|||||||
app.delete('/api/agents/:name/lock', async (req, res, next) => {
|
app.delete('/api/agents/:name/lock', async (req, res, next) => {
|
||||||
authRoutes.verifyToken(req, res, async () => {
|
authRoutes.verifyToken(req, res, async () => {
|
||||||
try {
|
try {
|
||||||
if (req.user.role !== 'admin' && !authRoutes.hasPermission(req.user, 'canViewAgentsPage')) {
|
if (!authRoutes.hasPermission(req.user, 'canViewAgentsPage')) {
|
||||||
return res.status(403).json({ success: false, error: '无权编辑智能体' })
|
return res.status(403).json({ success: false, error: '无权编辑智能体' })
|
||||||
}
|
}
|
||||||
const agent = await agentsCollection.findOne({ name: req.params.name })
|
const agent = await agentsCollection.findOne({ name: req.params.name })
|
||||||
|
|||||||
@ -4,6 +4,9 @@
|
|||||||
<meta charset="UTF-8">
|
<meta charset="UTF-8">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
<title>LikeCowork 安装指南</title>
|
<title>LikeCowork 安装指南</title>
|
||||||
|
<link rel="icon" type="image/png" href="https://oss.xintiao85.com/images/20260312_110209_2a7c6d79c7469d640f64cb663d6983b1.png">
|
||||||
|
<link rel="shortcut icon" type="image/png" href="https://oss.xintiao85.com/images/20260312_110209_2a7c6d79c7469d640f64cb663d6983b1.png">
|
||||||
|
<link rel="apple-touch-icon" href="https://oss.xintiao85.com/images/20260312_110209_2a7c6d79c7469d640f64cb663d6983b1.png">
|
||||||
<style>
|
<style>
|
||||||
* { margin: 0; padding: 0; box-sizing: border-box; }
|
* { margin: 0; padding: 0; box-sizing: border-box; }
|
||||||
body {
|
body {
|
||||||
|
|||||||
@ -1,11 +0,0 @@
|
|||||||
version: 1.2.1
|
|
||||||
files:
|
|
||||||
- url: likecowork-1.2.1-x64-mac.zip
|
|
||||||
sha512: QIOFv41c9MYOPPKaxz4QunYHZJoobVSXhQYXQFgFJUm89Hf3AEC8PtHx3N8e7A99Nun7UuYwO16EaS9YmMJu7w==
|
|
||||||
size: 978479730
|
|
||||||
- url: likecowork-1.2.1-arm64-mac.zip
|
|
||||||
sha512: L6CFKjmxD0Aep4gXkX2ojPuhPnwphHwoEVZXcI45WXmMzQRZfSG3bXY4o+PdZ+nfE7CFYkYbGl9g9yTbc/fPKQ==
|
|
||||||
size: 973708487
|
|
||||||
path: likecowork-1.2.1-x64-mac.zip
|
|
||||||
sha512: QIOFv41c9MYOPPKaxz4QunYHZJoobVSXhQYXQFgFJUm89Hf3AEC8PtHx3N8e7A99Nun7UuYwO16EaS9YmMJu7w==
|
|
||||||
releaseDate: '2026-03-27T08:06:20.025Z'
|
|
||||||
@ -1,8 +0,0 @@
|
|||||||
version: 1.2.1
|
|
||||||
files:
|
|
||||||
- url: likecowork-1.2.1-setup.exe
|
|
||||||
sha512: c3DLm7mfzV5e94GRDSkB0RWkqvSOJO04TVbMBR3FIoVsiRCTtGn/raRXYfs+xTS9mZ5JlpkYTdM+gtS3aQ3UhQ==
|
|
||||||
size: 938877301
|
|
||||||
path: likecowork-1.2.1-setup.exe
|
|
||||||
sha512: c3DLm7mfzV5e94GRDSkB0RWkqvSOJO04TVbMBR3FIoVsiRCTtGn/raRXYfs+xTS9mZ5JlpkYTdM+gtS3aQ3UhQ==
|
|
||||||
releaseDate: '2026-03-27T07:51:13.507Z'
|
|
||||||
Loading…
x
Reference in New Issue
Block a user