diff --git a/api.go b/api.go
index 3502c27..af24370 100644
--- a/api.go
+++ b/api.go
@@ -918,6 +918,74 @@ func (app *appContext) DeleteAnnounceTemplate(gc *gin.Context) {
respondBool(200, false, gc)
}
+// @Summary Generate password reset links for a list of users, sending the links to them if possible.
+// @Produce json
+// @Param AdminPasswordResetDTO body AdminPasswordResetDTO true "List of user IDs"
+// @Success 204 {object} boolResponse
+// @Success 200 {object} AdminPasswordResetRespDTO
+// @Failure 400 {object} boolResponse
+// @Failure 500 {object} boolResponse
+// @Router /users/password-reset [post]
+// @Security Bearer
+// @tags Users
+func (app *appContext) AdminPasswordReset(gc *gin.Context) {
+ var req AdminPasswordResetDTO
+ gc.BindJSON(&req)
+ if req.Users == nil || len(req.Users) == 0 {
+ app.debug.Println("Ignoring empty request for PWR")
+ respondBool(400, false, gc)
+ return
+ }
+ linkCount := 0
+ var pwr InternalPWR
+ var err error
+ resp := AdminPasswordResetRespDTO{}
+ for _, id := range req.Users {
+ pwr, err = app.GenInternalReset(id)
+ if err != nil {
+ app.err.Printf("Failed to get user from Jellyfin: %v", err)
+ respondBool(500, false, gc)
+ return
+ }
+ if app.internalPWRs == nil {
+ app.internalPWRs = map[string]InternalPWR{}
+ }
+ app.internalPWRs[pwr.PIN] = pwr
+ sendAddress := app.getAddressOrName(id)
+ if sendAddress == "" || len(req.Users) == 1 {
+ resp.Link, err = app.GenResetLink(pwr.PIN)
+ linkCount++
+ if sendAddress == "" {
+ resp.Manual = true
+ }
+ }
+ if sendAddress != "" {
+ msg, err := app.email.constructReset(
+ PasswordReset{
+ Pin: pwr.PIN,
+ Username: pwr.Username,
+ Expiry: pwr.Expiry,
+ Internal: true,
+ }, app, false,
+ )
+ if err != nil {
+ app.err.Printf("Failed to construct password reset message for \"%s\": %v", pwr.Username, err)
+ respondBool(500, false, gc)
+ return
+ } else if err := app.sendByID(msg, id); err != nil {
+ app.err.Printf("Failed to send password reset message to \"%s\": %v", sendAddress, err)
+ } else {
+ app.info.Printf("Sent password reset message to \"%s\"", sendAddress)
+ }
+ }
+ }
+ if resp.Link != "" && linkCount == 1 {
+ gc.JSON(200, resp)
+ return
+ }
+ respondBool(204, true, gc)
+}
+
// @Summary Create a new invite.
// @Produce json
// @Param generateInviteDTO body generateInviteDTO true "New invite request object"
@@ -1505,38 +1573,70 @@ func (app *appContext) ResetSetPassword(gc *gin.Context) {
gc.JSON(400, validation)
return
}
- resp, status, err := app.jf.ResetPassword(req.PIN)
- if status != 200 || err != nil || !resp.Success {
- app.err.Printf("Password Reset failed (%d): %v", status, err)
- respondBool(status, false, gc)
- return
+ isInternal := false
+ var userID, username string
+ if reset, ok := app.internalPWRs[req.PIN]; ok {
+ isInternal = true
+ if time.Now().After(reset.Expiry) {
+ app.info.Printf("Password reset failed: PIN \"%s\" has expired", reset.PIN)
+ respondBool(401, false, gc)
+ delete(app.internalPWRs, req.PIN)
+ return
+ }
+ userID = reset.ID
+ username = reset.Username
+ status, err := app.jf.ResetPasswordAdmin(userID)
+ if !(status == 200 || status == 204) || err != nil {
+ app.err.Printf("Password Reset failed (%d): %v", status, err)
+ respondBool(status, false, gc)
+ return
+ }
+ } else {
+ resp, status, err := app.jf.ResetPassword(req.PIN)
+ if status != 200 || err != nil || !resp.Success {
+ app.err.Printf("Password Reset failed (%d): %v", status, err)
+ respondBool(status, false, gc)
+ return
+ }
+ if req.Password == "" || len(resp.UsersReset) == 0 {
+ respondBool(200, false, gc)
+ return
+ }
+ username = resp.UsersReset[0]
}
- if req.Password == "" || len(resp.UsersReset) == 0 {
- respondBool(200, false, gc)
- return
+ var user mediabrowser.User
+ var status int
+ var err error
+ if isInternal {
+ user, status, err = app.jf.UserByID(userID, false)
+ } else {
+ user, status, err = app.jf.UserByName(username, false)
}
- user, status, err := app.jf.UserByName(resp.UsersReset[0], false)
if status != 200 || err != nil {
- app.err.Printf("Failed to get user \"%s\" (%d): %v", resp.UsersReset[0], status, err)
+ app.err.Printf("Failed to get user \"%s\" (%d): %v", username, status, err)
respondBool(500, false, gc)
return
}
- status, err = app.jf.SetPassword(user.ID, req.PIN, req.Password)
+ prevPassword := req.PIN
+ if isInternal {
+ prevPassword = ""
+ }
+ status, err = app.jf.SetPassword(user.ID, prevPassword, req.Password)
if !(status == 200 || status == 204) || err != nil {
- app.err.Printf("Failed to change password for \"%s\" (%d): %v", resp.UsersReset[0], status, err)
+ app.err.Printf("Failed to change password for \"%s\" (%d): %v", username, status, err)
respondBool(500, false, gc)
return
}
if app.config.Section("ombi").Key("enabled").MustBool(false) {
// Silently fail for changing ombi passwords
if status != 200 || err != nil {
- app.err.Printf("Failed to get user \"%s\" from jellyfin/emby (%d): %v", resp.UsersReset[0], status, err)
+ app.err.Printf("Failed to get user \"%s\" from jellyfin/emby (%d): %v", username, status, err)
respondBool(200, true, gc)
return
}
ombiUser, status, err := app.getOmbiUser(user.ID)
if status != 200 || err != nil {
- app.err.Printf("Failed to get user \"%s\" from ombi (%d): %v", resp.UsersReset[0], status, err)
+ app.err.Printf("Failed to get user \"%s\" from ombi (%d): %v", username, status, err)
respondBool(200, true, gc)
return
}
diff --git a/email.go b/email.go
index e65f84c..3f880df 100644
--- a/email.go
+++ b/email.go
@@ -514,6 +514,18 @@ func (emailer *Emailer) constructCreated(code, username, address string, invite
return email, nil
}
+// GenResetLink generates and returns a password reset link.
+func (app *appContext) GenResetLink(pin string) (string, error) {
+ url := app.config.Section("password_resets").Key("url_base").String()
+ var pinLink string
+ if url == "" {
+ return pinLink, fmt.Errorf("disabled as no URL Base provided. Set in Settings > Password Resets.")
+ }
+ // Strip /invite from end of this URL, ik it's ugly.
+ pinLink = fmt.Sprintf("%s/reset?pin=%s", url, pin)
+ return pinLink, nil
+}
+
func (emailer *Emailer) resetValues(pwr PasswordReset, app *appContext, noSub bool) map[string]interface{} {
d, t, expiresIn := emailer.formatExpiry(pwr.Expiry, true, app.datePattern, app.timePattern)
message := app.config.Section("messages").Key("message").String()
@@ -544,17 +556,16 @@ func (emailer *Emailer) resetValues(pwr PasswordReset, app *appContext, noSub bo
} else {
template["helloUser"] = emailer.lang.Strings.template("helloUser", tmpl{"username": pwr.Username})
template["codeExpiry"] = emailer.lang.PasswordReset.template("codeExpiry", tmpl{"date": d, "time": t, "expiresInMinutes": expiresIn})
- url := app.config.Section("password_resets").Key("url_base").String()
if linkResetEnabled {
- if url != "" {
+ pinLink, err := app.GenResetLink(pwr.Pin)
+ if err == nil {
// Strip /invite form end of this URL, ik its ugly.
template["link_reset"] = true
- pinLink := fmt.Sprintf("%s/reset?pin=%s", url, pwr.Pin)
template["pin"] = pinLink
// Only used in html email.
template["pin_code"] = pwr.Pin
} else {
- app.info.Println("Password Reset link disabled as no URL Base provided. Set in Settings > Password Resets.")
+ app.info.Println("Couldn't generate PWR link: %v", err)
template["pin"] = pwr.Pin
}
} else {
diff --git a/go.mod b/go.mod
index 1c229c8..214724f 100644
--- a/go.mod
+++ b/go.mod
@@ -12,6 +12,8 @@ replace github.com/hrfee/jfa-go/logger => ./logger
replace github.com/hrfee/jfa-go/linecache => ./linecache
+replace github.com/hrfee/mediabrowser => ../mediabrowser
+
require (
github.com/bwmarrin/discordgo v0.23.2
github.com/emersion/go-autostart v0.0.0-20210130080809-00ed301c8e9a
@@ -38,7 +40,7 @@ require (
github.com/hrfee/jfa-go/linecache v0.0.0-20211003145958-a220ba8dfb58
github.com/hrfee/jfa-go/logger v0.0.0-20211003145958-a220ba8dfb58
github.com/hrfee/jfa-go/ombi v0.0.0-20211003145958-a220ba8dfb58
- github.com/hrfee/mediabrowser v0.3.5
+ github.com/hrfee/mediabrowser v0.0.0-00010101000000-000000000000
github.com/itchyny/timefmt-go v0.1.3
github.com/json-iterator/go v1.1.12 // indirect
github.com/lib/pq v1.10.3 // indirect
diff --git a/go.sum b/go.sum
index f04dd61..3ee3d47 100644
--- a/go.sum
+++ b/go.sum
@@ -20,6 +20,7 @@ github.com/btcsuite/websocket v0.0.0-20150119174127-31079b680792/go.mod h1:ghJtE
github.com/btcsuite/winsvc v1.0.0/go.mod h1:jsenWakMcC0zFBFurPLEAyrnc/teJEM1O46fmI40EZs=
github.com/bwmarrin/discordgo v0.23.2 h1:BzrtTktixGHIu9Tt7dEE6diysEF9HWnXeHuoJEt2fH4=
github.com/bwmarrin/discordgo v0.23.2/go.mod h1:c1WtWUGN6nREDmzIpyTp/iD3VYt4Fpx+bVyfBG7JE+M=
+github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d h1:U+s90UTSYgptZMwQh2aRr3LuazLJIa+Pg3Kc1ylSYVY=
github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
github.com/davecgh/go-spew v0.0.0-20171005155431-ecdeabc65495/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
@@ -58,6 +59,7 @@ github.com/getlantern/ops v0.0.0-20200403153110-8476b16edcd6 h1:QthAQCekS1YOeYWS
github.com/getlantern/ops v0.0.0-20200403153110-8476b16edcd6/go.mod h1:D5ao98qkA6pxftxoqzibIBBrLSUli+kYnJqrgBf9cIA=
github.com/getlantern/systray v1.1.0 h1:U0wCEqseLi2ok1fE6b88gJklzriavPJixZysZPkZd/Y=
github.com/getlantern/systray v1.1.0/go.mod h1:AecygODWIsBquJCJFop8MEQcJbWFfw/1yWbVabNgpCM=
+github.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk=
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
github.com/gin-contrib/gzip v0.0.1 h1:ezvKOL6jH+jlzdHNE4h9h8q8uMpDQjyl0NN0Jd7jozc=
github.com/gin-contrib/gzip v0.0.1/go.mod h1:fGBJBCdt6qCZuCAOwWuFhBB4OOq9EFqlo5dEaFhhu5w=
@@ -138,8 +140,6 @@ github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoA
github.com/gorilla/websocket v1.4.2 h1:+/TMaTYc4QFitKJxsQ7Yye35DkWvkdLcvGKqM+x0Ufc=
github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
-github.com/hrfee/mediabrowser v0.3.5 h1:bOJlI2HLvw7v0c7mcRw5XDRMUHReQzk5z0EJYRyYjpo=
-github.com/hrfee/mediabrowser v0.3.5/go.mod h1:PnHZbdxmbv1wCVdAQyM7nwPwpVj9fdKx2EcET7sAk+U=
github.com/itchyny/timefmt-go v0.1.3 h1:7M3LGVDsqcd0VZH2U+x393obrzZisp7C0uEe921iRkU=
github.com/itchyny/timefmt-go v0.1.3/go.mod h1:0osSSCQSASBJMsIZnhAaF1C2fCBTJZXrnj37mG8/c+A=
github.com/jessevdk/go-flags v0.0.0-20141203071132-1679536dcc89/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI=
@@ -216,6 +216,7 @@ github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTE
github.com/rogpeppe/go-internal v1.8.0 h1:FCbCCtXNOY3UtUuHUYaghJg4y7Fd14rXifAYUAtL9R8=
github.com/rogpeppe/go-internal v1.8.0/go.mod h1:WmiCO8CzOY8rg0OYDC4/i/2WRWAB6poM+XZ2dLUbcbE=
github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
+github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk=
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0=
github.com/shopspring/decimal v1.2.0/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o=
@@ -263,8 +264,10 @@ github.com/ugorji/go/codec v1.1.5-pre/go.mod h1:tULtS6Gy1AE1yCENaw4Vb//HLH5njI2t
github.com/ugorji/go/codec v1.1.7/go.mod h1:Ax+UKWsSmolVDwsd+7N3ZtXu+yMGCf907BLYF3GoBXY=
github.com/ugorji/go/codec v1.2.6 h1:7kbGefxLoDBuYXOms4yD7223OpNMMPNPZxXk5TvFcyQ=
github.com/ugorji/go/codec v1.2.6/go.mod h1:V6TCNZ4PHqoHGFZuSG1W8nrCzzdgA2DozYxWFFpvxTw=
+github.com/urfave/cli v1.20.0 h1:fDqGv3UG/4jbVl/QkFwEdddtEDjh/5Ov6X+0B/3bPaw=
github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA=
github.com/urfave/cli/v2 v2.1.1/go.mod h1:SE9GqnLQmjVa0iPEY0f1w3ygNIYcIJ0OKPMoW2caLfQ=
+github.com/urfave/cli/v2 v2.3.0 h1:qph92Y649prgesehzOrQjdWyxFOp/QVM+6imKHad91M=
github.com/urfave/cli/v2 v2.3.0/go.mod h1:LJmUH05zAU44vOAcrfzZQKsZbVcdbOG8rtL3/XcUArI=
github.com/writeas/go-strip-markdown v2.0.1+incompatible h1:IIqxTM5Jr7RzhigcL6FkrCNfXkvbR+Nbu1ls48pXYcw=
github.com/writeas/go-strip-markdown v2.0.1+incompatible/go.mod h1:Rsyu10ZhbEK9pXdk8V6MVnZmTzRG0alMNLMwa0J01fE=
diff --git a/html/admin.html b/html/admin.html
index 568f5c7..92f37ec 100644
--- a/html/admin.html
+++ b/html/admin.html
@@ -12,6 +12,7 @@
window.ombiEnabled = {{ .ombiEnabled }};
window.usernameEnabled = {{ .username }};
window.langFile = JSON.parse({{ .language }});
+ window.linkResetEnabled = {{ .linkResetEnabled }};
window.language = "{{ .langName }}";
{{ template "header.html" . }}
@@ -253,6 +254,13 @@
{{ .strings.settingsRefreshPage }}
+
+
+
{{ .strings.sendPWR }}
+
+
{{ .strings.copy }}
+
+
diff --git a/html/form.html b/html/form.html
index 27d7ed2..04e2ed5 100644
--- a/html/form.html
+++ b/html/form.html
@@ -14,9 +14,9 @@
-
{{ .strings.successHeader }}
-
{{ .successMessage }}
-
{{ .strings.successContinueButton }}
+
{{ if .passwordReset }}{{ .strings.passwordReset }}{{ else }}{{ .strings.successHeader }}{{ end }}
+
{{ if .passwordReset }}{{ .strings.youCanLoginPassword }}{{ else }}{{ .successMessage }}{{ end }}
+
{{ .strings.continue }}
diff --git a/lang/admin/en-us.json b/lang/admin/en-us.json
index 16bc3cd..4c4a4ed 100644
--- a/lang/admin/en-us.json
+++ b/lang/admin/en-us.json
@@ -57,8 +57,13 @@
"reset": "Reset",
"edit": "Edit",
"donate": "Donate",
+ "sendPWR": "Send Password Reset",
"contactThrough": "Contact through:",
"extendExpiry": "Extend expiry",
+ "sendPWRManual": "User {n} has no method of contact, press copy to get a link to send to them.",
+ "sendPWRSuccess": "Password reset link sent.",
+ "sendPWRSuccessManual": "If the user hasn't received it, press copy to get a link to manually send to them.",
+ "sendPWRValidFor": "The link is valid for 30m.",
"customizeMessages": "Customize Messages",
"customizeMessagesDescription": "If you don't want to use jfa-go's message templates, you can create your own using Markdown.",
"markdownSupported": "Markdown is supported.",
diff --git a/lang/common/da-dk.json b/lang/common/da-dk.json
index c400679..569ac51 100644
--- a/lang/common/da-dk.json
+++ b/lang/common/da-dk.json
@@ -10,6 +10,7 @@
"submit": "Indsend",
"send": "Send",
"success": "Succes",
+ "continue": "Fortsæt",
"error": "Fejl",
"copy": "Kopiér",
"copied": "Kopiret",
diff --git a/lang/common/de-de.json b/lang/common/de-de.json
index a75e366..d25a613 100644
--- a/lang/common/de-de.json
+++ b/lang/common/de-de.json
@@ -9,6 +9,7 @@
"emailAddress": "E-Mail-Adresse",
"submit": "Absenden",
"success": "Erfolgreich",
+ "continue": "Weiter",
"error": "Fehler",
"copy": "Kopieren",
"theme": "Thema",
diff --git a/lang/common/el-gr.json b/lang/common/el-gr.json
index 8148f35..94d5c4f 100644
--- a/lang/common/el-gr.json
+++ b/lang/common/el-gr.json
@@ -9,6 +9,7 @@
"name": "Όνομα",
"submit": "Καταχώρηση",
"success": "Επιτυχία",
+ "continue": "Συνέχεια",
"error": "Σφάλμα",
"copy": "Αντιγραφή",
"theme": "Θέμα",
diff --git a/lang/common/en-us.json b/lang/common/en-us.json
index 5f8ec44..db977e8 100644
--- a/lang/common/en-us.json
+++ b/lang/common/en-us.json
@@ -10,6 +10,7 @@
"submit": "Submit",
"send": "Send",
"success": "Success",
+ "continue": "Continue",
"error": "Error",
"copy": "Copy",
"copied": "Copied",
diff --git a/lang/common/es-es.json b/lang/common/es-es.json
index 9451c13..52d4a6c 100644
--- a/lang/common/es-es.json
+++ b/lang/common/es-es.json
@@ -9,6 +9,7 @@
"name": "Nombre",
"submit": "Enviar",
"success": "Éxito",
+ "continue": "Continuar",
"error": "Error",
"copy": "Copiar",
"copied": "Copiado",
diff --git a/lang/common/fa-ir.json b/lang/common/fa-ir.json
index a3165eb..0931d08 100644
--- a/lang/common/fa-ir.json
+++ b/lang/common/fa-ir.json
@@ -10,6 +10,7 @@
"submit": "تایید",
"send": "ارسال",
"success": "موفقیت",
+ "continue": "ادامه دادن",
"error": "خطا",
"copy": "کپی",
"copied": "کپی شد",
diff --git a/lang/common/fr-fr.json b/lang/common/fr-fr.json
index ccfdf4c..deb0b16 100644
--- a/lang/common/fr-fr.json
+++ b/lang/common/fr-fr.json
@@ -10,6 +10,7 @@
"emailAddress": "Addresse Email",
"submit": "Soumettre",
"success": "Succès",
+ "continue": "Continuer",
"error": "Erreur",
"copy": "Copier",
"time24h": "Temps 24h",
diff --git a/lang/common/id-id.json b/lang/common/id-id.json
index bfe567a..72bcc26 100644
--- a/lang/common/id-id.json
+++ b/lang/common/id-id.json
@@ -9,6 +9,7 @@
"name": "Nama",
"submit": "Submit",
"success": "Sukses",
+ "continue": "Lanjut",
"error": "Error",
"copy": "Salin",
"time24h": "Waktu 24 jam",
diff --git a/lang/common/nl-nl.json b/lang/common/nl-nl.json
index 27c3e16..7d755a0 100644
--- a/lang/common/nl-nl.json
+++ b/lang/common/nl-nl.json
@@ -9,6 +9,7 @@
"emailAddress": "E-mailadres",
"submit": "Verstuur",
"success": "Succes",
+ "continue": "Doorgaan",
"error": "Fout",
"copy": "Kopiëer",
"theme": "Thema",
diff --git a/lang/common/pt-br.json b/lang/common/pt-br.json
index 3a43e37..a35f666 100644
--- a/lang/common/pt-br.json
+++ b/lang/common/pt-br.json
@@ -9,6 +9,7 @@
"emailAddress": "Endereço de Email",
"submit": "Enviar",
"success": "Sucesso",
+ "continue": "Continuar",
"error": "Erro",
"copy": "Copiar",
"theme": "Tema",
diff --git a/lang/common/sv-se.json b/lang/common/sv-se.json
index b7fd2cb..3354ca4 100644
--- a/lang/common/sv-se.json
+++ b/lang/common/sv-se.json
@@ -9,6 +9,7 @@
"name": "Namn",
"submit": "Skicka",
"success": "Lyckades",
+ "continue": "Fortsätt",
"error": "Fel",
"copy": "Kopiera",
"time24h": "24 timmarsklocka",
diff --git a/lang/common/zh-hans.json b/lang/common/zh-hans.json
index 0e92a11..0b00297 100644
--- a/lang/common/zh-hans.json
+++ b/lang/common/zh-hans.json
@@ -10,6 +10,7 @@
"submit": "提交",
"send": "发送",
"success": "成功",
+ "continue": "继续",
"error": "错误",
"copy": "复制",
"copied": "已复制",
diff --git a/lang/form/da-dk.json b/lang/form/da-dk.json
index 52e05e7..b92b722 100644
--- a/lang/form/da-dk.json
+++ b/lang/form/da-dk.json
@@ -14,7 +14,6 @@
"createAccountButton": "Opret Konto",
"passwordRequirementsHeader": "Adgangskodekrav",
"successHeader": "Succes!",
- "successContinueButton": "Fortsæt",
"confirmationRequired": "E-mail bekræftelse er påkrævet",
"confirmationRequiredMessage": "Tjek venligst din e-mail indbakke for at verificere din adresse.",
"yourAccountIsValidUntil": "Din konto er gyldig indtil {date}.",
diff --git a/lang/form/de-de.json b/lang/form/de-de.json
index 29cac14..2fbbba3 100644
--- a/lang/form/de-de.json
+++ b/lang/form/de-de.json
@@ -14,7 +14,6 @@
"createAccountButton": "Konto erstellen",
"passwordRequirementsHeader": "Passwortanforderungen",
"successHeader": "Erfolgreich!",
- "successContinueButton": "Weiter",
"confirmationRequired": "E-Mail-Bestätigung erforderlich",
"confirmationRequiredMessage": "Bitte überprüfe dein Posteingang und bestätige deine E-Mail-Adresse.",
"yourAccountIsValidUntil": "Dein Konto wird bis zum {date} gültig sein.",
diff --git a/lang/form/el-gr.json b/lang/form/el-gr.json
index df0062a..8301ad3 100644
--- a/lang/form/el-gr.json
+++ b/lang/form/el-gr.json
@@ -14,7 +14,6 @@
"createAccountButton": "Δημιουργία Λογαρισμού",
"passwordRequirementsHeader": "Απαιτήσεις Κωδικού",
"successHeader": "Επιτυχία!",
- "successContinueButton": "Συνέχεια",
"confirmationRequired": "Απαιτείται επιβεβαίωση Email",
"confirmationRequiredMessage": "Παρακαλώ ελέγξτε το email σας για να επιβεβαιώσετε την διεύθυνση σας .",
"yourAccountIsValidUntil": "Ο λογαριασμός σου θα ισχύει μέχρι {date}."
diff --git a/lang/form/en-us.json b/lang/form/en-us.json
index 7b85cfc..cec4319 100644
--- a/lang/form/en-us.json
+++ b/lang/form/en-us.json
@@ -14,7 +14,6 @@
"createAccountButton": "Create Account",
"passwordRequirementsHeader": "Password Requirements",
"successHeader": "Success!",
- "successContinueButton": "Continue",
"confirmationRequired": "Email confirmation required",
"confirmationRequiredMessage": "Please check your email inbox to verify your address.",
"yourAccountIsValidUntil": "Your account will be valid until {date}.",
diff --git a/lang/form/es-es.json b/lang/form/es-es.json
index 38190ef..b891a80 100644
--- a/lang/form/es-es.json
+++ b/lang/form/es-es.json
@@ -14,7 +14,6 @@
"createAccountButton": "Crear una cuenta",
"passwordRequirementsHeader": "Requisitos de contraseña",
"successHeader": "¡Éxito!",
- "successContinueButton": "Continuar",
"confirmationRequired": "Se requiere confirmación por correo electrónico",
"confirmationRequiredMessage": "Revise la bandeja de entrada de su correo electrónico para verificar su dirección.",
"yourAccountIsValidUntil": "Su cuenta será válida hasta el {date}."
diff --git a/lang/form/fa-ir.json b/lang/form/fa-ir.json
index 9009667..f3f1f05 100644
--- a/lang/form/fa-ir.json
+++ b/lang/form/fa-ir.json
@@ -14,7 +14,6 @@
"createAccountButton": "ساخت حساب کاربری",
"passwordRequirementsHeader": "کلمه عبور لازم است",
"successHeader": "موفقیت!",
- "successContinueButton": "ادامه دادن",
"confirmationRequired": "تایید ایمیل لازم است",
"confirmationRequiredMessage": "لطفاً برای تأیید آدرس خود ، صندوق پستی ایمیل خود را بررسی کنید.",
"yourAccountIsValidUntil": "حساب شما تا {date} معتبر خواهد بود.",
diff --git a/lang/form/fr-fr.json b/lang/form/fr-fr.json
index 9c80994..9de5f75 100644
--- a/lang/form/fr-fr.json
+++ b/lang/form/fr-fr.json
@@ -15,7 +15,6 @@
"createAccountButton": "Créer le compte",
"passwordRequirementsHeader": "Mot de passe requis",
"successHeader": "Succès !",
- "successContinueButton": "Continuer",
"confirmationRequired": "Confirmation de l'adresse e-mail requise",
"confirmationRequiredMessage": "Veuillez vérifier votre boite de réception pour confirmer votre adresse e-mail.",
"yourAccountIsValidUntil": "Votre compte sera valide jusqu'au {date}.",
diff --git a/lang/form/id-id.json b/lang/form/id-id.json
index 0fba7b4..e25bb90 100644
--- a/lang/form/id-id.json
+++ b/lang/form/id-id.json
@@ -14,7 +14,6 @@
"createAccountButton": "Buat Akun",
"passwordRequirementsHeader": "Persyaratan Kata Sandi",
"successHeader": "Sukses!",
- "successContinueButton": "Lanjut",
"confirmationRequired": "Konfirmasi email diperlukan",
"confirmationRequiredMessage": "Silakan periksa kotak masuk email Anda untuk memverifikasi alamat Anda."
},
diff --git a/lang/form/it-it.json b/lang/form/it-it.json
index e2553d3..3f3afc6 100644
--- a/lang/form/it-it.json
+++ b/lang/form/it-it.json
@@ -14,7 +14,6 @@
"createAccountButton": "Crea Un Account",
"passwordRequirementsHeader": "Requisiti Password",
"successHeader": "Successo!",
- "successContinueButton": "Continua",
"confirmationRequired": "Richiesta la conferma Email",
"confirmationRequiredMessage": "Controlla la tua casella email per verificare il tuo indirizzo."
},
diff --git a/lang/form/nl-nl.json b/lang/form/nl-nl.json
index a5d06d1..1323d5e 100644
--- a/lang/form/nl-nl.json
+++ b/lang/form/nl-nl.json
@@ -14,7 +14,6 @@
"createAccountButton": "Maak account aan",
"passwordRequirementsHeader": "Wachtwoordvereisten",
"successHeader": "Succes!",
- "successContinueButton": "Doorgaan",
"confirmationRequired": "Bevestiging van e-mailadres verplicht",
"confirmationRequiredMessage": "Controleer je e-mail inbox om je adres te bevestigen.",
"yourAccountIsValidUntil": "Je account zal geldig zijn tot {date}.",
diff --git a/lang/form/pt-br.json b/lang/form/pt-br.json
index e4caf46..81fa3d5 100644
--- a/lang/form/pt-br.json
+++ b/lang/form/pt-br.json
@@ -14,7 +14,6 @@
"createAccountButton": "Criar Conta",
"passwordRequirementsHeader": "Requisitos da Senha",
"successHeader": "Concluído!",
- "successContinueButton": "Continuar",
"confirmationRequired": "Confirmação por e-mail",
"confirmationRequiredMessage": "Verifique sua caixa de email para finalizar o cadastro.",
"yourAccountIsValidUntil": "Sua conta é válida até {date}.",
diff --git a/lang/form/sv-se.json b/lang/form/sv-se.json
index 8bf9bca..d65177a 100644
--- a/lang/form/sv-se.json
+++ b/lang/form/sv-se.json
@@ -14,7 +14,6 @@
"createAccountButton": "Skapa konto",
"passwordRequirementsHeader": "Lösenordskrav",
"successHeader": "Lyckades!",
- "successContinueButton": "Fortsätt",
"confirmationRequired": "E-postbekräftelse krävs",
"confirmationRequiredMessage": "Kontrollera din e-postkorg för att verifiera din adress.",
"yourAccountIsValidUntil": "Ditt konto är giltigt fram tills {date}.",
diff --git a/lang/form/zh-hans.json b/lang/form/zh-hans.json
index 1cd14d3..1f4c61d 100644
--- a/lang/form/zh-hans.json
+++ b/lang/form/zh-hans.json
@@ -14,7 +14,6 @@
"createAccountButton": "创建账户",
"passwordRequirementsHeader": "密码格式要求",
"successHeader": "成功!",
- "successContinueButton": "继续",
"confirmationRequired": "需要邮件确认",
"confirmationRequiredMessage": "请登录您的邮箱收件箱来验证您的地址。",
"yourAccountIsValidUntil": "您的账户将在 {date} 之前有效。",
diff --git a/lang/pwreset/en-us.json b/lang/pwreset/en-us.json
index 4fe7cd4..ab5967f 100644
--- a/lang/pwreset/en-us.json
+++ b/lang/pwreset/en-us.json
@@ -9,6 +9,7 @@
"tryAgain": "Please try again.",
"youCanLogin": "You can now log in with the below code as your password.",
"youCanLoginOmbi": "You can now log in to Jellyfin & Ombi with the below code as your password.",
+ "youCanLoginPassword": "You can now login with your new password. Press below to continue to Jellyfin.",
"changeYourPassword": "Make sure to change your password after you log in.",
"enterYourPassword": "Enter your new password below."
}
diff --git a/main.go b/main.go
index b9b93f4..b297814 100644
--- a/main.go
+++ b/main.go
@@ -108,6 +108,7 @@ type appContext struct {
newUpdate bool // Whether whatever's in update is new.
tag Tag
update Update
+ internalPWRs map[string]InternalPWR
}
func generateSecret(length int) (string, error) {
diff --git a/models.go b/models.go
index 0329da7..80b7ef0 100644
--- a/models.go
+++ b/models.go
@@ -1,5 +1,7 @@
package main
+import "time"
+
type stringResponse struct {
Response string `json:"response" example:"message"`
Error string `json:"error" example:"errorDescription"`
@@ -320,3 +322,20 @@ type ResetPasswordDTO struct {
PIN string `json:"pin"`
Password string `json:"password"`
}
+
+type AdminPasswordResetDTO struct {
+ Users []string `json:"users"` // List of Jellyfin user IDs
+}
+
+type AdminPasswordResetRespDTO struct {
+ Link string `json:"link"` // Only returned if one of the given users doesn't have a contact method set, or only one user was requested.
+ Manual bool `json:"manual"` // Whether or not the admin has to send the link manually or not.
+}
+
+// InternalPWR stores a local version of a password reset PIN used for resets triggered by the admin when reset links are enabled.
+type InternalPWR struct {
+ PIN string `json:"pin"`
+ Username string `json:"username"`
+ ID string `json:"id"`
+ Expiry time.Time `json:"expiry"`
+}
diff --git a/pwreset.go b/pwreset.go
index d40bb40..255c9e2 100644
--- a/pwreset.go
+++ b/pwreset.go
@@ -9,6 +9,22 @@ import (
"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()
@@ -38,6 +54,7 @@ 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) {
@@ -81,7 +98,7 @@ func pwrMonitor(app *appContext, watcher *fsnotify.Watcher) {
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.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)
diff --git a/router.go b/router.go
index 9276746..7c15996 100644
--- a/router.go
+++ b/router.go
@@ -169,6 +169,8 @@ func (app *appContext) loadRoutes(router *gin.Engine) {
api.GET(p+"/users/announce/:name", app.GetAnnounceTemplate)
api.DELETE(p+"/users/announce/:name", app.DeleteAnnounceTemplate)
+ api.POST(p+"/users/password-reset", app.AdminPasswordReset)
+
api.GET(p+"/config/update", app.CheckUpdate)
api.POST(p+"/config/update", app.ApplyUpdate)
api.GET(p+"/config/emails", app.GetCustomEmails)
diff --git a/ts/admin.ts b/ts/admin.ts
index 6efaa4e..32f2723 100644
--- a/ts/admin.ts
+++ b/ts/admin.ts
@@ -72,6 +72,10 @@ window.availableProfiles = window.availableProfiles || [];
if (window.discordEnabled) {
window.modals.discord = new Modal(document.getElementById("modal-discord"));
}
+
+ if (window.linkResetEnabled) {
+ window.modals.sendPWR = new Modal(document.getElementById("modal-send-pwr"));
+ }
})();
var inviteCreator = new createInvite();
diff --git a/ts/modules/accounts.ts b/ts/modules/accounts.ts
index 2100a09..b31c037 100644
--- a/ts/modules/accounts.ts
+++ b/ts/modules/accounts.ts
@@ -1,4 +1,4 @@
-import { _get, _post, _delete, toggleLoader, addLoader, removeLoader, toDateString, insertText } from "../modules/common.js";
+import { _get, _post, _delete, toggleLoader, addLoader, removeLoader, toDateString, insertText, toClipboard } from "../modules/common.js";
import { templateEmail } from "../modules/settings.js";
import { Marked } from "@ts-stack/markdown";
import { stripMarkdown } from "../modules/stripmd.js";
@@ -63,7 +63,7 @@ class user implements User {
id = "";
private _selected: boolean;
- private _lastNotifyMethod = (): string => {
+ lastNotifyMethod = (): string => {
// Telegram, Matrix, Discord
const telegram = window.telegramEnabled && this._telegramUsername && this._telegramUsername != "";
const discord = window.discordEnabled && this._discordUsername && this._discordUsername != "";
@@ -188,7 +188,7 @@ class user implements User {
this._notifyDropdown.querySelector(".accounts-area-matrix").classList.add("unfocused");
return;
}
- const lastNotifyMethod = this._lastNotifyMethod() == "matrix";
+ const lastNotifyMethod = this.lastNotifyMethod() == "matrix";
this._matrixID = u;
if (!u) {
this._notifyDropdown.querySelector(".accounts-area-matrix").classList.add("unfocused");
@@ -253,7 +253,7 @@ class user implements User {
this._notifyDropdown.querySelector(".accounts-area-telegram").classList.add("unfocused");
return;
}
- const lastNotifyMethod = this._lastNotifyMethod() == "telegram";
+ const lastNotifyMethod = this.lastNotifyMethod() == "telegram";
this._telegramUsername = u;
if (!u) {
this._notifyDropdown.querySelector(".accounts-area-telegram").classList.add("unfocused");
@@ -319,7 +319,7 @@ class user implements User {
this._notifyDropdown.querySelector(".accounts-area-discord").classList.add("unfocused");
return;
}
- const lastNotifyMethod = this._lastNotifyMethod() == "discord";
+ const lastNotifyMethod = this.lastNotifyMethod() == "discord";
this._discordUsername = u;
if (!u) {
this._discord.innerHTML = `Add`;
@@ -566,6 +566,7 @@ export class accountsList {
private _modifySettings = document.getElementById("accounts-modify-user") as HTMLSpanElement;
private _modifySettingsProfile = document.getElementById("radio-use-profile") as HTMLInputElement;
private _modifySettingsUser = document.getElementById("radio-use-user") as HTMLInputElement;
+ private _sendPWR = document.getElementById("accounts-send-pwr") as HTMLSpanElement;
private _profileSelect = document.getElementById("modify-user-profiles") as HTMLSelectElement;
private _userSelect = document.getElementById("modify-user-users") as HTMLSelectElement;
private _search = document.getElementById("accounts-search") as HTMLInputElement;
@@ -698,6 +699,7 @@ export class accountsList {
}
this._extendExpiry.classList.add("unfocused");
this._disableEnable.classList.add("unfocused");
+ this._sendPWR.classList.add("unfocused");
} else {
let visibleCount = 0;
for (let id in this._users) {
@@ -719,6 +721,7 @@ export class accountsList {
this._announceButton.classList.remove("unfocused");
}
let anyNonExpiries = list.length == 0 ? true : false;
+ let noContactCount = 0;
// Only show enable/disable button if all selected have the same state.
this._shouldEnable = this._users[list[0]].disabled
let showDisableEnable = true;
@@ -732,10 +735,19 @@ export class accountsList {
this._disableEnable.classList.add("unfocused");
}
if (!showDisableEnable && anyNonExpiries) { break; }
+ if (!this._users[id].lastNotifyMethod() && !this._users[id].email) {
+ noContactCount++;
+ }
}
if (!anyNonExpiries) {
this._extendExpiry.classList.remove("unfocused");
}
+ // Only show "Send PWR" if a maximum of 1 user selected doesn't have a contact method
+ if (noContactCount > 1) {
+ this._sendPWR.classList.add("unfocused");
+ } else {
+ this._sendPWR.classList.remove("unfocused");
+ }
if (showDisableEnable) {
let message: string;
if (this._shouldEnable) {
@@ -1042,6 +1054,58 @@ export class accountsList {
};
window.modals.deleteUser.show();
}
+
+ sendPWR = () => {
+ addLoader(this._sendPWR);
+ let list = this._collectUsers();
+ let manualUser: user;
+ for (let id of list) {
+ let user = this._users[id];
+ console.log(user, user.notify_email, user.notify_matrix, user.notify_discord, user.notify_telegram);
+ if (!user.lastNotifyMethod() && !user.email) {
+ manualUser = user;
+ break;
+ }
+ }
+ const messageBox = document.getElementById("send-pwr-note") as HTMLParagraphElement;
+ let message: string;
+ let send = {
+ users: list
+ };
+ _post("/users/password-reset", send, (req: XMLHttpRequest) => {
+ if (req.readyState != 4) return;
+ removeLoader(this._sendPWR);
+ let link: string;
+ if (req.status == 200) {
+ link = req.response["link"];
+ if (req.response["manual"] as boolean) {
+ message = window.lang.var("strings", "sendPWRManual", manualUser.name);
+ } else {
+ message = window.lang.strings("sendPWRSuccess") + " " + window.lang.strings("sendPWRSuccessManual");
+ }
+ } else if (req.status == 204) {
+ message = window.lang.strings("sendPWRSuccess");
+ } else {
+ window.notifications.customError("errorSendPWR", window.lang.strings("errorFailureCheckLogs"));
+ return;
+ }
+ message += " " + window.lang.strings("sendPWRValidFor");
+ messageBox.textContent = message;
+ let linkButton = document.getElementById("send-pwr-link") as HTMLSpanElement;
+ linkButton.onclick = () => {
+ toClipboard(link);
+ linkButton.textContent = window.lang.strings("copied");
+ linkButton.classList.add("~positive");
+ linkButton.classList.remove("~urge");
+ setTimeout(() => {
+ linkButton.textContent = window.lang.strings("copy");
+ linkButton.classList.add("~urge");
+ linkButton.classList.remove("~positive");
+ }, 800);
+ };
+ window.modals.sendPWR.show();
+ }, true);
+ }
modifyUsers = () => {
const modalHeader = document.getElementById("header-modify-user");
@@ -1203,6 +1267,12 @@ export class accountsList {
this._addUserName.classList.add("unfocused");
this._addUserName = this._addUserEmail;
}
+
+ if (!window.linkResetEnabled) {
+ this._sendPWR.classList.add("unfocused");
+ } else {
+ this._sendPWR.onclick = this.sendPWR;
+ }
/*if (!window.emailEnabled) {
this._deleteNotify.parentElement.classList.add("unfocused");
this._deleteNotify.checked = false;
diff --git a/ts/typings/d.ts b/ts/typings/d.ts
index c06785e..0738128 100644
--- a/ts/typings/d.ts
+++ b/ts/typings/d.ts
@@ -25,6 +25,7 @@ declare interface Window {
matrixEnabled: boolean;
ombiEnabled: boolean;
usernameEnabled: boolean;
+ linkResetEnabled: boolean;
token: string;
buttonWidth: number;
transitionEvent: string;
@@ -105,6 +106,7 @@ declare interface Modals {
telegram: Modal;
discord: Modal;
matrix: Modal;
+ sendPWR?: Modal;
}
interface Invite {
diff --git a/views.go b/views.go
index fae8c75..52b61f8 100644
--- a/views.go
+++ b/views.go
@@ -116,23 +116,24 @@ func (app *appContext) AdminPage(gc *gin.Context) {
}
license = string(l)
gcHTML(gc, http.StatusOK, "admin.html", gin.H{
- "urlBase": app.getURLBase(gc),
- "cssClass": app.cssClass,
- "contactMessage": "",
- "emailEnabled": emailEnabled,
- "telegramEnabled": telegramEnabled,
- "discordEnabled": discordEnabled,
- "matrixEnabled": matrixEnabled,
- "ombiEnabled": ombiEnabled,
- "notifications": notificationsEnabled,
- "version": version,
- "commit": commit,
- "username": !app.config.Section("email").Key("no_username").MustBool(false),
- "strings": app.storage.lang.Admin[lang].Strings,
- "quantityStrings": app.storage.lang.Admin[lang].QuantityStrings,
- "language": app.storage.lang.Admin[lang].JSON,
- "langName": lang,
- "license": license,
+ "urlBase": app.getURLBase(gc),
+ "cssClass": app.cssClass,
+ "contactMessage": "",
+ "emailEnabled": emailEnabled,
+ "telegramEnabled": telegramEnabled,
+ "discordEnabled": discordEnabled,
+ "matrixEnabled": matrixEnabled,
+ "ombiEnabled": ombiEnabled,
+ "linkResetEnabled": app.config.Section("password_resets").Key("link_reset").MustBool(false),
+ "notifications": notificationsEnabled,
+ "version": version,
+ "commit": commit,
+ "username": !app.config.Section("email").Key("no_username").MustBool(false),
+ "strings": app.storage.lang.Admin[lang].Strings,
+ "quantityStrings": app.storage.lang.Admin[lang].QuantityStrings,
+ "language": app.storage.lang.Admin[lang].JSON,
+ "langName": lang,
+ "license": license,
})
}
@@ -154,6 +155,11 @@ func (app *appContext) ResetPassword(gc *gin.Context) {
"success": false,
"ombiEnabled": app.config.Section("ombi").Key("enabled").MustBool(false),
}
+ if _, ok := app.internalPWRs[pin]; !ok {
+ app.debug.Printf("Ignoring PWR request due to invalid internal PIN: %s", pin)
+ app.NoRouteHandler(gc)
+ return
+ }
if setPassword {
data["helpMessage"] = app.config.Section("ui").Key("help_message").String()
data["successMessage"] = app.config.Section("ui").Key("success_message").String()
@@ -177,33 +183,47 @@ func (app *appContext) ResetPassword(gc *gin.Context) {
app.debug.Println("PWR: Ignoring magic link visit from bot")
data["success"] = true
data["pin"] = "NO-BO-TS"
+ return
+ }
+ // if reset, ok := app.internalPWRs[pin]; ok {
+ // status, err := app.jf.ResetPasswordAdmin(reset.ID)
+ // if !(status == 200 || status == 204) || err != nil {
+ // app.err.Printf("Password Reset failed (%d): %v", status, err)
+ // return
+ // }
+ // status, err = app.jf.SetPassword(reset.ID, "", pin)
+ // if !(status == 200 || status == 204) || err != nil {
+ // app.err.Printf("Password Reset failed (%d): %v", status, err)
+ // return
+ // }
+ // data["success"] = true
+ // data["pin"] = pin
+ // }
+ resp, status, err := app.jf.ResetPassword(pin)
+ if status == 200 && err == nil && resp.Success {
+ data["success"] = true
+ data["pin"] = pin
} else {
- resp, status, err := app.jf.ResetPassword(pin)
- if status == 200 && err == nil && resp.Success {
- data["success"] = true
- data["pin"] = pin
- } else {
- app.err.Printf("Password Reset failed (%d): %v", status, err)
+ app.err.Printf("Password Reset failed (%d): %v", status, err)
+ }
+ if app.config.Section("ombi").Key("enabled").MustBool(false) {
+ jfUser, status, err := app.jf.UserByName(resp.UsersReset[0], false)
+ if status != 200 || err != nil {
+ app.err.Printf("Failed to get user \"%s\" from jellyfin/emby (%d): %v", resp.UsersReset[0], status, err)
+ return
}
- if app.config.Section("ombi").Key("enabled").MustBool(false) {
- jfUser, status, err := app.jf.UserByName(resp.UsersReset[0], false)
- if status != 200 || err != nil {
- app.err.Printf("Failed to get user \"%s\" from jellyfin/emby (%d): %v", resp.UsersReset[0], status, err)
- return
- }
- ombiUser, status, err := app.getOmbiUser(jfUser.ID)
- if status != 200 || err != nil {
- app.err.Printf("Failed to get user \"%s\" from ombi (%d): %v", resp.UsersReset[0], status, err)
- return
- }
- ombiUser["password"] = pin
- status, err = app.ombi.ModifyUser(ombiUser)
- if status != 200 || err != nil {
- app.err.Printf("Failed to set password for ombi user \"%s\" (%d): %v", ombiUser["userName"], status, err)
- return
- }
- app.debug.Printf("Reset password for ombi user \"%s\"", ombiUser["userName"])
+ ombiUser, status, err := app.getOmbiUser(jfUser.ID)
+ if status != 200 || err != nil {
+ app.err.Printf("Failed to get user \"%s\" from ombi (%d): %v", resp.UsersReset[0], status, err)
+ return
+ }
+ ombiUser["password"] = pin
+ status, err = app.ombi.ModifyUser(ombiUser)
+ if status != 200 || err != nil {
+ app.err.Printf("Failed to set password for ombi user \"%s\" (%d): %v", ombiUser["userName"], status, err)
+ return
}
+ app.debug.Printf("Reset password for ombi user \"%s\"", ombiUser["userName"])
}
}