mirror of
https://github.com/1Panel-dev/KubePi.git
synced 2025-09-27 11:42:10 +08:00
332 lines
9.8 KiB
Go
332 lines
9.8 KiB
Go
package session
|
|
|
|
import (
|
|
goContext "context"
|
|
"errors"
|
|
"fmt"
|
|
v1Role "github.com/KubeOperator/kubepi/internal/model/v1/role"
|
|
"github.com/KubeOperator/kubepi/internal/service/v1/cluster"
|
|
"github.com/KubeOperator/kubepi/internal/service/v1/common"
|
|
"github.com/KubeOperator/kubepi/internal/service/v1/role"
|
|
"github.com/KubeOperator/kubepi/internal/service/v1/rolebinding"
|
|
"github.com/KubeOperator/kubepi/internal/service/v1/user"
|
|
"github.com/KubeOperator/kubepi/pkg/collectons"
|
|
"github.com/KubeOperator/kubepi/pkg/kubernetes"
|
|
"github.com/asdine/storm/v3"
|
|
"github.com/kataras/iris/v12"
|
|
"github.com/kataras/iris/v12/context"
|
|
"github.com/kataras/iris/v12/sessions"
|
|
"golang.org/x/crypto/bcrypt"
|
|
v1 "k8s.io/api/rbac/v1"
|
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
|
"strings"
|
|
)
|
|
|
|
type Handler struct {
|
|
userService user.Service
|
|
roleService role.Service
|
|
clusterService cluster.Service
|
|
rolebindingService rolebinding.Service
|
|
}
|
|
|
|
func NewHandler() *Handler {
|
|
return &Handler{
|
|
clusterService: cluster.NewService(),
|
|
userService: user.NewService(),
|
|
roleService: role.NewService(),
|
|
rolebindingService: rolebinding.NewService(),
|
|
}
|
|
}
|
|
|
|
func (h *Handler) IsLogin() iris.Handler {
|
|
return func(ctx *context.Context) {
|
|
session := sessions.Get(ctx)
|
|
loginUser := session.Get("profile")
|
|
ctx.StatusCode(iris.StatusOK)
|
|
ctx.Values().Set("data", loginUser != nil)
|
|
}
|
|
}
|
|
|
|
func (h *Handler) Login() iris.Handler {
|
|
return func(ctx *context.Context) {
|
|
var loginCredential LoginCredential
|
|
if err := ctx.ReadJSON(&loginCredential); err != nil {
|
|
ctx.StatusCode(iris.StatusBadRequest)
|
|
ctx.Values().Set("message", err.Error())
|
|
return
|
|
}
|
|
u, err := h.userService.GetByNameOrEmail(loginCredential.Username, common.DBOptions{})
|
|
if err != nil {
|
|
if errors.Is(err, storm.ErrNotFound) {
|
|
ctx.StatusCode(iris.StatusBadRequest)
|
|
ctx.Values().Set("message", fmt.Sprintf("user %s: not Found", loginCredential.Username))
|
|
return
|
|
}
|
|
ctx.StatusCode(iris.StatusInternalServerError)
|
|
ctx.Values().Set("message", fmt.Sprintf("query user %s failed ,: %s", loginCredential.Username, err.Error()))
|
|
return
|
|
}
|
|
|
|
if err := bcrypt.CompareHashAndPassword([]byte(u.Authenticate.Password), []byte(loginCredential.Password)); err != nil {
|
|
ctx.StatusCode(iris.StatusBadRequest)
|
|
ctx.Values().Set("message", "username or password error")
|
|
return
|
|
}
|
|
|
|
permissions, err := h.aggregateResourcePermissions(loginCredential.Username)
|
|
if err != nil {
|
|
ctx.StatusCode(iris.StatusInternalServerError)
|
|
ctx.Values().Set("message", err.Error())
|
|
return
|
|
}
|
|
session := sessions.Get(ctx)
|
|
profile := UserProfile{
|
|
Name: u.Name,
|
|
NickName: u.NickName,
|
|
Email: u.Email,
|
|
Language: u.Language,
|
|
ResourcePermissions: permissions,
|
|
IsAdministrator: u.IsAdmin,
|
|
}
|
|
session.Set("profile", profile)
|
|
ctx.StatusCode(iris.StatusOK)
|
|
ctx.Values().Set("data", profile)
|
|
}
|
|
}
|
|
|
|
func (h *Handler) aggregateResourcePermissions(name string) (map[string][]string, error) {
|
|
userRoleBindings, err := h.rolebindingService.GetRoleBindingBySubject(v1Role.Subject{
|
|
Kind: "User",
|
|
Name: name,
|
|
}, common.DBOptions{})
|
|
if err != nil && !errors.As(err, &storm.ErrNotFound) {
|
|
return nil, err
|
|
}
|
|
|
|
allRoleBindings := append(userRoleBindings)
|
|
var roleNames []string
|
|
for i := range allRoleBindings {
|
|
roleNames = append(roleNames, allRoleBindings[i].RoleRef)
|
|
}
|
|
|
|
rs, err := h.roleService.GetByNames(roleNames, common.DBOptions{})
|
|
if err != nil && !errors.As(err, &storm.ErrNotFound) {
|
|
return nil, err
|
|
}
|
|
mapping := map[string]*collectons.StringSet{}
|
|
var policyRoles []v1Role.PolicyRule
|
|
//merge permissions
|
|
for i := range rs {
|
|
for j := range rs[i].Rules {
|
|
policyRoles = append(policyRoles, rs[i].Rules[j])
|
|
}
|
|
}
|
|
for i := range policyRoles {
|
|
for j := range policyRoles[i].Resource {
|
|
_, ok := mapping[policyRoles[i].Resource[j]]
|
|
if !ok {
|
|
mapping[policyRoles[i].Resource[j]] = collectons.NewStringSet()
|
|
}
|
|
for k := range policyRoles[i].Verbs {
|
|
mapping[policyRoles[i].Resource[j]].Add(policyRoles[i].Verbs[k])
|
|
if policyRoles[i].Verbs[k] == "*" {
|
|
break
|
|
}
|
|
}
|
|
if policyRoles[i].Resource[j] == "*" {
|
|
break
|
|
}
|
|
}
|
|
}
|
|
resourceMapping := map[string][]string{}
|
|
for key := range mapping {
|
|
resourceMapping[key] = mapping[key].ToSlice()
|
|
}
|
|
return resourceMapping, nil
|
|
}
|
|
|
|
func (h *Handler) Logout() iris.Handler {
|
|
return func(ctx *context.Context) {
|
|
session := sessions.Get(ctx)
|
|
loginUser := session.Get("profile")
|
|
if loginUser == nil {
|
|
ctx.StatusCode(iris.StatusUnauthorized)
|
|
ctx.Values().Set("message", "no login user")
|
|
return
|
|
}
|
|
session.Delete("profile")
|
|
ctx.StatusCode(iris.StatusOK)
|
|
ctx.Values().Set("data", "logout success")
|
|
}
|
|
}
|
|
|
|
func (h *Handler) GetProfile() iris.Handler {
|
|
return func(ctx *context.Context) {
|
|
session := sessions.Get(ctx)
|
|
loginUser := session.Get("profile")
|
|
if loginUser == nil {
|
|
ctx.StatusCode(iris.StatusUnauthorized)
|
|
ctx.Values().Set("message", "no login user")
|
|
return
|
|
}
|
|
p, ok := loginUser.(UserProfile)
|
|
if !ok {
|
|
ctx.StatusCode(iris.StatusInternalServerError)
|
|
ctx.Values().Set("message", "can not parse to session user")
|
|
return
|
|
}
|
|
|
|
user, err := h.userService.GetByNameOrEmail(p.Name, common.DBOptions{})
|
|
if err != nil {
|
|
ctx.StatusCode(iris.StatusInternalServerError)
|
|
ctx.Values().Set("message", err.Error())
|
|
return
|
|
}
|
|
p = UserProfile{
|
|
Name: user.Name,
|
|
NickName: user.NickName,
|
|
Email: user.Email,
|
|
Language: user.Language,
|
|
IsAdministrator: user.IsAdmin,
|
|
}
|
|
if !user.IsAdmin {
|
|
permissions, err := h.aggregateResourcePermissions(p.Name)
|
|
if err != nil {
|
|
ctx.StatusCode(iris.StatusInternalServerError)
|
|
ctx.Values().Set("message", err.Error())
|
|
return
|
|
}
|
|
p.ResourcePermissions = permissions
|
|
}
|
|
session.Set("profile", p)
|
|
ctx.StatusCode(iris.StatusOK)
|
|
ctx.Values().Set("data", p)
|
|
}
|
|
}
|
|
|
|
func (h *Handler) ListUserNamespace() iris.Handler {
|
|
return func(ctx *context.Context) {
|
|
name := ctx.Params().GetString("cluster_name")
|
|
c, err := h.clusterService.Get(name, common.DBOptions{})
|
|
if err != nil {
|
|
ctx.StatusCode(iris.StatusInternalServerError)
|
|
ctx.Values().Set("message", fmt.Sprintf("get cluster failed: %s", err.Error()))
|
|
return
|
|
}
|
|
session := sessions.Get(ctx)
|
|
u := session.Get("profile")
|
|
profile := u.(UserProfile)
|
|
|
|
k := kubernetes.NewKubernetes(c)
|
|
ns, err := k.GetUserNamespaceNames(profile.Name, profile.IsAdministrator)
|
|
if err != nil {
|
|
ctx.StatusCode(iris.StatusInternalServerError)
|
|
ctx.Values().Set("message", err)
|
|
return
|
|
}
|
|
ctx.Values().Set("data", ns)
|
|
}
|
|
}
|
|
|
|
func (h *Handler) GetClusterProfile() iris.Handler {
|
|
return func(ctx *context.Context) {
|
|
session := sessions.Get(ctx)
|
|
clusterName := ctx.Params().GetString("cluster_name")
|
|
namesapce := ctx.URLParam("namespace")
|
|
c, err := h.clusterService.Get(clusterName, common.DBOptions{})
|
|
if err != nil {
|
|
ctx.StatusCode(iris.StatusInternalServerError)
|
|
ctx.Values().Set("message", err.Error())
|
|
return
|
|
}
|
|
k := kubernetes.NewKubernetes(c)
|
|
client, err := k.Client()
|
|
if err != nil {
|
|
ctx.StatusCode(iris.StatusInternalServerError)
|
|
ctx.Values().Set("message", fmt.Sprintf("get k8s client failed: %s", err.Error()))
|
|
return
|
|
}
|
|
u := session.Get("profile")
|
|
profile := u.(UserProfile)
|
|
|
|
if profile.IsAdministrator {
|
|
crp := ClusterUserProfile{
|
|
UserProfile: profile,
|
|
ClusterRoles: []v1.ClusterRole{},
|
|
}
|
|
ctx.Values().Set("data", &crp)
|
|
return
|
|
}
|
|
|
|
labels := []string{
|
|
fmt.Sprintf("%s=%s", kubernetes.LabelManageKey, "kubepi"),
|
|
fmt.Sprintf("%s=%s", kubernetes.LabelClusterId, c.UUID),
|
|
fmt.Sprintf("%s=%s", kubernetes.LabelUsername, profile.Name),
|
|
}
|
|
clusterRoleBindings, err := client.RbacV1().ClusterRoleBindings().List(goContext.TODO(), metav1.ListOptions{
|
|
LabelSelector: strings.Join(labels, ","),
|
|
})
|
|
if err != nil {
|
|
ctx.StatusCode(iris.StatusInternalServerError)
|
|
ctx.Values().Set("message", fmt.Sprintf("get cluster-role-binding failed: %s", err.Error()))
|
|
return
|
|
}
|
|
rolebindings, err := client.RbacV1().RoleBindings(namesapce).List(goContext.TODO(), metav1.ListOptions{
|
|
LabelSelector: strings.Join(labels, ","),
|
|
})
|
|
if err != nil {
|
|
ctx.StatusCode(iris.StatusInternalServerError)
|
|
ctx.Values().Set("message", fmt.Sprintf("get role-binding failed: %s", err.Error()))
|
|
return
|
|
}
|
|
roleSet := map[string]struct{}{}
|
|
for i := range clusterRoleBindings.Items {
|
|
for j := range clusterRoleBindings.Items[i].Subjects {
|
|
if clusterRoleBindings.Items[i].Subjects[j].Kind == "User" {
|
|
roleSet[clusterRoleBindings.Items[i].RoleRef.Name] = struct{}{}
|
|
}
|
|
}
|
|
}
|
|
for i := range rolebindings.Items {
|
|
for j := range rolebindings.Items[i].Subjects {
|
|
if rolebindings.Items[i].Subjects[j].Kind == "User" {
|
|
roleSet[rolebindings.Items[i].RoleRef.Name] = struct{}{}
|
|
}
|
|
}
|
|
}
|
|
var roles []v1.ClusterRole
|
|
for key := range roleSet {
|
|
r, err := client.RbacV1().ClusterRoles().Get(goContext.TODO(), key, metav1.GetOptions{})
|
|
if err != nil {
|
|
ctx.StatusCode(iris.StatusInternalServerError)
|
|
ctx.Values().Set("message", fmt.Sprintf("get cluster-role failed: %s", err.Error()))
|
|
return
|
|
}
|
|
roles = append(roles, *r)
|
|
}
|
|
|
|
crp := ClusterUserProfile{
|
|
UserProfile: profile,
|
|
ClusterRoles: roles,
|
|
}
|
|
if len(roles) <= 0 {
|
|
ctx.StatusCode(iris.StatusForbidden)
|
|
return
|
|
}
|
|
ctx.Values().Set("data", &crp)
|
|
}
|
|
}
|
|
|
|
func Install(parent iris.Party) {
|
|
handler := NewHandler()
|
|
sp := parent.Party("/sessions")
|
|
sp.Post("", handler.Login())
|
|
sp.Delete("", handler.Logout())
|
|
sp.Get("", handler.GetProfile())
|
|
sp.Get("/:cluster_name", handler.GetClusterProfile())
|
|
sp.Get("/status", handler.IsLogin())
|
|
sp.Get("/:cluster_name/namespaces", handler.ListUserNamespace())
|
|
sp.Put("", handler.UpdateProfile())
|
|
sp.Put("/password", handler.UpdatePassword())
|
|
}
|