首页
学习
活动
专区
圈层
工具
发布
社区首页 >专栏 >Jenkins授权卡?试试我的方法

Jenkins授权卡?试试我的方法

作者头像
没有故事的陈师傅
发布2025-11-17 19:18:45
发布2025-11-17 19:18:45
750
举报
文章被收录于专栏:运维开发故事运维开发故事

✍ 道路千万条,安全第一条。操作不规范,运维两行泪。

使用过 Jenkins 的应该都用过 Role Strategy plugin 来管理用户/组的权限管理,不是说它有多好用,而是没太多选择。

但是,不知道有没有发现一个问题:当用户的量级上去之后,打开授权界面经常会把浏览器卡死导致无法操作,为此我想是不是可以直接通过接口去授权,这样就不必打开那么卡的页面了。

通过 https://github.com/jenkinsci/role-strategy-plugin 学习,发现其有 REST API,提供一部分接口功能。

8ca930960a30b80f87512c5784aaeadd MD5
8ca930960a30b80f87512c5784aaeadd MD5

8ca930960a30b80f87512c5784aaeadd MD5

为此,我先写了一个 role-srategy-sdk 的SDK,地址是:https://github.com/joker-bai/go-role-strategy-sdk。该 SDK 基本涵盖了文档中的所有接口。

  • 权限模板管理 - 创建、删除和查询权限模板
  • 角色管理 - 支持全局角色、项目角色和节点角色的完整生命周期管理
  • 用户/组分配 - 灵活的用户和组角色分配功能
  • 查询功能 - 获取角色信息、分配情况和匹配的作业/代理
  • 多角色类型 - 支持 GlobalRole、ProjectRole 和 SlaveRole

SDK的使用

安装

代码语言:javascript
复制
go get github.com/joker-bai/go-role-strategy-sdk

初始化客户端

代码语言:javascript
复制
package main

import (
    "fmt"
    "log"
    
    rolestrategy "github.com/joker-bai/go-role-strategy-sdk"
)

func main() {
    // 创建客户端
    client := rolestrategy.NewClient(
        "https://your-jenkins-url",
        "your-username",
        "your-api-token",
    )
}

权限模板管理

代码语言:javascript
复制
// 添加权限模板
err := client.AddTemplate(
    "developer", 
    "hudson.model.Item.Read,hudson.model.Item.Build", 
    true, // overwrite
)
if err != nil {
    log.Fatal("AddTemplate:", err)
}

// 获取权限模板
template, err := client.GetTemplate("developer")
if err != nil {
    log.Fatal("GetTemplate:", err)
}
fmt.Printf("Template: %+v\n", template)

// 删除权限模板
err = client.RemoveTemplates([]string{"developer"}, false)
if err != nil {
    log.Fatal("RemoveTemplates:", err)
}

角色管理

代码语言:javascript
复制
// 添加全局角色
err := client.AddRole(
    rolestrategy.GlobalRole, 
    "dev-lead", 
    "hudson.model.Hudson.Administer", 
    true, // overwrite
    "",   // pattern (项目角色使用)
    "",   // template
)
if err != nil {
    log.Fatal("AddRole:", err)
}

// 添加项目角色(带模式匹配)
err = client.AddRole(
    rolestrategy.ProjectRole, 
    "project-dev", 
    "hudson.model.Item.Read,hudson.model.Item.Build", 
    true, 
    "dev-.*", // 匹配以 dev- 开头的项目
    "",
)

// 获取角色信息
role, err := client.GetRole(rolestrategy.GlobalRole, "dev-lead")
if err != nil {
    log.Fatal("GetRole:", err)
}
fmt.Printf("Role: %+v\n", role)

// 删除角色
err = client.RemoveRoles(rolestrategy.GlobalRole, []string{"dev-lead"})
if err != nil {
    log.Fatal("RemoveRoles:", err)
}

用户和组分配

代码语言:javascript
复制
// 分配用户角色
err := client.AssignUserRole(rolestrategy.GlobalRole, "dev-lead", "alice")
if err != nil {
    log.Fatal("AssignUserRole:", err)
}

// 分配组角色
err = client.AssignGroupRole(rolestrategy.ProjectRole, "project-dev", "developers")
if err != nil {
    log.Fatal("AssignGroupRole:", err)
}

// 取消用户角色分配
err = client.UnassignUserRole(rolestrategy.GlobalRole, "dev-lead", "alice")
if err != nil {
    log.Fatal("UnassignUserRole:", err)
}

// 取消组角色分配
err = client.UnassignGroupRole(rolestrategy.ProjectRole, "project-dev", "developers")
if err != nil {
    log.Fatal("UnassignGroupRole:", err)
}

查询功能

代码语言:javascript
复制
// 获取所有角色
allRoles, err := client.GetAllRoles(rolestrategy.GlobalRole)
if err != nil {
    log.Fatal("GetAllRoles:", err)
}
fmt.Printf("All Roles: %+v\n", allRoles)

// 获取角色分配情况
assignments, err := client.GetRoleAssignments(rolestrategy.GlobalRole)
if err != nil {
    log.Fatal("GetRoleAssignments:", err)
}
fmt.Printf("Assignments: %+v\n", assignments)

