mirror of
https://github.com/hrfee/jfa-go.git
synced 2024-06-23 01:47:46 +02:00
Harvey Tindall
f88f71d933
daemon now stops on the RESTART signal, and when it fails to read JSON, it no longer stops the whole daemon.
123 lines
3.4 KiB
Go
123 lines
3.4 KiB
Go
package main
|
|
|
|
import (
|
|
"encoding/json"
|
|
"os"
|
|
"strings"
|
|
"time"
|
|
|
|
"github.com/fsnotify/fsnotify"
|
|
)
|
|
|
|
// GenInternalReset generates a local password reset PIN, for use with the PWR option on the Admin page.
|
|
func (app *appContext) GenInternalReset(userID string) (InternalPWR, error) {
|
|
pin := genAuthToken()
|
|
user, status, err := app.jf.UserByID(userID, false)
|
|
if err != nil || status != 200 {
|
|
return InternalPWR{}, err
|
|
}
|
|
pwr := InternalPWR{
|
|
PIN: pin,
|
|
Username: user.Name,
|
|
ID: userID,
|
|
Expiry: time.Now().Add(30 * time.Minute),
|
|
}
|
|
return pwr, nil
|
|
}
|
|
|
|
func (app *appContext) StartPWR() {
|
|
app.info.Println("Starting password reset daemon")
|
|
path := app.config.Section("password_resets").Key("watch_directory").String()
|
|
if _, err := os.Stat(path); os.IsNotExist(err) {
|
|
app.err.Printf("Failed to start password reset daemon: Directory \"%s\" doesn't exist", path)
|
|
return
|
|
}
|
|
|
|
watcher, err := fsnotify.NewWatcher()
|
|
if err != nil {
|
|
app.err.Printf("Couldn't initialise password reset daemon")
|
|
return
|
|
}
|
|
defer watcher.Close()
|
|
|
|
go pwrMonitor(app, watcher)
|
|
err = watcher.Add(path)
|
|
if err != nil {
|
|
app.err.Printf("Failed to start password reset daemon: %s", err)
|
|
}
|
|
|
|
<-RESTART
|
|
}
|
|
|
|
// PasswordReset represents a passwordreset-xyz.json file generated by Jellyfin.
|
|
type PasswordReset struct {
|
|
Pin string `json:"Pin"`
|
|
Username string `json:"UserName"`
|
|
Expiry time.Time `json:"ExpirationDate"`
|
|
Internal bool `json:"Internal,omitempty"`
|
|
}
|
|
|
|
func pwrMonitor(app *appContext, watcher *fsnotify.Watcher) {
|
|
if !emailEnabled {
|
|
return
|
|
}
|
|
for {
|
|
select {
|
|
case event, ok := <-watcher.Events:
|
|
if !ok {
|
|
return
|
|
}
|
|
if event.Op&fsnotify.Write == fsnotify.Write && strings.Contains(event.Name, "passwordreset") {
|
|
var pwr PasswordReset
|
|
data, err := os.ReadFile(event.Name)
|
|
if err != nil {
|
|
app.debug.Printf("PWR: Failed to read file: %v", err)
|
|
return
|
|
}
|
|
err = json.Unmarshal(data, &pwr)
|
|
if len(pwr.Pin) == 0 || err != nil {
|
|
app.debug.Printf("PWR: Failed to read PIN: %v", err)
|
|
continue
|
|
}
|
|
app.info.Printf("New password reset for user \"%s\"", pwr.Username)
|
|
if currentTime := time.Now(); pwr.Expiry.After(currentTime) {
|
|
user, status, err := app.jf.UserByName(pwr.Username, false)
|
|
if !(status == 200 || status == 204) || err != nil {
|
|
app.err.Printf("Failed to get users from Jellyfin: Code %d", status)
|
|
app.debug.Printf("Error: %s", err)
|
|
return
|
|
}
|
|
app.storage.loadEmails()
|
|
uid := user.ID
|
|
if uid == "" {
|
|
app.err.Printf("Couldn't get user ID for user \"%s\"", pwr.Username)
|
|
return
|
|
}
|
|
name := app.getAddressOrName(uid)
|
|
if name != "" {
|
|
msg, err := app.email.constructReset(pwr, app, false)
|
|
|
|
if err != nil {
|
|
app.err.Printf("Failed to construct password reset message for \"%s\"", pwr.Username)
|
|
app.debug.Printf("%s: Error: %s", pwr.Username, err)
|
|
} else if err := app.sendByID(msg, uid); err != nil {
|
|
app.err.Printf("Failed to send password reset message to \"%s\"", name)
|
|
app.debug.Printf("%s: Error: %s", pwr.Username, err)
|
|
} else {
|
|
app.info.Printf("Sent password reset message to \"%s\"", name)
|
|
}
|
|
}
|
|
} else {
|
|
app.err.Printf("Password reset for user \"%s\" has already expired (%s). Check your time settings.", pwr.Username, pwr.Expiry)
|
|
}
|
|
|
|
}
|
|
case err, ok := <-watcher.Errors:
|
|
if !ok {
|
|
return
|
|
}
|
|
app.err.Printf("Password reset daemon: %s", err)
|
|
}
|
|
}
|
|
}
|