v0.2.0
This commit is contained in:
1 parent
23c7133fa9
commit
8c1b93c14e
16 files changed
+1187
-295
No files matched your search
+125
-60
@@ -24,7 +24,7 @@ func (m *Model) refreshUsersList() {
|
||||
items = append(items, simpleItem{id: u.Username, title: u.Username, desc: desc})
|
||||
}
|
||||
l := list.New(items, list.NewDefaultDelegate(), m.usersList.Width(), m.usersList.Height())
|
||||
l.Title = "Пользователи [n]новый [l]заблокировать [u]разблокировать [d]удалить [esc]назад"
|
||||
l.Title = "Пользователи [n]новый [g]группы [c]обновить конфиг [l]заблокировать [u]разблокировать [d]удалить [esc]назад"
|
||||
l.SetShowStatusBar(false)
|
||||
m.usersList = l
|
||||
}
|
||||
@@ -46,68 +46,95 @@ func (m Model) updateUsers(msg tea.Msg) (tea.Model, tea.Cmd) {
|
||||
m.userNewFocus = 0
|
||||
m.err, m.status = "", ""
|
||||
return m, nil
|
||||
case "l", "u", "d":
|
||||
case "g":
|
||||
sel, ok := m.usersList.SelectedItem().(simpleItem)
|
||||
if !ok {
|
||||
return m, nil
|
||||
}
|
||||
st := m.loadState()
|
||||
u := st.FindUser(sel.id)
|
||||
if u == nil {
|
||||
return m, nil
|
||||
}
|
||||
m.userNewGroupsSel = map[string]bool{}
|
||||
for _, g := range u.Groups {
|
||||
m.userNewGroupsSel[g] = true
|
||||
}
|
||||
m.userNewGroupsOrder = nil
|
||||
for _, g := range st.Groups {
|
||||
m.userNewGroupsOrder = append(m.userNewGroupsOrder, g.Name)
|
||||
}
|
||||
m.userNewCursor = 0
|
||||
m.userGroupsEditTarget = sel.id
|
||||
m.err, m.status = "", ""
|
||||
m.screen = scrUserNewGroups
|
||||
return m, nil
|
||||
case "l":
|
||||
sel, ok := m.usersList.SelectedItem().(simpleItem)
|
||||
if !ok {
|
||||
return m, nil
|
||||
}
|
||||
username := sel.id
|
||||
switch key.String() {
|
||||
case "l":
|
||||
if err := sysadm.LockSystemUser(username); err != nil {
|
||||
m.err = err.Error()
|
||||
return m, nil
|
||||
}
|
||||
_ = sysadm.DisableAuthorizedKeys(username)
|
||||
m.mutate("заблокирован пользователь "+username, func(st *model.State) error {
|
||||
u := st.FindUser(username)
|
||||
if u == nil {
|
||||
return fmt.Errorf("user %s not found in state", username)
|
||||
}
|
||||
now := time.Now()
|
||||
u.Status = model.StatusLocked
|
||||
u.LockedAt = &now
|
||||
return nil
|
||||
})
|
||||
m.logAudit(username, "admin", "locked")
|
||||
case "u":
|
||||
if err := sysadm.UnlockSystemUser(username); err != nil {
|
||||
m.err = err.Error()
|
||||
return m, nil
|
||||
}
|
||||
m.mutate("разблокирован пользователь "+username, func(st *model.State) error {
|
||||
u := st.FindUser(username)
|
||||
if u == nil {
|
||||
return fmt.Errorf("user %s not found in state", username)
|
||||
}
|
||||
u.Status = model.StatusActive
|
||||
u.LockedAt = nil
|
||||
if u.PublicKey != "" {
|
||||
if err := sysadm.WriteAuthorizedKeys(username, u.PublicKey, st.AllowedHosts(u)); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
})
|
||||
m.logAudit(username, "admin", "unlocked")
|
||||
case "d":
|
||||
if err := sysadm.DeleteSystemUser(username); err != nil {
|
||||
m.err = err.Error()
|
||||
return m, nil
|
||||
}
|
||||
m.mutate("удалён пользователь "+username, func(st *model.State) error {
|
||||
for i := range st.Users {
|
||||
if st.Users[i].Username == username {
|
||||
st.Users = append(st.Users[:i], st.Users[i+1:]...)
|
||||
break
|
||||
}
|
||||
}
|
||||
return nil
|
||||
})
|
||||
m.logAudit(username, "admin", "deleted")
|
||||
m.pendingConfirm = confirmLockUser
|
||||
m.confirmTarget = username
|
||||
m.confirmReturnTo = scrUsers
|
||||
m.confirmMessage = fmt.Sprintf(
|
||||
"Заблокировать пользователя %q?\n\n"+
|
||||
"Аккаунт будет немедленно заблокирован (пароль отключён, срок действия\n"+
|
||||
"истёк, ключ в authorized_keys закомментирован) — доступ пропадёт сразу.\n"+
|
||||
"Это обратимо: позже можно разблокировать клавишей [u].", username)
|
||||
m.screen = scrConfirmAction
|
||||
return m, nil
|
||||
case "u":
|
||||
sel, ok := m.usersList.SelectedItem().(simpleItem)
|
||||
if !ok {
|
||||
return m, nil
|
||||
}
|
||||
username := sel.id
|
||||
if err := sysadm.UnlockSystemUser(username); err != nil {
|
||||
m.err = err.Error()
|
||||
return m, nil
|
||||
}
|
||||
m.mutate("разблокирован пользователь "+username, func(st *model.State) error {
|
||||
u := st.FindUser(username)
|
||||
if u == nil {
|
||||
return fmt.Errorf("user %s not found in state", username)
|
||||
}
|
||||
u.Status = model.StatusActive
|
||||
u.LockedAt = nil
|
||||
if u.PublicKey != "" {
|
||||
if err := sysadm.WriteAuthorizedKeys(username, u.PublicKey, st.AllowedForwards(u)); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
})
|
||||
m.logAudit(username, "admin", "unlocked")
|
||||
m.refreshUsersList()
|
||||
return m, nil
|
||||
case "d":
|
||||
sel, ok := m.usersList.SelectedItem().(simpleItem)
|
||||
if !ok {
|
||||
return m, nil
|
||||
}
|
||||
username := sel.id
|
||||
m.pendingConfirm = confirmDeleteUser
|
||||
m.confirmTarget = username
|
||||
m.confirmReturnTo = scrUsers
|
||||
m.confirmMessage = fmt.Sprintf(
|
||||
"Удалить пользователя %q НЕОБРАТИМО?\n\n"+
|
||||
"Будет выполнено userdel -r: системный аккаунт и его домашняя папка\n"+
|
||||
"удаляются безвозвратно.\n"+
|
||||
"Если нужно просто временно отключить доступ — используйте блокировку\n"+
|
||||
"[l] вместо удаления, её можно отменить.", username)
|
||||
m.screen = scrConfirmAction
|
||||
return m, nil
|
||||
case "c":
|
||||
sel, ok := m.usersList.SelectedItem().(simpleItem)
|
||||
if !ok {
|
||||
return m, nil
|
||||
}
|
||||
return m.runConfigUpdate(sel.id)
|
||||
}
|
||||
}
|
||||
var cmd tea.Cmd
|
||||
@@ -184,11 +211,20 @@ func (m Model) viewUserNew() string {
|
||||
|
||||
// --- new user wizard: step 2, group checkboxes ---
|
||||
|
||||
// updateUserNewGroups drives the group-checkbox screen, shared by two
|
||||
// flows: assigning groups while creating a brand new user (userGroupsEditTarget
|
||||
// empty), and changing an existing user's groups later (userGroupsEditTarget
|
||||
// set to their username, entered via "g" on the users list).
|
||||
func (m Model) updateUserNewGroups(msg tea.Msg) (tea.Model, tea.Cmd) {
|
||||
if key, ok := msg.(tea.KeyMsg); ok {
|
||||
switch key.String() {
|
||||
case "esc":
|
||||
m.screen = scrUserNew
|
||||
if m.userGroupsEditTarget != "" {
|
||||
m.userGroupsEditTarget = ""
|
||||
m.screen = scrUsers
|
||||
} else {
|
||||
m.screen = scrUserNew
|
||||
}
|
||||
return m, nil
|
||||
case "up", "k":
|
||||
if m.userNewCursor > 0 {
|
||||
@@ -207,14 +243,37 @@ func (m Model) updateUserNewGroups(msg tea.Msg) (tea.Model, tea.Cmd) {
|
||||
}
|
||||
return m, nil
|
||||
case "enter":
|
||||
username := m.userNewName.Value()
|
||||
comment := m.userNewComment.Value()
|
||||
var groups []string
|
||||
for _, g := range m.userNewGroupsOrder {
|
||||
if m.userNewGroupsSel[g] {
|
||||
groups = append(groups, g)
|
||||
}
|
||||
}
|
||||
|
||||
if m.userGroupsEditTarget != "" {
|
||||
username := m.userGroupsEditTarget
|
||||
m.mutate("изменены группы пользователя "+username, func(st *model.State) error {
|
||||
u := st.FindUser(username)
|
||||
if u == nil {
|
||||
return fmt.Errorf("user %s not found in state", username)
|
||||
}
|
||||
u.Groups = groups
|
||||
if u.PublicKey != "" && u.Status == model.StatusActive {
|
||||
if err := sysadm.WriteAuthorizedKeys(username, u.PublicKey, st.AllowedForwards(u)); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
})
|
||||
m.logAudit(username, "admin", "groups changed to "+fmt.Sprint(groups))
|
||||
m.userGroupsEditTarget = ""
|
||||
m.screen = scrUsers
|
||||
m.refreshUsersList()
|
||||
return m, nil
|
||||
}
|
||||
|
||||
username := m.userNewName.Value()
|
||||
comment := m.userNewComment.Value()
|
||||
if err := sysadm.CreateSystemUser(username); err != nil {
|
||||
m.err = err.Error()
|
||||
m.screen = scrUsers
|
||||
@@ -242,7 +301,13 @@ func (m Model) updateUserNewGroups(msg tea.Msg) (tea.Model, tea.Cmd) {
|
||||
}
|
||||
|
||||
func (m Model) viewUserNewGroups() string {
|
||||
body := "Группы доступа для нового пользователя (union хостов всех выбранных групп)\n\n"
|
||||
title := "Группы доступа для нового пользователя"
|
||||
confirmHint := "создать пользователя"
|
||||
if m.userGroupsEditTarget != "" {
|
||||
title = fmt.Sprintf("Группы доступа пользователя %s", m.userGroupsEditTarget)
|
||||
confirmHint = "сохранить"
|
||||
}
|
||||
body := title + " (эффективный доступ = объединение сервисов всех отмеченных групп)\n\n"
|
||||
if len(m.userNewGroupsOrder) == 0 {
|
||||
body += warnStyle.Render("Группы ещё не созданы — сначала создайте хотя бы одну в разделе «Группы и хосты».") + "\n"
|
||||
}
|
||||
@@ -257,6 +322,6 @@ func (m Model) viewUserNewGroups() string {
|
||||
}
|
||||
body += fmt.Sprintf("%s%s %s\n", cursor, check, g)
|
||||
}
|
||||
body += "\n[space] отметить [enter] создать пользователя [esc] назад"
|
||||
body += fmt.Sprintf("\n[space] отметить [enter] %s [esc] назад", confirmHint)
|
||||
return borderStyle.Render(body)
|
||||
}
|
||||
Reference in new issue
Block a user