mirror of
https://github.com/hrfee/jfa-go.git
synced 2024-12-22 09:00:10 +00:00
PWR: Add option to set new password from magic link
For #103. Enable in Settings > Password Resets. Also changes the user's ombi password.
This commit is contained in:
parent
0014db44f0
commit
0a71d5b216
73
api.go
73
api.go
@ -1383,6 +1383,77 @@ func (app *appContext) ModifyEmails(gc *gin.Context) {
|
||||
respondBool(200, true, gc)
|
||||
}
|
||||
|
||||
// @Summary Resets a user's password with a PIN, and optionally set a new password if given.
|
||||
// @Produce json
|
||||
// @Success 200 {object} boolResponse
|
||||
// @Success 400 {object} PasswordValidation
|
||||
// @Failure 500 {object} boolResponse
|
||||
// @Param ResetPasswordDTO body ResetPasswordDTO true "Pin and optional Password."
|
||||
// @Router /reset [post]
|
||||
// @tags Other
|
||||
func (app *appContext) ResetSetPassword(gc *gin.Context) {
|
||||
var req ResetPasswordDTO
|
||||
gc.BindJSON(&req)
|
||||
validation := app.validator.validate(req.Password)
|
||||
valid := true
|
||||
for _, val := range validation {
|
||||
if !val {
|
||||
valid = false
|
||||
}
|
||||
}
|
||||
if !valid || req.PIN == "" {
|
||||
// 200 bcs idk what i did in js
|
||||
app.info.Printf("%s: Password reset failed: Invalid password", req.PIN)
|
||||
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
|
||||
}
|
||||
if req.Password == "" || len(resp.UsersReset) == 0 {
|
||||
respondBool(200, false, gc)
|
||||
return
|
||||
}
|
||||
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)
|
||||
respondBool(500, false, gc)
|
||||
return
|
||||
}
|
||||
status, err = app.jf.SetPassword(user.ID, req.PIN, 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)
|
||||
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)
|
||||
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)
|
||||
respondBool(200, true, gc)
|
||||
return
|
||||
}
|
||||
ombiUser["password"] = req.Password
|
||||
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(200, true, gc)
|
||||
return
|
||||
}
|
||||
app.debug.Printf("Reset password for ombi user \"%s\"", ombiUser["userName"])
|
||||
}
|
||||
respondBool(200, true, gc)
|
||||
}
|
||||
|
||||
// @Summary Apply settings to a list of users, either from a profile or from another user.
|
||||
// @Produce json
|
||||
// @Param userSettingsDTO body userSettingsDTO true "Parameters for applying settings"
|
||||
@ -2123,7 +2194,7 @@ func (app *appContext) TelegramAddUser(gc *gin.Context) {
|
||||
respondBool(200, true, gc)
|
||||
}
|
||||
|
||||
// @Summary Sets whether to notify a user through telegram or not.
|
||||
// @Summary Sets whether to notify a user through telegram/discord/matrix/email or not.
|
||||
// @Produce json
|
||||
// @Param SetContactMethodsDTO body SetContactMethodsDTO true "User's Jellyfin ID and whether or not to notify then through Telegram."
|
||||
// @Success 200 {object} boolResponse
|
||||
|
@ -784,6 +784,15 @@
|
||||
"value": false,
|
||||
"description": "Send users a link to reset their password instead of a PIN. Must be enabled to reset Ombi password at the same time as the Jellyfin password."
|
||||
},
|
||||
"set_password": {
|
||||
"name": "Set password through link",
|
||||
"required": false,
|
||||
"requires_restart": true,
|
||||
"depends_true": "link_reset",
|
||||
"type": "bool",
|
||||
"value": false,
|
||||
"description": "Instead of automatically setting the user's password to the PIN, allow them to set a new password through the reset link."
|
||||
},
|
||||
"language": {
|
||||
"name": "Default reset link language",
|
||||
"required": false,
|
||||
@ -1235,6 +1244,14 @@
|
||||
"value": "",
|
||||
"description": "Location of stored invites (json)."
|
||||
},
|
||||
"password_resets": {
|
||||
"name": "Password Resets",
|
||||
"required": false,
|
||||
"requires_restart": true,
|
||||
"type": "text",
|
||||
"value": "",
|
||||
"description": "Location of stored non-Jellyfin password resets (json)."
|
||||
},
|
||||
"emails": {
|
||||
"name": "Email Addresses",
|
||||
"required": false,
|
||||
|
18
go.mod
18
go.mod
@ -10,21 +10,22 @@ replace github.com/hrfee/jfa-go/ombi => ./ombi
|
||||
|
||||
replace github.com/hrfee/jfa-go/logger => ./logger
|
||||
|
||||
replace github.com/hrfee/mediabrowser => ../mediabrowser
|
||||
|
||||
require (
|
||||
github.com/bwmarrin/discordgo v0.23.2 // indirect
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.0 // indirect
|
||||
github.com/bwmarrin/discordgo v0.23.2
|
||||
github.com/dgrijalva/jwt-go v3.2.0+incompatible
|
||||
github.com/emersion/go-autostart v0.0.0-20210130080809-00ed301c8e9a // indirect
|
||||
github.com/emersion/go-autostart v0.0.0-20210130080809-00ed301c8e9a
|
||||
github.com/fatih/color v1.10.0
|
||||
github.com/fsnotify/fsnotify v1.4.9
|
||||
github.com/getlantern/systray v1.1.0 // indirect
|
||||
github.com/getlantern/systray v1.1.0
|
||||
github.com/gin-contrib/pprof v1.3.0
|
||||
github.com/gin-contrib/static v0.0.0-20200916080430-d45d9a37d28e
|
||||
github.com/gin-gonic/gin v1.6.3
|
||||
github.com/go-openapi/spec v0.20.3 // indirect
|
||||
github.com/go-openapi/swag v0.19.15 // indirect
|
||||
github.com/go-playground/validator/v10 v10.4.1 // indirect
|
||||
github.com/go-telegram-bot-api/telegram-bot-api v4.6.4+incompatible // indirect
|
||||
github.com/go-telegram-bot-api/telegram-bot-api v4.6.4+incompatible
|
||||
github.com/golang/protobuf v1.4.3 // indirect
|
||||
github.com/gomarkdown/markdown v0.0.0-20210408062403-ad838ccf8cdd
|
||||
github.com/google/uuid v1.1.2 // indirect
|
||||
@ -32,15 +33,14 @@ require (
|
||||
github.com/hrfee/jfa-go/docs v0.0.0-20201112212552-b6f3cd7c1f71
|
||||
github.com/hrfee/jfa-go/logger v0.0.0-00010101000000-000000000000
|
||||
github.com/hrfee/jfa-go/ombi v0.0.0-20201112212552-b6f3cd7c1f71
|
||||
github.com/hrfee/mediabrowser v0.3.3
|
||||
github.com/hrfee/mediabrowser v0.0.0-00010101000000-000000000000
|
||||
github.com/itchyny/timefmt-go v0.1.2
|
||||
github.com/jordan-wright/email v4.0.1-0.20210109023952-943e75fe5223+incompatible
|
||||
github.com/lithammer/shortuuid/v3 v3.0.4
|
||||
github.com/mailgun/mailgun-go/v4 v4.5.1
|
||||
github.com/mailru/easyjson v0.7.7 // indirect
|
||||
github.com/matrix-org/gomatrix v0.0.0-20210324163249-be2af5ef2e16 // indirect
|
||||
github.com/russross/blackfriday/v2 v2.1.0 // indirect
|
||||
github.com/skratchdot/open-golang v0.0.0-20200116055534-eef842397966 // indirect
|
||||
github.com/matrix-org/gomatrix v0.0.0-20210324163249-be2af5ef2e16
|
||||
github.com/skratchdot/open-golang v0.0.0-20200116055534-eef842397966
|
||||
github.com/smartystreets/goconvey v1.6.4 // indirect
|
||||
github.com/swaggo/files v0.0.0-20190704085106-630677cd5c14
|
||||
github.com/swaggo/gin-swagger v1.3.0
|
||||
|
29
go.sum
29
go.sum
@ -19,8 +19,6 @@ github.com/client9/misspell v0.3.4 h1:ta993UF76GwbvJcIo3Y68y/M3WxlpEHPWIGDkJYwzJ
|
||||
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
|
||||
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/cpuguy83/go-md2man/v2 v2.0.0 h1:EoUDS0afbrsXAZ9YQ9jdu/mZ2sXgT1/2yyNng4PGlyM=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
|
||||
github.com/creack/pty v1.1.9 h1:uDmaGzcdjhF4i/plgjmEsriH11Y0o7RKapEf/LDaM3w=
|
||||
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
@ -152,8 +150,6 @@ github.com/gorilla/mux v1.8.0 h1:i40aqfkR1h2SlN9hojwV5ZA91wcXFOvkdNIeFDP5koI=
|
||||
github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So=
|
||||
github.com/gorilla/websocket v1.4.0 h1:WDFjx/TMzVgy9VdMMQi2K2Emtwi2QcUQsztZ/zLaH/Q=
|
||||
github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=
|
||||
github.com/hrfee/mediabrowser v0.3.3 h1:7E05uiol8hh2ytKn3WVLrUIvHAyifYEIy3Y5qtuNh8I=
|
||||
github.com/hrfee/mediabrowser v0.3.3/go.mod h1:PnHZbdxmbv1wCVdAQyM7nwPwpVj9fdKx2EcET7sAk+U=
|
||||
github.com/itchyny/timefmt-go v0.1.2 h1:q0Xa4P5it6K6D7ISsbLAMwx1PnWlixDcJL6/sFs93Hs=
|
||||
github.com/itchyny/timefmt-go v0.1.2/go.mod h1:0osSSCQSASBJMsIZnhAaF1C2fCBTJZXrnj37mG8/c+A=
|
||||
github.com/jordan-wright/email v4.0.1-0.20210109023952-943e75fe5223+incompatible h1:jdpOPRN1zP63Td1hDQbZW73xKmzDvZHzVdNYxhnTMDA=
|
||||
@ -217,8 +213,6 @@ github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4 h1:gQz4mCb
|
||||
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
||||
github.com/russross/blackfriday/v2 v2.0.1 h1:lPqVAte+HuHNfhJ/0LC98ESWRz8afy9tM/0RK8m9o+Q=
|
||||
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 h1:0uYX9dsZ2yD7q2RtLRtPSdGDWzjeM3TbMJP9utgA0ww=
|
||||
github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0=
|
||||
github.com/shurcooL/sanitized_anchor_name v1.0.0 h1:PdmoCO6wvbs+7yrJyMORt4/BmY5IYyJwS/kOiWx8mHo=
|
||||
@ -267,7 +261,6 @@ 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=
|
||||
github.com/yuin/goldmark v1.2.1 h1:ruQGxdhGHe7FWOJPT0mKs5+pD2Xs1Bm/kdGlHO04FmM=
|
||||
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/goldmark v1.3.5 h1:dPmz1Snjq0kmkz159iL7S6WzdahUTHnHB5M56WFVifs=
|
||||
github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
|
||||
@ -286,7 +279,6 @@ golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTk
|
||||
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
|
||||
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3 h1:XQyxROzUlZH+WIQwySDgnISgOivlhjIEwaQaJEJrrN0=
|
||||
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||
golang.org/x/mod v0.3.0 h1:RM4zey1++hCTbCVQfnWeKs9/IEsaBLA8vTkd0WVtmH4=
|
||||
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.4.2 h1:Gz96sIWK3OalVv/I/qNygP42zyoKp3xptRVCWRFEBvo=
|
||||
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
@ -305,13 +297,7 @@ golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297/go.mod h1:z5CRVTTTmAJ677TzLL
|
||||
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||
golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||
golang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||
golang.org/x/net v0.0.0-20210331212208-0fccb6fa2b5c h1:KHUzaHIpjWVlVVNh65G3hhuj3KB1HnjY6Cq5cTvRQT8=
|
||||
golang.org/x/net v0.0.0-20210331212208-0fccb6fa2b5c/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
|
||||
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
|
||||
golang.org/x/net v0.0.0-20210510120150-4163338589ed h1:p9UgmWI9wKpfYmgaV/IZKGdXc5qEK45tDwwwDyjS26I=
|
||||
golang.org/x/net v0.0.0-20210510120150-4163338589ed/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/net v0.0.0-20210521195947-fe42d452be8f h1:Si4U+UcgJzya9kpiEUJKQvjr512OLli+gL4poHrz93U=
|
||||
golang.org/x/net v0.0.0-20210521195947-fe42d452be8f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/net v0.0.0-20210525063256-abc453219eb5 h1:wjuX4b5yYQnEQHzd+CBcrcC6OVR2J1CN6mUy0oSxIPo=
|
||||
golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be h1:vEDujvNQGv4jgYKudGeI/+DAX4Jffq6hpD55MmoEvKs=
|
||||
@ -320,7 +306,6 @@ golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJ
|
||||
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9 h1:SQFwaSi55rU7vdNs9Yr0Z324VNlrF+0wMqRXT4St8ck=
|
||||
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c h1:5KslGYwFpkhGh+Q16bwMP3cOontH8FOep7tGV86Y7SQ=
|
||||
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
@ -338,18 +323,9 @@ golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7w
|
||||
golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210331175145-43e1dd70ce54 h1:rF3Ohx8DRyl8h2zw9qojyLHLhrJpEMgyPOImREEryf0=
|
||||
golang.org/x/sys v0.0.0-20210331175145-43e1dd70ce54/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210514084401-e8d321eab015 h1:hZR0X1kPW+nwyJ9xRxqZk1vx5RUObAPBdKVvXPDUH/E=
|
||||
golang.org/x/sys v0.0.0-20210514084401-e8d321eab015/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210521203332-0cec03c779c1 h1:lCnv+lfrU9FRPGf8NeRuWAAPjNnema5WtBinMgs1fD8=
|
||||
golang.org/x/sys v0.0.0-20210521203332-0cec03c779c1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210525143221-35b2ab0089ea h1:+WiDlPBBaO+h9vPNZi8uJ3k4BkKQB7Iow3aqwHVA5hI=
|
||||
golang.org/x/sys v0.0.0-20210525143221-35b2ab0089ea/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210531080801-fdfd190a6549 h1:OL5GcZ2XPkte3dpfuFQ9o884vrE3BZQhajdntNMruv4=
|
||||
golang.org/x/sys v0.0.0-20210531080801-fdfd190a6549/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1 h1:v+OssWQX+hTHEmOBgwxdZxK4zHq3yOs8F9J7mk0PY8E=
|
||||
@ -358,7 +334,6 @@ golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.5 h1:i6eZZ+zk0SOf0xgBpEpPD18qWcJda6q1sxt3S0kzyUQ=
|
||||
golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.6 h1:aRYxNxv6iGQlyVaZmk6ZgYEDa+Jg18DxebPSrd6bg1M=
|
||||
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
@ -373,10 +348,6 @@ golang.org/x/tools v0.0.0-20190611222205-d73e1c7e250b/go.mod h1:/rFqwRUd4F7ZHNgw
|
||||
golang.org/x/tools v0.0.0-20190614205625-5aca471b1d59/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
|
||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20201120155355-20be4ac4bd6e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||
golang.org/x/tools v0.1.0 h1:po9/4sTYwZU9lPhi1tOrb4hCv3qrhiQ77LZfGa2OjwY=
|
||||
golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0=
|
||||
golang.org/x/tools v0.1.1 h1:wGiQel/hW0NnEkJUk8lbzkX2gFJU6PFxf1v5OlCfuOs=
|
||||
golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
|
||||
golang.org/x/tools v0.1.2 h1:kRBLX7v7Af8W7Gdbbc908OJcdgtK8bOz9Uaj8/F1ACA=
|
||||
golang.org/x/tools v0.1.2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
|
@ -26,5 +26,9 @@
|
||||
window.matrixRequired = {{ .matrixRequired }};
|
||||
window.matrixUserID = "{{ .matrixUser }}";
|
||||
</script>
|
||||
{{ if .passwordReset }}
|
||||
<script src="js/pwr.js" type="module"></script>
|
||||
{{ else }}
|
||||
<script src="js/form.js" type="module"></script>
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
|
@ -3,7 +3,13 @@
|
||||
<head>
|
||||
<link rel="stylesheet" type="text/css" href="css/bundle.css">
|
||||
{{ template "header.html" . }}
|
||||
<title>{{ .strings.pageTitle }}</title>
|
||||
<title>
|
||||
{{ if .passwordReset }}
|
||||
{{ .strings.passwordReset }}
|
||||
{{ else }}
|
||||
{{ .strings.pageTitle }}
|
||||
{{ end }}
|
||||
</title>
|
||||
</head>
|
||||
<body class="max-w-full overflow-x-hidden section">
|
||||
<div id="modal-success" class="modal">
|
||||
@ -80,8 +86,20 @@
|
||||
<div class="page-container">
|
||||
<div class="card ~neutral !low">
|
||||
<div class="row baseline">
|
||||
<span class="col heading">{{ .strings.createAccountHeader }}</span>
|
||||
<span class="col subheading"> {{ .helpMessage }}</span>
|
||||
<span class="col heading">
|
||||
{{ if .passwordReset }}
|
||||
{{ .strings.passwordReset }}
|
||||
{{ else }}
|
||||
{{ .strings.createAccountHeader }}
|
||||
{{ end }}
|
||||
</span>
|
||||
<span class="col subheading">
|
||||
{{ if .passwordReset }}
|
||||
{{ .strings.enterYourPassword }}
|
||||
{{ else }}
|
||||
{{ .helpMessage }}
|
||||
{{ end }}
|
||||
</span>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
@ -89,6 +107,7 @@
|
||||
<aside class="col aside sm ~warning" id="user-expiry-message"></aside>
|
||||
{{ end }}
|
||||
<form class="card ~neutral !normal" id="form-create" href="">
|
||||
{{ if not .passwordReset }}
|
||||
<label class="label supra">
|
||||
{{ .strings.username }}
|
||||
<input type="text" class="input ~neutral !high mt-half mb-1" placeholder="{{ .strings.username }}" id="create-username" aria-label="{{ .strings.username }}">
|
||||
@ -127,6 +146,7 @@
|
||||
{{ end }}
|
||||
</div>
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
<label class="label supra" for="create-password">{{ .strings.password }}</label>
|
||||
<input type="password" class="input ~neutral !high mt-half mb-1" placeholder="{{ .strings.password }}" id="create-password" aria-label="{{ .strings.password }}">
|
||||
|
||||
@ -134,7 +154,13 @@
|
||||
<input type="password" class="input ~neutral !high mt-half mb-1" placeholder="{{ .strings.password }}" id="create-reenter-password" aria-label="{{ .strings.reEnterPassword }}">
|
||||
<label>
|
||||
<input type="submit" class="unfocused">
|
||||
<span class="button ~urge !normal full-width center supra submit">{{ .strings.createAccountButton }}</span>
|
||||
<span class="button ~urge !normal full-width center supra submit">
|
||||
{{ if .passwordReset }}
|
||||
{{ .strings.reset }}
|
||||
{{ else }}
|
||||
{{ .strings.createAccountButton }}
|
||||
{{ end }}
|
||||
</span>
|
||||
</label>
|
||||
</form>
|
||||
</div>
|
||||
@ -159,4 +185,3 @@
|
||||
{{ template "form-base" . }}
|
||||
</body>
|
||||
</html>
|
||||
|
||||
|
@ -40,6 +40,6 @@
|
||||
</div>
|
||||
<i class="content">{{ .contactMessage }}</i>
|
||||
</div>
|
||||
<script src="{{ .urlBase }}/js/pwr.js" type="module"></script>
|
||||
<script src="{{ .urlBase }}/js/pwr-pin.js" type="module"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
@ -4,10 +4,12 @@
|
||||
},
|
||||
"strings": {
|
||||
"passwordReset": "Password reset",
|
||||
"reset": "Reset",
|
||||
"resetFailed": "Password reset failed",
|
||||
"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.",
|
||||
"changeYourPassword": "Make sure to change your password after you log in."
|
||||
"changeYourPassword": "Make sure to change your password after you log in.",
|
||||
"enterYourPassword": "Enter your new password below."
|
||||
}
|
||||
}
|
||||
|
@ -305,3 +305,8 @@ type MatrixLoginDTO struct {
|
||||
Username string `json:"username"`
|
||||
Password string `json:"password"`
|
||||
}
|
||||
|
||||
type ResetPasswordDTO struct {
|
||||
PIN string `json:"pin"`
|
||||
Password string `json:"password"`
|
||||
}
|
||||
|
@ -108,6 +108,9 @@ func (app *appContext) loadRoutes(router *gin.Engine) {
|
||||
|
||||
if app.config.Section("password_resets").Key("link_reset").MustBool(false) {
|
||||
router.GET(p+"/reset", app.ResetPassword)
|
||||
if app.config.Section("password_resets").Key("set_password").MustBool(false) {
|
||||
router.POST(p+"/reset", app.ResetSetPassword)
|
||||
}
|
||||
}
|
||||
|
||||
router.GET(p+"/accounts", app.AdminPage)
|
||||
|
83
storage.go
83
storage.go
@ -917,86 +917,3 @@ func storeJSON(path string, obj interface{}) error {
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
// // One build of JF 10.7.0 hyphenated user IDs while another one later didn't. These functions will hyphenate/de-hyphenate email storage.
|
||||
//
|
||||
// func hyphenate(userID string) string {
|
||||
// if userID[8] == '-' {
|
||||
// return userID
|
||||
// }
|
||||
// return userID[:8] + "-" + userID[8:12] + "-" + userID[12:16] + "-" + userID[16:20] + "-" + userID[20:]
|
||||
// }
|
||||
//
|
||||
// func (app *appContext) deHyphenateStorage(old map[string]interface{}) (map[string]interface{}, int, error) {
|
||||
// jfUsers, status, err := app.jf.GetUsers(false)
|
||||
// if status != 200 || err != nil {
|
||||
// return nil, status, err
|
||||
// }
|
||||
// newEmails := map[string]interface{}{}
|
||||
// for _, user := range jfUsers {
|
||||
// unHyphenated := user.ID
|
||||
// hyphenated := hyphenate(unHyphenated)
|
||||
// val, ok := old[hyphenated]
|
||||
// if ok {
|
||||
// newEmails[unHyphenated] = val
|
||||
// }
|
||||
// }
|
||||
// return newEmails, status, err
|
||||
// }
|
||||
//
|
||||
// func (app *appContext) hyphenateStorage(old map[string]interface{}) (map[string]interface{}, int, error) {
|
||||
// jfUsers, status, err := app.jf.GetUsers(false)
|
||||
// if status != 200 || err != nil {
|
||||
// return nil, status, err
|
||||
// }
|
||||
// newEmails := map[string]interface{}{}
|
||||
// for _, user := range jfUsers {
|
||||
// unstripped := user.ID
|
||||
// stripped := strings.ReplaceAll(unstripped, "-", "")
|
||||
// val, ok := old[stripped]
|
||||
// if ok {
|
||||
// newEmails[unstripped] = val
|
||||
// }
|
||||
// }
|
||||
// return newEmails, status, err
|
||||
// }
|
||||
//
|
||||
// func (app *appContext) hyphenateEmailStorage(old map[string]interface{}) (map[string]interface{}, int, error) {
|
||||
// return app.hyphenateStorage(old)
|
||||
// }
|
||||
//
|
||||
// func (app *appContext) deHyphenateEmailStorage(old map[string]interface{}) (map[string]interface{}, int, error) {
|
||||
// return app.deHyphenateStorage(old)
|
||||
// }
|
||||
//
|
||||
// func (app *appContext) hyphenateUserStorage(old map[string]time.Time) (map[string]time.Time, int, error) {
|
||||
// asInterface := map[string]interface{}{}
|
||||
// for k, v := range old {
|
||||
// asInterface[k] = v
|
||||
// }
|
||||
// fixed, status, err := app.hyphenateStorage(asInterface)
|
||||
// if err != nil {
|
||||
// return nil, status, err
|
||||
// }
|
||||
// out := map[string]time.Time{}
|
||||
// for k, v := range fixed {
|
||||
// out[k] = v.(time.Time)
|
||||
// }
|
||||
// return out, status, err
|
||||
// }
|
||||
//
|
||||
// func (app *appContext) deHyphenateUserStorage(old map[string]time.Time) (map[string]time.Time, int, error) {
|
||||
// asInterface := map[string]interface{}{}
|
||||
// for k, v := range old {
|
||||
// asInterface[k] = v
|
||||
// }
|
||||
// fixed, status, err := app.deHyphenateStorage(asInterface)
|
||||
// if err != nil {
|
||||
// return nil, status, err
|
||||
// }
|
||||
// out := map[string]time.Time{}
|
||||
// for k, v := range fixed {
|
||||
// out[k] = v.(time.Time)
|
||||
// }
|
||||
// return out, status, err
|
||||
// }
|
||||
|
23
ts/pwr-pin.ts
Normal file
23
ts/pwr-pin.ts
Normal file
@ -0,0 +1,23 @@
|
||||
import { toClipboard, notificationBox } from "./modules/common.js";
|
||||
|
||||
const pin = document.getElementById("pin") as HTMLSpanElement;
|
||||
|
||||
if (pin) {
|
||||
// Load this individual string into the DOM, so we don't have to load the whole language file.
|
||||
const copy = document.getElementById("copy-notification");
|
||||
const copyString = copy.textContent;
|
||||
copy.remove();
|
||||
|
||||
window.notifications = new notificationBox(document.getElementById("notification-box") as HTMLDivElement, 5);
|
||||
|
||||
pin.onclick = () => {
|
||||
toClipboard(pin.textContent);
|
||||
window.notifications.customPositive("copied", "", copyString);
|
||||
pin.classList.add("~positive");
|
||||
pin.classList.remove("~urge");
|
||||
setTimeout(() => {
|
||||
pin.classList.add("~urge");
|
||||
pin.classList.remove("~positive");
|
||||
}, 5000);
|
||||
};
|
||||
}
|
100
ts/pwr.ts
100
ts/pwr.ts
@ -1,24 +1,80 @@
|
||||
import { toClipboard, notificationBox } from "./modules/common.js";
|
||||
import { Modal } from "./modules/modal.js";
|
||||
import { initValidator } from "./modules/validator.js";
|
||||
import { _post, addLoader, removeLoader } from "./modules/common.js";
|
||||
|
||||
|
||||
const pin = document.getElementById("pin") as HTMLSpanElement;
|
||||
|
||||
if (pin) {
|
||||
// Load this individual string into the DOM, so we don't have to load the whole language file.
|
||||
const copy = document.getElementById("copy-notification");
|
||||
const copyString = copy.textContent;
|
||||
copy.remove();
|
||||
|
||||
window.notifications = new notificationBox(document.getElementById("notification-box") as HTMLDivElement, 5);
|
||||
|
||||
pin.onclick = () => {
|
||||
toClipboard(pin.textContent);
|
||||
window.notifications.customPositive("copied", "", copyString);
|
||||
pin.classList.add("~positive");
|
||||
pin.classList.remove("~urge");
|
||||
setTimeout(() => {
|
||||
pin.classList.add("~urge");
|
||||
pin.classList.remove("~positive");
|
||||
}, 5000);
|
||||
};
|
||||
interface formWindow extends Window {
|
||||
invalidPassword: string;
|
||||
successModal: Modal;
|
||||
telegramModal: Modal;
|
||||
discordModal: Modal;
|
||||
matrixModal: Modal;
|
||||
confirmationModal: Modal
|
||||
code: string;
|
||||
messages: { [key: string]: string };
|
||||
confirmation: boolean;
|
||||
telegramRequired: boolean;
|
||||
telegramPIN: string;
|
||||
discordRequired: boolean;
|
||||
discordPIN: string;
|
||||
discordStartCommand: string;
|
||||
discordInviteLink: boolean;
|
||||
discordServerName: string;
|
||||
matrixRequired: boolean;
|
||||
matrixUserID: string;
|
||||
userExpiryEnabled: boolean;
|
||||
userExpiryMonths: number;
|
||||
userExpiryDays: number;
|
||||
userExpiryHours: number;
|
||||
userExpiryMinutes: number;
|
||||
userExpiryMessage: string;
|
||||
}
|
||||
|
||||
declare var window: formWindow;
|
||||
|
||||
const form = document.getElementById("form-create") as HTMLFormElement;
|
||||
const submitButton = form.querySelector("input[type=submit]") as HTMLInputElement;
|
||||
const submitSpan = form.querySelector("span.submit") as HTMLSpanElement;
|
||||
const passwordField = document.getElementById("create-password") as HTMLInputElement;
|
||||
const rePasswordField = document.getElementById("create-reenter-password") as HTMLInputElement;
|
||||
|
||||
window.successModal = new Modal(document.getElementById("modal-success"), true);
|
||||
|
||||
var requirements = initValidator(passwordField, rePasswordField, submitButton, submitSpan)
|
||||
|
||||
interface sendDTO {
|
||||
pin: string;
|
||||
password: string;
|
||||
}
|
||||
|
||||
form.onsubmit = (event: Event) => {
|
||||
event.preventDefault();
|
||||
addLoader(submitSpan);
|
||||
const params = new URLSearchParams(window.location.search);
|
||||
let send: sendDTO = {
|
||||
pin: params.get("pin"),
|
||||
password: passwordField.value
|
||||
};
|
||||
_post("/reset", send, (req: XMLHttpRequest) => {
|
||||
if (req.readyState == 4) {
|
||||
removeLoader(submitSpan);
|
||||
if (req.status == 400) {
|
||||
for (let type in req.response) {
|
||||
if (requirements[type]) { requirements[type].valid = req.response[type] as boolean; }
|
||||
}
|
||||
return;
|
||||
} else if (req.status != 200) {
|
||||
const old = submitSpan.textContent;
|
||||
submitSpan.textContent = window.messages["errorUnknown"];
|
||||
submitSpan.classList.add("~critical");
|
||||
submitSpan.classList.remove("~urge");
|
||||
setTimeout(() => {
|
||||
submitSpan.classList.add("~urge");
|
||||
submitSpan.classList.remove("~critical");
|
||||
submitSpan.textContent = old;
|
||||
}, 2000);
|
||||
} else {
|
||||
window.successModal.show();
|
||||
}
|
||||
}
|
||||
}, true);
|
||||
};
|
||||
|
20
views.go
20
views.go
@ -139,6 +139,7 @@ func (app *appContext) AdminPage(gc *gin.Context) {
|
||||
|
||||
func (app *appContext) ResetPassword(gc *gin.Context) {
|
||||
isBot := strings.Contains(gc.Request.Header.Get("User-Agent"), "Bot")
|
||||
setPassword := app.config.Section("password_resets").Key("set_password").MustBool(false)
|
||||
pin := gc.Query("pin")
|
||||
if pin == "" {
|
||||
app.NoRouteHandler(gc)
|
||||
@ -148,11 +149,29 @@ func (app *appContext) ResetPassword(gc *gin.Context) {
|
||||
lang := app.getLang(gc, PWRPage, app.storage.lang.chosenPWRLang)
|
||||
data := gin.H{
|
||||
"urlBase": app.getURLBase(gc),
|
||||
"cssClass": app.cssClass,
|
||||
"contactMessage": app.config.Section("ui").Key("contact_message").String(),
|
||||
"strings": app.storage.lang.PasswordReset[lang].Strings,
|
||||
"success": false,
|
||||
"ombiEnabled": app.config.Section("ombi").Key("enabled").MustBool(false),
|
||||
}
|
||||
if setPassword {
|
||||
data["helpMessage"] = app.config.Section("ui").Key("help_message").String()
|
||||
data["successMessage"] = app.config.Section("ui").Key("success_message").String()
|
||||
data["jfLink"] = app.config.Section("jellyfin").Key("public_server").String()
|
||||
data["validate"] = app.config.Section("password_validation").Key("enabled").MustBool(false)
|
||||
data["requirements"] = app.validator.getCriteria()
|
||||
data["strings"] = app.storage.lang.PasswordReset[lang].Strings
|
||||
data["validationStrings"] = app.storage.lang.Form[lang].validationStringsJSON
|
||||
data["notifications"] = app.storage.lang.Form[lang].notificationsJSON
|
||||
data["langName"] = lang
|
||||
data["passwordReset"] = true
|
||||
data["telegramEnabled"] = false
|
||||
data["discordEnabled"] = false
|
||||
data["matrixEnabled"] = false
|
||||
gcHTML(gc, http.StatusOK, "form-loader.html", data)
|
||||
return
|
||||
}
|
||||
defer gcHTML(gc, http.StatusOK, "password-reset.html", data)
|
||||
// If it's a bot, pretend to be a success so the preview is nice.
|
||||
if isBot {
|
||||
@ -294,6 +313,7 @@ func (app *appContext) InviteProxy(gc *gin.Context) {
|
||||
"userExpiryMinutes": inv.UserMinutes,
|
||||
"userExpiryMessage": app.storage.lang.Form[lang].Strings.get("yourAccountIsValidUntil"),
|
||||
"langName": lang,
|
||||
"passwordReset": false,
|
||||
"telegramEnabled": telegramEnabled,
|
||||
"discordEnabled": discordEnabled,
|
||||
"matrixEnabled": matrixEnabled,
|
||||
|
Loading…
Reference in New Issue
Block a user