// 获取全局角色名列表
globalRoles, err := client.GetGlobalRoleNames()
if err != nil {
    log.Fatal("GetGlobalRoleNames:", err)
}
fmt.Println("Global Roles:", globalRoles)

// 获取项目角色名列表
projectRoles, err := client.GetProjectRoleNames()
if err != nil {
    log.Fatal("GetProjectRoleNames:", err)
}
fmt.Println("Project Roles:", projectRoles)

// 获取匹配的作业
matchingJobs, err := client.GetMatchingJobs("dev-.*", 10)
if err != nil {
    log.Fatal("GetMatchingJobs:", err)
}
fmt.Printf("Matching Jobs: %+v\n", matchingJobs)

// 获取匹配的代理
matchingAgents, err := client.GetMatchingAgents("agent-.*", 10)
if err != nil {
    log.Fatal("GetMatchingAgents:", err)
}
fmt.Printf("Matching Agents: %+v\n", matchingAgents)

开发一个 Web UI

我们有了 SDK,现在可以基于 SDK 开发一个简单的 Web UI,我这里后端使用 Gin 框架,前端就简单写个 Html 实现。

对于后端,我们这里只设计了四个接口,它们是:

  • /api/users:获取用户信息
  • /api/roles/global:获取全局角色
  • /api/roles/project:获取项目角色
  • /api/users/assign:用户授权

完整的后端代码如下:

代码语言:javascript
复制
// main.go
package main

import (
"fmt"
"log"
"net/http"
"strconv"
"strings"

"github.com/gin-gonic/gin"
 rolestrategy "github.com/joker-bai/go-role-strategy-sdk"
)

// Request models
type AssignRequest struct {
 Username     string   `json:"username" binding:"required"`
 GlobalRoles  []string`json:"globalRoles"`
 ProjectRoles []string`json:"projectRoles"`
}

type UserListResponse struct {
 Users      []UserRoles `json:"users"`
 Total      int         `json:"total"`
 Page       int         `json:"page"`
 PageSize   int         `json:"pageSize"`
 TotalPages int         `json:"totalPages"`
}

type RoleListResponse []string

type UserRoles struct {
 Username     string   `json:"username"`
 GlobalRoles  []string`json:"globalRoles"`
 ProjectRoles []string`json:"projectRoles"`
}

var jenkinsClient *rolestrategy.Client

func main() {
 jenkinsClient = rolestrategy.NewClient(
"http://127.0.0.1:8080/jenkins",
"admin",
"your-api-token",
 )

 r := gin.Default()
 r.StaticFile("/", "./web/index.html")

 api := r.Group("/api")
 {
  api.GET("/users", getUsersHandler)
  api.GET("/roles/global", getGlobalRolesHandler)
  api.GET("/roles/project", getProjectRolesHandler)
  api.POST("/users/assign", assignRolesHandler)
 }

 log.Println("Server starting on :8080")
 r.Run(":8080")
}

func getUsersHandler(c *gin.Context) {
var users []UserRoles
 userMap := make(map[string]*UserRoles)

 page, _ := strconv.Atoi(c.DefaultQuery("page", "1"))
 pageSize, _ := strconv.Atoi(c.DefaultQuery("pageSize", "10"))
 search := strings.TrimSpace(c.DefaultQuery("search", ""))
if page < 1 {
  page = 1
 }
if pageSize < 1 || pageSize > 100 {
  pageSize = 10
 }

// 从 Jenkins 获取数据
 processRoleMap := func(roleType rolestrategy.RoleType, addToGlobal bool) {
  roleMap, err := jenkinsClient.GetAllRoles(roleType)
if err != nil {
   c.JSON(http.StatusInternalServerError, gin.H{"error": fmt.Sprintf("Failed to get %s roles: %v", roleType, err)})
   c.Abort()
   return
  }
for roleName, sids := range roleMap {
   for _, sid := range sids {
    if sid.Type != "USER" {
     continue
    }
    if _, exists := userMap[sid.SID]; !exists {
     userMap[sid.SID] = &UserRoles{
      Username:     sid.SID,
      GlobalRoles:  []string{},
      ProjectRoles: []string{},
     }
    }
    if addToGlobal {
     userMap[sid.SID].GlobalRoles = append(userMap[sid.SID].GlobalRoles, roleName)
    } else {
     userMap[sid.SID].ProjectRoles = append(userMap[sid.SID].ProjectRoles, roleName)
    }
   }
  }
 }

 processRoleMap(rolestrategy.GlobalRole, true)
if c.IsAborted() {
return
 }
 processRoleMap(rolestrategy.ProjectRole, false)
if c.IsAborted() {
return
 }

for _, user := range userMap {
  users = append(users, *user)
 }

// 应用搜索过滤
if search != "" {
var filteredUsers []UserRoles
for _, user := range users {
   if strings.Contains(strings.ToLower(user.Username), strings.ToLower(search)) {
    filteredUsers = append(filteredUsers, user)
   }
  }
  users = filteredUsers
 }

 total := len(users)
 totalPages := (total + pageSize - 1) / pageSize
 start := (page - 1) * pageSize
 end := start + pageSize
if start >= total {
  users = []UserRoles{}
 } else {
if end > total {
   end = total
  }
  users = users[start:end]
 }

 c.JSON(http.StatusOK, UserListResponse{
  Users:      users,
  Total:      total,
  Page:       page,
  PageSize:   pageSize,
  TotalPages: totalPages,
 })
}

