Files
MirageServer/controller/console_api_users.go
2023-04-10 18:09:10 +08:00

182 lines
5.8 KiB
Go
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package controller
import (
_ "embed"
"encoding/json"
"net/http"
"strconv"
"time"
"tailscale.com/tailcfg"
)
type UsersData struct {
Users []UserData `json:"users"`
ExternalUsers []UserData `json:"externalUsers"`
CurrentUserID int64 `json:"currentUserID"`
OwnerID int64 `json:"ownerID"`
DomainHasOwner bool `json:"domainHasOwner"`
}
type UserData struct {
Id string `json:"id"`
StableId string `json:"stableId"`
DisplayName string `json:"displayName"`
LoginName string `json:"loginName"`
DomainName string `json:"domainName"` // company
SharedDomain bool `json:"sharedDomain"` // ? false
ProfilePicURL string `json:"profilePicURL"`
Created time.Time `json:"created"` //timestamp
Role string `json:"role"`
IsAdmin bool `json:"isAdmin"`
IsOwner bool `json:"isOwner"`
Status string `json:"status"` // "active", 是否suspend
DeviceCount int `json:"deviceCount"` // ? 0
CanEditBilling bool `json:"canEditBilling"`
NeedsOnboarding bool `json:"needsOnboarding"` // 待审核?
LastSeen time.Time `json:"lastSeen"` // timestamp
CurrentlyConnected bool `json:"currentlyConnected"`
}
// 接受/admin/api/users的Get请求用于查询用户
func (h *Mirage) CAPIGetUsers(
w http.ResponseWriter,
r *http.Request,
) {
user, err := h.verifyTokenIDandGetUser(w, r)
if err != nil || user.CheckEmpty() {
h.doAPIResponse(w, "用户信息核对失败:"+err.Error(), nil)
return
}
resData := UsersData{
ExternalUsers: []UserData{},
CurrentUserID: user.ID,
OwnerID: user.ID, // TODO: 后续区分Admin和Owner后不再直接赋值当前用户ID而需要查询
DomainHasOwner: true, // TODO: 在不确定该项何时为false的情况前先默认为true
}
users, err := h.ListOrgUsers(user.OrganizationID)
if err != nil {
h.doAPIResponse(w, "用户列表获取失败:"+err.Error(), nil)
return
}
for _, u := range users {
devCount := 0
lastSeen := u.CreatedAt
currentlyConnected := false
userMachines, err := h.ListMachinesByUser(u.ID)
if err == nil {
devCount = len(userMachines)
for _, m := range userMachines {
if m.LastSeen.After(lastSeen) {
lastSeen = *m.LastSeen
}
if !currentlyConnected && m.isOnline() {
currentlyConnected = true
}
}
}
resData.Users = append(resData.Users, UserData{
Id: strconv.FormatInt(u.ID, 10),
StableId: u.StableID,
DisplayName: u.Display_Name,
LoginName: u.Name,
DomainName: u.Organization.Name,
SharedDomain: false, //???
ProfilePicURL: "", // TODO: 我们暂时没存头像
Created: u.CreatedAt.UTC(),
Role: RoleStr[u.Role],
IsAdmin: u.Role == RoleOwner, // TODO: 后续区分Admin和Owner后需要变为不等比较
IsOwner: u.Role == RoleOwner,
Status: "active", // TODO: 在添加suspend功能后需要变为判断
DeviceCount: devCount, // TODO: 不知为啥官方目前是0我们做下统计
CanEditBilling: u.Role == RoleOwner, // TODO: 后续有更多角色时需要变更
NeedsOnboarding: false, // TODO: 后续增加新成员需approve后需要使用
LastSeen: lastSeen.UTC().Round(time.Second),
CurrentlyConnected: currentlyConnected,
})
}
h.doAPIResponse(w, "", resData)
}
// 请求报文:
type UserActionREQ struct {
UserID string `json:"userID"`
Action string `json:"action"` //"restore_user", "suspend_user", "delete_user", "set_owner","set_member"
}
// 接受/admin/api/users的Post请求用于对用户操作
func (h *Mirage) CAPIPostUsers(
w http.ResponseWriter,
r *http.Request,
) {
user, err := h.verifyTokenIDandGetUser(w, r)
if err != nil || user.CheckEmpty() {
h.doAPIResponse(w, "用户信息核对失败:"+err.Error(), nil)
return
}
err = r.ParseForm()
if err != nil {
h.doAPIResponse(w, "用户请求解析失败:"+err.Error(), nil)
return
}
reqData := UserActionREQ{}
json.NewDecoder(r.Body).Decode(&reqData)
switch reqData.Action {
case "set_owner":
if user.Role != RoleOwner {
h.doAPIResponse(w, "权限不足", nil)
return
}
targetUID, err := strconv.ParseInt(reqData.UserID, 10, 64)
if err != nil {
h.doAPIResponse(w, "目标用户ID解析失败:"+err.Error(), nil)
return
}
err = h.TransferOwner(tailcfg.UserID(user.ID), tailcfg.UserID(targetUID))
if err != nil {
h.doAPIResponse(w, "修改用户角色失败:"+err.Error(), nil)
return
}
h.doAPIResponse(w, "", nil)
case "delete_user":
targetUID, err := strconv.ParseInt(reqData.UserID, 10, 64)
if err != nil {
h.doAPIResponse(w, "目标用户ID解析失败:"+err.Error(), nil)
return
}
targetUser, err := h.GetUserByID(tailcfg.UserID(targetUID))
if err != nil {
h.doAPIResponse(w, "目标用户信息获取失败:"+err.Error(), nil)
return
}
if targetUser.Role == RoleOwner {
h.doAPIResponse(w, "无法删除Owner请联系我们", nil)
return
}
mlist, err := h.ListMachinesByUser(targetUID)
if err != nil {
h.doAPIResponse(w, "目标用户设备列表获取失败:"+err.Error(), nil)
return
}
for _, m := range mlist {
if m.ForcedTags != nil && len([]string(m.ForcedTags)) > 0 {
continue
}
err = h.HardDeleteMachine(&m)
if err != nil {
h.doAPIResponse(w, "目标用户设备删除失败:"+err.Error(), nil)
return
}
h.NotifyNaviOrgNodesChange(user.OrganizationID, "", m.NodeKey)
}
err = h.DestroyUser(targetUser.Name, targetUser.Organization.Name, targetUser.Organization.Provider)
if err != nil {
h.doAPIResponse(w, "目标用户删除失败:"+err.Error(), nil)
return
}
h.doAPIResponse(w, "", nil)
}
}