admin: add manual "Send Password reset" to accounts tab
Only appears with Reset links enabled. Pressing this sends a PWR link to the users selected. if one user is selected, or if one of you selected users doesn't have a method of contact, a link is given to the admin to send to them manually.
This commit is contained in:
@@ -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
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user