func getGlobalRolesHandler(c *gin.Context) {
 roles, err := jenkinsClient.GetGlobalRoleNames()
if err != nil {
  c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
return
 }
 c.JSON(http.StatusOK, RoleListResponse(roles))
}

func getProjectRolesHandler(c *gin.Context) {
 roles, err := jenkinsClient.GetProjectRoleNames()
if err != nil {
  c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
return
 }
 c.JSON(http.StatusOK, RoleListResponse(roles))
}

func assignRolesHandler(c *gin.Context) {
var req AssignRequest
if err := c.ShouldBindJSON(&req); err != nil {
  c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
return
 }

// 获取用户当前的角色
 globalMap, err := jenkinsClient.GetAllRoles(rolestrategy.GlobalRole)
if err != nil {
  c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to get current global roles: " + err.Error()})
return
 }
 projectMap, err := jenkinsClient.GetAllRoles(rolestrategy.ProjectRole)
if err != nil {
  c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to get current project roles: " + err.Error()})
return
 }

// 构建当前角色集合
 currentGlobalRoles := make(map[string]bool)
for roleName, sids := range globalMap {
for _, sid := range sids {
   if sid.Type == "USER" && sid.SID == req.Username {
    currentGlobalRoles[roleName] = true
   }
  }
 }

 currentProjectRoles := make(map[string]bool)
for roleName, sids := range projectMap {
for _, sid := range sids {
   if sid.Type == "USER" && sid.SID == req.Username {
    currentProjectRoles[roleName] = true
   }
  }
 }

// 分配新角色
for _, roleName := range req.GlobalRoles {
if !currentGlobalRoles[roleName] {
   if err := jenkinsClient.AssignUserRole(rolestrategy.GlobalRole, roleName, req.Username); err != nil {
    c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to assign global role: " + err.Error()})
    return
   }
  }
 }

for _, roleName := range req.ProjectRoles {
if !currentProjectRoles[roleName] {
   if err := jenkinsClient.AssignUserRole(rolestrategy.ProjectRole, roleName, req.Username); err != nil {
    c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to assign project role: " + err.Error()})
    return
   }
  }
 }

// 移除不再拥有的角色
for roleName := range currentGlobalRoles {
if !contains(req.GlobalRoles, roleName) {
   if err := jenkinsClient.UnassignUserRole(rolestrategy.GlobalRole, roleName, req.Username); err != nil {
    c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to unassign global role: " + err.Error()})
    return
   }
  }
 }

for roleName := range currentProjectRoles {
if !contains(req.ProjectRoles, roleName) {
   if err := jenkinsClient.UnassignUserRole(rolestrategy.ProjectRole, roleName, req.Username); err != nil {
    c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to unassign project role: " + err.Error()})
    return
   }
  }
 }

 c.JSON(http.StatusOK, gin.H{"success": true, "message": "Roles updated successfully"})
}

func contains(slice []string, item string) bool {
for _, s := range slice {
if s == item {
   returntrue
  }
 }
returnfalse
}

前端代码就不写了,直接到 仓库 查看。

实际的效果如下:

ff05662851d9db0302a62ec564a96b5b MD5
ff05662851d9db0302a62ec564a96b5b MD5

ff05662851d9db0302a62ec564a96b5b MD5

用户详情,可以查看目前该用户拥有的权限:

971f6b099af019c29ed8856a7b1bcb5d MD5
971f6b099af019c29ed8856a7b1bcb5d MD5

971f6b099af019c29ed8856a7b1bcb5d MD5

点击修改,可以直接修改用户的权限:

91dcad6460cbdb0c730c3a3843cce753 MD5
91dcad6460cbdb0c730c3a3843cce753 MD5

91dcad6460cbdb0c730c3a3843cce753 MD5

对于新用户,也可以直接授权:

7ac7f6d1b3f1541474cae991a45adef6 MD5
7ac7f6d1b3f1541474cae991a45adef6 MD5

7ac7f6d1b3f1541474cae991a45adef6 MD5

这样就不用害怕 Jenkins 卡死了。同时,我也将代码传到了 Github,有兴趣的可以了解一下。

上面的代码仅仅是一个简单的 demo,可以对其进行扩充,比如:

  • 用户接入企业用户中心,判断该用户是否存在
  • 增加多Jenkins管理,操作者可以切换不同的Jenkins进行授权操作

当然,目前的界面也比较丑,不过,就这样吧......

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2025-09-05,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 运维开发故事 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • SDK的使用
    • 安装
    • 初始化客户端
    • 权限模板管理
    • 角色管理
    • 用户和组分配
    • 查询功能
  • 开发一个 Web UI
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档