mirror of
https://github.com/hrfee/jfa-go.git
synced 2024-12-22 09:00:10 +00:00
userpage: implement change password functionality
This commit is contained in:
parent
12ce669566
commit
97db4d714a
@ -275,6 +275,7 @@ func (app *appContext) ModifyMyEmail(gc *gin.Context) {
|
||||
// @Failure 500 {object} boolResponse
|
||||
// @Param invCode path string true "invite Code"
|
||||
// @Router /my/discord/invite [get]
|
||||
// @Security Bearer
|
||||
// @tags User Page
|
||||
func (app *appContext) MyDiscordServerInvite(gc *gin.Context) {
|
||||
if app.discord.inviteChannelName == "" {
|
||||
@ -295,6 +296,7 @@ func (app *appContext) MyDiscordServerInvite(gc *gin.Context) {
|
||||
// @Failure 400 {object} stringResponse
|
||||
// Param service path string true "discord/telegram"
|
||||
// @Router /my/pin/{service} [get]
|
||||
// @Security Bearer
|
||||
// @tags User Page
|
||||
func (app *appContext) GetMyPIN(gc *gin.Context) {
|
||||
service := gc.Param("service")
|
||||
@ -319,6 +321,7 @@ func (app *appContext) GetMyPIN(gc *gin.Context) {
|
||||
// @Failure 401 {object} boolResponse
|
||||
// @Param pin path string true "PIN code to check"
|
||||
// @Router /my/discord/verified/{pin} [get]
|
||||
// @Security Bearer
|
||||
// @tags User Page
|
||||
func (app *appContext) MyDiscordVerifiedInvite(gc *gin.Context) {
|
||||
pin := gc.Param("pin")
|
||||
@ -347,6 +350,7 @@ func (app *appContext) MyDiscordVerifiedInvite(gc *gin.Context) {
|
||||
// @Failure 401 {object} boolResponse
|
||||
// @Param pin path string true "PIN code to check"
|
||||
// @Router /my/telegram/verified/{pin} [get]
|
||||
// @Security Bearer
|
||||
// @tags User Page
|
||||
func (app *appContext) MyTelegramVerifiedInvite(gc *gin.Context) {
|
||||
pin := gc.Param("pin")
|
||||
@ -386,6 +390,7 @@ func (app *appContext) MyTelegramVerifiedInvite(gc *gin.Context) {
|
||||
// @Failure 500 {object} boolResponse
|
||||
// @Param MatrixSendPINDTO body MatrixSendPINDTO true "User's Matrix ID."
|
||||
// @Router /my/matrix/user [post]
|
||||
// @Security Bearer
|
||||
// @tags User Page
|
||||
func (app *appContext) MatrixSendMyPIN(gc *gin.Context) {
|
||||
var req MatrixSendPINDTO
|
||||
@ -419,6 +424,7 @@ func (app *appContext) MatrixSendMyPIN(gc *gin.Context) {
|
||||
// @Param invCode path string true "invite Code"
|
||||
// @Param userID path string true "Matrix User ID"
|
||||
// @Router /my/matrix/verified/{userID}/{pin} [get]
|
||||
// @Security Bearer
|
||||
// @tags User Page
|
||||
func (app *appContext) MatrixCheckMyPIN(gc *gin.Context) {
|
||||
userID := gc.Param("userID")
|
||||
@ -452,7 +458,8 @@ func (app *appContext) MatrixCheckMyPIN(gc *gin.Context) {
|
||||
// @Produce json
|
||||
// @Success 200 {object} boolResponse
|
||||
// @Router /my/discord [delete]
|
||||
// @Tags Users
|
||||
// @Security Bearer
|
||||
// @Tags User Page
|
||||
func (app *appContext) UnlinkMyDiscord(gc *gin.Context) {
|
||||
app.storage.DeleteDiscordKey(gc.GetString("jfId"))
|
||||
respondBool(200, true, gc)
|
||||
@ -462,7 +469,8 @@ func (app *appContext) UnlinkMyDiscord(gc *gin.Context) {
|
||||
// @Produce json
|
||||
// @Success 200 {object} boolResponse
|
||||
// @Router /my/telegram [delete]
|
||||
// @Tags Users
|
||||
// @Security Bearer
|
||||
// @Tags User Page
|
||||
func (app *appContext) UnlinkMyTelegram(gc *gin.Context) {
|
||||
app.storage.DeleteTelegramKey(gc.GetString("jfId"))
|
||||
respondBool(200, true, gc)
|
||||
@ -472,7 +480,8 @@ func (app *appContext) UnlinkMyTelegram(gc *gin.Context) {
|
||||
// @Produce json
|
||||
// @Success 200 {object} boolResponse
|
||||
// @Router /my/matrix [delete]
|
||||
// @Tags Users
|
||||
// @Security Bearer
|
||||
// @Tags User Page
|
||||
func (app *appContext) UnlinkMyMatrix(gc *gin.Context) {
|
||||
app.storage.DeleteMatrixKey(gc.GetString("jfId"))
|
||||
respondBool(200, true, gc)
|
||||
@ -485,7 +494,7 @@ func (app *appContext) UnlinkMyMatrix(gc *gin.Context) {
|
||||
// @Failure 400 {object} boolResponse
|
||||
// @Failure 500 {object} boolResponse
|
||||
// @Router /my/password/reset/{address} [post]
|
||||
// @tags Users
|
||||
// @Tags User Page
|
||||
func (app *appContext) ResetMyPassword(gc *gin.Context) {
|
||||
// All requests should take 1 second, to make it harder to tell if a success occured or not.
|
||||
timerWait := make(chan bool)
|
||||
@ -551,3 +560,63 @@ func (app *appContext) ResetMyPassword(gc *gin.Context) {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// @Summary Change your password, given the old one and the new one.
|
||||
// @Produce json
|
||||
// @Param ChangeMyPasswordDTO body ChangeMyPasswordDTO true "User's old & new passwords."
|
||||
// @Success 204 {object} boolResponse
|
||||
// @Failure 400 {object} PasswordValidation
|
||||
// @Failure 401 {object} boolResponse
|
||||
// @Failure 500 {object} boolResponse
|
||||
// @Router /my/password [post]
|
||||
// @Security Bearer
|
||||
// @Tags User Page
|
||||
func (app *appContext) ChangeMyPassword(gc *gin.Context) {
|
||||
var req ChangeMyPasswordDTO
|
||||
gc.BindJSON(&req)
|
||||
if req.Old == "" || req.New == "" {
|
||||
respondBool(400, false, gc)
|
||||
}
|
||||
validation := app.validator.validate(req.New)
|
||||
for _, val := range validation {
|
||||
if !val {
|
||||
app.debug.Printf("%s: Change password failed: Invalid password", gc.GetString("jfId"))
|
||||
gc.JSON(400, validation)
|
||||
return
|
||||
}
|
||||
}
|
||||
user, status, err := app.jf.UserByID(gc.GetString("jfId"), false)
|
||||
if status != 200 || err != nil {
|
||||
app.err.Printf("Failed to change password: couldn't find user (%d): %+v", status, err)
|
||||
respondBool(500, false, gc)
|
||||
return
|
||||
}
|
||||
// Authenticate as user to confirm old password.
|
||||
user, status, err = app.authJf.Authenticate(user.Name, req.Old)
|
||||
if status != 200 || err != nil {
|
||||
respondBool(401, false, gc)
|
||||
return
|
||||
}
|
||||
status, err = app.jf.SetPassword(gc.GetString("jfId"), req.Old, req.New)
|
||||
if (status != 200 && status != 204) || err != nil {
|
||||
respondBool(500, false, gc)
|
||||
return
|
||||
}
|
||||
if app.config.Section("ombi").Key("enabled").MustBool(false) {
|
||||
ombiUser, status, err := app.getOmbiUser(gc.GetString("jfId"))
|
||||
if status != 200 || err != nil {
|
||||
app.err.Printf("Failed to get user \"%s\" from ombi (%d): %v", user.Name, status, err)
|
||||
respondBool(204, true, gc)
|
||||
return
|
||||
}
|
||||
ombiUser["password"] = req.New
|
||||
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)
|
||||
respondBool(204, true, gc)
|
||||
return
|
||||
}
|
||||
app.debug.Printf("Reset password for ombi user \"%s\"", ombiUser["userName"])
|
||||
}
|
||||
respondBool(204, true, gc)
|
||||
}
|
||||
|
@ -463,6 +463,7 @@ func (app *appContext) NewUser(gc *gin.Context) {
|
||||
for _, val := range validation {
|
||||
if !val {
|
||||
valid = false
|
||||
break
|
||||
}
|
||||
}
|
||||
if !valid {
|
||||
|
@ -124,7 +124,7 @@
|
||||
</div>
|
||||
<div class="flex-initial">
|
||||
<div class="card ~neutral @low mb-4">
|
||||
<span class="label supra" for="inv-uses">{{ .strings.passwordRequirementsHeader }}</span>
|
||||
<span class="label supra">{{ .strings.passwordRequirementsHeader }}</span>
|
||||
<ul>
|
||||
{{ range $key, $value := .requirements }}
|
||||
<li class="" id="requirement-{{ $key }}" min="{{ $value }}">
|
||||
|
@ -50,6 +50,8 @@
|
||||
"errorCaptcha": "Captcha incorrect.",
|
||||
"errorPassword": "Check password requirements.",
|
||||
"errorNoMatch": "Passwords don't match.",
|
||||
"errorOldPassword": "Old password incorrect.",
|
||||
"passwordChanged": "Password Changed.",
|
||||
"verified": "Account verified."
|
||||
},
|
||||
"validationStrings": {
|
||||
|
@ -408,3 +408,8 @@ const (
|
||||
type GetMyPINDTO struct {
|
||||
PIN string `json:"pin"`
|
||||
}
|
||||
|
||||
type ChangeMyPasswordDTO struct {
|
||||
Old string `json:"old"`
|
||||
New string `json:"new"`
|
||||
}
|
||||
|
@ -241,6 +241,7 @@ func (app *appContext) loadRoutes(router *gin.Engine) {
|
||||
user.DELETE(p+"/discord", app.UnlinkMyDiscord)
|
||||
user.DELETE(p+"/telegram", app.UnlinkMyTelegram)
|
||||
user.DELETE(p+"/matrix", app.UnlinkMyMatrix)
|
||||
user.POST(p+"/password", app.ChangeMyPassword)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
13
ts/form.ts
13
ts/form.ts
@ -2,7 +2,7 @@ import { Modal } from "./modules/modal.js";
|
||||
import { notificationBox, whichAnimationEvent } from "./modules/common.js";
|
||||
import { _get, _post, toggleLoader, addLoader, removeLoader, toDateString } from "./modules/common.js";
|
||||
import { loadLangSelector } from "./modules/lang.js";
|
||||
import { Validator, ValidatorConf } from "./modules/validator.js";
|
||||
import { Validator, ValidatorConf, ValidatorRespDTO } from "./modules/validator.js";
|
||||
import { Discord, Telegram, Matrix, ServiceConfiguration, MatrixConfiguration } from "./modules/account-linking.js";
|
||||
|
||||
interface formWindow extends Window {
|
||||
@ -257,11 +257,6 @@ if (window.emailRequired) {
|
||||
emailField.addEventListener("keyup", validator.validate)
|
||||
}
|
||||
|
||||
interface respDTO {
|
||||
response: boolean;
|
||||
error: string;
|
||||
}
|
||||
|
||||
interface sendDTO {
|
||||
code: string;
|
||||
email: string;
|
||||
@ -340,11 +335,11 @@ const create = (event: SubmitEvent) => {
|
||||
}
|
||||
_post("/newUser", send, (req: XMLHttpRequest) => {
|
||||
if (req.readyState == 4) {
|
||||
let vals = req.response as respDTO;
|
||||
let vals = req.response as ValidatorRespDTO;
|
||||
let valid = true;
|
||||
for (let type in vals) {
|
||||
if (requirements[type]) { requirements[type].valid = vals[type]; }
|
||||
if (!vals[type]) { valid = false; }
|
||||
if (requirements[type]) requirements[type].valid = vals[type];
|
||||
if (!vals[type]) valid = false;
|
||||
}
|
||||
if (req.status == 200 && valid) {
|
||||
if (window.redirectToJellyfin == true) {
|
||||
|
@ -9,6 +9,11 @@ interface pwValString {
|
||||
plural: string;
|
||||
}
|
||||
|
||||
export interface ValidatorRespDTO {
|
||||
response: boolean;
|
||||
error: string;
|
||||
}
|
||||
|
||||
interface pwValStrings {
|
||||
length: pwValString;
|
||||
uppercase: pwValString;
|
||||
|
30
ts/user.ts
30
ts/user.ts
@ -1,10 +1,10 @@
|
||||
import { ThemeManager } from "./modules/theme.js";
|
||||
import { lang, LangFile, loadLangSelector } from "./modules/lang.js";
|
||||
import { Modal } from "./modules/modal.js";
|
||||
import { _get, _post, _delete, notificationBox, whichAnimationEvent, toDateString, toggleLoader } from "./modules/common.js";
|
||||
import { _get, _post, _delete, notificationBox, whichAnimationEvent, toDateString, toggleLoader, addLoader, removeLoader } from "./modules/common.js";
|
||||
import { Login } from "./modules/login.js";
|
||||
import { Discord, Telegram, Matrix, ServiceConfiguration, MatrixConfiguration } from "./modules/account-linking.js";
|
||||
import { Validator, ValidatorConf } from "./modules/validator.js";
|
||||
import { Validator, ValidatorConf, ValidatorRespDTO } from "./modules/validator.js";
|
||||
|
||||
interface userWindow extends Window {
|
||||
jellyfinID: string;
|
||||
@ -385,7 +385,7 @@ const matrixConf: MatrixConfiguration = {
|
||||
};
|
||||
|
||||
let matrix = new Matrix(matrixConf);
|
||||
|
||||
|
||||
|
||||
const oldPasswordField = document.getElementById("user-old-password") as HTMLInputElement;
|
||||
const newPasswordField = document.getElementById("user-new-password") as HTMLInputElement;
|
||||
@ -405,9 +405,31 @@ let validatorConf: ValidatorConf = {
|
||||
};
|
||||
|
||||
let validator = new Validator(validatorConf);
|
||||
let requirements = validator.requirements;
|
||||
// let requirements = validator.requirements;
|
||||
|
||||
oldPasswordField.addEventListener("keyup", validator.validate);
|
||||
changePasswordButton.addEventListener("click", () => {
|
||||
addLoader(changePasswordButton);
|
||||
_post("/my/password", { old: oldPasswordField.value, new: newPasswordField.value }, (req: XMLHttpRequest) => {
|
||||
if (req.readyState != 4) return;
|
||||
removeLoader(changePasswordButton);
|
||||
if (req.status == 400) {
|
||||
window.notifications.customError("errorPassword", window.lang.notif("errorPassword"));
|
||||
} else if (req.status == 500) {
|
||||
window.notifications.customError("errorUnknown", window.lang.notif("errorUnknown"));
|
||||
} else if (req.status == 204) {
|
||||
window.notifications.customSuccess("passwordChanged", window.lang.notif("passwordChanged"));
|
||||
setTimeout(() => { window.location.reload() }, 2000);
|
||||
}
|
||||
}, true, (req: XMLHttpRequest) => {
|
||||
if (req.readyState != 4) return;
|
||||
if (req.status == 401) {
|
||||
removeLoader(changePasswordButton);
|
||||
window.notifications.customError("oldPasswordError", window.lang.notif("errorOldPassword"));
|
||||
return;
|
||||
}
|
||||
});
|
||||
});
|
||||
// FIXME: Submit & Validate
|
||||
|
||||
document.addEventListener("details-reload", () => {
|
||||
|
Loading…
Reference in New Issue
Block a user