Delete internal/api/webui_credentials.go
This commit is contained in:
@@ -1,272 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 2026 Safronov Grigorii
|
|
||||||
*
|
|
||||||
* Licensed under the CDDL, Version 1.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
*
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
* https://opensource.org/licenses/CDDL-1.0
|
|
||||||
*/
|
|
||||||
|
|
||||||
// Файл: internal/api/webui_credentials.go
|
|
||||||
// Назначение: Управление учётными данными для веб-интерфейса
|
|
||||||
// Хранит логин/пароль в скрытом файле .credentials в директории futriis
|
|
||||||
|
|
||||||
package api
|
|
||||||
|
|
||||||
import (
|
|
||||||
"crypto/sha256"
|
|
||||||
"encoding/base64"
|
|
||||||
"encoding/json"
|
|
||||||
"fmt"
|
|
||||||
"os"
|
|
||||||
"path/filepath"
|
|
||||||
"sync"
|
|
||||||
)
|
|
||||||
|
|
||||||
// CredentialManager управляет учётными данными веб-интерфейса
|
|
||||||
type CredentialManager struct {
|
|
||||||
mu sync.RWMutex
|
|
||||||
credentials *Credentials
|
|
||||||
credFile string
|
|
||||||
currentUser string
|
|
||||||
}
|
|
||||||
|
|
||||||
// Credentials структура для хранения учётных данных
|
|
||||||
type Credentials struct {
|
|
||||||
Username string `json:"username"`
|
|
||||||
Password string `json:"password"` // хранится в виде хеша
|
|
||||||
Avatar map[string]string `json:"avatar,omitempty"` // username -> base64 avatar
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewCredentialManager создаёт новый менеджер учётных данных
|
|
||||||
func NewCredentialManager() *CredentialManager {
|
|
||||||
// Определяем путь к директории futriis
|
|
||||||
execPath, err := os.Executable()
|
|
||||||
if err != nil {
|
|
||||||
execPath = "."
|
|
||||||
}
|
|
||||||
futriisDir := filepath.Dir(execPath)
|
|
||||||
|
|
||||||
// Ищем директорию futriis (поднимаемся вверх, если нужно)
|
|
||||||
for {
|
|
||||||
if _, err := os.Stat(filepath.Join(futriisDir, "futriis")); err == nil {
|
|
||||||
futriisDir = filepath.Join(futriisDir, "futriis")
|
|
||||||
break
|
|
||||||
}
|
|
||||||
parent := filepath.Dir(futriisDir)
|
|
||||||
if parent == futriisDir {
|
|
||||||
// Не нашли директорию futriis, создаём в текущей
|
|
||||||
futriisDir = filepath.Join(execPath, "futriis")
|
|
||||||
os.MkdirAll(futriisDir, 0700)
|
|
||||||
break
|
|
||||||
}
|
|
||||||
futriisDir = parent
|
|
||||||
}
|
|
||||||
|
|
||||||
credFile := filepath.Join(futriisDir, ".credentials")
|
|
||||||
|
|
||||||
return &CredentialManager{
|
|
||||||
credentials: &Credentials{
|
|
||||||
Username: "admin",
|
|
||||||
Password: "",
|
|
||||||
Avatar: make(map[string]string),
|
|
||||||
},
|
|
||||||
credFile: credFile,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// hashPassword создаёт хеш пароля
|
|
||||||
func (cm *CredentialManager) hashPassword(password string) string {
|
|
||||||
hash := sha256.Sum256([]byte(password))
|
|
||||||
return base64.StdEncoding.EncodeToString(hash[:])
|
|
||||||
}
|
|
||||||
|
|
||||||
// Load загружает учётные данные из файла
|
|
||||||
func (cm *CredentialManager) Load() error {
|
|
||||||
cm.mu.Lock()
|
|
||||||
defer cm.mu.Unlock()
|
|
||||||
|
|
||||||
data, err := os.ReadFile(cm.credFile)
|
|
||||||
if err != nil {
|
|
||||||
if os.IsNotExist(err) {
|
|
||||||
return cm.createDefaultFile()
|
|
||||||
}
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
var creds Credentials
|
|
||||||
if err := json.Unmarshal(data, &creds); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
cm.credentials = &creds
|
|
||||||
if cm.credentials.Avatar == nil {
|
|
||||||
cm.credentials.Avatar = make(map[string]string)
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// createDefaultFile создаёт файл с учётными данными по умолчанию
|
|
||||||
func (cm *CredentialManager) createDefaultFile() error {
|
|
||||||
cm.credentials.Username = "admin"
|
|
||||||
cm.credentials.Password = cm.hashPassword("admin")
|
|
||||||
cm.credentials.Avatar = make(map[string]string)
|
|
||||||
|
|
||||||
return cm.save()
|
|
||||||
}
|
|
||||||
|
|
||||||
// CreateDefault создаёт учётные данные по умолчанию (если файл не существует)
|
|
||||||
func (cm *CredentialManager) CreateDefault() error {
|
|
||||||
cm.mu.Lock()
|
|
||||||
defer cm.mu.Unlock()
|
|
||||||
|
|
||||||
// Проверяем, существует ли файл
|
|
||||||
if _, err := os.Stat(cm.credFile); err == nil {
|
|
||||||
return nil // файл уже существует
|
|
||||||
}
|
|
||||||
|
|
||||||
cm.credentials.Username = "admin"
|
|
||||||
cm.credentials.Password = cm.hashPassword("admin")
|
|
||||||
cm.credentials.Avatar = make(map[string]string)
|
|
||||||
|
|
||||||
return cm.save()
|
|
||||||
}
|
|
||||||
|
|
||||||
// save сохраняет учётные данные в файл
|
|
||||||
func (cm *CredentialManager) save() error {
|
|
||||||
data, err := json.MarshalIndent(cm.credentials, "", " ")
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Устанавливаем права доступа 0600 (только владелец может читать/писать)
|
|
||||||
return os.WriteFile(cm.credFile, data, 0600)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Validate проверяет учётные данные
|
|
||||||
func (cm *CredentialManager) Validate(username, password string) bool {
|
|
||||||
cm.mu.RLock()
|
|
||||||
defer cm.mu.RUnlock()
|
|
||||||
|
|
||||||
if cm.credentials == nil {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
if cm.credentials.Username != username {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
return cm.credentials.Password == cm.hashPassword(password)
|
|
||||||
}
|
|
||||||
|
|
||||||
// ChangePassword изменяет пароль пользователя
|
|
||||||
func (cm *CredentialManager) ChangePassword(username, currentPassword, newPassword string) error {
|
|
||||||
cm.mu.Lock()
|
|
||||||
defer cm.mu.Unlock()
|
|
||||||
|
|
||||||
if cm.credentials == nil {
|
|
||||||
return fmt.Errorf("credentials not loaded")
|
|
||||||
}
|
|
||||||
|
|
||||||
if cm.credentials.Username != username {
|
|
||||||
return fmt.Errorf("user not found")
|
|
||||||
}
|
|
||||||
|
|
||||||
if cm.credentials.Password != cm.hashPassword(currentPassword) {
|
|
||||||
return fmt.Errorf("current password is incorrect")
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(newPassword) < 4 {
|
|
||||||
return fmt.Errorf("new password must be at least 4 characters")
|
|
||||||
}
|
|
||||||
|
|
||||||
cm.credentials.Password = cm.hashPassword(newPassword)
|
|
||||||
|
|
||||||
return cm.save()
|
|
||||||
}
|
|
||||||
|
|
||||||
// SetAvatar устанавливает аватар для пользователя
|
|
||||||
func (cm *CredentialManager) SetAvatar(username, avatarBase64 string) error {
|
|
||||||
cm.mu.Lock()
|
|
||||||
defer cm.mu.Unlock()
|
|
||||||
|
|
||||||
if cm.credentials == nil {
|
|
||||||
return fmt.Errorf("credentials not loaded")
|
|
||||||
}
|
|
||||||
|
|
||||||
if cm.credentials.Username != username {
|
|
||||||
return fmt.Errorf("user not found")
|
|
||||||
}
|
|
||||||
|
|
||||||
if cm.credentials.Avatar == nil {
|
|
||||||
cm.credentials.Avatar = make(map[string]string)
|
|
||||||
}
|
|
||||||
|
|
||||||
cm.credentials.Avatar[username] = avatarBase64
|
|
||||||
|
|
||||||
return cm.save()
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetAvatar возвращает аватар пользователя
|
|
||||||
func (cm *CredentialManager) GetAvatar(username string) (string, error) {
|
|
||||||
cm.mu.RLock()
|
|
||||||
defer cm.mu.RUnlock()
|
|
||||||
|
|
||||||
if cm.credentials == nil {
|
|
||||||
return "", fmt.Errorf("credentials not loaded")
|
|
||||||
}
|
|
||||||
|
|
||||||
if cm.credentials.Username != username {
|
|
||||||
return "", fmt.Errorf("user not found")
|
|
||||||
}
|
|
||||||
|
|
||||||
if cm.credentials.Avatar == nil {
|
|
||||||
return "", nil
|
|
||||||
}
|
|
||||||
|
|
||||||
avatar, ok := cm.credentials.Avatar[username]
|
|
||||||
if !ok {
|
|
||||||
return "", nil
|
|
||||||
}
|
|
||||||
|
|
||||||
return avatar, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// DeleteAvatar удаляет аватар пользователя
|
|
||||||
func (cm *CredentialManager) DeleteAvatar(username string) error {
|
|
||||||
cm.mu.Lock()
|
|
||||||
defer cm.mu.Unlock()
|
|
||||||
|
|
||||||
if cm.credentials == nil {
|
|
||||||
return fmt.Errorf("credentials not loaded")
|
|
||||||
}
|
|
||||||
|
|
||||||
if cm.credentials.Username != username {
|
|
||||||
return fmt.Errorf("user not found")
|
|
||||||
}
|
|
||||||
|
|
||||||
if cm.credentials.Avatar != nil {
|
|
||||||
delete(cm.credentials.Avatar, username)
|
|
||||||
}
|
|
||||||
|
|
||||||
return cm.save()
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetCurrentUsername возвращает имя текущего пользователя
|
|
||||||
func (cm *CredentialManager) GetCurrentUsername() string {
|
|
||||||
cm.mu.RLock()
|
|
||||||
defer cm.mu.RUnlock()
|
|
||||||
|
|
||||||
if cm.credentials == nil {
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
|
|
||||||
return cm.credentials.Username
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetCredentialsFile возвращает путь к файлу с учётными данными
|
|
||||||
func (cm *CredentialManager) GetCredentialsFile() string {
|
|
||||||
return cm.credFile
|
|
||||||
}
|
|
||||||
Reference in New Issue
Block a user