mirror of
https://github.com/hrfee/jfa-go.git
synced 2024-12-23 01:20:11 +00:00
Harvey Tindall
fefe2d82a4
ioutil's contents are now in io and os. Eventually jfa-go's files will be embedded in the binary with go1.16's new embed feature. Using io/fs will provide abstraction for accessing these files, and allow for both embedded and non-embedded versions. Also, internal paths to things like email templates, etc. will be prefixed with "jfa-go:" to indicate to use the app's own Filesystem instead of reading the file normally. This also allows for custom files to continue to be used as they are currently.
108 lines
3.1 KiB
Go
108 lines
3.1 KiB
Go
package main
|
|
|
|
import (
|
|
"encoding/json"
|
|
"os"
|
|
"strings"
|
|
"time"
|
|
|
|
"github.com/fsnotify/fsnotify"
|
|
)
|
|
|
|
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()
|
|
|
|
done := make(chan bool)
|
|
go pwrMonitor(app, watcher)
|
|
err = watcher.Add(path)
|
|
if err != nil {
|
|
app.err.Printf("Failed to start password reset daemon: %s", err)
|
|
}
|
|
<-done
|
|
}
|
|
|
|
// 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"`
|
|
}
|
|
|
|
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 {
|
|
return
|
|
}
|
|
err = json.Unmarshal(data, &pwr)
|
|
if len(pwr.Pin) == 0 || err != nil {
|
|
return
|
|
}
|
|
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()
|
|
var address string
|
|
uid := user["Id"]
|
|
if uid == nil {
|
|
app.err.Printf("Couldn't get user ID for user \"%s\"", pwr.Username)
|
|
app.debug.Printf("user maplength: %d", len(user))
|
|
return
|
|
}
|
|
addr, ok := app.storage.emails[user["Id"].(string)]
|
|
if !ok || addr == nil {
|
|
app.err.Printf("Couldn't find email for user \"%s\". Make sure it's set", pwr.Username)
|
|
return
|
|
}
|
|
address = addr.(string)
|
|
msg, err := app.email.constructReset(pwr, app)
|
|
if err != nil {
|
|
app.err.Printf("Failed to construct password reset email for %s", pwr.Username)
|
|
app.debug.Printf("%s: Error: %s", pwr.Username, err)
|
|
} else if err := app.email.send(address, msg); err != nil {
|
|
app.err.Printf("Failed to send password reset email to \"%s\"", address)
|
|
app.debug.Printf("%s: Error: %s", pwr.Username, err)
|
|
} else {
|
|
app.info.Printf("Sent password reset email to \"%s\"", address)
|
|
}
|
|
} 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)
|
|
}
|
|
}
|
|
}
|